Generating slugs in factories

Cleaner than it sounds...

Aaron Saray
Aaron Saray
2024-07-01

Normally, I would generate a slug for an Eloquent model with an observer or an event. The creating event can verify if the slug field is filled, and if not, use the Str helper to add it to the current creation payload. Pretty cool.

But, sometimes you can't do it like this. Perhaps it's a legacy project, events are turned off in your tests, or any number of reasons. So, when using Eloquent factories, you might find yourself doing something like this:

App\Models\Article::factory()->create([
    'title' => 'The Baron of the Hoops',
    'slug' => Str::slug('The Baron of the Hoops'),
    'author' => 'R J J Kientol',
]);

I don't know about you, but I really don't want to do that. Seems repetitive - especially since I know I want the slug to be generated directly from the title field.

Laravel provides a solution, though. When you pass a closure to a field definition in the factory declaration, you get access to the other attributes that are being used to create the model. So, let's update our factory definition to this:

public function definition(): array
{
    return [
        'title' => $this->faker->sentence(),
        'slug' => fn(array $attributes) => Str::slug($attributes['title']),
        'author' => $this->faker->name(),
    ];
}

Now, our slug is generated automatically from our title. (If we want a different one, we can still pass it in using the create() method.)

So, now, we can do this:

App\Models\Article::factory()->create([
    'title' => 'The Baron of the Hoops',
    'author' => 'R J J Kientol',
]);

Nice!

Here to help,

Aaron

PS: Fine. I'll give it back to Joel for a while. One of us has to do the real work here pretty soon anyway. :)

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼

Level up your Laravel skills!

Each 2-minute email has real-world advice you can use.