1

Good Afternoon, I'm looking for some help please with Laravel 10.

I am looking to write a database seeder which will use the Incidents factory to generate new incidents. Each Incident should have additional models created called SkillUsed during their creation.

Lets say the skill_id are: 1, 2, 3.

The catch here is an Incident factory should not have duplicate skills. For example: 1,2 is fine but 1,1 is not.

Database Seeder:

protected function seedIncidents() : void {
        // Add some incidents made-to-scene, excluding scheme vehicles and case reviews and no POCUS skill.
        $incident = Incident::factory()->state([
            'incident_snr' => 0,
        ])->count(50)->create()
        ->each(function($incident) {
            SkillUsed::factory()
                ->count(fake()->numberBetween(1,5))
                ->create();
        });

Skill Used Factory:

public function definition(array $attributes = []): array
    {
        $incident = $attributes['incident_id'] ?? Incident::inRandomOrder()->first()->value('incident_id');
        $existingSkillsUsed = Incident::find($incident)->SkillsUsed()->pluck('sid')->toArray();

        $skill = Skill::whereNotIn('sid', $existingSkillsUsed)->inRandomOrder()->first();

        return [
            'sid' => $skill->sid,
            'incident_id' => $incident,
            'deleted_at' => fake()->optional($weight = 0.2)->dateTimeBetween($startDate = '-6 months', $endDate = 'now', $timezone = null),
        ];
    }

My problem is how to access the previously created Incident model to ensure the Skill isn't duplicated.

Much appreciated :)

1 Answers1

0

I can see few issues in your implementation so I encourage you to re-read the documentation about the factories especially factory relationships.

That said I propose some changes to your SkillUsedFactory, I can see you're running many queries like inRandomOrder, find, SkillsUsed() which isn't ideal for factories so I suggest you remove those and replace them with a placeholder that can be fed from the outside

// SkillUsedFactory

public function definition(): array
{
    return [
        'sid' => 0,
        'incident_id' => 0,
        'deleted_at' => // stays the same,
    ];
}

Then in your database seeder there are few changes to achieve creating a unique skills.

You do not need to access the previously created Incident you just need to store them in an array in this case something like $existingSkillsUsed

Try this modified seeder, there are slight changes from your implementation for example I replaced each with afterCreating which is a factory method rather than looping on a collection.

protected function seedIncidents(): void
{
    Incident::factory()
        ->state(['incident_snr' => 0])
        ->count(50)
        ->afterCreating(function ($incident) {
            $this->createUniqueSkills($incident);
        })
        ->create();
}

protected function createUniqueSkills(Incident $incident): void
{
    $numberOfSkills = rand(1, 5);
    $existingSkillsUsed = [];

    for ($i = 0; $i < $numberOfSkills; $i++) {
        $skill = $this->getUniqueSkill($existingSkillsUsed);
        $existingSkillsUsed[] = $skill->sid;

        SkillUsed::factory()->create([
            'sid' => $skill->sid,
            'incident_id' => $incident->id,
        ]);
    }
}

protected function getUniqueSkill(array $existingSkillsUsed)
{
    return Skill::whereNotIn('sid', $existingSkillsUsed)
        ->inRandomOrder()
        ->first();
}

I have not tested this code because I am not sure where else you're using the SkillUsedFactory, but if it doesn't it should get you the idea.

Salam
  • 1,126
  • 14
  • 20