In the previous lesson, we explored how Drupal services are vital components that provide reusable functionalities across modules. Today, we'll dive deeper into how you can inject these services into a controller using the constructor method, allowing you to leverage Drupal's robust service container effectively.
Understanding Service Injection
Service injection is a design pattern used in Drupal that enhances modularity and decouples services and classes. By injecting services into a controller, we reduce the dependency on static calls, making our code easier to maintain and test.
Why Use Constructor Injection?
Constructor injection offers several benefits:
- Improved Testability: It's easier to write unit tests for your controllers as you can mock service dependencies.
- Better Code Organization: Dependencies are explicit, making the code more understandable and manageable.
- Loose Coupling: Reduces the direct dependency between the class and the service container.
Example Scenario: A Custom Weather Controller
We'll create a simple controller that fetches weather information using Drupal's HTTP client service. This practical example will illustrate how injecting services through the constructor works.
Step 1: Define Your Module
Ensure you've created a custom module as described in previous lessons. For consistency, let's assume you're continuing with a module named weather_module
.
Step 2: Create a Controller
Inside your module directory, create a src/Controller
directory if it doesn't exist, and then create a PHP file named WeatherController.php
.
// weather_module/src/Controller/WeatherController.php
namespace Drupal\weather_module\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Controller\ControllerBase;
use GuzzleHttp\ClientInterface;
class WeatherController extends ControllerBase {
protected $httpClient;
public function __construct(ClientInterface $http_client) {
$this->httpClient = $http_client;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('http_client')
);
}
public function content() {
// Example usage of the service
$response = $this->httpClient->get('http://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=London');
$data = json_decode($response->getBody(), TRUE);
return [
'#markup' => $this->t('The weather in London is: @condition with a temperature of @temp degrees.',
['@condition' => $data['current']['condition']['text'], '@temp' => $data['current']['temp_c']]),
];
}
}
Step 3: Define a Route
Create a YAML file named weather_module.routing.yml
in your module's root directory to define the path for your controller.
weather_module.weather:
path: '/weather'
defaults:
_controller: '\Drupal\weather_module\Controller\WeatherController::content'
_title: 'Weather Information'
requirements:
_permission: 'access content'
Step 4: Enable the Module and Test
- Clear the cache using `drush cr` or by navigating to Admin > Configuration > Development > Performance.
- Enable the module via the command line with
drush en weather_module -y
or through the admin interface. - Access the URL
/weather
on your site to see the weather information displayed.
Conclusion and Next Steps
In this lesson, you learned how to inject services into your controller classes using constructors, allowing you to build more modular and testable code. Make sure you understand this pattern well as it forms the foundation for the next steps in module development.
In the next lesson, we will explore how you can access services in plugins or other Drupal classes. This will help you utilize the service container outside of controllers, expanding your ability to develop powerful and rich Drupal functionality.
Stay tuned, and let’s keep building!