Laravel101: A Practical Guide for Seeders and Factories

kazemmdev

Kazem

Posted on August 31, 2023

Laravel101: A Practical Guide for Seeders and Factories

During the development process, there are times when we need to evaluate the functionality of a system by using data. In Laravel, we have two useful tools called seeders and factories that help us generate random data. In this article, we’ll explore these tools and learn how to use them effectively.


The factory class in Laravel acts like a factory that generates random data for our database tables. If you navigate to the database/factories directory in your project, you’ll find a class called UserFactory:



class UserFactory extends Factory
{
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}


Enter fullscreen mode Exit fullscreen mode

In each factory class, there is an important function called definition that specifies how each attribute should be filled out. To accomplish this, Laravel utilizes a powerful package called fakerPHP, which can generate random data such as names, sentences, paragraphs, and even images. You can find more information about the helper functions and features of this package at the following link.

FakerPHP / Faker

Documentation for FakerPHP / Faker

favicon fakerphp.org

Another point in UserFactory class is that the string that is considered a fixed password is the hashed word “password”.

Laravel includes a variety of global helper PHP functions, and one of them is called Str. This helper function is useful for working with strings, such as generating random strings. You can refer to the documentation for this helper function at here.

Now, let’s get back to our project and generate a factory for our tags. To create a factory, we can use the following artisan command:



php artisan make:factory TagFactory


Enter fullscreen mode Exit fullscreen mode

It’s generally recommended to use a singular name for factory classes. However, if you want to specify the model while generating the factory, you can use the “-m” option in the command, like this:



php artisan make:factory TagFactory -m Tag


Enter fullscreen mode Exit fullscreen mode

In our Tag model, we only have a simple “name” attribute, which can be defined as follows:



use Illuminate\Database\Eloquent\Factories\HasFactory;

class Tag extends Model
{
    use HasFactory;
}


Enter fullscreen mode Exit fullscreen mode

Once we have set up the factory, we can use it to create instances of our model. To do this, simply call the factory method whenever you want to create a model instance.

You can also control the number of random data entries you want to generate. You can achieve this by using either the factory function or the count function. For example, if you want to create 5 random tags, you can use the following code:



Tag::factory(5)->create();


Enter fullscreen mode Exit fullscreen mode

Furthermore, it’s possible to define a specific attribute with a desired value while generating the data. For instance, if you want to create 5 users with the name “test,” but you don’t care about the other attributes, you can use the following code:



User::factory(5)->create(['name' => 'test']);


Enter fullscreen mode Exit fullscreen mode

In our project, we currently have two factories for our user and tag models. Now, we need to create another factory for our task model.

When defining the factory model, it is important to consider the relational model. In our task model, each task is associated with a user. So, what we want to achieve is creating a random task with the id of a given user.

However, during testing your application, there might be situations where no user has been created in the environment. In such cases, generating a random user could be a viable solution. To accomplish this, you can use factory model again just like bellow:



public function definition(): array
{
    return [
        "title" => fake()->sentence(),
        "description" => fake()->paragraph(),
        "expired_at" => fake()->dateTimeThisMonth(),
        "user_id" => User::factory(),
    ];
}


Enter fullscreen mode Exit fullscreen mode

If you run the code in Tinker, you will notice that a new user has been created for the task model.

Image description

Also we have the option to specify a user_id when generating a task. However, I suggest refactoring the code to utilize the users that are likely to already exist in database:



public function definition(): array
{
    return [
        "title" => fake()->sentence(),
        "description" => fake()->paragraph(),
        "expired_at" => fake()->dateTimeThisMonth(),
        "user_id" => User::query()->inRandomOrder()->first()?->id ?? 
           User::factory(),
    ];
}


Enter fullscreen mode Exit fullscreen mode

To generate random data more efficiently in Laravel, we have a better solution than using Tinker. It’s called a seeder.

In the database/seeders directory, you will find a class named DatabaseSeeder, which allows us to manage the generation of random data.

Let's update the DatabaseSeeder class as shown below, and then execute an artisan command to run this function.



class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        Task::factory()->create();
    }
}


Enter fullscreen mode Exit fullscreen mode

And here is the artisan command for seeding:



php artisan db:seed


Enter fullscreen mode Exit fullscreen mode

That’s it!

With Seeder, we can easily create a random task record in our database using artisan. Additionally, we have the flexibility to create multiple seeders for different scenarios. To create a seeder, you simply need to run php artisan make:seed <seeder-name> command.

Now, let’s create a TaskSeeder that generates 5 random task records, with each task associated with two random tags:



namespace Database\Seeders;

use App\Models\Tag;
use App\Models\Task;
use Illuminate\Database\Seeder;

class TaskSeeder extends Seeder
{
    public function run(): void
    {
        Task::factory(5)->hasTags(2)->create();
    }
}


Enter fullscreen mode Exit fullscreen mode

To run this seeder, you can use the artisan command by specifying your desired seed class.



php artisan db:seed TaskSeeder


Enter fullscreen mode Exit fullscreen mode

Or you can use call method inside DatabaseSeeder to execute your seed classes:



class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call(TaskSeeder::class);
    }
}


Enter fullscreen mode Exit fullscreen mode

Initial data model

Well, sometimes there are certain values that are need to be initialized, such as the status of an article (e.g., published or draft) or the status of a transaction (e.g., paid or non-payment).

I want to apply a similar approach to tags in this project. We have a couple of options to achieve this.

One option is to store the initial values in a configuration file. To do this, we can create a PHP file called “defaults” at the specified config path and define the desired values there:



<?php

return [
    'tags' => ['php', 'laravel', 'develop', 'backend']
];


Enter fullscreen mode Exit fullscreen mode

We can then initialize these values using a migration file.

Alternatively, we can initialize these values using a seeder. This means we would define a seeder like TagSeeder that populates the default value in the database.

Both approaches work, but I prefer to use seeder because it’s more clear:

Let’s define TagSeeder and initialize it’s values:



class TagSeeder extends Seeder
{
    public function run(): void
    {
        foreach (config('defaults.tags') as $value) {
            Tag::firstOrCreate(['name' => $value]);
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

As you can see, the helper function config in Laravel is utilized to retrieve static information from config files.

Here I use firstOrCreate method which adds a new tag record only if it hasn’t been previously added.

Great! Now, let’s all come together and work on implementing a scenario like the one described below inside our TaskSeeder:



namespace Database\Seeders;

use App\Models\Tag;
use App\Models\Task;
use App\Models\User;
use Illuminate\Database\Seeder;

class TaskSeeder extends Seeder
{
    public function run(): void
    {
        // create a user with specified credentials
        $user = User::factory()->create(['email' => 'test@test.dev']);

        // init tag with defined value
        $this->call(TagSeeder::class);

        // create 5 random tasks for the user
        $tasks = Task::factory(5)->create(['user_id' => $user->id]);

        // assosiate each generated tasks with 2 predefined tags
        foreach ($tasks as $task) {
            $tags = Tag::query()->inRandomOrder()->take(2)->pluck('id');
            $task->tags()->attach($tags);
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Now let’s rebuild our database and fill it with this defined seeder. To rebuild the database you can run migrate:fresh artisan command which will drop all tables and re-run all of your migrations:

Image description

And if you login with the user you’ll see the tasks generated successfully:

Image description


I hope this explanation clarifies the process of using seeders and factories in Laravel. If you have any further questions, feel free to ask!

💖 💪 🙅 🚩
kazemmdev
Kazem

Posted on August 31, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related