2

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!'),
    );
  }    
}
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

| improve this answer | |
  • Perfect, it is exactly what i was looking for : a complete working example. – Sébastien Gicquel Sep 13 '17 at 8:12
8

@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();
  }

}
| improve this answer | |
5

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.

| improve this answer | |
  • 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. – marcvangend Apr 30 '18 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. – 4k4 Apr 30 '18 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. – 4k4 Apr 30 '18 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 ;-)) – marcvangend Apr 30 '18 at 15:43
1

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" .

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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