EntityFormDisplay.php 7.84 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\Core\Entity\Entity\EntityFormDisplay.
6 7
 */

8
namespace Drupal\Core\Entity\Entity;
9

10
use Drupal\Core\Entity\ContentEntityInterface;
11
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
12
use Drupal\Core\Entity\EntityDisplayBase;
13
use Drupal\Core\Form\FormStateInterface;
14 15 16 17 18

/**
 * Configuration entity that contains widget options for all components of a
 * entity form in a given form mode.
 *
19
 * @ConfigEntityType(
20 21 22 23
 *   id = "entity_form_display",
 *   label = @Translation("Entity form display"),
 *   entity_keys = {
 *     "id" = "id",
24
 *     "status" = "status"
25 26 27
 *   }
 * )
 */
28
class EntityFormDisplay extends EntityDisplayBase implements EntityFormDisplayInterface {
29

30 31 32 33 34
  /**
   * {@inheritdoc}
   */
  protected $displayContext = 'form';

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
  /**
   * Returns the entity_form_display object used to build an entity form.
   *
   * Depending on the configuration of the form mode for the entity bundle, this
   * can be either the display object associated to the form mode, or the
   * 'default' display.
   *
   * This method should only be used internally when rendering an entity form.
   * When assigning suggested display options for a component in a given form
   * mode, entity_get_form_display() should be used instead, in order to avoid
   * inadvertently modifying the output of other form modes that might happen to
   * use the 'default' display too. Those options will then be effectively
   * applied only if the form mode is configured to use them.
   *
   * hook_entity_form_display_alter() is invoked on each display, allowing 3rd
   * party code to alter the display options held in the display before they are
   * used to generate render arrays.
   *
53
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
54 55 56 57 58 59 60 61 62 63
   *   The entity for which the form is being built.
   * @param string $form_mode
   *   The form mode.
   *
   * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface
   *   The display object that should be used to build the entity form.
   *
   * @see entity_get_form_display()
   * @see hook_entity_form_display_alter()
   */
64
  public static function collectRenderDisplay(ContentEntityInterface $entity, $form_mode) {
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    $entity_type = $entity->getEntityTypeId();
    $bundle = $entity->bundle();

    // Check the existence and status of:
    // - the display for the form mode,
    // - the 'default' display.
    if ($form_mode != 'default') {
      $candidate_ids[] = $entity_type . '.' . $bundle . '.' . $form_mode;
    }
    $candidate_ids[] = $entity_type . '.' . $bundle . '.default';
    $results = \Drupal::entityQuery('entity_form_display')
      ->condition('id', $candidate_ids)
      ->condition('status', TRUE)
      ->execute();

    // Load the first valid candidate display, if any.
81
    $storage = \Drupal::entityManager()->getStorage('entity_form_display');
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    foreach ($candidate_ids as $candidate_id) {
      if (isset($results[$candidate_id])) {
        $display = $storage->load($candidate_id);
        break;
      }
    }
    // Else create a fresh runtime object.
    if (empty($display)) {
      $display = $storage->create(array(
        'targetEntityType' => $entity_type,
        'bundle' => $bundle,
        'mode' => $form_mode,
        'status' => TRUE,
      ));
    }

    // Let the display know which form mode was originally requested.
    $display->originalMode = $form_mode;

    // Let modules alter the display.
    $display_context = array(
      'entity_type' => $entity_type,
      'bundle' => $bundle,
      'form_mode' => $form_mode,
    );
    \Drupal::moduleHandler()->alter('entity_form_display', $display, $display_context);

    return $display;
  }

112 113 114 115 116 117 118 119 120 121 122 123
  /**
   * {@inheritdoc}
   */
  public function __construct(array $values, $entity_type) {
    $this->pluginManager = \Drupal::service('plugin.manager.field.widget');

    parent::__construct($values, $entity_type);
  }

