PHP Luminova: Dependency Injection in Routable Controller Methods
Luminova’s Dependency Injector automatically provides an object to your routable controller methods without manually initializing them. It also handles any values from the URL at the same time.
Luminova’s Dependency Injection (DI) system simplifies how classes and interfaces are bound and resolved. It requires little to no configuration, making it easier to manage dependencies across your application.
With DI, you can inject dependencies directly into controller methods or route closures. This promotes clean, testable, and modular code.
Enabling Dependency Injection
Dependency injection is disabled by default in Luminova. To enable it, add the following line to your environment file:
feature.route.dependency.injection = enable
Note: Luminova does not support injecting dependencies into controller constructors—only routable methods.
Key Features
- Supports binding interfaces or abstract classes to specific implementations.
- Works automatically with routable controller methods and closures.
- Handles both dependencies and URL parameters in the same method.
Controller Injection
Routable controller methods can accept both URI parameters and type-hinted dependencies. The injector will resolve and inject any dependencies regardless of the order in the method signature.
// /app/Controllers/Http/Controller.php
namespace App\Controllers\Http;
use Luminova\Base\BaseController;
use Luminova\Email\Mailer;
use App\Models\UserRepository;
class Controller extends BaseController
{
public function myMethod(Mailer $mailer, UserRepository $user, int $id): int
{
$service->doSomething();
return $this->view('myView', ['account' => $user->find($id)]);
}
}
$mailer
is resolved from the system.$users
is injected via DI.$id
is extracted from the route.
The injection engine handles everything behind the scenes — no container fetch or manual wiring needed.
Closure Injection
Luminova also supports DI in routable closures, allowing you to inject dependencies directly into the route handlers.
// /routes/web.php
use Luminova\Core\CoreApplication;
use Luminova\Email\Mailer;
use App\Models\UserRepository;
$router->get('/example', static function(CoreApplication $app, Mailer $mailer, UserRepository $user, int $id): int {
$service->doSomething();
return $app->view('myView')
->render(['account' => $user->find($id)]);
});
Command Group Method with Dependency Injection:
// /routes/cli.php
use Luminova\Interface\RouterInterface;
$router->group('http-get', static function(RouterInterface $router){
$router->command('request', 'Command::myMethod');
});
Note: Ensure that the method parameters are correctly typed and that the injected class is available and instantiable to resolve the dependencies.
Custom Dependency Injections
Luminova’s DI system allows you to map abstract classes or interfaces to concrete implementations. This makes it easy to resolve dependencies automatically during routing. You can configure bindings in your application class using either:
- The static
Luminova\Routing\DI::bind()
method, or - The protected
bind()
method available in yourCoreApplication
class.
Binding Dependencies
You can bind simple classes or complex closures depending on your use case.
Basic Binding (Interface → Class)
use Luminova\Core\CoreApplication;
class Application extends CoreApplication
{
protected function onPreCreate(): void
{
// Bind an interface to a concrete class
$this->bind(MyInterface::class, MyConcreteClass::class);
}
}
Or using the DI class directly if needed to:
use Luminova\Routing\DI;
DI::bind(MyInterface::class, MyConcreteClass::class);
Binding with a Closure (Custom Instantiation)
Useful when the class requires additional setup or constructor dependencies.
$this->bind('custom_service', function () {
return new MyService(dependency: new SomeDependency());
});
// Or using DI directly
DI::bind('custom_service', function () {
return new MyService(dependency: new SomeDependency());
});
Logger Example
$this->bind(\Psr\Log\LoggerInterface::class, function () {
return new \MyApp\Log\FileLogger('/writable/logs/app/');
});
// Or using DI
DI::bind(\Psr\Log\LoggerInterface::class, function () {
return new \MyApp\Log\FileLogger('/writable/logs/app/');
});
Notes:
- Recommend class names for simple, stateless objects.
- Use closures for services needing constructor logic or additional dependencies.
- Bound services are resolved automatically during controller or route execution.