illuminatech/array-factory
Illuminatech/Array Factory is a small Laravel/PHP utility for building arrays from reusable definitions. It supports defaults, overrides, sequences, and nested values, making test data, fixtures, and configuration arrays easier to generate consistently.
Minimal steps
composer require illuminatech/array-factory. The package targets the Laravel/Illuminate container, so it will pull illuminate/contracts and illuminate/support at compatible versions if you're not already on a full Laravel app.src/Factory.php (the whole package is essentially one class) and the README.md examples — this is a small package (~21 stars), so the source is the real documentation. Skim the composer constraints for the supported Illuminate versions before adopting.NOASSERTION, meaning the license file wasn't machine-recognizable. Check LICENSE/composer.json in the tree and get sign-off if you're in a company that gates dependencies.First use case
The natural entry point is replacing imperative app()->make() + setter boilerplate when an object needs both container-resolved dependencies and a few data-driven options:
use Illuminatech\ArrayFactory\Factory;
$factory = new Factory();
$notifier = $factory->make([
'class' => SlackNotifier::class,
'channel' => '#deployments', // public property (or setter) assignment
'__construct' => [ // only what the container can't guess
// e.g. explicit scalars
],
]);
The class key selects the type; every other non-reserved key is assigned to the instance, and any constructor parameters you don't specify are resolved from the Laravel container. That combination — container-aware construction plus array-driven configuration — is the entire value proposition. If you're not already sitting on a config array you want to turn into an object, app()->make() is simpler and you should use it instead.
Config-driven strategy/plugin registries
This is where the package earns its place. Keep a config map of named definitions and resolve them lazily:
// config/payments.php
return [
'gateways' => [
'stripe' => [
'class' => \App\Payments\StripeGateway::class,
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
'paypal' => [
'class' => \App\Payments\PaypalGateway::class,
],
],
];
$gateway = $factory->make(config("payments.gateways.{$name}"));
Consumers only hold an interface; the concrete class and its scalar options live in config, and constructor dependencies (HTTP client, logger, etc.) still come from the container. This is the pattern that keeps a switch statement out of your service classes.
Named definitions as a lightweight registry
If the factory supports named definitions (via define() in your version — confirm in source), register them once in a service provider and resolve by string key. This gives you a poor-man's plugin system without new container bindings per class:
// AppServiceProvider::register()
$this->app->singleton(Factory::class, function ($app) {
$factory = new Factory();
// $factory->define('report.pdf', [...]);
return $factory;
});
Bind it as a singleton so you're not rebuilding the definition map on every resolution, and inject Factory (or a thin wrapper) rather than resolving it with app().
Closures/callables for conditional construction
When the definition needs branching (e.g. pick a driver based on config), prefer a callable over a giant array. It keeps the "how" out of the data and lets you unit-test the branch.
Integration tip: keep domain code unaware
Inject the resolved object, not the factory, wherever possible. The factory is infrastructure; leaking Factory into business logic means every caller is now coupled to a definition format. Resolve in a service provider or a dedicated *Factory/*Manager class.
Testing pattern
Because definitions are plain arrays, tests can inject variants without touching bindings:
$factory = new Factory();
$subject = $factory->make([
'class' => ReportGenerator::class,
'format' => 'csv',
]);
This is genuinely nicer than app()->instance() gymnastics when the only variation is a constructor/property value. For anything more complex, fall back to normal container bindings — don't force this package into dependency-swapping duty.
Composition
Nest definitions only when the package explicitly resolves them (i.e. an inner array with a class key). Otherwise you'll get an array assigned to a property where an object was expected. When in doubt, build children first and pass the instance.
class is reserved. Don't define a property named class. Same for any other key the package treats specially (__construct for constructor arguments, and any documented keys like a factory callable). Dump a suspicious definition and read Factory.php for the exact reserved list.BindingResolutionException, not a factory-specific exception. Wrap resolution if you want a domain-specific failure message; otherwise you'll be reading stack traces into Illuminate\Container.composer.json and vendor the class mentally (it's small). Don't build architecture that depends on features you didn't personally verify in the source.dd($definition) right before make(). Most "wrong object" bugs are a typo'd config key becoming a dynamic property rather than an error.Factory or decorate it and register the decorator in the container. Because it's a single small class, composition is cheap and safer than patching vendor code.$this->app->bind(Interface::class, Impl::class) are clearer, better supported by tooling/static analysis, and have no license or maintenance caveats. Reserve this package for genuinely data-driven construction — config files, database-stored class names, user-supplied plugin maps.How can I help you explore Laravel packages today?