You are here

class FieldLayoutBuilder in Drupal 8

Same name and namespace in other branches
  1. 9 core/modules/field_layout/src/FieldLayoutBuilder.php \Drupal\field_layout\FieldLayoutBuilder

Builds a field layout.

Hierarchy

Expanded class hierarchy of FieldLayoutBuilder

2 files declare their use of FieldLayoutBuilder
FieldLayoutBuilderTest.php in core/modules/field_layout/tests/src/Unit/FieldLayoutBuilderTest.php
field_layout.module in core/modules/field_layout/field_layout.module
Provides hook implementations for Field Layout.

File

core/modules/field_layout/src/FieldLayoutBuilder.php, line 15

Namespace

Drupal\field_layout
View source
class FieldLayoutBuilder implements ContainerInjectionInterface {

  /**
   * The layout plugin manager.
   *
   * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
   */
  protected $layoutPluginManager;

  /**
   * The entity field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * Constructs a new FieldLayoutBuilder.
   *
   * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_plugin_manager
   *   The layout plugin manager.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
   *   The entity field manager.
   */
  public function __construct(LayoutPluginManagerInterface $layout_plugin_manager, EntityFieldManagerInterface $entity_field_manager) {
    $this->layoutPluginManager = $layout_plugin_manager;
    $this->entityFieldManager = $entity_field_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('plugin.manager.core.layout'), $container
      ->get('entity_field.manager'));
  }

  /**
   * Applies the layout to an entity build.
   *
   * @param array $build
   *   A renderable array representing the entity content or form.
   * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display
   *   The entity display holding the display options configured for the entity
   *   components.
   */
  public function buildView(array &$build, EntityDisplayWithLayoutInterface $display) {
    $layout_definition = $this->layoutPluginManager
      ->getDefinition($display
      ->getLayoutId(), FALSE);
    if ($layout_definition && ($fields = $this
      ->getFields($build, $display, 'view'))) {

      // Add the regions to the $build in the correct order.
      $regions = array_fill_keys($layout_definition
        ->getRegionNames(), []);
      foreach ($fields as $name => $field) {

        // If the region is controlled by the layout, move the field from the
        // top-level of $build into a region-specific section. Custom regions
        // could be set by other code at run-time; these should be ignored.
        // @todo Ideally the array structure would remain unchanged, see
        //   https://www.drupal.org/node/2846393.
        if (isset($regions[$field['region']])) {
          $regions[$field['region']][$name] = $build[$name];
          unset($build[$name]);
        }
      }

      // Ensure this will not conflict with any existing array elements by
      // prefixing with an underscore.
      $build['_field_layout'] = $display
        ->getLayout()
        ->build($regions);
    }
  }

  /**
   * Applies the layout to an entity form.
   *
   * @param array $build
   *   A renderable array representing the entity content or form.
   * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display
   *   The entity display holding the display options configured for the entity
   *   components.
   */
  public function buildForm(array &$build, EntityDisplayWithLayoutInterface $display) {
    $layout_definition = $this->layoutPluginManager
      ->getDefinition($display
      ->getLayoutId(), FALSE);
    if ($layout_definition && ($fields = $this
      ->getFields($build, $display, 'form'))) {
      $fill = [];
      $fill['#process'][] = '\\Drupal\\Core\\Render\\Element\\RenderElement::processGroup';
      $fill['#pre_render'][] = '\\Drupal\\Core\\Render\\Element\\RenderElement::preRenderGroup';

      // Add the regions to the $build in the correct order.
      $regions = array_fill_keys($layout_definition
        ->getRegionNames(), $fill);
      foreach ($fields as $name => $field) {

        // As this is a form, #group can be used to relocate the fields. This
        // avoids breaking hook_form_alter() implementations by not actually
        // moving the field in the form structure. If a #group is already set,
        // do not overwrite it.
        if (isset($regions[$field['region']]) && !isset($build[$name]['#group'])) {
          $build[$name]['#group'] = $field['region'];
        }
      }

      // Ensure this will not conflict with any existing array elements by
      // prefixing with an underscore.
      $build['_field_layout'] = $display
        ->getLayout()
        ->build($regions);
    }
  }

  /**
   * Gets the fields that need to be processed.
   *
   * @param array $build
   *   A renderable array representing the entity content or form.
   * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display
   *   The entity display holding the display options configured for the entity
   *   components.
   * @param string $display_context
   *   The display context, either 'form' or 'view'.
   *
   * @return array
   *   An array of configurable fields present in the build.
   */
  protected function getFields(array $build, EntityDisplayWithLayoutInterface $display, $display_context) {
    $components = $display
      ->getComponents();

    // Ignore any extra fields from the list of field definitions. Field
    // definitions can have a non-configurable display, but all extra fields are
    // always displayed.
    $field_definitions = array_diff_key($this->entityFieldManager
      ->getFieldDefinitions($display
      ->getTargetEntityTypeId(), $display
      ->getTargetBundle()), $this->entityFieldManager
      ->getExtraFields($display
      ->getTargetEntityTypeId(), $display
      ->getTargetBundle()));
    $fields_to_exclude = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) use ($display_context) {

      // Remove fields with a non-configurable display.
      return !$field_definition
        ->isDisplayConfigurable($display_context);
    });
    $components = array_diff_key($components, $fields_to_exclude);

    // Only include fields present in the build.
    $components = array_intersect_key($components, $build);
    return $components;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
FieldLayoutBuilder::$entityFieldManager protected property The entity field manager.
FieldLayoutBuilder::$layoutPluginManager protected property The layout plugin manager.
FieldLayoutBuilder::buildForm public function Applies the layout to an entity form.
FieldLayoutBuilder::buildView public function Applies the layout to an entity build.
FieldLayoutBuilder::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create
FieldLayoutBuilder::getFields protected function Gets the fields that need to be processed.
FieldLayoutBuilder::__construct public function Constructs a new FieldLayoutBuilder.