Tracking Seeders in PHP Laravel

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.
- 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.




