zenstruck / foundry

A model factory library for creating expressive, auto-completable, on-demand dev/test fixtures with Symfony and Doctrine.
https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html
MIT License
611 stars 63 forks source link

Impossible to create object when using different name for field in constructor vs property #465

Closed norkunas closed 1 year ago

norkunas commented 1 year ago

For example I have this entity:

class EntityWithConstructorUnionType {
  private EntityType1 $field1;
  private EntityType2 $field2;
  public function __construct(EntityType1|EntityType2 $object) {
    if ($object instanceof EntityType2) { $this->type2 = $object; $this->type1 = null; }
    else {  $this->type2 = null; $this->type1 = $object; }
  }
}

and three foundry factories for all these entities. Now in the EntityWithConstructorUnionType factory i declare:

    protected function getDefaults(): array
    {
        return [
            'object' => EntityType1Factory::new(),
        ];
    }

And an error is thrown:

RuntimeException: Field App\Entity\EntityWithConstructorUnionType::$object does not exist or is not a relationship field.

/var/www/html/vendor/zenstruck/foundry/src/Factory.php:453
/var/www/html/vendor/zenstruck/foundry/src/Factory.php:385
/var/www/html/vendor/zenstruck/foundry/src/Factory.php:111
/var/www/html/vendor/zenstruck/foundry/src/ModelFactory.php:86

If i rename from object to type1 in getDefaults() then other error is thrown:

InvalidArgumentException: Missing constructor argument "object" for "App\Entity\EntityWithConstructorUnionType".

/var/www/html/vendor/zenstruck/foundry/src/Instantiator.php:260
/var/www/html/vendor/zenstruck/foundry/src/Instantiator.php:43
/var/www/html/vendor/zenstruck/foundry/src/Factory.php:134
/var/www/html/vendor/zenstruck/foundry/src/ModelFactory.php:86
nikophil commented 1 year ago

hello,

I think what you should do is to configure the instantiator as following:

// EntityWithConstructorUnionTypeFactory

    public function initialize(): self
    {
        return $this->instantiateWith(
            (new Instantiator())->alwaysForceProperties()->withoutConstructor()
        );
    }

this way, your constructor won't be called and you will be able to rely on the properties' name

norkunas commented 1 year ago

But my point that the constructor logic should run,i dont want to duplicate it :)

nikophil commented 1 year ago

If you set the $fieldX nullable by defaults, this logic is not needed anymore 😇

norkunas commented 1 year ago

Lib should not force to change entities imho:)

nikophil commented 1 year ago

Anyway, I think this has been fixed by #457 which is not released yet. I'll check before releasing

nikophil commented 1 year ago

ok I can confirm it works on the main branch... see https://github.com/zenstruck/foundry/pull/466

I'll release it today

norkunas commented 1 year ago

thank you :)

nikophil commented 1 year ago

version 1.33.0 has just been release, is the issue fixed?

norkunas commented 1 year ago

version 1.33.0 has just been release, is the issue fixed?

yes :raised_hands: