ConfigurableEntityReferenceItem.php 8.3 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\entity_reference\ConfigurableEntityReferenceItem.
6 7
 */

8
namespace Drupal\entity_reference;
9

10
use Drupal\Component\Utility\String;
11
use Drupal\Core\Field\FieldStorageDefinitionInterface;
12
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
13
use Drupal\Core\Form\FormStateInterface;
14
use Drupal\Core\Form\OptGroup;
15
use Drupal\Core\Session\AccountInterface;
16
use Drupal\Core\TypedData\OptionsProviderInterface;
17
use Drupal\Core\TypedData\DataDefinition;
18
use Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint;
19
use Drupal\field\FieldStorageConfigInterface;
20 21

/**
22
 * Alternative plugin implementation of the 'entity_reference' field type.
23
 *
24 25
 * Replaces the Core 'entity_reference' entity field type implementation, this
 * supports configurable fields, auto-creation of referenced entities and more.
26
 *
27
 * Required settings are:
28 29
 *  - target_type: The entity type to reference.
 *
30
 * @see entity_reference_field_info_alter().
31
 */
32
class ConfigurableEntityReferenceItem extends EntityReferenceItem implements OptionsProviderInterface {
33

34 35 36
  /**
   * {@inheritdoc}
   */
37 38
  public static function defaultStorageSettings() {
    $settings = parent::defaultStorageSettings();
39 40 41 42 43 44 45 46 47
    // The target bundle is handled by the 'target_bundles' property in the
    // 'handler_settings' instance setting.
    unset($settings['target_bundle']);
    return $settings;
  }

  /**
   * {@inheritdoc}
   */
48
  public static function defaultFieldSettings() {
49 50
    return array(
      'handler_settings' => array(),
51
    ) + parent::defaultFieldSettings();
52 53
  }

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  /**
   * {@inheritdoc}
   */
  public function getPossibleValues(AccountInterface $account = NULL) {
    return $this->getSettableValues($account);
  }

  /**
   * {@inheritdoc}
   */
  public function getPossibleOptions(AccountInterface $account = NULL) {
    return $this->getSettableOptions($account);
  }

  /**
   * {@inheritdoc}
   */
  public function getSettableValues(AccountInterface $account = NULL) {
72
    // Flatten options first, because "settable options" may contain group
73
    // arrays.
74
    $flatten_options = OptGroup::flattenOptions($this->getSettableOptions($account));
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    return array_keys($flatten_options);
  }

  /**
   * {@inheritdoc}
   */
  public function getSettableOptions(AccountInterface $account = NULL) {
    $field_definition = $this->getFieldDefinition();
    if (!$options = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($field_definition, $this->getEntity())->getReferenceableEntities()) {
      return array();
    }

    // Rebuild the array by changing the bundle key into the bundle label.
    $target_type = $field_definition->getSetting('target_type');
    $bundles = \Drupal::entityManager()->getBundleInfo($target_type);

    $return = array();
    foreach ($options as $bundle => $entity_ids) {
      $bundle_label = String::checkPlain($bundles[$bundle]['label']);
      $return[$bundle_label] = $entity_ids;
    }

    return count($return) == 1 ? reset($return) : $return;
  }
99

100 101 102
  /**
   * {@inheritdoc}
   */
103
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
104
    $settings = $field_definition->getSettings();
105 106
    $target_type = $settings['target_type'];

107 108 109 110 111 112 113 114 115
    // Call the parent to define the target_id and entity properties.
    $properties = parent::propertyDefinitions($field_definition);

    // Only add the revision ID property if the target entity type supports
    // revisions.
    $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
    if ($target_type_info->hasKey('revision') && $target_type_info->getRevisionTable()) {
      $properties['revision_id'] = DataDefinition::create('integer')
        ->setLabel(t('Revision ID'))
116
        ->setSetting('unsigned', TRUE);
117 118
    }

119
    return $properties;
120 121
  }

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  /**
   * {@inheritdoc}
   */
  public function getConstraints() {
    $constraints = parent::getConstraints();

    // Remove the 'AllowedValuesConstraint' validation constraint because entity
    // reference fields already use the 'ValidReference' constraint.
    foreach ($constraints as $key => $constraint) {
      if ($constraint instanceof AllowedValuesConstraint) {
        unset($constraints[$key]);
      }
    }

    return $constraints;
  }

