Seeder
Seeders are handled by the roolith/migration package (^2.0) through App\Database\MigrationFactory. Seeder files live in database/seeders as *.seeder.php and applied names are tracked in the shared migrations status table (file_type = 'seeder') so seed data never duplicates between deploys.
Commands
Run from the project root:
php roolith seed:create add_users
php roolith seed
php roolith seed:status
php roolith seed:run
php roolith seed:run add_usersseed:create <Name>scaffoldsdatabase/seeders/<timestamp>_<rand>_<Slug>.seeder.phpwith arun()skeleton. The directory is created when missing and the name includes a timestamp plus random suffix so concurrent creates never collide.seeder:createis an accepted alias.seed [Name]runs pending seeders in filename order, or one named seeder when given (full timestamped name or short logical name). A failing seeder marks its rowfailedand the rest still run.seed:run [Name]runs all pending seeders, or one named seeder when given (full timestamped name or short logical name).seeder:runis an accepted alias with the same behavior.seed:status [Name]lists seeder rows with their status.seeder:statusis an accepted alias. There is intentionally no seeder rollback; seed data is generally non-reversible.
Seeder files
php roolith seed:create add_users<?php
use Roolith\Migration\Interfaces\SeederInterface;
use Roolith\Store\Interfaces\DatabaseInterface;
class _Seeder_20250908120000_ab12_AddUsers implements SeederInterface
{
/**
* Run the seeder.
*
* @param DatabaseInterface $db Shared database connection.
* @return void
*/
public function run(DatabaseInterface $db): void
{
}
}Inside run() you get the database connection, so every driver method from the database page is available on $db.
Complete example
Here is a complete seeder file that inserts dummy users into the users table created in the migration example.
<?php
use Roolith\Migration\Interfaces\SeederInterface;
use Roolith\Store\Interfaces\DatabaseInterface;
class _Seeder_20250908120000_ab12_UserData implements SeederInterface
{
public function run(DatabaseInterface $db): void
{
$db->execute("
INSERT INTO users (name, email, role, last_logged_in)
VALUES
('John Doe', 'john.doe@example.com', 'developer', '2025-11-01 09:15:00'),
('Bob Smith', 'bob.smith@example.com', 'qa', '2025-11-02 10:30:00'),
('Charlie Nguyen', 'charlie.nguyen@example.com', 'developer', '2025-11-03 11:45:00'),
('Diana Patel', 'diana.patel@example.com', 'dba', '2025-11-04 12:00:00'),
('Ethan Clark', 'ethan.clark@example.com', 'developer', '2025-11-05 13:10:00'),
('Fatima Ali', 'fatima.ali@example.com', 'qa', '2025-11-06 14:20:00'),
('George Brown', 'george.brown@example.com', 'developer', '2025-11-07 15:35:00'),
('Hannah Wilson', 'hannah.wilson@example.com', 'qa', '2025-11-08 16:40:00'),
('Imran Qureshi', 'imran.qureshi@example.com', 'dba', '2025-11-09 17:55:00'),
('Jessica Thompson', 'jessica.thompson@example.com', 'developer', '2025-11-10 18:05:00');
");
}
}Seeders pair with migrations: migrate the schema first, then seed the rows.
Using Faker for dummy data
Install Faker as a dev dependency when you need realistic dummy rows, then use it inside run() the same way as any other $db->table()->insert() call.
composer require --dev fakerphp/faker<?php
use Faker\Factory as FakerFactory;
use Roolith\Migration\Interfaces\SeederInterface;
use Roolith\Store\Interfaces\DatabaseInterface;
class _Seeder_20250908120000_ab12_ProductData implements SeederInterface
{
public function run(DatabaseInterface $db): void
{
$faker = FakerFactory::create();
for ($i = 0; $i < 10; $i++) {
$db->table('products')->insert([
'name' => $faker->words(3, true),
'price' => $faker->randomFloat(2, 1, 500),
'stock' => $faker->numberBetween(0, 200),
]);
}
}
}Notes
- The shared
migrationsstatus table also tracks seeders (file_type = 'seeder'); it is created automatically when missing. The legacyseedstable from the old internal runner is no longer written. - Only
*.seeder.phpfiles indatabase/seedersrun. Commands accept the full timestamped name or the short logical name; ambiguous short names ask for the full name. - Names allow letters, numbers, underscore, and hyphen; paths,
..traversal, and null bytes are rejected before touching the filesystem. A missing file, missing class, or class not implementingSeederInterfacefails the row instead of crashing the batch. - Class names are generated by the package (
_Seeder_<timestamp>_<rand>_<Name>);App\Database\SeederInterfaceremains as a BC alias. - Seeders run via
roolith/migrationthroughApp\Database\MigrationFactorywithphp roolith seed,seed:status,seed:create,seed:run(plusseeder:*aliases) commands.