Summer Sale! All accounts are 50% off this week.

vlauciani's avatar

Factory with some fields already into the DB

Hi

I'm using a Factory Class like this:

class TypeEventFactory extends Factory
{
    protected $model = TypeEvent::class;
    public function definition()
    {
        $type_event__name = ['A','B','C'];

        return [
            'name'          => $this->faker->unique()->randomElement($type_event__name),
            'description'   => $this->faker->words(3, true),
        ];
    }
}

It is called from an external Class with line:

$myArray = [
    'type_event'                            => TypeEventFactory::new()->make()->name,
];

The problem is that the table type_event could not be empty (It contains some default values inserted from DB administrator) and one of the values A,B,C is already in the table type_event. If the Factory try to insert one of the existing value, return:

SQLSTATE[23505]: Unique violation: 7 ERROR:  duplicate key value violates unique constraint...

is there a way to use Factory with something like firstOrCreate() to check if the value already exists? If "no", insert and return Model inserted... if "yes" return the Model already in the DB.

Thank you

0 likes
5 replies
vincent15000's avatar

You are probably trying to insert a big amount of fake datas and the fake data generator turns always around the same datas.

vlauciani's avatar

Hi @vincent15000, thank you for your answer

No, I'm not inserting a big amount of fake data... but I'm inserting a small set of data that can be already present in the table.

For example, my field into the table can contains only three values: A, B or C. These fields may be just present or not... then I'm looking for something like firstOrCreate() for Factory; If the field is already present the Factory must not insert it again.

1 like
vlauciani's avatar

Thanks @vincent15000 , I'll try it...

In the meantime, I've updated the Factory Class with:

class TypeEventFactory extends Factory
{
    protected $model = TypeEvent::class;
    public function definition()
    {
        $name = $this->faker->unique()->randomElement(['A','B','C');

        /* Find or Insert 'name' value */
        $t = TypeEvent::firstOrCreate(['name' => $name], [
            'description' => $this->faker->words(3, true),
        ]);

        $tArray = $t->toArray();
        if ($t->wasRecentlyCreated) {
            return $tArray;
        } else {
            $tArray['wasRecentlyCreated'] = $t->wasRecentlyCreated;
            return $tArray;
        }
    }
}

And In the father Class:

private function getTypeEvent()
{
        $f = TypeEvent::factory();
        $fArray = $f->raw();

        if (isset($fArray['wasRecentlyCreated']) && ! empty($fArray['wasRecentlyCreated'])) {
            return $f;
        } else {
            return $fArray['id'];
        }
}

public function definition()
{
    $myArray = [
        'type_event'                            => $this->getTypeEvent(),
    ];
}

With this update, the il the value A, B or C is already in the DB, It returns the id; otherwise the Factory Model is returned. It seems to work...

What do you think about?

1 like
LaryAI's avatar
Level 58

Certainly! You can modify your factory to use the firstOrCreate method by adding a custom state or method to your factory that handles this logic. Here's an example of how you could implement this:

class TypeEventFactory extends Factory
{
    protected $model = TypeEvent::class;

    public function definition()
    {
        $type_event__name = ['A', 'B', 'C'];

        return [
            'name'        => $this->faker->unique()->randomElement($type_event__name),
            'description' => $this->faker->words(3, true),
        ];
    }

    public function firstOrCreate()
    {
        $attributes = $this->definition();

        return TypeEvent::firstOrCreate(
            ['name' => $attributes['name']],
            $attributes
        );
    }
}

Now, when you want to use this factory to create a new TypeEvent or get the existing one from the database, you can call the firstOrCreate method:

$typeEvent = TypeEventFactory::new()->firstOrCreate();
$myArray = [
    'type_event' => $typeEvent->name,
];

This will attempt to find an existing TypeEvent with the same name in the database. If it doesn't exist, it will create a new one with the attributes provided by the factory definition. If it does exist, it will return the existing model without creating a duplicate.

Remember that when using firstOrCreate, the first parameter is an array of conditions to look for an existing record, and the second parameter is an array of values to use when creating a new record if an existing one isn't found.

Please or to participate in this conversation.