Field.php

Contains \Drupal\Core\Entity\Field\Type\Field.

Namespace

Drupal\Core\Entity\Field\Type

File

drupal/core/lib/Drupal/Core/Entity/Field/Type/Field.php
View source
<?php

/**
 * @file
 * Contains \Drupal\Core\Entity\Field\Type\Field.
 */
namespace Drupal\Core\Entity\Field\Type;

use Drupal\Core\Entity\Field\FieldInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\TypedData\ItemList;

/**
 * Represents an entity field; that is, a list of field item objects.
 *
 * An entity field is a list of field items, which contain only primitive
 * properties or entity references. Note that even single-valued entity
 * fields are represented as list of items, however for easy access to the
 * contained item the entity field delegates __get() and __set() calls
 * directly to the first item.
 *
 * @see \Drupal\Core\Entity\Field\FieldInterface
 */
class Field extends ItemList implements FieldInterface {

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

  /**
   * Overrides TypedData::__construct().
   */
  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
    parent::__construct($definition, $name, $parent);

    // Always initialize one empty item as most times a value for at least one
    // item will be present. That way prototypes created by
    // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
    // already have this field item ready for use after cloning.
    $this->list[0] = $this
      ->createItem(0);
  }

  /**
   * {@inheritdoc}
   */
  public function filterEmptyValues() {
    if (isset($this->list)) {
      $this->list = array_values(array_filter($this->list, function ($item) {
        return !$item
          ->isEmpty();
      }));
    }
  }

  /**
   * Overrides \Drupal\Core\TypedData\ItemList::setValue().
   */
  public function setValue($values, $notify = TRUE) {

    // Notify the parent of any changes to be made.
    if ($notify && isset($this->parent)) {
      $this->parent
        ->onChange($this->name);
    }
    if (!isset($values) || $values === array()) {
      $this->list = $values;
    }
    else {

      // Support passing in only the value of the first item.
      if (!is_array($values) || !is_numeric(current(array_keys($values)))) {
        $values = array(
          0 => $values,
        );
      }

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

      // Set the values.
      foreach ($values as $delta => $value) {
        if (!is_numeric($delta)) {
          throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
        }
        elseif (!isset($this->list[$delta])) {
          $this->list[$delta] = $this
            ->createItem($delta, $value);
        }
        else {
          $this->list[$delta]
            ->setValue($value, FALSE);
        }
      }
    }
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::getPropertyDefinition().
   */
  public function getPropertyDefinition($name) {
    return $this
      ->offsetGet(0)
      ->getPropertyDefinition($name);
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::getPropertyDefinitions().
   */
  public function getPropertyDefinitions() {
    return $this
      ->offsetGet(0)
      ->getPropertyDefinitions();
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::__get().
   */
  public function __get($property_name) {
    return $this
      ->offsetGet(0)
      ->__get($property_name);
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::get().
   */
  public function get($property_name) {
    return $this
      ->offsetGet(0)
      ->get($property_name);
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::__set().
   */
  public function __set($property_name, $value) {
    $this
      ->offsetGet(0)
      ->__set($property_name, $value);
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::__isset().
   */
  public function __isset($property_name) {
    return $this
      ->offsetGet(0)
      ->__isset($property_name);
  }

  /**
   * Implements \Drupal\Core\Entity\Field\FieldInterface::__unset().
   */
  public function __unset($property_name) {
    return $this
      ->offsetGet(0)
      ->__unset($property_name);
  }

  /**
   * Implements \Drupal\Core\TypedData\AccessibleInterface::access().
   */
  public function access($operation = 'view', AccountInterface $account = NULL) {
    global $user;
    if (!isset($account) && $user->uid) {
      $account = user_load($user->uid);
    }

    // Get the default access restriction that lives within this field.
    $access = $this
      ->defaultAccess($operation, $account);

    // Invoke hook and collect grants/denies for field access from other
    // modules. Our default access flag is masked under the ':default' key.
    $grants = array(
      ':default' => $access,
    );
    $hook_implementations = \Drupal::moduleHandler()
      ->getImplementations('entity_field_access');
    foreach ($hook_implementations as $module) {
      $grants = array_merge($grants, array(
        $module => module_invoke($module, 'entity_field_access', $operation, $this, $account),
      ));
    }

    // Also allow modules to alter the returned grants/denies.
    $context = array(
      'operation' => $operation,
      'field' => $this,
      'account' => $account,
    );
    drupal_alter('entity_field_access', $grants, $context);

    // One grant being FALSE is enough to deny access immediately.
    if (in_array(FALSE, $grants, TRUE)) {
      return FALSE;
    }

    // At least one grant has the explicit opinion to allow access.
    if (in_array(TRUE, $grants, TRUE)) {
      return TRUE;
    }

    // All grants are NULL and have no opinion - deny access in that case.
    return FALSE;
  }

  /**
   * Contains the default access logic of this field.
   *
   * See \Drupal\Core\TypedData\AccessibleInterface::access() for the parameter
   * doucmentation. This method can be overriden by field sub classes to provide
   * a different default access logic. That allows them to inherit the complete
   * access() method which contains the access hook invocation logic.
   *
   * @return bool
   *   TRUE if access to this field is allowed per default, FALSE otherwise.
   */
  public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {

    // Grant access per default.
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function getConstraints() {

    // Constraints usually apply to the field item, but required does make
    // sense on the field only. So we special-case it to apply to the field for
    // now.
    // @todo: Separate list and list item definitions to separate constraints.
    $constraints = array();
    if (!empty($this->definition['required'])) {
      $constraints[] = \Drupal::typedData()
        ->getValidationConstraintManager()
        ->create('NotNull', array());
    }
    return $constraints;
  }

}

Classes

Namesort descending Description
Field Represents an entity field; that is, a list of field item objects.