vierge-noire / cakephp-fixture-factories

CakePHP Fixture Factories
https://vierge-noire.github.io/
MIT License
83 stars 21 forks source link

Feature Request: Add back-reference property to sub-entities #198

Closed LordSimal closed 1 year ago

LordSimal commented 1 year ago

Hey there, really love this plugin and what it achieves 😁 But recently I came to a sort of weird edge-case where I need to have back-reference properties in sub-entities created by the fixture factory. What do I mean exactly?

Lets go with the concrete example: I have 2 Models - PipingSystems and PipingSystemEntries 1 PipingSystem hasMany PipingSystemEntries (as you may have alread guessed) so I just baked the factories, set the default data in them and added my associations as ->withAssocName() as usual (and as described in the docs)

So with that I can do something like

$entity = PipingSystemFactory::make([
    'unit_price' => 2
])
    ->withPipingSystemEntries(5)
    ->getEntity();

Loveley, BUT now I have a problem. The fixture factories are built in such a way, that ONLY the association properties are set in the "direction" how the associations are called, not in reverse.

So here my $entity would indeed have a $entity->piping_system_entries array containing 5 connected entities of type PipingSystemEntry

BUT each of these sub-entities does NOT contain a reference back to the via belongsTo connected PipingSystem entity.

image

My quick guess why this currently is the case is to prevent any kind of ciruclar references but then again, this may be a good configurable flag?

My current pretty horrible workaround is this:

$entity = PipingSystemFactory::make([
    'unit_price' => 2
])->withProject()->persist();
PipingSystemEntryFactory::make(5)
    ->withPipingSystem($entity)
    ->withPositionDetail()
    ->persist();
$entity = PipingSystemFactory::get($entity->id, [
    'contain' => [
        'PipingSystemEntries' => [
            'PositionDetails' => ['Positions'],
            'PipingSystems'
        ]
    ]
]);
$this->assertTrue($entity->price > 0);

Because in the end this model test should perform at test on a virtual field which depends on a virtual field of its sub-entities (PipingSystemEntries). And that PipingSystemEntries sub-entity virtual field needs an actual field from its connected PipingSystem entity... I hope you see what my problem is here ^^

pabloelcolombiano commented 1 year ago

Hi @LordSimal ,

If you use getEntity(), the entities will not be persisted, so indeed there will be no reference in the PipingSystemEntries to the PipingSystem they belong to. Did you try with persist()?

LordSimal commented 1 year ago

Persisting the entity does result in a proper piping_system_id being present in the sub-entity but it doesn't have a piping_system property containing the PipingSystem entity inside it.

image

pabloelcolombiano commented 1 year ago

The piping system is the root entity, you have it in the $entity variable, isn't it? Do you really need it additionnally in the associated entity?

LordSimal commented 1 year ago

In this scenario yes because my price calculation $entity->price happens via a a virtual field and in there I expect that my current entity has its properties like so:

protected function _getPrice(): float
{
    $price = 0;
    if ($this->piping_system_entries) {
        foreach ($this->piping_system_entries as $pipingSystemEntry) {
            $price += $pipingSystemEntry->row_price;
        }
    }

    return $price;
}
LordSimal commented 1 year ago

I'd say we leave it like it is 😁 Just have to build my entities differently if I need those references