directorytree/dummy
A Laravel package from DirectoryTree for generating and managing dummy data, making it easy to scaffold placeholder records and test content during development. A lightweight helper for faster prototyping, seeding, and testing in your Laravel apps.
Install as a dev dependency, pinned to the current major:
composer require directorytree/dummy:^2.0.1 --dev
Do not add it to require. There is no runtime use case for this package, and every release note since v1.1.0 has been about test-time ergonomics.
Where to look first, in order:
Data attribute interaction changed. If you find a blog post or snippet from the 1.x era, assume the names are wrong until the source says otherwise.HasFactory states and factory attribute expansion.HasFactory trait re-export and the Data attribute — these two are where the package actually does something Laravel-native factories don't.First use case to try: pick one model whose factory definition is a long hand-maintained array of Faker calls and duplicate constants, and one model with a state method you keep copy-pasting between factories. Those two are the sweet spot. Swap the array for a dummy call and move the state onto the model class via HasFactory. If neither of those reads better afterwards, stop — you've answered the question for free.
// The kind of thing worth replacing: repeated array plumbing in a factory
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
// ...20 more lines that are identical across 4 factories
];
}
Augment, don't rewrite. The strongest pattern is layering this over working native factories rather than replacing them. Keep definition() doing what it does; use the package for (a) dynamic/named states attached to the model, and (b) attribute expansion that reuses an existing factory definition instead of duplicating it (v1.6.0). That keeps your revert path to "delete the trait line."
Dynamic factory states on the model. Since v1.3.0 you can declare states on the class using HasFactory rather than as methods on each factory. Use this when the same state (:published, :expired, :with-refund) is needed from multiple factories, tests, and seeders — that's exactly the duplication it removes. Keep state names identical across related models so a test reads as a graph, not a lookup.
Factory attribute expansion for nested graphs. v1.6.0's expansion means a child factory can inherit attributes from the parent's factory definition. Reach for it in order → payment → refund style fixtures where you have been manually threading foreign keys and denormalized columns. This is the single feature most likely to pay for the package in a feature-test suite.
// Sketch — confirm exact names against the v2.x source
$order = Order::factory()->dummy(); // model-aware record
$refund = Refund::factory()
->for($order)
->dummy(); // expands attributes from the order
Wrap the call sites. Introduce an internal trait or base test case (e.g. App\Testing\BuildsDummyModels) that forwards to the package. Two benefits: a single file to change on a 2.x bump, and a single file to delete if you walk away. Scattering DirectoryTree\Dummy\... imports across 40 test files is how a one-day evaluation becomes a one-week reversal.
Seeders and QA environments. This is the highest-leverage use outside tests. Because generation is model-aware rather than Faker-string-soup, a seeder producing varied demo records for a QA/demo environment stays readable and survives schema changes better than hand-written fixtures. Gate it on a local/APP_ENV !== production check and keep the package in require-dev so production deploys physically cannot autoload it.
Let generics do their job. v1.5.1 added generics. If you run PHPStan or Psalm at level 6+, run the analysis as part of the evaluation rather than after adoption — clean static analysis is the cheapest objective signal you'll get here, and it will also surface any 1.x-era signatures you accidentally copied.
Compose with Data attribute deliberately. v2.0.0 reworked how the Data attribute interacts with the rest of the package. If you need per-attribute overrides or typed values, that's the extension point — but read the 2.x source before designing around it, because the 1.x behaviour is not the 2.x behaviour.
Pin the major, and read every minor's notes before bumping. v2.0.0 was a breaking rename. That was the right call for conflict avoidance with other packages, but it tells you the public surface is still settling. A ^2.0.1 constraint plus a short upgrade runbook is the correct posture; * or dev-main is not.
Upgrade to at least 2.0.1 for the filled(...) fix. Pre-2.0.1, filled(...) threw when an attribute held a non-Stringable object — which is exactly what happens when you hand it a model instance, a Carbon date, or a backed enum cast. If you see an unexplained exception on an attribute you're certain is populated, this is your culprit.
Closure binding in HasFactory is a known sharp edge. v1.6.1 fixed late static closure binding. If you're writing state closures that rely on static:: or on $this resolving to the model, verify you're on ≥1.6.1 semantics (i.e. on 2.0.1 you're fine, but don't copy patterns from 1.6.0-era examples).
No Laravel framework dependency ≠ no Laravel needed for bootstrap. v1.1.0 dropping the hard framework dependency widens where this can live, but the semantics are still Laravel-shaped (HasFactory, Arr, factory conventions). In a bare package repo or CLI harness you will likely need Testbench (or a hand-rolled container + Faker bootstrap) to make it behave. Budget that before promising it works "without Laravel."
data_* helpers are gone. v1.3.1 moved everything to the Arr helper. Old snippets using data_get/data_set-style helpers against package internals will not apply.
Version support is a moving matrix, not a guarantee. Laravel 12 landed in v1.2.0, Laravel 13 in v1.4.0. Treat anything below Laravel 11 as untested. If your estate is mid-upgrade, test the dummy layer early in the upgrade — it's cheap to check and expensive to discover mid-sprint.
Extension points ranked by safety: (1) your own wrapper trait; (2) the Data attribute; (3) forking. With 43 stars and zero known dependents there's no upstream pressure to take your PR, so a fork is a legitimate, low-cost option — the codebase is small and MIT. Decide which of these you'd use before you adopt, and write it down.
Debugging playbook: the package's filled(...) and attribute resolution are the two paths most likely to surprise you. When a value comes out wrong, dump the resolved attribute array before it hits the model rather than guessing at the state chain — the failure is usually in attribute merging order, not in Faker. Then check the package's tests for the same scenario; they're more current than any external write-up.
Do the review checklist item. Add "not in production require" to your PR template or a CI assertion (composer show --direct filtered to require). Given zero external dependents and single-maintainer concentration, accidental promotion to production is the only failure mode here that isn't just wasted migration effort.
Measure the thing you claimed. Fixture authoring time for the two or three factories you actually replaced, plus CI wall-clock and flake rate on that suite. If the numbers aren't clearly better than native factories plus fakerphp/faker, revert — the exit is a revert, not a rewrite, and that's precisely what makes the trial cheap.
How can I help you explore Laravel packages today?