When Entity Marshaling Happens

I have an entity type which, when loaded from the db (or indeed any time they are created) need to have an additional class placed into them, a class chosen based on the content of a property of the entity (corresponding to a column in the database).

Alternative one

My thought was that the new afterMarshal event would be a good trigger for the examination process and the dependency injection. But I can’t seem to confirm that this actually runs as part of the process of querying the db and creating entities.

It may be that I have some detail wrong in my event set-up. Or it may be that the patchEntity() method is not used to convert the fetched data into entities. (patchEntity() and newEntity() are the methods that trigger the afterMarshal event).

Does anyone know for sure that patchEntity() is or is not used to produce entities from query data?

Alternative two

As an alternative I think I might be able to use the entity constructor to do my injection after the parent constructor has populated the entity object.

This is not my first choice because it will make the entity classes heavier and it just doesn’t feel right.

Though I guess I could trigger my own event in the constructor. This would give me the opportunity to put the required code out in a nice DRY class outside the entities. (This one has promise).

Alternate three

There is of course the map/reduce process that can be added to the queries. This isn’t very attractive though because I need the extra classes inserted into the entities ALWAYS, even when they are contained in another entity.

With this requirement, insuring I get the mapper/reducers added in every case and getting them written to do the job where ever my entities appear in the query result structure is not an attractive prospect.

Conclusion

Any thoughts?

You can use the Model.beforeMarshal event to modify request data before it is converted into entities.
Get the object used to marshal/convert array data into objects.
public function marshaller()
{
return new Marshaller($this);
}

You can check in the following files :–
vendor/cakephp/cakephp/src/ORM/Table.php where you get exact idea.

Hi @dreamingmind,

You can use patchEntity() to create Cake\Database\Query objects into Cake\ORM\Entity objects. There is not direct way but since Query object contains collection of Entity data object, you can loop through each entity to patch particular entity individually.

Regarding overriding constructor, can’t you use initialize hook to do this?

Is this not a scenario where a magic accessor function would work?

It doesn’t appear that the marshal process actually runs during the conversion of query data to entities. Marshaling seems to be use when converting post data (or other developer provided array data) into entities when using patchEntity or newEntity. Those methods don’t seem to be a part of the query process nor does marshal.

Am I misunderstanding your suggestion?

Yeah. That would definitely trigger the event in question. It solves my basic problem ‘how to get the process to run naturally in the flow of all queries against a table’ by saying, ‘I’ll manually loop on the results’. Not really what I was hoping for :slight_smile:

There doesn’t seem to be an initialize() hook in entities!

1 Like

Well, that is not completely out of the question…

The thing is, my goal is not to set a property value. Rather it is to bring in a class that implements a part of the entities interface in a special way.

A bit more detail

A db column in tableX holds json data.

For now, I know the structure of that data and can deal with it in forms. But the json schema may change in unknown ways because of customer requests and this could easily break my ‘normal’ implementation code.

So, that code needs to be a swap-able module in the entity.

In reality, it’s not just one json field in an entity I’m managing. There are several hook points in the system where a new implementation would have to be used. The entity modifications are just one of the inflection points. So I want them to be as transparent as possible.

back to your suggestion

I suppose I could put the implementing class on a property, then call methods of it off that property. Hmm… I don’t see why that wouldn’t work.

Probably that would have to be a lazy property though since the accessors only run when you use them.

Something like this?


    public function _getStrategy()
    {
        if (!isset($this->_strategy)) {
            $this->_strategy = $this->chooseStrategy();
        }
        return $this->_strategy();
    }

    public function OneOfTheInterfaceCalls($arg)
    {
        return $this->strategy->OneOfTheInterfaceCalls($arg);
    }

I think @Zuluru’s idea could work.

One last note on my system; I actually have two entity types that need to respond in this same way. That’s why the solution below takes the extra steps to abstract everything out of the entity class.

Here is my conclusions regarding afterMarshal:

  • It is triggered by newEntity(), patchEntity(), and patchEntities()
  • It’s not triggered by any query process that creates entities or by newEmptyEntity()

So, in the entity I can override the constructor like this:

    public function __construct(array $properties = [], array $options = [])
    {
        parent::__construct($properties, $options);

        /**
         * Queries and various patchEntity calls run through here.
         * Patches trigger afterMarshal for them selves but queries do not.
         * So, we watch for the options that are unique to queries and manually
         * trigger the event when they appear.
         */
        StratCon::afterMarshalReferee(
            $this,
            $properties,
            $options,
            $this->getTableLocator());
    }

