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
607 stars 62 forks source link

Creating linked entities dependent on parent entity #606

Closed orangevinz closed 1 month ago

orangevinz commented 1 month ago

Foundry: 1.23.x

I am facing a problem while creating entities that are linked and depend on the parent entity. I need to access $this during the creation of the child entity.

Until now I used afterPersist(), but this saves the object twice: once for creation and once for updating the relationships. This can trigger my doctrine listenners for postPersist and postUpdate, which I want to avoid.

If I use afterInstantiate, I encounter issues with cascading reverse relations.

Here's a schematic of the entities and their relationships:

// User Entity
class User
{
    private ?string $name = null;
    private Collection $pictures;
    private ?Person $mainPerson = null;

    public function __construct()
    {
        $this->pictures = new ArrayCollection();
    }
}

// Picture Entity
class Picture
{
    private ?User $user = null;
    private ?Person $person = null;
}

// Person Entity
class Person
{
    private Collection $pictures;
    private Collection $users;

    public function __construct()
    {
        $this->pictures = new ArrayCollection();
        $this->users = new ArrayCollection();
    }
}

// PicturePerson Entity
class PicturePerson
{
    private ?Picture $picture = null;
    private ?Person $person = null;
}
final class UserFactory extends ModelFactory
{
    protected function getDefaults(): array
    {
        return [
            'name' => self::faker()->name,
        ];
    }

    protected function initialize(): self
    {
        return $this
            ->afterPersist(function (User $user) {
                // Create 1 Person for each User
                $person = PersonFactory::new()->create()->object();

                // Create 2 Pictures for each User
                $picture1 = PictureFactory::new()->create([
                    'user' => $user,
                ]);
                $picture2 = PictureFactory::new()->create([
                    'user' => $user,
                ]);

                // Associate each Picture with the Person
                PicturePersonFactory::createOne([
                    'picture' => $picture1,
                    'person' => $person,
                ]);
                PicturePersonFactory::createOne([
                    'picture' => $picture2,
                    'person' => $person,
                ]);

                // Associate the Person with the User
                $user->setMainPerson($person);
            });
    }

    protected static function getClass(): string
    {
        return User::class;
    }
}

In summary, I want to create a User, with each one having 2 Pictures and each Picture having 1 Person associated with it. The Person should be set as the MainPerson for the User.

UserFactory::createMany(10);

nikophil commented 1 month ago

Hi @orangevinz

If I use afterInstantiate, I encounter issues with cascading reverse relations.

what's exactly this issue?

orangevinz commented 1 month ago

Sorry @nikophil I'm ashamed I set cascade: ['persist'] and it works ^^