Using Discriminated Entities When the Base Class Has No Knowledge of Child Classes

Discriminated entities are powerful in Quick, and allow you to create subclasses through simple changes to your entity definitions. However, one caveat is that the base class must know all of the possible discriminated entities:

// Array of all possible subclass entities
variables._discriminators = [
    "BookMedia",
    "MusicMedia"
];

What if the base class has no knowledge of the possible child entities? For example, lets say you had a MaintenanceTask base entity, and the individual subclasses that extend the base class were established via the Coldbox configuration?

I thought of a possible solution, but it isn’t working out of the box. My idea was to use the diComplete() method of the base entity to grab the Coldbox settings and dynamically assemble the _discrimintators value. That way, all possible subclasses “register” themselves whenever the base class initializes.

Here’s what that approach might look like:

// MaintenanceTask.cfc (Base class for all maintenance tasks)

// list of discriminators comes from settings
property name="settings" inject="coldbox:setting:maintenance" persistent="false";

variables._discriminators = [];  // will be populated

function onDiComplete() {

    super.onDiComplete(); // make sure Quick can do it's thing.    

    // we need to populate the discriminators based on Coldbox settings
    // the settings key 'classList' contains an array of possible subclass entities.
    settings.classList.each( function( item ) {
        variables._discriminators.append( item ) );
    } );

}

The above solution seems to work when all the subclasses can be pulled from the Coldbox config. I wonder if there are any other ways this could be accomplished?

@DaveL I think that’s probably the best approach, though I would suggest appending the discriminators before you call super.onDIComplete(). That way, if anything changes in the implementation detection of parent discriminator entities, it will be able to detect them in the metadata inspection.

@elpete and I went back and forth on that variables._discriminators array when I was implementing it. It turned out to be the best solution to avoid recursion and re-looping Quick entities when they are scanned/registered. As such, dynamically appending is your best bet.

1 Like

One thing I’m not clear on, though. Why would you need to configure those in your Coldbox.cfc? If you declare a discriminated child, but never use that discriminator value, it would never look for it.

@clausen,
I wanted to come up with a way where a MaintenanceTask child entity could be developed separately from the core application (like a plug-in or module). I wanted to use all the benefits of subclassing, but didn’t want to couple the base class to all of its children.

Good tip about reversing the order of my code in diComplete(). I will try that.

@elpete, one issue I discovered when playing around with this is that property casts, don’t seem to work with discriminated entities, specifically the JsonCast@quick.

If I use the class like this, it works:

component 
    extends="quick.models.BaseEntity" 
    accessors="true"
    table="MaintenanceTask" 
{

    property name="data" casts="JsonCast@quick"; 

Output: "data":{"rowsDeleted":0,"targetDate":"June, 25 2023 13:15:59"}

If I use the class like this, it doesn’t:

component 
    extends="quick.models.BaseEntity" 
    accessors="true"
    table="MaintenanceTask" 
    discriminatorColumn="name"
    singleTableInheritance="true"
{

    property name="data" casts="JsonCast@quick"; 

Output: "data":"{\"rowsDeleted\":0,\"targetDate\":\"June, 25 2023 13:17:27\"}"

If this isn’t a simple fix, let me know and I will create an issue on Github.

Yeah, create a ticket please.

1 Like

Is there a solution for getting Casts working again with discriminated entities?
I’m using quick 8.0.2 and just split my Model into subclasses and came across this issue.
My JsonCast’ed values are now being populated in the model as strings rather than being deserialized as they were before.

I have tried moving the properties to the subclass cfc and also creating my own version of the Cast but it doesn’t work. As far as I can see quick isn’t calling the Cast code at all. (I added some systemOutput() to the Cast CFC.

I’ve been trying to follow this conversation but for someone who doesn’t already have a deep understanding of quick it’s very hard to follow.

@Catmacey welcome to the community!

Unfortunately [Quick 7] Casts No Longer Work With Discriminated Entities · Issue #233 · coldbox-modules/quick · GitHub still doesn’t have a fix in place yet. @ryanalbrecht did write a PR for it: Fixed issue where virtualAttributes were not being applied to discriminated child entities when loading through the parent by ryanalbrecht · Pull Request #237 · coldbox-modules/quick · GitHub but it hasn’t been merged.

@elpete, have you had a change to check out the PR to fix this issue yet?

I have worked around the issue myself by using getters and setters to check for the type of data and manually casting them myself. It’s a bit of a PITA, but it’s the only thing I could think of until there’s a fix in place.

@DaveL Thank you very much for the overview.
Getters and setters it is for now then.

@Catmacey, I had another thought as well. You might be able to use the interception points to accomplish the same thing all in one place. For example, the preSave() would be an opportunity to check the data and cast it as needed.

It’s funny because I know I had to solve this problem with one of my projects, but I can’t think of what workaround I came up with for the life of me right now. If I think of it, I’ll post back here. Otherwise, I hope there will be a fix for Quick sometime soon.

1 Like