SelectionBase.php 10.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
<?php

/**
 * @file
 * Contains \Drupal\entity_reference\Plugin\entity_reference\selection\SelectionBase.
 */

namespace Drupal\entity_reference\Plugin\entity_reference\selection;

10
use Drupal\Component\Annotation\Plugin;
11
12
13
14
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityInterface;
15
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
use Drupal\Component\Utility\NestedArray;
use Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface;

/**
 * Plugin implementation of the 'selection' entity_reference.
 *
 * @Plugin(
 *   id = "default",
 *   module = "entity_reference",
 *   label = @Translation("Default"),
 *   group = "default",
 *   weight = 0,
 *   derivative = "Drupal\entity_reference\Plugin\Derivative\SelectionBase"
 * )
 */
class SelectionBase implements SelectionInterface {

  /**
34
   * The field definition.
35
   *
36
   * @var \Drupal\Core\Entity\Field\FieldDefinitionInterface
37
   */
38
  protected $fieldDefinition;
39
40
41
42
43
44
45
46
47
48
49

  /**
   * The entity object, or NULL
   *
   * @var NULL|EntityInterface
   */
  protected $entity;

  /**
   * Constructs a SelectionBase object.
   */
50
51
  public function __construct(FieldDefinitionInterface $field_definition, EntityInterface $entity = NULL) {
    $this->fieldDefinition = $field_definition;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    $this->entity = $entity;
  }

  /**
   * Implements SelectionInterface::settingsForm().
   */
  public static function settingsForm(&$field, &$instance) {
    $entity_info = entity_get_info($field['settings']['target_type']);
    $bundles = entity_get_bundles($field['settings']['target_type']);

    // Merge-in default values.
    if (!isset($instance['settings']['handler_settings'])) {
      $instance['settings']['handler_settings'] = array();
    }
    $instance['settings']['handler_settings'] += array(
      'target_bundles' => array(),
      'sort' => array(
        'field' => '_none',
      ),
      'auto_create' => FALSE,
    );

    if (!empty($entity_info['entity_keys']['bundle'])) {
      $bundle_options = array();
      foreach ($bundles as $bundle_name => $bundle_info) {
        $bundle_options[$bundle_name] = $bundle_info['label'];
      }

      $target_bundles_title = t('Bundles');
      // Default core entity types with sensible labels.
      if ($field['settings']['target_type'] == 'node') {
        $target_bundles_title = t('Content types');
      }
      elseif ($field['settings']['target_type'] == 'taxonomy_term') {
        $target_bundles_title = t('Vocabularies');
      }

      $form['target_bundles'] = array(
        '#type' => 'checkboxes',
        '#title' => $target_bundles_title,
        '#options' => $bundle_options,
93
94
        '#default_value' => (!empty($instance['settings']['handler_settings']['target_bundles'])) ? $instance['settings']['handler_settings']['target_bundles'] : array(),
        '#required' => TRUE,
95
96
97
98
99
100
101
102
103
104
105
106
107
108
        '#size' => 6,
        '#multiple' => TRUE,
        '#element_validate' => array('_entity_reference_element_validate_filter'),
      );
    }
    else {
      $form['target_bundles'] = array(
        '#type' => 'value',
        '#value' => array(),
      );
    }

    // @todo Use Entity::getPropertyDefinitions() when all entity types are
    // converted to the new Field API.
109
    $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
    foreach (field_info_instances($field['settings']['target_type']) as $bundle_instances) {
      foreach ($bundle_instances as $instance_name => $instance_info) {
        $field_info = field_info_field($instance_name);
        foreach ($field_info['columns'] as $column_name => $column_info) {
          $fields[$instance_name . '.' . $column_name] = t('@label (@column)', array('@label' => $instance_info['label'], '@column' => $column_name));
        }
      }
    }

    $form['sort']['field'] = array(
      '#type' => 'select',
      '#title' => t('Sort by'),
      '#options' => array(
        '_none' => t('- None -'),
      ) + $fields,
      '#ajax' => TRUE,
      '#limit_validation_errors' => array(),
      '#default_value' => $instance['settings']['handler_settings']['sort']['field'],
    );

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

    if ($instance['settings']['handler_settings']['sort']['field'] != '_none') {
      // Merge-in default values.
      $instance['settings']['handler_settings']['sort'] += array(
        'direction' => 'ASC',
      );

      $form['sort']['settings']['direction'] = array(
        '#type' => 'select',
        '#title' => t('Sort direction'),
        '#required' => TRUE,
        '#options' => array(
          'ASC' => t('Ascending'),
          'DESC' => t('Descending'),
        ),
        '#default_value' => $instance['settings']['handler_settings']['sort']['direction'],
      );
    }

    return $form;
  }

  /**
   * Implements SelectionInterface::getReferencableEntities().
   */
  public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
161
    $target_type = $this->fieldDefinition->getFieldSetting('target_type');
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

    $query = $this->buildEntityQuery($match, $match_operator);
    if ($limit > 0) {
      $query->range(0, $limit);
    }

