Updated >= Drupal 8.0.5+: Rendering forms/entities programmatically on theming level

There are several attempts and questions already asked about near to that topic in combination with the new Drupal core (8), but non of them came down to the bottom of the basic question. And non of them are up-to-date enough (2015) since many late changes in core arrived afterwards (2016). Answering the basic question will hopefully lead to answers for many depending questions and open HOWTO topics and could be a good basis for updated docs on drupal.org.

The basic question is:

How to programmatically render path/page based forms or any other entity bundles on the theming level to display them path independently, e.g. in blocks or other pages other than the given pages/paths of itself? And how can I find the right routing for the correct system calls or class paths for the needed preprocess functions in the new D8 core logic if I am new to D8?

The question arises from 2 points of view:

  1. Why should a form be hard binded to a page view or fixed path? It's a page element, not a page. It should be available easely in any other places without complicated coding struggles in a website building CMF like Drupal on the theming level. It's part of theme design to decide to have a form on frontpage, for example. (ok, this is arguable, I know, but in some cases it is)

  2. From the basic structure how Drupal is build and what it tries to achieve in these times (the goal to rather build web applications instead of classic and static websites), it should be easily reachable to maintain such task like a form on the bottom of a one-page site or to build a survey or render entities on unexpected pages.

To keep with the form example:

here are some links to questions regarding this topic and I've tried already to interfere there and suggest to inform about changes in core here and there to update the posts or to link to sources:

The basic structure

.. how it works is common, mostly clear, and not so far from how it worked in D7:

You will use a preprocess_xxx() function in your D8 twig YOURTHEMENAME.theme file to call and pipe the form or entity array into the region variable array, to be able to render it via {{ var }} twig-tags in your xxx.twig file: e.g. block.html.twig or page.html.twig, since the form or entity is maybe not known in this layer (enviroment variable) by default.

Just an example (needs update and correction) of your THEMENAME.theme file:

preprocess_page(){
  $form = Drupal::routeMatch()->getParameter('contact_form'); // ???
  $message = \Drupal::entityTypeManager() // ???
  ->getStorage('contact_message')
  ->create(array(
      'contact_form' => $form->id(),
  ));
  /// ???
  $form = Drupal::service('entity.form_builder')->getForm($message);
  $variables['contact_form'] = $form;
}

Just an example page.html.twig file:

<header>
  {{ header_region }}
</header> 
<div id="main"> ... </div>
<footer>
  {{ contact_form }} <!-- from $variables['contact_form'] = $form; -->
</footer>

But now comes the tricky part:

The docs are not up-to-date enough to make clear how to build the preprocess_xxx() function correctly, demonstrated with \\ ??? here. Using the demo example here as-is (atm) will cause an almost empty frontpage, only rendering html.html.twig but without the nested page.html.twig, obviously caused by a wrong coding in the preprocess function.

Somebody who tries to find updated docs or QA for that will have a hard time ...

Answers 3

  • How to programmatically render path/page based forms or any other entity bundles on the theming level to display them path independently, […]

    Because in Drupal 8/7/6/…, forms do not have dedicated routes/controllers (D8) or page callbacks/paths (D7/6…). Hence they are path-dependent.

    Once that is solved, then yes, you would be able to do that. For now, it's literally impossible.

    See https://www.drupal.org/node/2503429

    The only way you could perhaps make this work, but even then only for forms you define (and not for every form), is by defining a route where your form always POSTs to, and then setting it as the form action ($form['#action'] = 'foo'; is mapped to <form action="foo").


    For now, the recommended approach to be able to put a form anywhere, is to expose it as a block. For an example, see \Drupal\search\Plugin\Block\SearchBlock and how it uses \Drupal\search\Form\SearchBlockForm.


  • I'm still new to drupal but here is what i did to render a form in node.html.twig:

    //file themename.theme
    function themename_preprocess_node(&$variables) {
      if(isset($variables['node'])){
        $node = $variables['node'];
        if($node->getType() == 'application') {
            $message = \Drupal::entityTypeManager()
                ->getStorage('contact_message')
                ->create(array(
                'contact_form' => 'webentwickler_job', //ID(Machine name) of form
            ));
            $form = \Drupal::service('entity.form_builder')->getForm($message);
            $variables['application_form'] = $form;
        }
      }
    }
    
    //file node--application.html.twig
    {{ application_form }}
    

  • The question is a feature request. Providing this feature "entity/form as a block" can be done in the core module, but usually this kind of features are first provided in contrib and later they may become part of core. There are projects to address this:

    https://www.drupal.org/project/entityblock

    https://www.drupal.org/project/contact_block

    https://www.drupal.org/project/entityform_block

    In my opinion putting the code in theming is only a short time solution, until this is solved in contrib/core. As the OP has experienced, you don't know how long it works, because this code doesn't get updated.


Related Questions