lkovace18/entity-factory-bundle
A Symfony bundle offering a factory-based approach to creating and hydrating entities, with configurable definitions to simplify fixtures and test data setup in your applications.
Scoping note before you start. Despite the Laravel framing of this assessment, lkovace18/entity-factory-bundle is a Symfony bundle built around Doctrine entities. The -bundle suffix is Symfony convention, "entity" is Doctrine vocabulary, and there is no service provider, no auto-discovery entry, and no integration with Illuminate\Database\Eloquent\Factories\Factory. It will not boot in a Laravel app as-shipped. So there are exactly two ways a Laravel developer "gets started" here:
packages/ namespace you vendor and own.Minimal steps, in order:
composer.json first. Look for symfony/* and doctrine/* in require vs. require-dev, plus the PHP constraint. Any hard Symfony or Doctrine ORM constraint is a blocker for Laravel and a likely platform-pin risk even in Symfony. This one file usually ends the evaluation.require-dev only, pinned to an exact tag or commit hash. Zero stars and zero dependents means nothing is holding the API stable. Register the bundle in config/bundles.php under env: ['test'] so it never loads in prod.src/. That is the contract: what method builds the entity, what method persists it, and how it acquires the EntityManager. Everything else in the package hangs off those three answers.tests/ before the README. At this maturity level the test suite is the documentation. If there is no test suite, stop — you are the QA department.setUp() that hand-constructs an entity, assigns a dozen properties, and calls persist()/flush() with a single factory call. Start with the non-persisted path — build an object, assert on defaults, no database — then graduate to persisted creation once you trust the builder.One factory per entity, with complete defaults. The whole value of a factory is that every default is valid. Define the factory once with a fully-populated "happy path" entity and let tests deviate only where they care. A factory with one required field missing just moves the boilerplate.
Overrides at the call site, not in the factory. The description's headline feature is per-case overrides. Use it as the primary variation mechanism: build the default entity, pass an override map for the two or three attributes the test actually asserts on. This keeps the factory from growing a branching if forest.
Compose named variants on top of the base factory (admin user, expired order, unpaid invoice) as small wrapper methods that pass a fixed override map — never as separate factories and never as flags threaded through the base. In Laravel terms this is the equivalent of ->state([...]); you're building it by hand.
Persisted vs. non-persisted is a deliberate split, not a default. Use non-persisted creation for unit tests and for building object graphs you'll assert on in memory. Use persisted creation for functional tests, fixtures, and seed data. Don't let the factory quietly persist when the test didn't ask for a database — that's the most common source of slow, flaky suites.
Relationships: build parents first, inject via overrides. Factories here have no relationship graph or for()/has() magic, so the caller owns the topology: create the parent, pass it into the child's override map. This is fine for two models and gets ugly fast at ten — which is exactly why mature solutions (foundry, alice) exist.
Bulk generation via a loop with index-aware overrides. For fixtures, iterate N times and vary attributes by index (unique emails, staggered dates) rather than hoping randomness produces valid distinct data.
Determinism where it matters. Any randomized defaults should be seedable, or explicitly disabled in tests that assert on exact values. Random data in a test is a future flake.
If you port to Laravel, the mapping is nearly 1:1: factory definition → Model::factory()->state([...]), non-persisted → make(), persisted → create(), override map → ->state([...]), named variant → a dedicated state method. Eloquent gives you sequences, recycling, and relationship factories for free, so the port shrinks rather than grows.
-bundle + Entity means Symfony + Doctrine. Confirm compatibility in composer.json before spending an hour; there is no Laravel service provider to register and no illuminate/* constraint to satisfy.require. Dev/test tooling only. If a symfony/* or doctrine/* entry lands in your production dependency tree, you've inherited an upgrade blocker that will outlive the package's usefulness.EntityManager; without clearing it you get stale objects and phantom rows across test methods. Check that your test base class clears or recreates the EM.Factory and Entity are generic; alias imports or namespace your internal copies under something app-specific so they don't shadow or confuse Eloquent's Factory in IDE resolution and static analysis.How can I help you explore Laravel packages today?