Skip to main content

Command Palette

Search for a command to run...

Tracking Seeders in PHP Laravel

Published
5 min read
Tracking Seeders in PHP Laravel
N

Product Development as an Expertise Since 2015 Founded in August 2015, we are a USA-based Bespoke Engineering Studio providing Product Development as an Expertise. With 80+ satisfied clients worldwide, we serve startups and enterprises across San Francisco, Seattle, New York, London, Pune, Bangalore, Tokyo and other prominent technology hubs.

Background

For some time, I was working on some other assignment and then switched back to the Laravel project that I was working on earlier. There were some new features added to the application, which needed some new tables, changes in some existing tables, and some master records were also added. Pulled latest develop branch. Executed migrations. But when it came to seeders I had to search for which ones were newly added and needed to make sure that I don’t accidentally execute some seeder twice or miss any. I had pulled code a couple of times earlier so checking modified seeders in the repository was also not a foolproof solution.

Migrations and Seeders in Laravel

Laravel provides very useful tools Migration and Seeder for structuring and modifying schema and data. We can use migrations to define and modify the database schema, We create and alter tables, and define columns, indexes, and foreign keys. Migrations allow you to version control your database schema, making it easy to share changes with others and roll back changes if necessary.

Seeders on the other hand are used for populating the database with sample or default data. Seeders are commonly used for creating initial admin users, inserting predefined reference data, or generating random data for testing and development purposes. You can run specific seeders or run all seeders to populate the entire database.

How seeders are different from migrations?

Migrations keep track of the changes made to the database schema over time. Each migration file represents a set of changes and can be rolled back if needed. Migrations keep track of the changes made to the database schema over time. Each migration file represents a set of changes and can be rolled back if needed. Laravel keeps track of all executed migrations scripts in a table called migrations in the database. This table also keeps track of the batch in which the migration is executed. As migrations history is maintained by Laravel, You don’t need to keep track of which migrations are executed.

You can just execute the following command and all pending migrations will be executed

php artisan migrate

All seed classes are stored in app/database/seeds folder. You need to specify the seeder class that you want to execute. If you have a seeder who fills the role in the master table as follows

<?php

use Illuminate\Database\Seeder;
use App\Models\Role;


class RolesTableSeeder extends Seeder
{
    public function run()
    {
        // Seed different roles for users
        Role::create(['name' => 'Admin']);
        Role::create(['name' => 'Manager']);
        Role::create(['name' => 'SiteAdmin']);
        Role::create(['name' => 'User']);
        Role::create(['name' => 'Guest']);
    }
}

To apply this seeder, you need to execute the seeder mentioned in RolesTableSeeder seeder class.

php artisan db:seed --class=RolesTableSeeder

Unlike Migrations, Laravel however doesn’t keep track of seeders that are executed. So if you need to keep track of the seeders that were applied earlier then find one one are new and apply those. It’s a tedious job if you are adding new seeders frequently. And if you execute the same seeder twice, it is likely that duplicate records will be created.

Possible Solutions.

  1. Write seeders as Migrations.

One way to handle this problem is to create seeders as migrations and write seeder code inside the migration file.

php artisan make:migration roles_seeder

Above command will actually create a migration script, but we can use this migration script to seed roles table as follows.

<?php

use Illuminate\Database\Migrations\Migration;
use App\Models\Roles;

class RolesSeeder extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {

    // Seed different roles for users
    Roles::create(['name' => 'Admin']);
    Roles::create(['name' => 'Manager']);
    Roles::create(['name' => 'SiteAdmin']);
    Roles::create(['name' => 'User']);
    Roles::create(['name' => 'Guest']);
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Roles::where('name','Admin')->delete();
        Roles::where('name','Manager')->delete();
        Roles::where('name','SiteAdmin')->delete();
        Roles::where('name','User')->delete();
        Roles::where('name','Guest')->delete();
    }
}

In the above example, we have written down migration as well which will delete added records from the roles table. For this seeder, we don’t need to keep track of whether it’s applied or not. We can execute it as a migration.

2. Execute seeders from Migration

Another possible solution is to call the seeder when the corresponding migration is executed. This approach will work when we have at least one migration script along with each seeder script. Here is how we can achieve in our case.

<?php


use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Artisan;


class CreateRolesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_roles', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('name');
        });


        Artisan::call('db:seed', [
            '--class' => RolesTableSeeder::class
        ]);
    }


    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_roles');
    }
}

In the above case, we are calling RolesTableSeeder when up migration is executed and deleting the table along with data when down seeder is executed. This approach might not work for incremental seeders where we add a few more roles in a separate seeder at a later stage after the initial seeder is applied. We will have to handle down seeder for this case separately.

3. Check if data already exists using updateOrCreate() method

One more possible solution is to use updateOrCreate() method on a model while creating a seeder.

<?php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use App\Models\Roles;

class RolesTableSeeder extends Seeder
{
    public function run()
    {
        // Seed different roles for users
        Roles::UpdateOrCreate(['name' => 'Admin']);
        Roles::UpdateOrCreate(['name' => 'Manager']);
        Roles::UpdateOrCreate(['name' => 'SiteAdmin']);
        Roles::UpdateOrCreate(['name' => 'User']);
        Roles::UpdateOrCreate(['name' => 'Guest']);
    }
}

This will avoid duplicate entries in the table in case we execute seeders multiple times.

My other article explains Formatting API response in PHP using Eloquent API Resources.

More from this blog

N

NonStop io Technologies

164 posts

Founded in August 2015, we are a USA-based Bespoke Engineering Studio providing Product Development as an Expertise.