3

To render a form in a block, I can use the following code.

class LoremIpsumBlock extends BlockBase {

  public function build() {
    // Return the form @ Form/LoremIpsumBlockForm.php.
    return \Drupal::formBuilder()->getForm('Drupal\loremipsum\Form\LoremIpsumBlockForm');
  }

How can I render in a block the output of a controller? Is it possible, or do I have to duplicate the controller class code in the block class?

The example in the documentation is too simple and it shows how to render a literal string.

class HelloBlock extends BlockBase {

  public function build() {
    return array(
      '#markup' => $this->t('Hello, World!'),
    );
  }    
}

5 Answers 5

4

you can call the controller class inside the custom block,

check this below example,

Controller:

   <?php

    namespace Drupal\custom_example\Controller;


    class ExampleController {

         public function example(){

           return array(
             '#title' => 'Example',
             '#markup' => 'This is a custom module', 

            );
         }
    }

custom Block: Here, Use the ExampleController like this,

 use Drupal\custom_example\Controller\ExampleController;

ExampleController should be the class name of the Controller

<?php

namespace Drupal\custom_example\Plugin\Block;


use Drupal\Core\Block\BlockBase;
use Drupal\custom_example\Controller\ExampleController;

class CustomBlock extends BlockBase {

  public function build() {

     $controller_variable = new ExampleController;
     $rendering_in_block = $controller_variable->example();
     return $rendering_in_block;

  }

}

Create the new instance using the class name and call the function in the block

2
  • Perfect, it is exactly what i was looking for : a complete working example. Sep 13, 2017 at 8:12
  • Creating a controller instance in this way will fail when the controller has dependencies. This isn't the correct way to create a controller.
    – apaderno
    Nov 25, 2022 at 9:50
13

@4k4 is correct in their answer that it is preferable to refactor the shared code into a separate class (probably a service).

However, if that is not an option (because you are building on core or contributed code that you cannot change) it is still possible to re-use the output of a controller that uses dependency injection (DI). To do so, inject the class_resolver service into your block and use it to get a controller object. Your block code would look something like this:

<?php

namespace Drupal\my_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\DependencyInjection\ClassResolverInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a custom block containing the output of a controller.
 *
 * @Block(
 *  id = "controller_block",
 *  admin_label = @Translation("Controller block"),
 * )
 */
class ControllerBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * Class Resolver service.
   *
   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
   */
  protected $classResolver;

  /**
   * Constructs a new ControllerBlock object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param string $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
   *   The class resolver service.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    ClassResolverInterface $class_resolver
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->classResolver = $class_resolver;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('class_resolver')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    // Assuming that \Drupal\my_module\Controller\MyController::build is a
    // public function that returns the controller output.
    $controller = $this->classResolver->getInstanceFromDefinition('\Drupal\my_module\Controller\MyController');
    return $controller->build();
  }

}
6

A controller depends on a request and route match. In simple cases you can treat the controller like a normal class, but more complex controllers with DI and route arguments will fail. You should refactor the code so that the shared content of the controller and block is built in a different place both have access to, like this is the case in your block example, where this is a form.

4
  • 1
    That "more complex controllers with DI and route arguments will fail" is only true if you simply use the new keyword to instantiate the controller class. See my answer for a solution that will work for controllers with DI. Apr 30, 2018 at 11:01
  • @marcvangend, great answer. The OP said, "when i have a controller in my custom module", so I suggested to refactor the code. If refactoring is not an option, I agree, your answer is the best approach.
    – 4uk4
    Apr 30, 2018 at 12:55
  • @marcvangend, on second thought you have to be careful which dependencies you pull from the class resolver. A controller has a different context than a block. So in general I would first try to refactor the code and by doing this enforce the proper handling of these dependencies.
    – 4uk4
    Apr 30, 2018 at 13:22
  • Thanks for the feedback, I totally agree. My answer is not the recommended approach because of the reasons you mention. Also, controllers are not meant to be reused this way; a controller is not considered an API. That means the developers who maintain the code could decide it needs refactoring and unintentionally break your code. (Still, it was fun figuring out how to make it work ;-)) Apr 30, 2018 at 15:43
0

Use namespace for your controller like:

"use Drupal\your_custom_module\Controller\your_class_name;"

then you can call the function of "your_class_name", where you have generated all the tables/html, inside you build() of Class "HelloBlock" .

0

Either the part of the code used by the controller and the block is implemented by a service, or the block code instances the controller using the controller_resolver service.

namespace Drupal\mymodule\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Controller\ControllerResolverInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a custom block that shows a controller's output.
 *
 * @Block(
 *  id = "mymodule_block",
 *  admin_label = @Translation("Mymodule block"),
 * )
 */
class MymoduleBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The controller resolver service.
   *
   * @var \Drupal\Core\Controller\ControllerResolverInterface
   */
  protected $controllerResolver;

  /**
   * Constructs a new \Drupal\mymodule\Plugin\Block\MymoduleBlock object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param string $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
   *   The controller resolver service.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, ControllerResolverInterface $controller_resolver) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->controllerResolver = $controller_resolver;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('controller_resolver')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    // $controller is a string "$class_or_service:$method" or "$class_or_service::$method".
    $callable = $this->controllerResolver->getControllerFromDefinition($controller);
    // $callable is an array [$object, $method].
  }

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.