  /**
   * {@inheritdoc}
   */
124
  public function getRenderer($field_name) {
125 126 127 128 129
    if (isset($this->plugins[$field_name])) {
      return $this->plugins[$field_name];
    }

    // Instantiate the widget object from the stored display properties.
130
    if (($configuration = $this->getComponent($field_name)) && isset($configuration['type']) && ($definition = $this->getFieldDefinition($field_name))) {
131
      $widget = $this->pluginManager->getInstance(array(
132
        'field_definition' => $definition,
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
        'form_mode' => $this->originalMode,
        // No need to prepare, defaults have been merged in setComponent().
        'prepare' => FALSE,
        'configuration' => $configuration
      ));
    }
    else {
      $widget = NULL;
    }

    // Persist the widget object.
    $this->plugins[$field_name] = $widget;
    return $widget;
  }

148 149 150
  /**
   * {@inheritdoc}
   */
151
  public function buildForm(ContentEntityInterface $entity, array &$form, FormStateInterface $form_state) {
152 153 154 155 156 157 158 159
    // Set #parents to 'top-level' by default.
    $form += array('#parents' => array());

    // Let each widget generate the form elements.
    foreach ($entity as $name => $items) {
      if ($widget = $this->getRenderer($name)) {
        $items->filterEmptyItems();
        $form[$name] = $widget->form($items, $form, $form_state);
160
        $form[$name]['#access'] = $items->access('edit');
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

        // Assign the correct weight. This duplicates the reordering done in
        // processForm(), but is needed for other forms calling this method
        // directly.
        $form[$name]['#weight'] = $this->getComponent($name)['weight'];
      }
    }

    // Add a process callback so we can assign weights and hide extra fields.
    $form['#process'][] = array($this, 'processForm');
  }

  /**
   * Process callback: assigns weights and hides extra fields.
   *
   * @see \Drupal\entity\Entity\EntityFormDisplay::buildForm()
   */
178
  public function processForm($element, FormStateInterface $form_state, $form) {
179 180 181 182 183 184 185 186
    // Assign the weights configured in the form display.
    foreach ($this->getComponents() as $name => $options) {
      if (isset($element[$name])) {
        $element[$name]['#weight'] = $options['weight'];
      }
    }

    // Hide extra fields.
187 188
    $extra_fields = \Drupal::entityManager()->getExtraFields($this->targetEntityType, $this->bundle);
    $extra_fields = isset($extra_fields['form']) ? $extra_fields['form'] : array();
189 190 191 192 193 194 195 196 197 198 199
    foreach ($extra_fields as $extra_field => $info) {
      if (!$this->getComponent($extra_field)) {
        $element[$extra_field]['#access'] = FALSE;
      }
    }
    return $element;
  }

  /**
   * {@inheritdoc}
   */
200
  public function extractFormValues(ContentEntityInterface $entity, array &$form, FormStateInterface $form_state) {
201 202 203 204 205 206 207 208 209 210 211 212 213
    $extracted = array();
    foreach ($entity as $name => $items) {
      if ($widget = $this->getRenderer($name)) {
        $widget->extractFormValues($items, $form, $form_state);
        $extracted[$name] = $name;
      }
    }
    return $extracted;
  }

  /**
   * {@inheritdoc}
   */
214
  public function validateFormValues(ContentEntityInterface $entity, array &$form, FormStateInterface $form_state) {
215 216 217 218 219 220 221 222 223 224 225 226
    foreach ($entity as $field_name => $items) {
      // Only validate the fields that actually appear in the form, and let the
      // widget assign the violations to the right form elements.
      if ($widget = $this->getRenderer($field_name)) {
        $violations = $items->validate();
        if (count($violations)) {
          $widget->flagErrors($items, $violations, $form, $form_state);
        }
      }
    }
  }

227 228 229
  /**
   * {@inheritdoc}
   */
230
  public function __sleep() {
231
    // Only store the definition, not external objects or derived data.
232
    $keys = array_keys($this->toArray());
233 234
    $keys[] = 'entityTypeId';
    return $keys;
235 236 237 238 239
  }

  /**
   * {@inheritdoc}
   */
240
  public function __wakeup() {
241 242
    // Run the values from self::toArray() through __construct().
    $values = array_intersect_key($this->toArray(), get_object_vars($this));
243
    $this->__construct($values, $this->entityTypeId);
244 245 246
  }

}