FieldItemList.php 6.51 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\Core\Field\FieldItemList.
6 7
 */

8
namespace Drupal\Core\Field;
9

10
use Drupal\Core\Session\AccountInterface;
11
use Drupal\Core\TypedData\DataDefinitionInterface;
12
use Drupal\Core\TypedData\TypedDataInterface;
13
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
14
use Drupal\Core\Language\Language;
15 16

/**
17
 * Represents an entity field; that is, a list of field item objects.
18
 *
19 20 21 22
 * An entity field is a list of field items, each containing a set of
 * properties. Note that even single-valued entity fields are represented as
 * list of field items, however for easy access to the contained item the entity
 * field delegates __get() and __set() calls directly to the first item.
23
 */
24
class FieldItemList extends ItemList implements FieldItemListInterface {
25 26 27 28 29 30 31 32 33

  /**
   * Numerically indexed array of field items, implementing the
   * FieldItemInterface.
   *
   * @var array
   */
  protected $list = array();

34 35 36 37 38
  /**
   * The langcode of the field values held in the object.
   *
   * @var string
   */
39
  protected $langcode = Language::LANGCODE_NOT_SPECIFIED;
40

41
  /**
42
   * {@inheritdoc}
43
   */
44
  public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) {
45
    parent::__construct($definition, $name, $parent);
46 47
    // Always initialize one empty item as most times a value for at least one
    // item will be present. That way prototypes created by
48
    // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
49
    // already have this field item ready for use after cloning.
50 51
    $this->list[0] = $this->createItem(0);
  }
52

53 54 55 56 57 58 59
  /**
   * {@inheritdoc}
   */
  public function getEntity() {
    return $this->getParent();
  }

60 61 62 63 64 65 66 67 68 69 70 71 72 73
  /**
   * {@inheritdoc}
   */
  public function setLangcode($langcode) {
    $this->langcode = $langcode;
  }

  /**
   * {@inheritdoc}
   */
  public function getLangcode() {
    return $this->langcode;
  }

74 75 76 77
  /**
   * {@inheritdoc}
   */
  public function getFieldDefinition() {
78
    return $this->definition;
79 80
  }

81 82 83 84 85 86 87 88 89 90 91 92 93 94
  /**
   * {@inheritdoc}
   */
  public function getSettings() {
    return $this->definition->getSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function getSetting($setting_name) {
    return $this->definition->getSetting($setting_name);
  }

95
  /**
96
   * {@inheritdoc}
97
   */
98
  public function filterEmptyItems() {
99
    if (isset($this->list)) {
100 101 102
      $this->list = array_values(array_filter($this->list, function($item) {
        return !$item->isEmpty();
      }));
103 104 105
    }
  }

106 107 108 109 110 111 112 113 114 115 116 117 118 119
  /**
   * {@inheritdoc}
   * @todo Revisit the need when all entity types are converted to NG entities.
   */
  public function getValue($include_computed = FALSE) {
    if (isset($this->list)) {
      $values = array();
      foreach ($this->list as $delta => $item) {
        $values[$delta] = $item->getValue($include_computed);
      }
      return $values;
    }
  }

120
  /**
121
   * {@inheritdoc}
122
   */
123
  public function setValue($values, $notify = TRUE) {
124 125 126 127
    if (!isset($values) || $values === array()) {
      $this->list = $values;
    }
    else {
128 129 130 131
      // Support passing in only the value of the first item.
      if (!is_array($values) || !is_numeric(current(array_keys($values)))) {
        $values = array(0 => $values);
      }
132 133

      // Clear the values of properties for which no value has been passed.
134 135
      if (isset($this->list)) {
        $this->list = array_intersect_key($this->list, $values);
136 137 138 139 140
      }

      // Set the values.
      foreach ($values as $delta => $value) {
        if (!is_numeric($delta)) {
141
          throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
142 143
        }
        elseif (!isset($this->list[$delta])) {
144
          $this->list[$delta] = $this->createItem($delta, $value);
145 146
        }
        else {
147
          $this->list[$delta]->setValue($value, FALSE);
148 149 150
        }
      }
    }
151 152 153 154
    // Notify the parent of any changes.
    if ($notify && isset($this->parent)) {
      $this->parent->onChange($this->name);
    }
155 156
  }

157
  /**
158
   * {@inheritdoc}
159 160
   */
  public function __get($property_name) {
161
    return $this->first()->__get($property_name);
162 163 164
  }

  /**
165
   * {@inheritdoc}
166 167
   */
  public function __set($property_name, $value) {
168
    $this->first()->__set($property_name, $value);
169 170 171
  }

  /**
172
   * {@inheritdoc}
173 174
   */
  public function __isset($property_name) {
175
    return $this->first()->__isset($property_name);
176 177 178
  }

  /**
179
   * {@inheritdoc}
180 181
   */
  public function __unset($property_name) {
182
    return $this->first()->__unset($property_name);
183 184 185
  }

  /**
186
   * {@inheritdoc}
187
   */
188
  public function access($operation = 'view', AccountInterface $account = NULL) {
189
    $access_controller = \Drupal::entityManager()->getAccessController($this->getEntity()->getEntityTypeId());
190
    return $access_controller->fieldAccess($operation, $this->getFieldDefinition(), $account, $this);
191 192 193
  }

  /**
194
   * {@inheritdoc}
195
   */
196
  public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
197 198
    // Grant access per default.
    return TRUE;
199
  }
200

201 202 203 204
  /**
   * {@inheritdoc}
   */
  public function applyDefaultValue($notify = TRUE) {
205
    $value = $this->getDefaultValue();
206

207 208 209
    // NULL or array() mean "no default value", but  0, '0' and the empty string
    // are valid default values.
    if (!isset($value) || (is_array($value) && empty($value))) {
210
      // Create one field item and apply defaults.
211
      $this->first()->applyDefaultValue(FALSE);
212
    }
213 214 215
    else {
      $this->setValue($value, $notify);
    }
216 217 218
    return $this;
  }

219 220 221 222 223 224 225
  /**
   * Returns the default value for the field.
   *
   * @return array
   *   The default value for the field.
   */
  protected function getDefaultValue() {
226
    return $this->getFieldDefinition()->getDefaultValue($this->getEntity());
227
  }
228 229 230 231 232 233

  /**
   * {@inheritdoc}
   */
  public function preSave() {
    // Filter out empty items.
234
    $this->filterEmptyItems();
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 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280

    $this->delegateMethod('presave');
  }

  /**
   * {@inheritdoc}
   */
  public function insert() {
    $this->delegateMethod('insert');
  }

  /**
   * {@inheritdoc}
   */
  public function update() {
    $this->delegateMethod('update');
  }

  /**
   * {@inheritdoc}
   */
  public function delete() {
    $this->delegateMethod('delete');
  }

  /**
   * {@inheritdoc}
   */
  public function deleteRevision() {
    $this->delegateMethod('deleteRevision');
  }

  /**
   * Calls a method on each FieldItem.
   *
   * @param string $method
   *   The name of the method.
   */
  protected function delegateMethod($method) {
    if (isset($this->list)) {
      foreach ($this->list as $item) {
        $item->{$method}();
      }
    }
  }

281
}