Seeders load local, demo, fixture, or bootstrap data into application stores.
The project owns seeding conventions, while the framework provides the lower-level database, Redis, Mongo, model, factory, and faker pieces.
- Common Commands
- Seeder Files
- Factory Seeders
- FakeData Helpers
- Reverse Seeders
- Choosing Stores
- Configuration
- Further Reading
Run the default seeder:
./myxa db:seedRun a specific seeder:
./myxa db:seed UserSeeder
./myxa db:seed Demo/UserSeederGenerate a seeder:
./myxa make:seeder UserSeeder
./myxa make:seeder Demo/UserSeederGenerate a seeder from existing relational database data:
./myxa make:reverse-seed
./myxa make:reverse-seed --limit=100
./myxa make:reverse-seed --tables=users,posts
./myxa make:reverse-seed --table=users
./myxa make:reverse-seed --table=users --ignore-relations=logs
./myxa make:reverse-seed --connection=mysqlOverride default store connections for one run:
./myxa db:seed --connection=mysql
./myxa db:seed --redis-connection=cache
./myxa db:seed --mongo-connection=documentsAsk the selected seeder to reset its own target data before seeding:
./myxa db:seed --truncate--truncate runs automatically for seeders that provide a truncate() method.
Seeder files live in database/seeders by default. The root seeder is:
Database\Seeders\DatabaseSeederEach seeder extends App\Database\Seeders\Seeder:
namespace Database\Seeders;
use App\Database\Seeders\SeedContext;
use App\Database\Seeders\Seeder;
use App\Database\Seeders\ShouldTruncate;
use App\Models\Post;
use Myxa\Database\Factory\FakeData;
final class DatabaseSeeder extends Seeder
{
use ShouldTruncate;
protected function tablesToTruncate(): array
{
return ['posts'];
}
public function run(SeedContext $context): void
{
$fake = new FakeData();
for ($index = 0; $index < 10; $index++) {
Post::create([
'title' => $fake->sentence(3, 6),
'body' => $fake->paragraph(3),
'slug' => $fake->unique('post-slugs')->slug(),
'status' => $fake->choice(['draft', 'published']),
'views' => $fake->number(0, 5000),
]);
}
}
}All models extending Myxa\Database\Model\Model support ::create([...]) for declared, non-guarded attributes.
The app boot process wires models to the shared database manager. Calling $context->database() first is only
needed when the seeder should honor db:seed --connection=... before static model calls.
Use factories for reusable model-shaped records. See Database Models and Queries for defining a model factory.
Use state() for seeder-specific factory values. A callback state receives FakeData, so generated values can
change for each model:
namespace Database\Seeders;
use App\Database\Seeders\SeedContext;
use App\Database\Seeders\Seeder;
use App\Models\Post;
use Myxa\Database\Factory\FakeData;
final class PublishedPostSeeder extends Seeder
{
public function run(SeedContext $context): void
{
Post::factory($context->database())
->count(10)
->state(static fn (array $attributes, FakeData $faker): array => [
'status' => $faker->choice(['draft', 'published']),
'foo' => $faker->string(),
'bar' => 123,
])
->create();
}
}Common helpers:
string(16): random alphanumeric stringalpha(12): random lettersdigits(6): random numeric stringnumber(1, 100): random integerdecimal(10, 99, 2): random floatboolean(25): true roughly 25 percent of the timechoice(['draft', 'published']): pick one valueword(),words(3),sentence(),paragraph(): text helpersemail('example.test'): generated email addressslug(3): generated slugunique()->email(): unique generated valueunique('scope-name')->slug(): unique generated value within a named scope
make:reverse-seed reads live SQL tables and writes a normal PHP seeder under database/seeders.
It is intended for local/demo fixtures where developers have already shaped useful relational data by hand.
By default, it writes up to 20 rows from every relational table:
./myxa make:reverse-seedUse --tables for an exact list. It does not discover relations.
Use --table for one root table plus directly related tables discovered from foreign keys:
./myxa make:reverse-seed --tables=users,posts
./myxa make:reverse-seed --table=users
./myxa make:reverse-seed --table=users --ignore-relations=logsCurrent relation discovery is intentionally shallow: it follows direct incoming and outgoing foreign keys only. It does not crawl multi-hop relation graphs.
Useful shaping options:
./myxa make:reverse-seed --limit=100
./myxa make:reverse-seed --exclude-columns=remember_token
./myxa make:reverse-seed --mask=email,name
./myxa make:reverse-seed --override=status=active
./myxa make:reverse-seed --password="local password"--exclude-columns removes columns from generated rows.
--mask keeps the column but replaces non-null values with deterministic safe fixture values.
--override=column=value keeps the column and writes the same value to every generated row.
Use --password for credential fixtures. The generator detects columns named password or password_hash and
the generated seeder hashes the supplied plain password at seed time with App\Auth\PasswordHasher.
If --connection=mysql is supplied, the generated seeder replays into that named SQL connection. Without it,
the seeder uses the normal db:seed connection defaults and any db:seed --connection=... override.
Generated reverse seeders include truncation support. Truncation deletes selected tables in child-before-parent order where foreign keys are visible. If you ignore a related table that still contains rows pointing at a parent table, your database may reject truncation until those rows are cleared or included.
Use SeedContext when a seeder needs to target a specific backing store:
$context->database('mysql')->insert($sql, $bindings);
$context->redis('cache')->set('demo:ready', true);
$context->mongo('documents')->collection('profiles')->insertOne([
'name' => 'Demo User',
]);Mongo aliases come from config/services.php. When no alias is passed, the context uses
config/seeders.php defaults or CLI overrides.
--truncate is intentionally opt-in per seeder. This keeps destructive behavior local to the seeder that knows which SQL tables, Redis keys, Mongo collections, or external services are safe to reset.
For SQL tables, use the ShouldTruncate trait. For other stores, implement truncate() directly:
use App\Database\Seeders\SeedContext;
use App\Database\Seeders\Seeder;
final class SearchIndexSeeder extends Seeder
{
public function truncate(SeedContext $context): void
{
$context->redis('cache')->delete('search:index');
}
public function run(SeedContext $context): void
{
// Seed Redis data...
}
}Seeder config lives in config/seeders.php:
path: where seeder PHP files livenamespace: namespace for generated and discovered seedersdefault: root seeder classconnections.database: default SQL connection aliasconnections.redis: default Redis connection aliasconnections.mongo: default Mongo connection alias