laravel/legacy-factories
Provides support for the previous generation of Laravel factories (7.x and earlier) on Laravel 8.x and above, letting existing factory code keep working after upgrading.
Architecture fit: laravel/legacy-factories is a deliberately thin compatibility shim, not a feature package. It reinstates the pre-8.x factory contract — the global factory() helper plus $factory->define() / $factory->state() closure definitions — on Laravel 8 and later. It does not patch or fork core; it layers a helper and service provider over the existing Eloquent model layer, so the blast radius is confined to test and seeding paths and the package ships as a require-dev dependency. For any application carrying a large corpus of closure factories across an upgrade, this is the lowest-effort way to remove factory rewrites from the critical path of a framework jump.
Integration feasibility: High and cheap. composer require laravel/legacy-factories --dev, auto-discovery, no application code changes, no configuration surface. Validation is binary: install, run the existing suite, observe parity. Realistic engineering effort is hours, not days. The material constraint is scope: the package restores legacy definitions only. It does not bridge legacy closures into the modern fluent API (ModelFactory::new(), ->for(), ->has(), ->state(), asJSON()), so a hybrid codebase runs two parallel definition systems that do not share state. That is an architectural boundary to plan around, not a defect to work around.
Technical risk: Moderate-low, but concentrated in two places. First, provenance: the supplied metadata points the repository at laravel/framework while the release notes reference PRs against a distinct laravel/legacy-factories repository, and the dependents: 0 figure plainly understates real-world usage for a first-party Laravel package. Underlying signals (Laravel org, MIT, consecutive releases aligned to each Laravel major) indicate an official, actively maintained package — but the TPM should confirm the installed vendor/source in the lockfile before treating it as supported. Second, version lockstep: support arrives one Laravel major at a time (9 → v1.2.0, 10 → v1.3.2, 11 → v1.4.0, 12 → v1.4.1, 13 → v1.4.2), so every future Laravel upgrade is gated on a corresponding shim release. Cadence has historically been fast, but a lag becomes a hard upgrade blocker. The third, structural risk is the deprecation ceiling: legacy factories are end-of-life upstream and will receive no new capability.
Key questions:
$factory->define/->state definitions and factory() invocations, clustered by module?factory() referenced outside tests — seeders, demo-data scripts, internal admin tooling, third-party packages?Stack fit: Laravel 8 through 13 with closure-based factories still in database/factories. Strong fit where test coverage is a release gate and the factory suite is large enough that a big-bang rewrite would dominate an upgrade cycle. Poor fit for greenfield services, teams already on class-based factories, or any codebase small enough that a direct conversion is cheaper than adopting and later retiring a dependency.
Migration path: Treat this as a bridge with a scheduled demolition date, sequenced explicitly.
$factory->define usage. New factories are class-based from day one. This is the single highest-leverage governance step; without it the legacy surface grows and the exit date recedes.composer.json at zero call sites, as an explicit milestone rather than a cleanup afterthought.Compatibility: Version-pair the shim to the Laravel major and hold framework upgrades behind the corresponding shim release — an intentional, temporary constraint on the upgrade train. Validate helper/service-provider load order where legacy and class-based factories coexist, and manually register the provider in orchestra/testbench-based suites if auto-discovery does not fire.
Sequencing: Install before the Laravel upgrade, not after. The whole value proposition is decoupling two risky changes: land the framework jump with factories untouched, then run the factory modernization as a separate, later workstream. Bundling both into one release recreates the big-bang risk the package exists to avoid.
Maintenance: Near zero while pinned — minimal surface, no configuration, MIT licence, official org. The real cost is that it is a dependency with a fixed expiry date. Carry it explicitly on the tech-debt register with a named owner, a target removal release, and a mandatory review checkpoint at every Laravel upgrade.
Support: No commercial SLA. Escalation is effectively fix-forward; the genuine mitigation is the conversion backlog, not the package. Plan on the assumption that if a future Laravel major lands without a shim release, the team either converts factories under deadline pressure or defers the framework upgrade — both are schedule events the roadmap should anticipate rather than absorb as surprises.
Scaling: No production runtime dimension — this never executes in front of users, so latency, throughput, and availability are non-issues. The scaling risk is organizational: a codebase with two factory idioms permanently doubles the onboarding surface. Document the convention, name the legacy pattern as deprecated in the contributing guide, and enforce it in CI so the pattern does not propagate through new hires.
Failure modes:
factory() undefined after install → autoload or config cache; composer dump-autoload and php artisan optimize:clear.Ramp-up: Engineering ramp is hours. The TPM ramp is governance: build the inventory, set the convention, publish the exit criteria, and size the conversion epic by call-site count rather than by package adoption. Default recommendation: adopt now to unblock the framework upgrade in a single release, and commit publicly to retiring the shim within a defined number of release cycles.
How can I help you explore Laravel packages today?