spiral/prototype
Prototype generation tools for Spiral Framework to speed up application scaffolding. Provides helpers to define models, repositories, and services with less boilerplate, keeping code consistent while accelerating development and iteration.
Installation Add via Composer:
composer require spiral/prototype
Require the package in bootstrap/app.php (Laravel 8+):
$app->register(\Spiral\Prototype\PrototypeServiceProvider::class);
First Use Case: Auto-Wiring a Class Define a class with type-hinted dependencies (no manual binding needed):
class UserService {
public function __construct(private UserRepository $repository) {}
}
Laravel’s container will now auto-resolve UserRepository via spiral/prototype.
Where to Look First
config/prototype.php: Default settings (e.g., ignored namespaces, custom resolvers).app/Providers/AppServiceProvider.php: Override default behavior via Prototype::extend().Auto-Wiring Without Bindings
Replace manual bind() calls with type-hinted constructors:
// Before
$app->bind(UserService::class, function ($app) {
return new UserService($app->make(UserRepository::class));
});
// After (spiral/prototype handles it)
Custom Resolvers Extend for non-standard dependencies (e.g., closures, dynamic classes):
Prototype::extend(function ($container, $abstract, $parameters) {
if ($abstract === Closure::class) {
return fn() => new DynamicClass(...$parameters);
}
});
Ignoring Namespaces
Exclude specific namespaces from auto-wiring in config/prototype.php:
'ignored_namespaces' => [
'App\IgnoredNamespace\\',
],
Integration with Laravel’s Container Use alongside Laravel’s built-in DI:
// Works seamlessly with Laravel’s make()/singleton()
$service = app()->make(UserService::class);
Dynamic Class Resolution Resolve classes defined at runtime (e.g., via config):
Prototype::extend(function ($container, $abstract) {
if (is_string($abstract) && class_exists($abstract)) {
return $container->make($abstract);
}
});
Parameter Overrides Pass constructor arguments dynamically:
$service = app()->makeWith(UserService::class, ['repository' => $customRepo]);
AST Limitations
new calls may not resolve.
Fix: Use explicit bindings or custom resolvers.prototype.debug in config to log unresolved classes.Circular Dependencies
spiral/prototype may not handle circular references like Laravel’s container.
Fix: Refactor to use interfaces or inject the container itself.Namespace Conflicts
ignored_namespaces and use fully qualified names.Performance Overhead
'enabled' => env('APP_ENV') !== 'production').Log Unresolved Classes
Enable debug mode in config/prototype.php:
'debug' => true,
Check Laravel logs for PrototypeResolver warnings.
Override Resolvers Temporarily
Use Prototype::extend() in a service provider to test custom logic without modifying core files.
Custom Resolver Priority Register resolvers in reverse order of priority (last registered wins):
Prototype::extend($customResolver1);
Prototype::extend($customResolver2); // Higher priority
Post-Resolution Hooks Modify instances after resolution:
Prototype::extend(function ($container, $abstract) {
$instance = $container->make($abstract);
if ($abstract === UserService::class) {
$instance->setLogger(app()->make(Logger::class));
}
return $instance;
});
Integration with Packages
laravel/tinker) may bypass the container.
Fix: Patch the package’s bootstrap or use app()->bind() as a fallback.How can I help you explore Laravel packages today?