7

For the first time I am trying to architect a reasonably complex Drupal 8 module, and not sure of best practice for the controller.

  • I need to route paths to a controller.
  • I intend the controller to be thin (very little logic, doesn't need to testable)
  • Heavy lifting will be via service class/es configured and defined by the module.

Extending ControllerBase should be the right path for a thin controller, however when I look in Core at the moment I notice a number of controllers that both extend ControllerBase and implement ContainerInjectionInterface. I would only implement ContainerInjectionInterface to be able to inject a different container for testing, not just selection of service in the create method, but Core is still in flux and I am not sure exactly why they are doing this.

I feel that I should extend ControllerBase, add a __construct that fetches the container and pulls in the non-standard service/s that I require via $this->container()->get() and indeed that does work. I also don't need the create method required to implement ContainerInjectionInterface.

Am I correct? I know I will need to go through a few iterations of getting the services right but want to get THIS bit right from the start and understand why I am doing it.

Thanks in advance.

4
  • 1
    In this context, I have more context with the Symfony components rather than Drupal. With that said, I don't understand why you'd create a special container solution with __construct rather than just implement ContainerInjectionInterface. Commented Nov 14, 2013 at 16:10
  • Because with only extending controllerBase I can access the container provided in my __construct function to get the instance of my service (a bunch of standard services are automatically available but not my non-standard one). Commented Nov 14, 2013 at 22:27
  • Sorry editing on a tablet, this is extension of above comment. If I implement ContainerInjection interface I have to add a create method to pull desired services from an injected controller, AND a construct method to receive them as parameters, rather long winded. Commented Nov 14, 2013 at 22:38
  • ControllerBase::container() is being removed shortly. Do not rely on it. Commented Nov 21, 2013 at 22:22

1 Answer 1

7

ContainerInjectionInterface is used as a part of a static factory method pattern.

This way, a class's constructor can specify all of the services it needs, and its factory method, in this case create(), can satisfy the constructor by retrieving services from the container.

The instantiated controller should never have its own copy of the container, if at all avoidable.

A brief example:

<?php namespace Drupal\book\Controller; use Drupal\book\BookManager; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Symfony\Component\DependencyInjection\ContainerInterface; class BookController extends ControllerBase implements ContainerInjectionInterface { protected $bookManager; public function __construct(BookManager $bookManager) { $this->bookManager = $bookManager; } public static function create(ContainerInterface $container) { return new static( $container->get('book.manager') ); } public function bookRender() { $book_list = array(); foreach ($this->bookManager->getAllBooks() as $book) { $book_list[] = l($book['title'], $book['href'], $book['options']); } return array( '#theme' => 'item_list', '#items' => $book_list, ); } } 

This would be declared as a controller in a routing file like this:

_content: \Drupal\book\Controller\BookController::bookRender

2
  • 1
    Removing container from ControllerBase that is food for thought, feels like many contrib devs will start with this class and then get stuck when they need to access non-standard services. ControllerBase was touted as lighter way to make a controller but begins to look like a dead end. Commented Nov 26, 2013 at 14:18
  • 1
    You would use ContainerInjectionInterface for any non-standard services. ControllerBase is just to get you started with the common services. Commented Nov 28, 2013 at 5:36

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.