139 140 141
  /**
   * {@inheritdoc}
   */
142
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
143 144 145
    $schema = parent::schema($field_definition);

    $target_type = $field_definition->getSetting('target_type');
146 147
    $target_type_info = \Drupal::entityManager()->getDefinition($target_type);

148
    if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface') && $field_definition instanceof FieldStorageConfigInterface) {
149 150 151 152 153
      $schema['columns']['revision_id'] = array(
        'description' => 'The revision ID of the target entity.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => FALSE,
154 155 156
      );
    }

157 158 159 160 161 162
    return $schema;
  }

  /**
   * {@inheritdoc}
   */
163
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
164 165 166
    $element['target_type'] = array(
      '#type' => 'select',
      '#title' => t('Type of item to reference'),
167
      '#options' => \Drupal::entityManager()->getEntityTypeLabels(TRUE),
168
      '#default_value' => $this->getSetting('target_type'),
169 170 171 172 173 174 175 176 177 178 179
      '#required' => TRUE,
      '#disabled' => $has_data,
      '#size' => 1,
    );

    return $element;
  }

  /**
   * {@inheritdoc}
   */
180 181
  public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
    $field = $form_state->get('field');
182 183

    // Get all selection plugins for this entity type.
184
    $selection_plugins = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionGroups($this->getSetting('target_type'));
185 186 187 188 189 190 191 192 193
    $handler_groups = array_keys($selection_plugins);

    $handlers = \Drupal::service('plugin.manager.entity_reference.selection')->getDefinitions();
    $handlers_options = array();
    foreach ($handlers as $plugin_id => $plugin) {
      // We only display base plugins (e.g. 'default', 'views', ...) and not
      // entity type specific plugins (e.g. 'default_node', 'default_user',
      // ...).
      if (in_array($plugin_id, $handler_groups)) {
194
        $handlers_options[$plugin_id] = String::checkPlain($plugin['label']);
195 196 197 198 199 200
      }
    }

    $form = array(
      '#type' => 'container',
      '#process' => array(
201
        '_entity_reference_field_field_settings_ajax_process',
202
      ),
203
      '#element_validate' => array(array(get_class($this), 'fieldSettingsFormValidate')),
204 205 206 207
    );
    $form['handler'] = array(
      '#type' => 'details',
      '#title' => t('Reference type'),
208
      '#open' => TRUE,
209 210 211 212 213 214 215 216
      '#tree' => TRUE,
      '#process' => array('_entity_reference_form_process_merge_parent'),
    );

    $form['handler']['handler'] = array(
      '#type' => 'select',
      '#title' => t('Reference method'),
      '#options' => $handlers_options,
217
      '#default_value' => $field->getSetting('handler'),
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
      '#required' => TRUE,
      '#ajax' => TRUE,
      '#limit_validation_errors' => array(),
    );
    $form['handler']['handler_submit'] = array(
      '#type' => 'submit',
      '#value' => t('Change handler'),
      '#limit_validation_errors' => array(),
      '#attributes' => array(
        'class' => array('js-hide'),
      ),
      '#submit' => array('entity_reference_settings_ajax_submit'),
    );

    $form['handler']['handler_settings'] = array(
      '#type' => 'container',
      '#attributes' => array('class' => array('entity_reference-settings')),
    );

237 238
    $handler = \Drupal::service('plugin.manager.entity_reference.selection')->getSelectionHandler($field);
    $form['handler']['handler_settings'] += $handler->settingsForm($field);
239 240 241 242 243 244 245 246 247

    return $form;
  }

  /**
   * Form element validation handler; Stores the new values in the form state.
   *
   * @param array $form
   *   The form where the settings form is being included in.
248
   * @param \Drupal\Core\Form\FormStateInterface $form_state
249 250
   *   The form state of the (entire) configuration form.
   */
251 252 253 254
  public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {
    if ($form_state->hasValue('field')) {
      $form_state->unsetValue(array('field', 'settings', 'handler_submit'));
      $form_state->get('field')->settings = $form_state->getValue(['field', 'settings']);
255 256 257 258
    }
  }

}