    $result = $query->execute();

    if (empty($result)) {
      return array();
    }

    $options = array();
    $entities = entity_load_multiple($target_type, $result);
    foreach ($entities as $entity_id => $entity) {
      $bundle = $entity->bundle();
      $options[$bundle][$entity_id] = check_plain($entity->label());
    }

    return $options;
  }

  /**
   * Implements SelectionInterface::countReferencableEntities().
   */
  public function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') {
    $query = $this->buildEntityQuery($match, $match_operator);
    return $query
      ->count()
      ->execute();
  }

  /**
   * Implements SelectionInterface::validateReferencableEntities().
   */
  public function validateReferencableEntities(array $ids) {
    $result = array();
    if ($ids) {
200
      $target_type = $this->fieldDefinition->getFieldSetting('target_type');
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
      $entity_info = entity_get_info($target_type);
      $query = $this->buildEntityQuery();
      $result = $query
        ->condition($entity_info['entity_keys']['id'], $ids, 'IN')
        ->execute();
    }

    return $result;
  }

  /**
   * Implements SelectionInterface::validateAutocompleteInput().
   */
  public function validateAutocompleteInput($input, &$element, &$form_state, $form, $strict = TRUE) {
    $entities = $this->getReferencableEntities($input, '=', 6);
    $params = array(
      '%value' => $input,
      '@value' => $input,
    );
    if (empty($entities)) {
      if ($strict) {
        // Error if there are no entities available for a required field.
        form_error($element, t('There are no entities matching "%value".', $params));
      }
    }
    elseif (count($entities) > 5) {
      $params['@id'] = key($entities);
      // Error if there are more than 5 matching entities.
      form_error($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)".', $params));
    }
    elseif (count($entities) > 1) {
      // More helpful error if there are only a few matching entities.
      $multiples = array();
      foreach ($entities as $id => $name) {
        $multiples[] = $name . ' (' . $id . ')';
      }
      $params['@id'] = $id;
      form_error($element, t('Multiple entities match this reference; "%multiple". Specify the one you want by appending the id in parentheses, like "@value (@id)".', array('%multiple' => implode('", "', $multiples))));
    }
    else {
      // Take the one and only matching entity.
      return key($entities);
    }
  }

  /**
   * Builds an EntityQuery to get referencable entities.
   *
   * @param string|null $match
   *   (Optional) Text to match the label against. Defaults to NULL.
   * @param string $match_operator
   *   (Optional) The operation the matching should be done with. Defaults
   *   to "CONTAINS".
   *
   * @return \Drupal\Core\Entity\Query\QueryInterface
   *   The EntityQuery object with the basic conditions and sorting applied to
   *   it.
   */
  public function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
260
    $target_type = $this->fieldDefinition->getFieldSetting('target_type');
261
    $handler_settings = $this->fieldDefinition->getFieldSetting('handler_settings');
262
263
    $entity_info = entity_get_info($target_type);

264
    $query = \Drupal::entityQuery($target_type);
265
266
    if (!empty($handler_settings['target_bundles'])) {
      $query->condition($entity_info['entity_keys']['bundle'], $handler_settings['target_bundles'], 'IN');
267
268
269
270
271
272
273
    }

    if (isset($match) && isset($entity_info['entity_keys']['label'])) {
      $query->condition($entity_info['entity_keys']['label'], $match, $match_operator);
    }

    // Add entity-access tag.
274
    $query->addTag($this->fieldDefinition->getFieldSetting('target_type') . '_access');
275
276
277
278

    // Add the Selection handler for
    // entity_reference_query_entity_reference_alter().
    $query->addTag('entity_reference');
279
    $query->addMetaData('field_definition', $this->fieldDefinition);
280
281
282
    $query->addMetaData('entity_reference_selection_handler', $this);

    // Add the sort option.
283
284
285
    $handler_settings = $this->fieldDefinition->getFieldSetting('handler_settings');
    if (!empty($handler_settings['sort'])) {
      $sort_settings = $handler_settings['sort'];
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
      if ($sort_settings['field'] != '_none') {
        $query->sort($sort_settings['field'], $sort_settings['direction']);
      }
    }

    return $query;
  }

  /**
   * Implements SelectionInterface::entityQueryAlter().
   */
  public function entityQueryAlter(SelectInterface $query) { }

  /**
   * Helper method: Passes a query to the alteration system again.
   *
   * This allows Entity Reference to add a tag to an existing query so it can
   * ask access control mechanisms to alter it again.
   */
  protected function reAlterQuery(AlterableInterface $query, $tag, $base_table) {
    // Save the old tags and metadata.
    // For some reason, those are public.
    $old_tags = $query->alterTags;
    $old_metadata = $query->alterMetaData;

    $query->alterTags = array($tag => TRUE);
    $query->alterMetaData['base_table'] = $base_table;
    drupal_alter(array('query', 'query_' . $tag), $query);

    // Restore the tags and metadata.
    $query->alterTags = $old_tags;
    $query->alterMetaData = $old_metadata;
  }
}