You can define a service that acts as a controller and use the __invoke() method to handle incoming requests. This approach can simplify your code because you don’t need to explicitly reference a specific method in the routing YAML file. Instead, you can reference the service ID directly, and Drupal will automatically call the __invoke() method.
Here’s how to implement it:
Steps to Define a Controller Service Using __invoke()
1. Define the Controller as a Service
In your my_module.services.yml file, define a service that will act as a controller. This service will automatically have its __invoke() method called when it is referenced in the route.
yaml
services:
my_module.custom_invoke_controller:
class: Drupal\my_module\Controller\CustomInvokeController
arguments: ['@some_dependency_service'] # Inject dependencies as needed
tags:
- { name: controller.service_arguments }
- my_module.custom_invoke_controller is the service ID.
- class refers to the PHP class implementing the controller logic.
- arguments allows you to inject services, such as a custom service, if necessary.
2. Create the Controller Class with __invoke() Method
Now, define the controller class. This class must include the __invoke() method, which will handle the request when the route is accessed.
php
namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;
use Drupal\my_module\Service\CustomService;
class CustomInvokeController extends ControllerBase {
protected $customService;
// Constructor for injecting the service.
public function __construct(CustomService $customService) {
$this->customService = $customService;
}
// The create() method to instantiate the service via the service container.
public static function create(ContainerInterface $container) {
return new static(
$container->get('my_module.custom_service')
);
}
// The magic __invoke() method, which will be called by default.
public function __invoke() {
$data = $this->customService->getData(); // Access the service.
return [
'#markup' => $this->t('Data: @data', ['@data' => $data]),
];
}
}
In this example:
- The __invoke() method is called automatically when the route is accessed.
- You can still inject services like CustomService through the constructor, making use of dependency injection.
3. Configure Routing to Use the Service
In the routing YAML file (my_module.routing.yml), reference the service ID without specifying a method. Drupal will automatically call the __invoke() method.
yaml
my_module.custom_invoke_page:
path: '/custom-invoke-page'
defaults:
_controller: 'my_module.custom_invoke_controller' # Reference the service ID
_title: 'Custom Invoke Page'
requirements:
_permission: 'access content'
- _controller: Instead of pointing to a specific method (like CustomPageController::content), you just reference the service ID (my_module.custom_invoke_controller).
- When a user accesses /custom-invoke-page, Drupal will automatically call the __invoke() method in the CustomInvokeController.
Why Use __invoke()?
- Simplicity: You don’t need to specify the method name in the routing file. The __invoke() method is automatically called when the service is referenced.
- Clean Code: This approach can lead to cleaner code, especially for simple controllers that only need one action.
- Consistency: If your controller has only one main action, using __invoke() provides a consistent and predictable interface.
Summary
- Service Definition: Define your controller as a service in services.yml.
- Magic __invoke(): Implement the __invoke() method in the controller class to handle requests.
- Routing: Reference the service ID in the routing file without specifying a method. Drupal will automatically call the __invoke() method.
- Dependency Injection: You can still inject services or other dependencies into the controller, as with any other service-based controller.
Comments