php artisan migrate:fresh --seed Can't Seed a Table Into MySQL
By the end of this you'll have every table filling cleanly from one command, and it takes about 15 minutes. If php artisan migrate:fresh --seed can't seed a table into MySQL in your project, the cause is almost always one of a few things in your seeder files, not in MySQL itself.
Short answer: migrate:fresh --seed only runs Database\Seeders\DatabaseSeeder. If your seeder isn't listed in its run() method, it never executes. If it does run and fails, the usual causes are seeding a child table before its parent (foreign key error), a model that points to the wrong table name, or missing timestamps columns.
Here's what you need before starting:
- A Laravel project that uses the
Database\Seedersnamespace (the default for current projects) - A MySQL user that can drop and create tables in your database
- A local or staging database.
migrate:freshdrops every table, so never point it at production data you want to keep.
Throughout this guide I'll use one example: a categories table and a products table, where every product belongs to a category.
Step-by-Step: Getting migrate:fresh --seed to Fill Your Tables
1. Confirm Laravel is talking to the right database
Open .env and check these values:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=shop
DB_USERNAME=root
DB_PASSWORD=
Then clear the cached config. If you ran config:cache at some point, Laravel keeps using the old values until you clear them:
php artisan config:clear
php artisan migrate:status
If migrate:status prints a list of migrations, the connection is working. If it throws a connection error, fix that before you look at the seeders at all.
2. Put the migrations in the right order
Laravel runs migrations in filename order. The parent table needs to come first:
database/migrations/2026_01_01_000001_create_categories_table.php
database/migrations/2026_01_01_000002_create_products_table.php
The products migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->string('name');
$table->decimal('price', 10, 2);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('products');
}
};
Don't forget $table->timestamps(). Eloquent writes created_at and updated_at by default, and without those columns the insert fails.
3. Check the model's table name
Eloquent guesses the table name from the class name: Category maps to categories and Product maps to products. If your table name doesn't follow that pattern (a legacy tbl_product, for example), you have to tell the model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $table = 'tbl_product'; // only needed if the name breaks convention
protected $fillable = ['category_id', 'name', 'price'];
public function category()
{
return $this->belongsTo(Category::class);
}
}
4. Create the seeders
php artisan make:seeder CategorySeeder
php artisan make:seeder ProductSeeder
Edit database/seeders/CategorySeeder.php:
<?php
namespace Database\Seeders;
use App\Models\Category;
use Illuminate\Database\Seeder;
class CategorySeeder extends Seeder
{
public function run(): void
{
foreach (['Engine Parts', 'Tyres', 'Accessories'] as $name) {
Category::create(['name' => $name]);
}
}
}
And database/seeders/ProductSeeder.php:
<?php
namespace Database\Seeders;
use App\Models\Category;
use App\Models\Product;
use Illuminate\Database\Seeder;
class ProductSeeder extends Seeder
{
public function run(): void
{
$tyres = Category::where('name', 'Tyres')->firstOrFail();
Product::create([
'category_id' => $tyres->id,
'name' => 'All-Season Tyre 205/55 R16',
'price' => 320.00,
]);
}
}
Notice that the product looks up a real category ID instead of hardcoding 1. Personally, I'd always do it this way. Hardcoded IDs work right up until someone changes the seed order.
5. Register the seeders in DatabaseSeeder
This is the step people miss most often. --seed runs database/seeders/DatabaseSeeder.php and nothing else. A seeder file that exists but isn't called from here simply never runs:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
CategorySeeder::class, // parent first
ProductSeeder::class, // then the child
]);
}
}
The order in this array is the order they run.
6. Refresh the autoloader and run it
composer dump-autoload
php artisan migrate:fresh --seed
If you only want to run one seeder while testing, you can point straight at it:
php artisan migrate:fresh --seed --seeder=CategorySeeder
Verify the Seeded Data Landed in MySQL
Check from Laravel's side first:
php artisan tinker
App\Models\Category::count();
App\Models\Product::with('category')->first();
Then check MySQL directly, so you know the data really is in the database and isn't coming from some other connection:
SELECT p.name, c.name AS category
FROM products p
JOIN categories c ON c.id = p.category_id;
If tinker shows rows but this query doesn't, your model is probably using a different $connection than the one you're querying.
Common Errors When Laravel Seeder Is Not Working With MySQL
"Target class [ProductSeeder] does not exist"
This means the class can't be autoloaded. Check that the file starts with namespace Database\Seeders;, that the class name exactly matches the filename (case matters on Linux servers), and then run composer dump-autoload again.
SQLSTATE[23000]: 1452 Cannot add or update a child row
The child was seeded before its parent existed. Swap the order in DatabaseSeeder. This also happens when a seeder hardcodes a category_id that doesn't exist yet.
SQLSTATE[42S02]: Base table or view not found: 1146
The model is querying a table name that doesn't exist. Either the migration created a different name or the model's guess is wrong. Set protected $table on the model.
SQLSTATE[42S22]: Unknown column 'updated_at'
The table has no timestamp columns. Add $table->timestamps() to the migration, or if the table really shouldn't have them, add public $timestamps = false; to the model.
No error, but the table is empty
This one's annoying because nothing tells you what went wrong. Look at the console output. Recent Laravel versions print a line for each seeder as it runs. If your seeder's name isn't in that output, it isn't registered in DatabaseSeeder (step 5).
Honestly, people often blame $fillable here, but seeders started by db:seed (which --seed calls) run with mass-assignment protection turned off. A missing $fillable entry won't break a seeder run this way. It will break the same create() call in your controllers later, though, so add it anyway.
SQLSTATE[HY000] [1045] Access denied or [2002] Connection refused
This is a credentials or host problem, not a seeding problem. Go back to step 1. On XAMPP, make sure MySQL is actually running in the control panel. If you're debugging an old non-Laravel script against the same server, the mysql_fetch_array() resource error is often the same connection failure showing up in a different way.
"Application In Production" prompt
Laravel asks for confirmation when APP_ENV=production. You can bypass it with --force, but think twice before doing that. That flag is how production databases get wiped.
Undoing a migrate:fresh Run
There's no undo. migrate:fresh drops every table and rebuilds from scratch, and migrate:rollback can't bring back dropped data. The only safety net is a backup taken before you run it:
mysqldump -u root -p shop > shop_backup.sql
Restore it with:
mysql -u root -p shop < shop_backup.sql
On Windows with XAMPP, both tools are in C:\xampp\mysql\bin\ if they aren't on your PATH. If you only want to re-seed without dropping tables, use php artisan db:seed instead. Just be aware that it will insert duplicate rows unless your seeders use firstOrCreate() or updateOrCreate().
FAQ
What is the difference between migrate:fresh and migrate:refresh?
migrate:fresh drops all tables directly, without running any down() methods. migrate:refresh rolls back every migration through its down() method and then migrates again. fresh is faster and doesn't care about broken down() methods, which is why most people use it in local development.
How do I run only one seeder in Laravel?
Use php artisan db:seed --class=ProductSeeder to run it against your existing tables. If you also want to rebuild the schema first, use php artisan migrate:fresh --seed --seeder=ProductSeeder.
Why does my seeder work with db:seed but not with migrate:fresh --seed?
With db:seed --class=... you're naming the seeder directly. migrate:fresh --seed only runs DatabaseSeeder, so the seeder must be listed in its call() array. The other possibility is that the fresh schema is missing a column the old database still had.
Why is php artisan migrate:fresh --seed not inserting data into MySQL with no error?
When php artisan migrate:fresh --seed can't seed a table into MySQL and shows no error, the seeder almost certainly isn't registered in DatabaseSeeder. Add it to the $this->call([...]) array in dependency order, run composer dump-autoload, and run the command again.