The referee method is in another class and looks like this:

    /**
     * Decide whether to manually trigger the afterMarshal event
     *
     * Queries won't do it for themselves. But $table->patchXX calls do.
     * So to avoid double work we guard the manual trigger.
     *
     * @param Entity|EntityInterface $entity
     * @param array $data
     * @param array $options
     * @param LocatorInterface $tableLocator
     */
    static public function afterMarshalReferee($entity, $data, $options, $tableLocator)
    {
        if (StratCon::isQuery($options) ) {
            $table = $tableLocator->get($options['source']);
            $data = new ArrayObject($data);
            $options = new ArrayObject($options);
            $table->dispatchEvent(
                'Model.afterMarshal',
                compact('entity', 'data', 'options'));
        }
    }
The whole StratCon class if you're interested
<?php


namespace App\Constants;

use ArrayObject;
use Cake\Datasource\EntityInterface;
use Cake\ORM\Entity;
use Cake\ORM\Locator\LocatorInterface;

/**
 * Class StratCon
 *
 * Constants for the Zone/Location strategy system
 *
 * The system that inflects the code to handle different Zone storage schemas
 * and Locations->location json data describing a specific storage slot in a Zone
 *
 * @package App\Constants
 */
class StratCon
{

    /**
     * When __constructing an entity, this set of option keys
     * indicates query results are being turned into entities.
     */
    const Q_OPTS = [
        'useSetters' => '',
        'markClean' => '',
        'markNew' => '',
        'guard' => '',
        'source' => ''
    ];

    /**
     * Is the set of array keys the set that signals an ongoing query?
     *
     * Entity::_construct always gets an option array. But the set of
     * keys during construction from query data is unique.
     *
     * @param array $options
     * @return bool
     */
    static public function isQuery($options) {
        return count(array_intersect_key($options, StratCon::Q_OPTS)) === 5;
    }

    /**
     * Decide whether to manually trigger the afterMarshal event
     *
     * Queries won't do it for themselves. But $table->patchXX calls do.
     * So to avoid double work we guard the manual trigger.
     *
     * @param Entity|EntityInterface $entity
     * @param array $data
     * @param array $options
     * @param LocatorInterface $tableLocator
     */
    static public function afterMarshalReferee($entity, $data, $options, $tableLocator)
    {
        if (StratCon::isQuery($options) ) {
            $table = $tableLocator->get($options['source']);
            $data = new ArrayObject($data);
            $options = new ArrayObject($options);
            $table->dispatchEvent(
                'Model.afterMarshal',
                compact('entity', 'data', 'options'));
        }
    }

}

Just want to ask whether there’s any chance that this is all basically an elaborate way to get functionality that a trait would provide. I’m always better with concrete examples than abstract ones, and what you’ve presented so far is still fairly abstract, despite the presence of actual code. :slight_smile:

A properly flavored trait would have to be selected at run-time. I’m not sure the trait use statement can be wrapped in logic. Though I guess I could write a trait that did it’s own implementer selection. But if many of these variations were defined things could get very muddy.

My choice does seem a bit crazy. My programing partner routinely raises his eyebrows at my solutions, but on average they prove their worth :stuck_out_tongue:

I’m pretty sure this will accomplish my goal which is:

To extend the system, write a small set of classes, each of which implements a small defined interface, put them in the proper locations, and the system now implements a customer requested warehouse organization and naming pattern.

Though new classes are added, no other program logic is changed. Ideal!

Though ‘abstract’, it still feels Cake-y to me. This is similar to the way $controller->paginate() allows the swap-in of different Datasource\Paginator classes so we aren’t limited to defined pagination of ORM results. Or the way $controller->setTableLocator() lets us define a whole new way of finding and assembling ‘tables’.

Maybe you could try to look at UseMuffin/Sti It selects which class to use based on a field, whether via request data or loading from database

Yeah, that’s interesting. I could probably make that work too.

My event based solution will patch in a small behavioral change change for my entities. Sti would swap in a different class from a hierarchy.

Those classes could extend from BaseEntity to keep code DRY, and each class can implement each getter/virtual property.

I think it is a more OOP way to tackle the problem.

Using families of classes that inherit is a more familiar solution to me than what I’m proposing.

I’m trying to extend the range of my skills and the scope of my OOP tool kit. Though this effort sometimes leads me to a clumsier solution, it always increases my knowledge of why one approach is better than another for a particular case.

I’ve been searching for a problem where dependency injection might be an appropriate solution. In this problem it is at least plausible. Time will tell if it is actually a ‘good’ solution.

It wasn’t clear to me whether the affected entities all needed the same code, which might be changed at some point for all of them together. If they do, then a trait could work. If they don’t, then not so much.