trappar/alice-generator
A Laravel package that generates Nelmio Alice YAML fixture files from your database, making it easy to capture realistic records for testing and seeding. It bridges Eloquent models with Alice's expressive fixture format and artisan workflows.
Treat this as a bootstrapping tool, not a runtime dependency. The package's job is simple: take live PHP objects (usually Doctrine entities), walk them recursively, and emit Alice-compatible fixture data — class name, fixture name, properties — so you get a starting YAML file instead of hand-writing one.
Install it as a dev dependency and check the resolver before anything else, because it was last released in early 2019 and pulls in the PHP 7-era stack (nelmio/alice v3, fzaninotto/faker, doctrine/annotations):
composer require --dev trappar/alice-generator -W --dry-run
If that resolves without dragging your app backwards, the first use case is a ten-line script that dumps a slice of a real database:
// bin/dump-fixtures.php
require __DIR__ . '/../vendor/autoload.php';
$em = require __DIR__ . '/../bootstrap/em.php'; // whatever boots Doctrine
$users = $em->getRepository(App\Entity\User::class)->findBy([], null, 10);
foreach ($users as $user) {
$user->getPosts()->initialize(); // force lazy collections first
}
$generator = new Trappar\AliceGenerator\AliceGenerator();
$fixtureSet = $generator->generateFixtures($users);
// walk the fixtures into the Class: { Name: { prop: value } } shape Alice expects
$rows = [];
foreach ($fixtureSet->getFixtureObjects() as $fixture) {
$rows[$fixture->getClass()][$fixture->getName()] = $fixture->getProperties();
}
file_put_contents('fixtures/users.yml', Symfony\Component\Yaml\Yaml::dump($rows, 6, 2));
Where to look first, in this order:
src/AliceGenerator.php — the whole entry point is one method.src/FixtureGenerationContext.php — everything tunable lives here (recursion depth, naming, faker/metadata wiring).src/Naming/ — the default namer (Class_1, Class_2, …) is what you will almost certainly replace.src/Annotation/ — @Faker and @Skip are how you make the output safe and stable.The package is small (a few dozen files) and the README is thin, so read the source once. Verify the fixture-object accessors (getClass()/getName()/getProperties()) against the version you install before wiring them into a command.
The realistic first win: point it at 20–50 rows of a staging database, get a YAML file, load it back with Alice, and confirm your app renders. That's a half-hour job that replaces a day of fixture typing.
Wrap it in a console command, not a script. One command per aggregate root, with --limit, --depth, and an output path. Guard it on APP_ENV=local and give it a read-only database user. This is the difference between "a tool the team uses" and "a file somebody ran once".
Scrub at the source with annotations. Real data contains real PII. Annotate the entity properties you must not commit — names, emails, phone numbers, addresses — with @Faker, and annotate the ones Alice should not care about with @Skip:
/**
* @Trappar\AliceGenerator\Annotation\Faker("safeEmail")
* @Trappar\AliceGenerator\Annotation\Skip() // on $id, $passwordHash, $apiToken
*/
private $email;
Then a generated fixture is email: '<safeEmail()>' instead of a customer's actual address. This is the single most valuable pattern in the package.
Replace the default namer with business keys. User_1, User_2 makes every regeneration a 400-line diff and produces references (@User_3) that nobody can read. Implement NamerInterface so a user becomes user_admin, a category becomes category_electronics. Now references are stable, diffs are reviewable, and hand-written fixtures can reference generated ones.
Dump one side of each bidirectional relation. The generator recurses, so User → posts → author → posts … blows up. Pair a modest maximum recursion depth on the context with @Skip on the inverse-side collection. Let Alice resolve the owning side via @user_admin references instead of trying to print the graph twice.
Load it back through Alice, not by hand. Use nelmio/alice's loader plus doctrine/data-fixtures' Alice loader (or hautelook/alice-bundle if you're on Symfony), and let Alice's persist order the inserts so foreign keys resolve. For tests, commit the YAML next to the test class and load it into a SQLite schema in setUpBeforeClass.
Keep generated and curated fixtures in separate directories. Generated files are a bootstrap artifact; curated files are the ones humans edit to add edge cases, empty collections, nullable fields, and <dateTimeBetween(...)> expressions. Regeneration overwrites the first set and must never touch the second.
Use it to snapshot object graphs you already build in tests. If you have a factory or builder producing a complex graph, feeding it through the generator gives you a readable, diffable YAML representation — genuinely useful when debugging "what does our fixture actually contain".
Error: must not be accessed before initialization. Initialize defaults or @Skip those properties.doctrine/annotations. If you've migrated your entities to attributes, this package won't see @Faker/@Skip and will happily dump raw values.PersistentCollections serialise as empty or as __CG__\App\Entity\User class names in the output. Call initialize() (or unwrap()) on every relation before generating.id with @Skip in the generator, not with a regex on the YAML.DateTime/DateTimeImmutable and binary/blob/resource properties are the usual sources of garbage output or hard failures. Normalise dates in a post-processing pass; @Skip anything stream-like.<firstName()> at load time, so every reload produces different data and any assertion baked against it flakes. Seed Faker at bootstrap if you need reproducible databases.iterate()/toIterable() on the query and dump per aggregate root with a --limit.NamerInterface, custom object/faker handlers, metadata, and value filters. The built-in naming and default metadata handling are the parts you will outgrow first — plan to replace them rather than fight them.How can I help you explore Laravel packages today?