• David Duymelinck

  • Software Engineer

Rendering a form is simple when the form and the entity relate one to one. But what if you need fields from multiple entities? Or what if you want to show the fields of an entity in a multistep format? This post is about a function that gets a render array for a field from an entity. This function enables you to select fields from one or more entities.

When making custom forms in Drupal 8 I was limited by the restriction to use all the fields from a single entity. To break open the one-to-one relation between entity and form, I created a function that gets the render array from the field settings. These field settings are defined in the baseFieldDefinitions method of the entity.

modules/utils/Plugin/Field/FieldUtils.php

use Drupal\Core\Entity\Entity\EntityFormDisplay;


class FieldUtils

{

    public static function getFieldRenderarray($field_name,                              

                    $entity, &$form, $form_state, $options) {

      // the #parents key is needed for the form method to add

      // the fields to

      if(!isset($form['#parents'])) {

       $form['#parents'] = [];

      }

      // store the render display collection to reduce the

      // queries if multiple fields use the function

      $entity_form_display = $entity->getEntityTypeId(). 

                                '_form_display';

      $display = $form_state->get($entity_form_display);


      if(empty($display)) {

        // get the display for all the fields of the entity

        $display = EntityFormDisplay::collectRenderDisplay(

                                    $entity, 'default');


        $form_state->set($entity_form_display, $display);

      }


      $render_array = $display

                        // get the fields render object

                        ->getRenderer($field_name) 

                        // get the render array

                        ->form($entity->get($field_name), $form, 

                                    $form_state); 

      // manipulate the render array

      if(!empty($options)) {

        if(isset($render_array['widget'][0])) {

          $render_array['widget'][0]['value'] = 

                array_merge($render_array['widget'][0]['value'], 

                    $options);

        }else{

          $render_array['widget'] = 

            array_merge($render_array['widget'], $options);

        }


      }


      return $render_array;

    }

}

I added comments to the function to give you a  better idea how the render array is fetched. There are a few things I want to provide more details for:

    1. The EntityFormDisplay::collectRenderDisplay method uses the following code to fetch the BasefieldDefinitions settings: 
\Drupal::entityManager()->getStorage('entity_form_display')->load($entity_type . '.' . $bundle . '.' . $form_mode)

This is the database query that I'm caching in the getFieldRenderarray method. Drupal uses the collectRenderDisplay method in the ContentEntityForm and the QuickEditFieldForm classes.

  1. The output of the getRenderer method contains the base render array. To obtain the render array of a specific widget for a form, you have to call theWidgetBase::form method.  
  2. The inspiration for the getFieldRenderarray method was the EntityFormDisplay::buildForm. This method contains the #parents key addition and both the getRenderer and form methods. If you want to change the field order, you have to add the #weight key to the fields. If you want to control access to the fields, you have to add the #access key. Both keys are set in EntityFormDisplay::buildForm.

In this article I've shown you how to get render arrays for fields from the BaseFieldDefinition settings. Building a form based on this render array should be straightforward. I also gave a peek into the way how Drupal builds its forms. If you dig a little you can make Drupal work for you instead of against.

Verwante Artikels