Issue #2047229 by fago, smiletrl, Berdir, effulgentsia, amateescu: Make use of classes for entity field and data definitions.
parent
66f8fb8253
commit
9bcaedec15
|
@ -399,7 +399,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
}
|
||||
// Non-translatable fields are always stored with
|
||||
// Language::LANGCODE_DEFAULT as key.
|
||||
if ($langcode != Language::LANGCODE_DEFAULT && empty($definition['translatable'])) {
|
||||
if ($langcode != Language::LANGCODE_DEFAULT && !$definition->isFieldTranslatable()) {
|
||||
if (!isset($this->fields[$property_name][Language::LANGCODE_DEFAULT])) {
|
||||
$this->fields[$property_name][Language::LANGCODE_DEFAULT] = $this->getTranslatedField($property_name, Language::LANGCODE_DEFAULT);
|
||||
}
|
||||
|
@ -437,7 +437,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
public function getProperties($include_computed = FALSE) {
|
||||
$properties = array();
|
||||
foreach ($this->getPropertyDefinitions() as $name => $definition) {
|
||||
if ($include_computed || empty($definition['computed'])) {
|
||||
if ($include_computed || !$definition->isComputed()) {
|
||||
$properties[$name] = $this->get($name);
|
||||
}
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
$definitions = $translation->getPropertyDefinitions();
|
||||
|
||||
foreach ($values as $name => $value) {
|
||||
if (isset($definitions[$name]) && !empty($definitions[$name]['translatable'])) {
|
||||
if (isset($definitions[$name]) && $definitions[$name]->isFieldTranslatable()) {
|
||||
$translation->$name = $value;
|
||||
}
|
||||
}
|
||||
|
@ -724,7 +724,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
public function removeTranslation($langcode) {
|
||||
if (isset($this->translations[$langcode]) && $langcode != Language::LANGCODE_DEFAULT && $langcode != $this->getDefaultLanguage()->id) {
|
||||
foreach ($this->getPropertyDefinitions() as $name => $definition) {
|
||||
if (!empty($definition['translatable'])) {
|
||||
if ($definition->isFieldTranslatable()) {
|
||||
unset($this->values[$name][$langcode]);
|
||||
unset($this->fields[$name][$langcode]);
|
||||
}
|
||||
|
@ -779,7 +779,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
return;
|
||||
}
|
||||
foreach ($this->getPropertyDefinitions() as $name => $definition) {
|
||||
if (empty($definition['computed']) && !empty($this->fields[$name])) {
|
||||
if (!$definition->isComputed() && !empty($this->fields[$name])) {
|
||||
foreach ($this->fields[$name] as $langcode => $field) {
|
||||
$field->filterEmptyValues();
|
||||
$this->values[$name][$langcode] = $field->getValue();
|
||||
|
@ -911,7 +911,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
|
|||
// object keyed by language. To avoid creating different field objects
|
||||
// we retain just the original value, as references will be recreated
|
||||
// later as needed.
|
||||
if (empty($definitions[$name]['translatable']) && count($values) > 1) {
|
||||
if (!$definitions[$name]->isFieldTranslatable() && count($values) > 1) {
|
||||
$values = array_intersect_key($values, array(Language::LANGCODE_DEFAULT => TRUE));
|
||||
}
|
||||
foreach ($values as $langcode => $items) {
|
||||
|
|
|
@ -42,14 +42,20 @@ interface ContentEntityInterface extends EntityInterface, RevisionableInterface,
|
|||
/**
|
||||
* Defines the base fields of the entity type.
|
||||
*
|
||||
* Implementations typically use the class \Drupal\Core\Field\FieldDefinition
|
||||
* for creating the field definitions; for example a 'name' field could be
|
||||
* defined as the following:
|
||||
* @code
|
||||
* $fields['name'] = FieldDefinition::create('string')
|
||||
* ->setLabel(t('Name'));
|
||||
* @endcode
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to return properties for. Useful when a single class is
|
||||
* used for multiple, possibly dynamic entity types.
|
||||
*
|
||||
* @return array
|
||||
* An array of entity field definitions as specified by
|
||||
* \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions(), keyed by field
|
||||
* name.
|
||||
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
|
||||
* An array of entity field definitions, keyed by field name.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\Core\Entity;
|
|||
use Drupal\Component\Plugin\PluginManagerBase;
|
||||
use Drupal\Component\Plugin\Factory\DefaultFactory;
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Field\FieldDefinition;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManager;
|
||||
|
@ -333,6 +334,8 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
|
|||
$this->entityFieldInfo[$entity_type] = $cache->data;
|
||||
}
|
||||
else {
|
||||
// @todo: Refactor to allow for per-bundle overrides.
|
||||
// See https://drupal.org/node/2114707.
|
||||
$class = $this->factory->getPluginClass($entity_type, $this->getDefinition($entity_type));
|
||||
|
||||
$base_definitions = $class::baseFieldDefinitions($entity_type);
|
||||
|
@ -357,22 +360,31 @@ class EntityManager extends PluginManagerBase implements EntityManagerInterface
|
|||
$result = $this->moduleHandler->invokeAll('entity_field_info', array($entity_type));
|
||||
$this->entityFieldInfo[$entity_type] = NestedArray::mergeDeep($this->entityFieldInfo[$entity_type], $result);
|
||||
|
||||
// Enforce field definitions to be objects.
|
||||
foreach (array('definitions', 'optional') as $key) {
|
||||
foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
|
||||
if (is_array($definition)) {
|
||||
$definition = FieldDefinition::createFromOldStyleDefinition($definition);
|
||||
}
|
||||
// Automatically set the field name for non-configurable fields.
|
||||
if ($definition instanceof FieldDefinition) {
|
||||
$definition->setFieldName($field_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke alter hooks.
|
||||
$hooks = array('entity_field_info', $entity_type . '_field_info');
|
||||
$this->moduleHandler->alter($hooks, $this->entityFieldInfo[$entity_type], $entity_type);
|
||||
|
||||
// Enforce fields to be multiple and untranslatable by default.
|
||||
// Ensure all basic fields are not defined as translatable.
|
||||
$entity_info = $this->getDefinition($entity_type);
|
||||
$keys = array_intersect_key(array_filter($entity_info['entity_keys']), array_flip(array('id', 'revision', 'uuid', 'bundle')));
|
||||
$untranslatable_fields = array_flip(array('langcode') + $keys);
|
||||
foreach (array('definitions', 'optional') as $key) {
|
||||
foreach ($this->entityFieldInfo[$entity_type][$key] as $name => &$definition) {
|
||||
$definition['list'] = TRUE;
|
||||
// Ensure ids and langcode fields are never made translatable.
|
||||
if (isset($untranslatable_fields[$name]) && !empty($definition['translatable'])) {
|
||||
throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition['label'])));
|
||||
}
|
||||
if (!isset($definition['translatable'])) {
|
||||
$definition['translatable'] = FALSE;
|
||||
foreach ($this->entityFieldInfo[$entity_type][$key] as $field_name => &$definition) {
|
||||
if (isset($untranslatable_fields[$field_name]) && $definition->isFieldTranslatable()) {
|
||||
throw new \LogicException(format_string('The @field field cannot be translatable.', array('@field' => $definition->getFieldLabel())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,7 @@ interface EntityManagerInterface extends PluginManagerInterface {
|
|||
/**
|
||||
* Gets an array of content entity field definitions.
|
||||
*
|
||||
* If a bundle is passed, fields specific to this bundle are included. Entity
|
||||
* fields are always multi-valued, so 'list' is TRUE for each returned field
|
||||
* definition.
|
||||
* If a bundle is passed, fields specific to this bundle are included.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to get field definitions for. Only entity types that
|
||||
|
@ -36,19 +34,11 @@ interface EntityManagerInterface extends PluginManagerInterface {
|
|||
* (optional) The entity bundle for which to get field definitions. If NULL
|
||||
* is passed, no bundle-specific fields are included. Defaults to NULL.
|
||||
*
|
||||
* @return array
|
||||
* An array of field definitions of entity fields, keyed by field
|
||||
* name. In addition to the typed data definition keys as described at
|
||||
* \Drupal\Core\TypedData\TypedDataManager::create() the following keys are
|
||||
* supported:
|
||||
* - queryable: Whether the field is queryable via QueryInterface.
|
||||
* Defaults to TRUE if 'computed' is FALSE or not set, to FALSE otherwise.
|
||||
* - translatable: Whether the field is translatable. Defaults to FALSE.
|
||||
* - configurable: A boolean indicating whether the field is configurable
|
||||
* via field.module. Defaults to FALSE.
|
||||
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
|
||||
* An array of entity field definitions, keyed by field name.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\TypedDataManager::create()
|
||||
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitionsByConstraints()
|
||||
* @see \Drupal\Core\Entity\EntityManager::getFieldDefinitionsByConstraints()
|
||||
*/
|
||||
public function getFieldDefinitions($entity_type, $bundle = NULL);
|
||||
|
||||
|
|
|
@ -263,9 +263,7 @@ abstract class FieldableEntityStorageControllerBase extends EntityStorageControl
|
|||
*/
|
||||
public function onFieldItemsPurge(EntityInterface $entity, FieldInstanceInterface $instance) {
|
||||
if ($values = $this->readFieldItemsToPurge($entity, $instance)) {
|
||||
$field = $instance->getField();
|
||||
$definition = _field_generate_entity_field_definition($field, $instance);
|
||||
$items = \Drupal::typedData()->create($definition, $values, $field->getFieldName(), $entity);
|
||||
$items = \Drupal::typedData()->create($instance, $values, $instance->getFieldName(), $entity);
|
||||
$items->delete();
|
||||
}
|
||||
$this->purgeFieldItems($entity, $instance);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Field;
|
||||
|
||||
use Drupal\Core\Field\Plugin\DataType\FieldInstanceInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\field\Field;
|
||||
|
||||
|
@ -25,10 +26,11 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
if (isset($definition['instance'])) {
|
||||
$this->instance = $definition['instance'];
|
||||
// Definition can be the field config or field instance.
|
||||
if ($definition instanceof FieldInstanceInterface) {
|
||||
$this->instance = $definition;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +38,10 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDefinition() {
|
||||
// Configurable fields have the field_config entity injected as definition,
|
||||
// but we want to return the more specific field instance here.
|
||||
// @todo: Overhaul this once we have per-bundle field definitions injected,
|
||||
// see https://drupal.org/node/2114707.
|
||||
if (!isset($this->instance)) {
|
||||
$entity = $this->getEntity();
|
||||
$instances = Field::fieldInfo()->getBundleInstances($entity->entityType(), $entity->bundle());
|
||||
|
@ -43,9 +49,7 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
|
|||
$this->instance = $instances[$this->getName()];
|
||||
}
|
||||
else {
|
||||
// For base fields, fall back to the parent implementation.
|
||||
// @todo: Inject the field definition with
|
||||
// https://drupal.org/node/2047229.
|
||||
// For base fields, fall back to return the general definition.
|
||||
return parent::getFieldDefinition();
|
||||
}
|
||||
}
|
||||
|
@ -73,13 +77,6 @@ class ConfigFieldItemList extends FieldItemList implements ConfigFieldItemListIn
|
|||
return $constraints;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultValue() {
|
||||
return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -8,27 +8,25 @@
|
|||
namespace Drupal\Core\Field;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\ListDefinition;
|
||||
|
||||
/**
|
||||
* A class for defining entity fields.
|
||||
*/
|
||||
class FieldDefinition implements FieldDefinitionInterface {
|
||||
class FieldDefinition extends ListDefinition implements FieldDefinitionInterface, \ArrayAccess {
|
||||
|
||||
/**
|
||||
* The array holding values for all definition keys.
|
||||
* Creates a new field definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definition = array();
|
||||
|
||||
/**
|
||||
* Constructs a new FieldDefinition object.
|
||||
* @param string $type
|
||||
* The type of the field.
|
||||
*
|
||||
* @param array $definition
|
||||
* (optional) If given, a definition represented as array.
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* A new field definition object.
|
||||
*/
|
||||
public function __construct(array $definition = array()) {
|
||||
$this->definition = $definition;
|
||||
public static function create($type) {
|
||||
return new static(array(), DataDefinition::create('field_item:' . $type));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +42,7 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* @param string $name
|
||||
* The field name to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldName($name) {
|
||||
|
@ -56,58 +54,63 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldType() {
|
||||
$data_type = $this->getItemDefinition()->getDataType();
|
||||
// Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'.
|
||||
$parts = explode(':', $this->definition['type']);
|
||||
$parts = explode(':', $data_type);
|
||||
return $parts[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field type.
|
||||
*
|
||||
* @param string $type
|
||||
* The field type to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldType($type) {
|
||||
$this->definition['type'] = 'field_item:' . $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field setting.
|
||||
*
|
||||
* @param string $type
|
||||
* The field type to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldSetting($setting_name, $value) {
|
||||
$this->definition['settings'][$setting_name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldSettings() {
|
||||
return $this->definition['settings'];
|
||||
return $this->getItemDefinition()->getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets field settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* The value to set.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldSettings(array $settings) {
|
||||
$this->getItemDefinition()->setSettings($settings);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldSetting($setting_name) {
|
||||
return isset($this->definition['settings'][$setting_name]) ? $this->definition['settings'][$setting_name] : NULL;
|
||||
$settings = $this->getFieldSettings();
|
||||
return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a field setting.
|
||||
*
|
||||
* @param string $setting_name
|
||||
* The field setting to set.
|
||||
* @param mixed $value
|
||||
* The value to set.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldSetting($setting_name, $value) {
|
||||
$settings = $this->getFieldSettings();
|
||||
$settings[$setting_name] = $value;
|
||||
return $this->setFieldSettings($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldPropertyNames() {
|
||||
return array_keys(\Drupal::typedData()->create($this->definition['type'])->getPropertyDefinitions());
|
||||
return array_keys(\Drupal::typedData()->create($this->getItemDefinition())->getPropertyDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +126,7 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* @param bool $translatable
|
||||
* Whether the field is translatable.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setTranslatable($translatable) {
|
||||
|
@ -135,42 +138,28 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldLabel() {
|
||||
return $this->definition['label'];
|
||||
return $this->getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field label.
|
||||
*
|
||||
* @param string $label
|
||||
* The field label to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* The object itself for chaining.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFieldLabel($label) {
|
||||
$this->definition['label'] = $label;
|
||||
return $this;
|
||||
return $this->setLabel($label);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDescription() {
|
||||
return $this->definition['description'];
|
||||
return $this->getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field label.
|
||||
*
|
||||
* @param string $description
|
||||
* The field label to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* The object itself for chaining.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFieldDescription($description) {
|
||||
$this->definition['description'] = $description;
|
||||
return $this;
|
||||
return $this->setDescription($description);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,7 +174,7 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldRequired() {
|
||||
return !empty($this->definition['required']);
|
||||
return $this->isRequired();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,13 +189,33 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* Sets whether the field is required.
|
||||
*
|
||||
* @param bool $required
|
||||
* TRUE if the field is required, FALSE otherwise.
|
||||
* Whether the field is required.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldRequired($required) {
|
||||
$this->definition['required'] = $required;
|
||||
return $this->setRequired($required);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldQueryable() {
|
||||
return isset($this->definition['queryable']) ? $this->definition['queryable'] : !$this->isComputed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the field is queryable.
|
||||
*
|
||||
* @param bool $queryable
|
||||
* Whether the field is queryable.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setFieldQueryable($queryable) {
|
||||
$this->definition['queryable'] = $queryable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -218,11 +227,13 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
* @param array $constraints
|
||||
* The constraints to set.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldDefinition
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setPropertyConstraints($name, array $constraints) {
|
||||
$this->definition['item_definition']['constraints']['ComplexData'][$name] = $constraints;
|
||||
$item_constraints = $this->getItemDefinition()->getConstraints();
|
||||
$item_constraints['ComplexData'][$name] = $constraints;
|
||||
$this->getItemDefinition()->setConstraints($item_constraints);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -240,4 +251,77 @@ class FieldDefinition implements FieldDefinitionInterface {
|
|||
return $this->getFieldSetting('default_value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows creating field definition objects from old style definition arrays.
|
||||
*
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public static function createFromOldStyleDefinition(array $definition) {
|
||||
unset($definition['list']);
|
||||
|
||||
// Separate the list item definition from the list definition.
|
||||
$list_definition = $definition;
|
||||
unset($list_definition['type']);
|
||||
|
||||
// Constraints, class and settings apply to the list item.
|
||||
unset($list_definition['constraints']);
|
||||
unset($list_definition['class']);
|
||||
unset($list_definition['settings']);
|
||||
|
||||
$field_definition = new FieldDefinition($list_definition);
|
||||
if (isset($definition['list_class'])) {
|
||||
$field_definition->setClass($definition['list_class']);
|
||||
}
|
||||
else {
|
||||
$type_definition = \Drupal::typedData()->getDefinition($definition['type']);
|
||||
if (isset($type_definition['list_class'])) {
|
||||
$field_definition->setClass($type_definition['list_class']);
|
||||
}
|
||||
}
|
||||
if (isset($definition['translatable'])) {
|
||||
$field_definition->setTranslatable($definition['translatable']);
|
||||
unset($definition['translatable']);
|
||||
}
|
||||
|
||||
// Take care of the item definition now.
|
||||
// Required applies to the field definition only.
|
||||
unset($definition['required']);
|
||||
$item_definition = new DataDefinition($definition);
|
||||
$field_definition->setItemDefinition($item_definition);
|
||||
return $field_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function &offsetGet($offset) {
|
||||
if ($offset == 'type') {
|
||||
// What previously was "type" is now the type of the list item.
|
||||
$type = &$this->itemDefinition->offsetGet('type');
|
||||
return $type;
|
||||
}
|
||||
if (!isset($this->definition[$offset])) {
|
||||
$this->definition[$offset] = NULL;
|
||||
}
|
||||
return $this->definition[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
if ($offset == 'type') {
|
||||
// What previously was "type" is now the type of the list item.
|
||||
$this->itemDefinition->setDataType($value);
|
||||
}
|
||||
else {
|
||||
$this->definition[$offset] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Field;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\TypedData\ListDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for entity field definitions.
|
||||
|
@ -36,10 +37,9 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
*
|
||||
* However, entity base fields, such as $node->title, are not managed by
|
||||
* field.module and its "field_entity"/"field_instance" configuration entities.
|
||||
* Therefore, their definitions are provided by different objects that implement
|
||||
* this interface.
|
||||
* @todo That is still in progress: https://drupal.org/node/1949932. Update this
|
||||
* documentation with details when that's implemented.
|
||||
* Therefore, their definitions are provided by different objects based on the
|
||||
* class \Drupal\Core\Field\FieldDefinition, which implements this
|
||||
* interface as well.
|
||||
*
|
||||
* Field definitions may fully define a concrete data object (e.g.,
|
||||
* $node_1->body), or may provide a best-guess definition for a data object that
|
||||
|
@ -51,7 +51,7 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
* based on that abstract definition, even though that abstract definition can
|
||||
* differ from the concrete definition of any particular node's body field.
|
||||
*/
|
||||
interface FieldDefinitionInterface {
|
||||
interface FieldDefinitionInterface extends ListDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Value indicating a field accepts an unlimited number of values.
|
||||
|
@ -130,9 +130,18 @@ interface FieldDefinitionInterface {
|
|||
* Determines whether the field is configurable via field.module.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field is configurable.
|
||||
*/
|
||||
public function isFieldConfigurable();
|
||||
|
||||
/**
|
||||
* Determines whether the field is queryable via QueryInterface.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field is queryable.
|
||||
*/
|
||||
public function isFieldQueryable();
|
||||
|
||||
/**
|
||||
* Returns the human-readable label for the field.
|
||||
*
|
||||
|
@ -148,8 +157,8 @@ interface FieldDefinitionInterface {
|
|||
* descriptive information is helpful. For example, as help text below the
|
||||
* form element in entity edit forms.
|
||||
*
|
||||
* @return string
|
||||
* The field description.
|
||||
* @return string|null
|
||||
* The field description, or NULL if no description is available.
|
||||
*/
|
||||
public function getFieldDescription();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ abstract class FieldItemBase extends Map implements FieldItemInterface {
|
|||
/**
|
||||
* Overrides \Drupal\Core\TypedData\TypedData::__construct().
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
// Initialize computed properties by default, such that they get cloned
|
||||
// with the whole item.
|
||||
|
|
|
@ -9,20 +9,16 @@ namespace Drupal\Core\Field;
|
|||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\TypedData\ItemList;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
|
||||
use Drupal\Core\Language\Language;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Supported settings (below the definition's 'settings' key) are:
|
||||
* - default_value: (optional) If set, the default value to apply to the field.
|
||||
* 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.
|
||||
*/
|
||||
class FieldItemList extends ItemList implements FieldItemListInterface {
|
||||
|
||||
|
@ -42,11 +38,10 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
|||
protected $langcode = Language::LANGCODE_DEFAULT;
|
||||
|
||||
/**
|
||||
* Overrides TypedData::__construct().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
$this->definition['field_name'] = $name;
|
||||
// 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
|
||||
|
@ -79,7 +74,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDefinition() {
|
||||
return new FieldDefinition($this->definition);
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +103,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\TypedData\ItemList::setValue().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($values, $notify = TRUE) {
|
||||
if (!isset($values) || $values === array()) {
|
||||
|
@ -213,10 +208,8 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function applyDefaultValue($notify = TRUE) {
|
||||
// @todo Remove getDefaultValue() and directly call
|
||||
// FieldDefinition::getFieldDefaultValue() here, once
|
||||
// https://drupal.org/node/2047229 is fixed.
|
||||
$value = $this->getDefaultValue();
|
||||
|
||||
// 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))) {
|
||||
|
@ -236,24 +229,7 @@ class FieldItemList extends ItemList implements FieldItemListInterface {
|
|||
* The default value for the field.
|
||||
*/
|
||||
protected function getDefaultValue() {
|
||||
if (isset($this->definition['settings']['default_value'])) {
|
||||
return $this->definition['settings']['default_value'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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;
|
||||
return $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver.
|
||||
* Contains \Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\Plugin\DataType\Deriver;
|
||||
namespace Drupal\Core\Field\Plugin\DataType\Deriver;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDerivativeInterface;
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Entity\Plugin\DataType\FieldItem.
|
||||
* Contains \Drupal\Core\Field\Plugin\DataType\FieldItem.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Entity\Plugin\DataType;
|
||||
namespace Drupal\Core\Field\Plugin\DataType;
|
||||
|
||||
use Drupal\Core\TypedData\Annotation\DataType;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
|
||||
/**
|
||||
* Defines the base plugin for deriving data types for field types.
|
||||
|
@ -20,7 +18,7 @@ use Drupal\Core\Annotation\Translation;
|
|||
* id = "field_item",
|
||||
* label = @Translation("Field item"),
|
||||
* list_class = "\Drupal\Core\Field\FieldItemList",
|
||||
* derivative = "Drupal\Core\Entity\Plugin\DataType\Deriver\FieldItemDeriver"
|
||||
* derivative = "Drupal\Core\Field\Plugin\DataType\Deriver\FieldItemDeriver"
|
||||
* )
|
||||
*/
|
||||
abstract class FieldItem {
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Field\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Defines the 'integer' entity field type.
|
||||
|
@ -31,15 +32,13 @@ class IntegerItem extends FieldItemBase {
|
|||
static $propertyDefinitions;
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinitions() {
|
||||
|
||||
if (!isset(static::$propertyDefinitions)) {
|
||||
static::$propertyDefinitions['value'] = array(
|
||||
'type' => 'integer',
|
||||
'label' => t('Integer value'),
|
||||
);
|
||||
static::$propertyDefinitions['value'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Integer value'));
|
||||
}
|
||||
return static::$propertyDefinitions;
|
||||
}
|
||||
|
|
|
@ -102,8 +102,8 @@ class LegacyConfigFieldItemList extends ConfigFieldItemList {
|
|||
* The name of the hook, e.g. 'presave', 'validate'.
|
||||
*/
|
||||
protected function legacyCallback($hook, $args = array()) {
|
||||
$definition = $this->getPluginDefinition();
|
||||
$module = $definition['provider'];
|
||||
$type_definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getFieldDefinition()->getFieldType());
|
||||
$module = $type_definition['provider'];
|
||||
$callback = "{$module}_field_{$hook}";
|
||||
if (function_exists($callback)) {
|
||||
// We need to remove the empty "prototype" item here.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Field\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Defines the 'string' entity field type.
|
||||
|
@ -31,15 +32,13 @@ class StringItem extends FieldItemBase {
|
|||
static $propertyDefinitions;
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinitions() {
|
||||
|
||||
if (!isset(static::$propertyDefinitions)) {
|
||||
static::$propertyDefinitions['value'] = array(
|
||||
'type' => 'string',
|
||||
'label' => t('Text value'),
|
||||
);
|
||||
static::$propertyDefinitions['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Text value'));
|
||||
}
|
||||
return static::$propertyDefinitions;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use Drupal\Component\Annotation\Plugin;
|
|||
*
|
||||
* Furthermore, lists of data items are represented by objects implementing the
|
||||
* \Drupal\Core\TypedData\ListInterface. A list contains items of the same data
|
||||
* type, is ordered and may contain duplicates. The classed used for a list of
|
||||
* type, is ordered and may contain duplicates. The class used for a list of
|
||||
* items of a certain type may be specified using the 'list class' key.
|
||||
*
|
||||
* @see \Drupal::typedData()
|
||||
|
@ -67,7 +67,7 @@ class DataType extends Plugin {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $list_class = '\Drupal\Core\TypedData\ItemList';
|
||||
public $list_class = '\Drupal\Core\TypedData\Plugin\DataType\ItemList';
|
||||
|
||||
/**
|
||||
* The pre-defined primitive type that this data type maps to.
|
||||
|
|
|
@ -105,10 +105,6 @@ interface ComplexDataInterface extends \Traversable, TypedDataInterface {
|
|||
/**
|
||||
* Gets an array of property definitions of contained properties.
|
||||
*
|
||||
* @param array $definition
|
||||
* The definition of the container's property, e.g. the definition of an
|
||||
* entity reference property.
|
||||
*
|
||||
* @return array
|
||||
* An array of property definitions of contained properties, keyed by
|
||||
* property name.
|
||||
|
|
|
@ -0,0 +1,371 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\DataDefinition.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
|
||||
/**
|
||||
* A class for defining data based on defined data types.
|
||||
*/
|
||||
class DataDefinition implements DataDefinitionInterface, \ArrayAccess {
|
||||
|
||||
/**
|
||||
* The array holding values for all definition keys.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $definition = array();
|
||||
|
||||
/**
|
||||
* Creates a new data definition.
|
||||
*
|
||||
* @param string $type
|
||||
* The data type of the data; e.g., 'string', 'integer' or 'any'.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\DataDefinition
|
||||
* A new DataDefinition object.
|
||||
*/
|
||||
public static function create($type) {
|
||||
$definition['type'] = $type;
|
||||
return new static($definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new data definition object.
|
||||
*
|
||||
* @param array $definition
|
||||
* (optional) If given, a data definition represented as array.
|
||||
*/
|
||||
public function __construct(array $definition = array()) {
|
||||
$this->definition = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDataType() {
|
||||
return !empty($this->definition['type']) ? $this->definition['type'] : 'any';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the data type.
|
||||
*
|
||||
* @param string $type
|
||||
* The data type to set.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setDataType($type) {
|
||||
$this->definition['type'] = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return isset($this->definition['label']) ? $this->definition['label'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the human-readable label.
|
||||
*
|
||||
* @param string $label
|
||||
* The label to set.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setLabel($label) {
|
||||
$this->definition['label'] = $label;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return isset($this->definition['description']) ? $this->definition['description'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the human-readable description.
|
||||
*
|
||||
* @param string $description
|
||||
* The description to set.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setDescription($description) {
|
||||
$this->definition['description'] = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isList() {
|
||||
return ($this instanceof ListDefinitionInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadOnly() {
|
||||
if (!isset($this->definition['read-only'])) {
|
||||
// Default to read-only if the data value is computed.
|
||||
return $this->isComputed();
|
||||
}
|
||||
return !empty($this->definition['read-only']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the data is read-only.
|
||||
*
|
||||
* @param bool $read_only
|
||||
* Whether the data is read-only.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setReadOnly($read_only) {
|
||||
$this->definition['read-only'] = $read_only;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComputed() {
|
||||
return !empty($this->definition['computed']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the data is computed.
|
||||
*
|
||||
* @param bool $computed
|
||||
* Whether the data is computed.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setComputed($computed) {
|
||||
$this->definition['computed'] = $computed;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRequired() {
|
||||
return !empty($this->definition['required']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the data is required.
|
||||
*
|
||||
* @param bool $required
|
||||
* Whether the data is required.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setRequired($required) {
|
||||
$this->definition['required'] = $required;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClass() {
|
||||
return isset($this->definition['class']) ? $this->definition['class'] : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the class used for creating the typed data object.
|
||||
*
|
||||
* @param string|null $class
|
||||
* The class to use.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setClass($class) {
|
||||
$this->definition['class'] = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of settings, as required by the used class.
|
||||
*
|
||||
* See the documentation of the class for supported or required settings.
|
||||
*
|
||||
* @return array
|
||||
* The array of settings.
|
||||
*/
|
||||
public function getSettings() {
|
||||
return isset($this->definition['settings']) ? $this->definition['settings'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the array of settings, as required by the used class.
|
||||
*
|
||||
* @param array $settings
|
||||
* The array of settings.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setSettings(array $settings) {
|
||||
$this->definition['settings'] = $settings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of validation constraints.
|
||||
*
|
||||
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
|
||||
*
|
||||
* @return array
|
||||
* Array of constraints, each being an instance of
|
||||
* \Symfony\Component\Validator\Constraint.
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return isset($this->definition['constraints']) ? $this->definition['constraints'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the array of validation constraints.
|
||||
*
|
||||
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
|
||||
*
|
||||
* @param array $constraints
|
||||
* The array of constraints.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setConstraints(array $constraints) {
|
||||
$this->definition['constraints'] = $constraints;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a validation constraint.
|
||||
*
|
||||
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
|
||||
*
|
||||
* @param string $constraint_name
|
||||
* The name of the constraint to add, i.e. its plugin id.
|
||||
* @param array|null $options
|
||||
* The constraint options as required by the constraint plugin, or NULL.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function addConstraint($constraint_name, $options = NULL) {
|
||||
$this->definition['constraints'][$constraint_name] = $options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
// PHP's array access does not work correctly with isset(), so we have to
|
||||
// bake isset() in here. See https://bugs.php.net/bug.php?id=41727.
|
||||
return array_key_exists($offset, $this->definition) && isset($this->definition[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function &offsetGet($offset) {
|
||||
if (!isset($this->definition[$offset])) {
|
||||
$this->definition[$offset] = NULL;
|
||||
}
|
||||
return $this->definition[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->definition[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* This is for BC support only.
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->definition[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all definition values as array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray() {
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows creating data definition objects from old style definition arrays.
|
||||
*
|
||||
* @todo: Remove once https://drupal.org/node/2112239 is in.
|
||||
*/
|
||||
public static function createFromOldStyleDefinition(array $definition) {
|
||||
if (empty($definition['list'])) {
|
||||
return new DataDefinition($definition);
|
||||
}
|
||||
|
||||
// If the definition describes a list, separate the list item definition
|
||||
// from the list definition.
|
||||
unset($definition['list']);
|
||||
|
||||
$list_definition = $definition;
|
||||
unset($list_definition['type']);
|
||||
|
||||
// Constraints, class and settings apply to the list item.
|
||||
unset($list_definition['constraints']);
|
||||
unset($list_definition['class']);
|
||||
unset($list_definition['settings']);
|
||||
|
||||
$list_definition = new ListDefinition($list_definition);
|
||||
if (isset($definition['list_class'])) {
|
||||
$list_definition->setClass($definition['list_class']);
|
||||
}
|
||||
else {
|
||||
$type_definition = \Drupal::typedData()->getDefinition($definition['type']);
|
||||
if (isset($type_definition['list_class'])) {
|
||||
$list_definition->setClass($type_definition['list_class']);
|
||||
}
|
||||
}
|
||||
|
||||
// Take care of the item definition now.
|
||||
// Required applies to the list definition only.
|
||||
unset($definition['required']);
|
||||
$item_definition = new DataDefinition($definition);
|
||||
$list_definition->setItemDefinition($item_definition);
|
||||
return $list_definition;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\DataDefinitionInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
|
||||
/**
|
||||
* Interface for data definitions.
|
||||
*
|
||||
* Data definitions are used to describe data based upon available data types.
|
||||
* For example, a plugin could describe its parameters using data definitions
|
||||
* in order to specify what kind of data is required for it.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\DataDefinition
|
||||
*/
|
||||
interface DataDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Returns the data type of the data.
|
||||
*
|
||||
* @return string
|
||||
* The data type.
|
||||
*/
|
||||
public function getDataType();
|
||||
|
||||
/**
|
||||
* Returns a human readable label.
|
||||
*
|
||||
* @return string
|
||||
* The label.
|
||||
*/
|
||||
public function getLabel();
|
||||
|
||||
/**
|
||||
* Returns a human readable description.
|
||||
*
|
||||
* @return string|null
|
||||
* The description, or NULL if no description is available.
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* Returns whether the data is multi-valued, i.e. a list of data items.
|
||||
*
|
||||
* This is equivalent to checking whether the data definition implements the
|
||||
* \Drupal\Core\TypedData\ListDefinitionInterface interface.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the data is multi-valued.
|
||||
*/
|
||||
public function isList();
|
||||
|
||||
/**
|
||||
* Determines whether the data is read-only.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the data is read-only.
|
||||
*/
|
||||
public function isReadOnly();
|
||||
|
||||
/**
|
||||
* Determines whether the data value is computed.
|
||||
*
|
||||
* For example, data could be computed depending on some other values.
|
||||
*
|
||||
* @return bool
|
||||
* Whether the data value is computed.
|
||||
*/
|
||||
public function isComputed();
|
||||
|
||||
/**
|
||||
* Determines whether a data value is required.
|
||||
*
|
||||
* For required data a non-NULL value is mandatory.
|
||||
*
|
||||
* @return bool
|
||||
* Whether a data value is required.
|
||||
*/
|
||||
public function isRequired();
|
||||
|
||||
/**
|
||||
* Returns the class used for creating the typed data object.
|
||||
*
|
||||
* If not specified, the default class of the data type will be used.
|
||||
*
|
||||
* @return string|null
|
||||
* The class used for creating the typed data object.
|
||||
*/
|
||||
public function getClass();
|
||||
|
||||
/**
|
||||
* Returns the array of settings, as required by the used class.
|
||||
*
|
||||
* See the documentation of the class for supported or required settings.
|
||||
*
|
||||
* @return array
|
||||
* The array of settings.
|
||||
*/
|
||||
public function getSettings();
|
||||
|
||||
/**
|
||||
* Returns an array of validation constraints.
|
||||
*
|
||||
* See \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
|
||||
*
|
||||
* @return array
|
||||
* Array of constraints, each being an instance of
|
||||
* \Symfony\Component\Validator\Constraint.
|
||||
*/
|
||||
public function getConstraints();
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\ListDefinition.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
|
||||
/**
|
||||
* A class for defining data based on defined data types.
|
||||
*/
|
||||
class ListDefinition extends DataDefinition implements ListDefinitionInterface {
|
||||
|
||||
/**
|
||||
* The data definition of a list item.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
*/
|
||||
protected $itemDefinition;
|
||||
|
||||
/**
|
||||
* Creates a new list definition.
|
||||
*
|
||||
* @param string $item_type
|
||||
* The data type of the list items; e.g., 'string', 'integer' or 'any'.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\ListDefinition
|
||||
* A new List Data Definition object.
|
||||
*/
|
||||
public static function create($item_type) {
|
||||
return new static(array(), DataDefinition::create($item_type));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param
|
||||
*/
|
||||
public function __construct(array $definition = array(), DataDefinitionInterface $item_definition = NULL) {
|
||||
parent::__construct($definition);
|
||||
$this->itemDefinition = isset($item_definition) ? $item_definition : DataDefinition::create('any');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDataType() {
|
||||
return 'list';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDataType($type) {
|
||||
if ($type != 'list') {
|
||||
throw new \LogicException('Lists must always be of data type "list".');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClass() {
|
||||
$class = isset($this->definition['class']) ? $this->definition['class'] : NULL;
|
||||
if (!empty($class)) {
|
||||
return $class;
|
||||
}
|
||||
else {
|
||||
// If a list definition is used but no class has been specified, derive
|
||||
// the default list class from the item type.
|
||||
$item_type_definition = \Drupal::typedData()
|
||||
->getDefinition($this->getItemDefinition()->getDataType());
|
||||
return $item_type_definition['list_class'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemDefinition() {
|
||||
return $this->itemDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item definition.
|
||||
*
|
||||
* @param \Drupal\Core\TypedData\DataDefinition $definition
|
||||
* A list item's data definition.
|
||||
*
|
||||
* @return self
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setItemDefinition(DataDefinitionInterface $definition) {
|
||||
$this->itemDefinition = $definition;
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\ListDefinitionInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
|
||||
/**
|
||||
* Interface for data definitions of lists.
|
||||
*
|
||||
* This interface is present on a data definition if it describes a list. The
|
||||
* actual lists implement the \Drupal\Core\TypedData\ListInterface.
|
||||
*/
|
||||
interface ListDefinitionInterface extends DataDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Gets the data definition of an item of the list.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
* A data definition describing the list items.
|
||||
*/
|
||||
public function getItemDefinition();
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ namespace Drupal\Core\TypedData;
|
|||
* Interface for a list of typed data.
|
||||
*
|
||||
* A list of typed data contains only items of the same type, is ordered and may
|
||||
* contain duplicates.
|
||||
* contain duplicates. Note that the data type of a list is always 'list'.
|
||||
*
|
||||
* When implementing this interface which extends Traversable, make sure to list
|
||||
* IteratorAggregate or Iterator before this interface in the implements clause.
|
||||
|
@ -29,7 +29,7 @@ interface ListInterface extends TypedDataInterface, \ArrayAccess, \Countable, \T
|
|||
/**
|
||||
* Gets the definition of a contained item.
|
||||
*
|
||||
* @return array
|
||||
* @return \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
* The data definition of contained items.
|
||||
*/
|
||||
public function getItemDefinition();
|
||||
|
|
|
@ -2,16 +2,28 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\TypedData\List.
|
||||
* Contains \Drupal\Core\TypedData\Plugin\DataType\ItemList.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\TypedData;
|
||||
namespace Drupal\Core\TypedData\Plugin\DataType;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\ListInterface;
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
|
||||
/**
|
||||
* A generic list class.
|
||||
*
|
||||
* This class can serve as list for any type of items.
|
||||
* This class can serve as list for any type of items and is used by default.
|
||||
* Data types may specify the default list class in their definition, see
|
||||
* Drupal\Core\TypedData\Annotation\DataType.
|
||||
* Note: The class cannot be called "List" as list is a reserved PHP keyword.
|
||||
*
|
||||
* @DataType(
|
||||
* id = "list",
|
||||
* label = @Translation("List of items")
|
||||
* )
|
||||
*/
|
||||
class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
|
||||
|
||||
|
@ -88,14 +100,6 @@ class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
|
||||
*/
|
||||
public function getConstraints() {
|
||||
// Apply the constraints to the list items only.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \ArrayAccess::offsetExists().
|
||||
*/
|
||||
|
@ -140,7 +144,7 @@ class ItemList extends TypedData implements \IteratorAggregate, ListInterface {
|
|||
* Implements \Drupal\Core\TypedData\ListInterface::getItemDefinition().
|
||||
*/
|
||||
public function getItemDefinition() {
|
||||
return array('list' => FALSE) + $this->definition;
|
||||
return $this->definition->getItemDefinition();
|
||||
}
|
||||
|
||||
/**
|
|
@ -20,7 +20,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
|
|||
/**
|
||||
* The data definition.
|
||||
*
|
||||
* @var array
|
||||
* @var \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
|
@ -41,7 +41,7 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
|
|||
/**
|
||||
* Constructs a TypedData object given its definition and context.
|
||||
*
|
||||
* @param array $definition
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* The data definition.
|
||||
* @param string $name
|
||||
* (optional) The name of the created property, or NULL if it is the root
|
||||
|
@ -51,8 +51,12 @@ abstract class TypedData implements TypedDataInterface, PluginInspectionInterfac
|
|||
* root of a typed data tree. Defaults to NULL.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\TypedDataManager::create()
|
||||
*
|
||||
* @todo When \Drupal\Core\Config\TypedConfigManager has been fixed to use
|
||||
* class-based definitions, type-hint $definition to
|
||||
* DataDefinitionInterface.
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
$this->definition = $definition;
|
||||
$this->parent = $parent;
|
||||
$this->name = $name;
|
||||
|
|
|
@ -17,8 +17,8 @@ interface TypedDataInterface {
|
|||
/**
|
||||
* Gets the data definition.
|
||||
*
|
||||
* @return array
|
||||
* The data definition array.
|
||||
* @return \Drupal\Core\TypedData\DataDefinitionInterface
|
||||
* The data definition object.
|
||||
*/
|
||||
public function getDefinition();
|
||||
|
||||
|
|
|
@ -52,77 +52,49 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Component\Plugin\PluginManagerInterface::createInstance().
|
||||
* Instantiates a typed data object.
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The id of a plugin, i.e. the data type.
|
||||
* @param string $data_type
|
||||
* The data type, for which a typed object should be instantiated.
|
||||
* @param array $configuration
|
||||
* The plugin configuration, i.e. the data definition.
|
||||
* @param string $name
|
||||
* (optional) If a property or list item is to be created, the name of the
|
||||
* property or the delta of the list item.
|
||||
* @param mixed $parent
|
||||
* (optional) If a property or list item is to be created, the parent typed
|
||||
* data object implementing either the ListInterface or the
|
||||
* ComplexDataInterface.
|
||||
* The plugin configuration array, i.e. an array with the following keys:
|
||||
* - data definition: The data definition object, i.e. an instance of
|
||||
* \Drupal\Core\TypedData\DataDefinitionInterface.
|
||||
* - name: (optional) If a property or list item is to be created, the name
|
||||
* of the property or the delta of the list item.
|
||||
* - parent: (optional) If a property or list item is to be created, the
|
||||
* parent typed data object implementing either the ListInterface or the
|
||||
* ComplexDataInterface.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
* The instantiated typed data object.
|
||||
*/
|
||||
public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
|
||||
$type_definition = $this->getDefinition($plugin_id);
|
||||
public function createInstance($data_type, array $configuration) {
|
||||
$data_definition = $configuration['data_definition'];
|
||||
$type_definition = $this->getDefinition($data_type);
|
||||
|
||||
if (!isset($type_definition)) {
|
||||
throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $plugin_id)));
|
||||
throw new \InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $data_type)));
|
||||
}
|
||||
|
||||
// Allow per-data definition overrides of the used classes, i.e. take over
|
||||
// classes specified in the data definition.
|
||||
$key = empty($configuration['list']) ? 'class' : 'list_class';
|
||||
if (isset($configuration[$key])) {
|
||||
$class = $configuration[$key];
|
||||
}
|
||||
elseif (isset($type_definition[$key])) {
|
||||
$class = $type_definition[$key];
|
||||
}
|
||||
// classes specified in the type definition.
|
||||
$class = $data_definition->getClass();
|
||||
$class = isset($class) ? $class : $type_definition['class'];
|
||||
|
||||
if (!isset($class)) {
|
||||
throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
|
||||
throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $data_type));
|
||||
}
|
||||
return new $class($configuration, $name, $parent);
|
||||
return new $class($data_definition, $configuration['name'], $configuration['parent']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new typed data object instance.
|
||||
*
|
||||
* @param array $definition
|
||||
* The data definition array with the following array keys and values:
|
||||
* - type: The data type of the data to wrap. Required.
|
||||
* - label: A human readable label.
|
||||
* - description: A human readable description.
|
||||
* - list: Whether the data is multi-valued, i.e. a list of data items.
|
||||
* Defaults to FALSE.
|
||||
* - computed: A boolean specifying whether the data value is computed by
|
||||
* the object, e.g. depending on some other values.
|
||||
* - read-only: A boolean specifying whether the data is read-only. Defaults
|
||||
* to TRUE for computed properties, to FALSE otherwise.
|
||||
* - class: If set and 'list' is FALSE, the class to use for creating the
|
||||
* typed data object; otherwise the default class of the data type will be
|
||||
* used.
|
||||
* - list_class: If set and 'list' is TRUE, the class to use for creating
|
||||
* the typed data object; otherwise the default list class of the data
|
||||
* type will be used.
|
||||
* - settings: An array of settings, as required by the used 'class'. See
|
||||
* the documentation of the class for supported or required settings.
|
||||
* - list_settings: An array of settings as required by the used
|
||||
* 'list_class'. See the documentation of the list class for support or
|
||||
* required settings.
|
||||
* - constraints: An array of validation constraints. See
|
||||
* \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
|
||||
* - required: A boolean specifying whether a non-NULL value is mandatory.
|
||||
* Further keys may be supported in certain usages, e.g. for further keys
|
||||
* supported for entity field definitions see
|
||||
* \Drupal\Core\Entity\StorageControllerInterface::getPropertyDefinitions().
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* The data definition of the typed data object. For backwards-compatibility
|
||||
* an array representation of the data definition may be passed also.
|
||||
* @todo: Need to remove array support in https://drupal.org/node/2112239.
|
||||
* @param mixed $value
|
||||
* (optional) The data value. If set, it has to match one of the supported
|
||||
* data type format as documented for the data type classes.
|
||||
|
@ -148,12 +120,20 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
* @see \Drupal\Core\TypedData\Plugin\DataType\Uri
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\Binary
|
||||
*/
|
||||
public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
|
||||
$wrapper = $this->createInstance($definition['type'], $definition, $name, $parent);
|
||||
if (isset($value)) {
|
||||
$wrapper->setValue($value, FALSE);
|
||||
public function create($definition, $value = NULL, $name = NULL, $parent = NULL) {
|
||||
// @todo: Remove array support once https://drupal.org/node/2112239 is in.
|
||||
if (is_array($definition)) {
|
||||
$definition = DataDefinition::createFromOldStyleDefinition($definition);
|
||||
}
|
||||
return $wrapper;
|
||||
$typed_data = $this->createInstance($definition->getDataType(), array(
|
||||
'data_definition' => $definition,
|
||||
'name' => $name,
|
||||
'parent' => $parent,
|
||||
));
|
||||
if (isset($value)) {
|
||||
$typed_data->setValue($value, FALSE);
|
||||
}
|
||||
return $typed_data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -335,18 +315,20 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
*
|
||||
* @see \Drupal\Core\Validation\ConstraintManager
|
||||
*
|
||||
* @param array $definition
|
||||
* A data definition array.
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* A data definition.
|
||||
*
|
||||
* @return array
|
||||
* Array of constraints, each being an instance of
|
||||
* \Symfony\Component\Validator\Constraint.
|
||||
*
|
||||
* @todo: Having this as well as $definition->getConstraints() is confusing.
|
||||
*/
|
||||
public function getConstraints($definition) {
|
||||
public function getConstraints(DataDefinitionInterface $definition) {
|
||||
$constraints = array();
|
||||
$validation_manager = $this->getValidationConstraintManager();
|
||||
|
||||
$type_definition = $this->getDefinition($definition['type']);
|
||||
$type_definition = $this->getDefinition($definition->getDataType());
|
||||
// Auto-generate a constraint for data types implementing a primitive
|
||||
// interface.
|
||||
if (is_subclass_of($type_definition['class'], '\Drupal\Core\TypedData\PrimitiveInterface')) {
|
||||
|
@ -363,19 +345,21 @@ class TypedDataManager extends DefaultPluginManager {
|
|||
}
|
||||
}
|
||||
// Add any constraints specified as part of the data definition.
|
||||
if (isset($definition['constraints'])) {
|
||||
foreach ($definition['constraints'] as $name => $options) {
|
||||
$constraints[] = $validation_manager->create($name, $options);
|
||||
}
|
||||
$defined_constraints = $definition->getConstraints();
|
||||
foreach ($defined_constraints as $name => $options) {
|
||||
$constraints[] = $validation_manager->create($name, $options);
|
||||
}
|
||||
// Add the NotNull constraint for required data.
|
||||
if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
|
||||
if ($definition->isRequired() && !isset($defined_constraints['NotNull'])) {
|
||||
$constraints[] = $validation_manager->create('NotNull', array());
|
||||
}
|
||||
|
||||
// If the definition does not provide a class use the class from the type
|
||||
// definition for performing interface checks.
|
||||
$class = isset($definition['class']) ? $definition['class'] : $type_definition['class'];
|
||||
$class = $definition->getClass();
|
||||
if (!$class) {
|
||||
$class = $type_definition['class'];
|
||||
}
|
||||
// Check if the class provides allowed values.
|
||||
if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) {
|
||||
$constraints[] = $validation_manager->create('AllowedValues', array());
|
||||
|
|
|
@ -19,6 +19,9 @@ class ValidReferenceConstraintValidator extends ConstraintValidator {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
if (!isset($value)) {
|
||||
return;
|
||||
}
|
||||
$id = $value->get('target_id')->getValue();
|
||||
// '0' or NULL are considered valid empty references.
|
||||
if (empty($id)) {
|
||||
|
|
|
@ -101,7 +101,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
|
|||
// translation.
|
||||
// @todo Remove this special casing as soon as configurable and
|
||||
// base field definitions are "unified".
|
||||
if (!empty($definition['configurable']) && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
|
||||
if ($definition->isFieldConfigurable() && ($field = FieldService::fieldInfo()->getField($entity_type, $field_name))) {
|
||||
$instance = FieldService::fieldInfo()->getInstance($entity_type, $bundle, $field_name);
|
||||
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
|
||||
'#label' => $instance->getFieldLabel(),
|
||||
|
@ -122,7 +122,7 @@ function _content_translation_form_language_content_settings_form_alter(array &$
|
|||
// fields support translation. Whether they are actually enabled is
|
||||
// determined through our settings. As a consequence only fields
|
||||
// that support translation can be enabled or disabled.
|
||||
elseif (isset($field_settings[$field_name]) || !empty($definition['translatable'])) {
|
||||
elseif (isset($field_settings[$field_name]) || $definition->isFieldTranslatable()) {
|
||||
$form['settings'][$entity_type][$bundle]['fields'][$field_name] = array(
|
||||
'#label' => $definition['label'],
|
||||
'#type' => 'checkbox',
|
||||
|
|
|
@ -134,7 +134,7 @@ function content_translation_entity_field_info_alter(&$info, $entity_type) {
|
|||
foreach ($fields as $name => $translatable) {
|
||||
foreach ($keys as $key) {
|
||||
if (isset($info[$key][$name])) {
|
||||
$info[$key][$name]['translatable'] = (bool) $translatable;
|
||||
$info[$key][$name]->setTranslatable((bool) $translatable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ function content_translation_form_alter(array &$form, array &$form_state) {
|
|||
if (!$entity->isNew() && (!isset($translations[$form_langcode]) || count($translations) > 1)) {
|
||||
foreach ($entity->getPropertyDefinitions() as $property_name => $definition) {
|
||||
if (isset($form[$property_name])) {
|
||||
$form[$property_name]['#multilingual'] = !empty($definition['translatable']);
|
||||
$form[$property_name]['#multilingual'] = $definition->isFieldTranslatable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class DateTimeComputed extends TypedData {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
if (!isset($definition['settings']['date source'])) {
|
||||
throw new \InvalidArgumentException("The definition's 'date source' key has to specify the name of the date property to be computed.");
|
||||
|
|
|
@ -189,7 +189,7 @@ function edit_preprocess_field(&$variables) {
|
|||
// Fields that are not part of the entity (i.e. dynamically injected "pseudo
|
||||
// fields") and computed fields are not editable.
|
||||
$definition = $entity->getPropertyDefinition($element['#field_name']);
|
||||
if ($definition && empty($definition['computed'])) {
|
||||
if ($definition && !$definition->isComputed()) {
|
||||
$variables['attributes']['data-edit-field-id'] = $entity->entityType() . '/' . $entity->id() . '/' . $element['#field_name'] . '/' . $element['#language'] . '/' . $element['#view_mode'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -557,7 +557,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) {
|
|||
// Find all configurable fields, because only they could have a
|
||||
// text_processing setting.
|
||||
$configurable_fields = array_keys(array_filter($properties, function ($definition) {
|
||||
return isset($definition['configurable']) && $definition['configurable'] === TRUE;
|
||||
return $definition->isFieldConfigurable();
|
||||
}));
|
||||
if (empty($configurable_fields)) {
|
||||
return array();
|
||||
|
|
|
@ -27,35 +27,6 @@ function entity_reference_field_info_alter(&$info) {
|
|||
$info['entity_reference']['list_class'] = '\Drupal\entity_reference\Plugin\Field\FieldType\ConfigurableEntityReferenceFieldItemList';
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_field_info_alter().
|
||||
*
|
||||
* Set the "target_type" and "list_class" property definition for entity
|
||||
* reference fields.
|
||||
*
|
||||
* @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::getPropertyDefinitions()
|
||||
*
|
||||
* @param array $info
|
||||
* The property info array as returned by hook_entity_field_info().
|
||||
* @param string $entity_type
|
||||
* The entity type for which entity properties are defined.
|
||||
*/
|
||||
function entity_reference_entity_field_info_alter(&$info, $entity_type) {
|
||||
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
|
||||
foreach ($instances as $field_name => $instance) {
|
||||
if ($instance->getFieldType() != 'entity_reference') {
|
||||
continue;
|
||||
}
|
||||
if (isset($info['definitions'][$field_name])) {
|
||||
$info['definitions'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
|
||||
}
|
||||
elseif (isset($info['optional'][$field_name])) {
|
||||
$info['optional'][$field_name]['settings']['target_type'] = $instance->getFieldSetting('target_type');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_widget_info_alter().
|
||||
*/
|
||||
|
|
|
@ -616,7 +616,7 @@ function field_attach_form_validate(ContentEntityInterface $entity, $form, &$for
|
|||
$has_violations = FALSE;
|
||||
foreach ($entity as $field_name => $field) {
|
||||
$definition = $field->getDefinition();
|
||||
if (!empty($definition['configurable']) && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
|
||||
if ($definition->isFieldConfigurable() && (empty($options['field_name']) || $options['field_name'] == $field_name)) {
|
||||
$field_violations = $field->validate();
|
||||
if (count($field_violations)) {
|
||||
$has_violations = TRUE;
|
||||
|
@ -926,13 +926,13 @@ function field_language(EntityInterface $entity, $field_name = NULL, $langcode =
|
|||
if (!isset($field_name)) {
|
||||
$display_langcodes = array();
|
||||
foreach ($definitions as $name => $definition) {
|
||||
if (!empty($definition['configurable'])) {
|
||||
if ($definition->isFieldConfigurable()) {
|
||||
$display_langcodes[$name] = $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
return $display_langcodes;
|
||||
}
|
||||
elseif (!empty($definitions[$field_name]['configurable'])) {
|
||||
elseif ($definitions[$field_name]->isFieldConfigurable()) {
|
||||
return $translatable ? $langcode : Language::LANGCODE_NOT_SPECIFIED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,16 +194,17 @@ function field_entity_field_info($entity_type) {
|
|||
|
||||
foreach (field_info_instances($entity_type) as $bundle_name => $instances) {
|
||||
$optional = $bundle_name != $entity_type;
|
||||
// @todo: Improve hook_entity_field_info() to allow per-bundle field
|
||||
// definitions, such that we can pass on field instances as field
|
||||
// definitions here. See https://drupal.org/node/2114707.
|
||||
|
||||
foreach ($instances as $field_name => $instance) {
|
||||
$definition = _field_generate_entity_field_definition($instance->getField());
|
||||
|
||||
if ($optional) {
|
||||
$property_info['optional'][$field_name] = $definition;
|
||||
$property_info['optional'][$field_name] = $instance->getField();
|
||||
$property_info['bundle map'][$bundle_name][] = $field_name;
|
||||
}
|
||||
else {
|
||||
$property_info['definitions'][$field_name] = $definition;
|
||||
$property_info['definitions'][$field_name] = $instance->getField();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,33 +212,6 @@ function field_entity_field_info($entity_type) {
|
|||
return $property_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an entity field definition for a configurable field.
|
||||
*
|
||||
* @param \Drupal\field\FieldInterface $field
|
||||
* The field definition.
|
||||
* @param \Drupal\field\FieldInstanceInterface $instance
|
||||
* (Optional) The field instance definition.
|
||||
*
|
||||
* @return array
|
||||
* The entity field definition.
|
||||
*/
|
||||
function _field_generate_entity_field_definition(FieldInterface $field, FieldInstanceInterface $instance = NULL) {
|
||||
// @todo: Allow for adding field type settings.
|
||||
$definition = array(
|
||||
'label' => t('Field !name', array('!name' => $field->name)),
|
||||
'type' => 'field_item:' . $field->type,
|
||||
'list' => TRUE,
|
||||
'configurable' => TRUE,
|
||||
'translatable' => !empty($field->translatable),
|
||||
);
|
||||
if ($instance) {
|
||||
$definition['instance'] = $instance;
|
||||
}
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_bundle_create().
|
||||
*/
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Utility\Unicode;
|
|||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\field\FieldException;
|
||||
use Drupal\field\FieldInterface;
|
||||
|
||||
|
@ -197,6 +198,13 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
public $original = NULL;
|
||||
|
||||
/**
|
||||
* The data definition of a field item.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinition
|
||||
*/
|
||||
protected $itemDefinition;
|
||||
|
||||
/**
|
||||
* Constructs a Field object.
|
||||
*
|
||||
|
@ -566,6 +574,20 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
return $this->translatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the field is translatable.
|
||||
*
|
||||
* @param bool $translatable
|
||||
* Whether the field is translatable.
|
||||
*
|
||||
* @return \Drupal\field\Entity\Field
|
||||
* The object itself for chaining.
|
||||
*/
|
||||
public function setTranslatable($translatable) {
|
||||
$this->translatable = $translatable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -577,7 +599,7 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDescription() {
|
||||
return '';
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -607,6 +629,20 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
*/
|
||||
public function getFieldDefaultValue(EntityInterface $entity) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldConfigurable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldQueryable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of columns that can not be used as field type columns.
|
||||
*
|
||||
|
@ -675,8 +711,90 @@ class Field extends ConfigEntityBase implements FieldInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldConfigurable() {
|
||||
public function getDataType() {
|
||||
return 'list';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->getFieldDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isList() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadOnly() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComputed() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRequired() {
|
||||
return $this->isFieldRequired();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClass() {
|
||||
// Derive list class from the field type.
|
||||
$type_definition = \Drupal::service('plugin.manager.field.field_type')
|
||||
->getDefinition($this->getFieldType());
|
||||
return $type_definition['list_class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSettings() {
|
||||
// This should actually return the settings for field item list, which are
|
||||
// not the field settings. However, there is no harm in returning field
|
||||
// settings here, so we do that to avoid confusion for now.
|
||||
// @todo: Unify with getFieldSettings() or remove once typed data moved
|
||||
// to the adapter approach.
|
||||
return $this->getFieldSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemDefinition() {
|
||||
if (!isset($this->itemDefinition)) {
|
||||
$this->itemDefinition = DataDefinition::create('field_item:' . $this->type)
|
||||
->setSettings($this->getFieldSettings());
|
||||
}
|
||||
return $this->itemDefinition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\field\FieldException;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\field\FieldInstanceInterface;
|
||||
|
||||
/**
|
||||
|
@ -206,6 +207,13 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
*/
|
||||
public $original = NULL;
|
||||
|
||||
/**
|
||||
* The data definition of a field item.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinition
|
||||
*/
|
||||
protected $itemDefinition;
|
||||
|
||||
/**
|
||||
* Constructs a FieldInstance object.
|
||||
*
|
||||
|
@ -566,17 +574,24 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function allowBundleRename() {
|
||||
$this->bundle_rename_allowed = TRUE;
|
||||
public function isFieldConfigurable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldConfigurable() {
|
||||
public function isFieldQueryable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function allowBundleRename() {
|
||||
$this->bundle_rename_allowed = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements the magic __sleep() method.
|
||||
*
|
||||
|
@ -598,4 +613,82 @@ class FieldInstance extends ConfigEntityBase implements FieldInstanceInterface {
|
|||
$this->__construct($values);
|
||||
}
|
||||
|
||||
public function getDataType() {
|
||||
return 'list';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return $this->getFieldDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isList() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadOnly() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComputed() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRequired() {
|
||||
// Only field instances can be required.
|
||||
return $this->isFieldRequired();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClass() {
|
||||
return $this->field->getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSettings() {
|
||||
return $this->getFieldSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItemDefinition() {
|
||||
if (!isset($this->itemDefinition)) {
|
||||
$this->itemDefinition = DataDefinition::create('field_item:' . $this->field->type)
|
||||
->setSettings($this->getFieldSettings());
|
||||
}
|
||||
return $this->itemDefinition;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,8 @@ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidR
|
|||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
$field_item = $context['target_instance'];
|
||||
$field_definition = $field_item->getDefinition();
|
||||
$target_type = $field_definition['settings']['target_type'];
|
||||
$field_definition = $field_item->getFieldDefinition();
|
||||
$target_type = $field_definition->getFieldSetting('target_type');
|
||||
if ($id = $this->entityResolver->resolve($this, $data, $target_type)) {
|
||||
return array('target_id' => $id);
|
||||
}
|
||||
|
|
|
@ -57,8 +57,8 @@ class FieldItemNormalizer extends NormalizerBase {
|
|||
if (isset($data['lang'])) {
|
||||
$langcode = $data['lang'];
|
||||
unset($data['lang']);
|
||||
$field_definition = $field_item->getDefinition();
|
||||
if ($field_definition['translatable']) {
|
||||
$field_definition = $field_item->getFieldDefinition();
|
||||
if ($field_definition->isFieldTranslatable()) {
|
||||
$field_item = $this->createTranslatedInstance($field_item, $langcode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@ class FieldNormalizer extends NormalizerBase {
|
|||
// Get the field definition.
|
||||
$entity = $field->getEntity();
|
||||
$field_name = $field->getName();
|
||||
$field_definition = $entity->getPropertyDefinition($field_name);
|
||||
$field_definition = $field->getFieldDefinition();
|
||||
|
||||
// If this field is not translatable, it can simply be normalized without
|
||||
// separating it into different translations.
|
||||
if (empty($field_definition['translatable'])) {
|
||||
if (!$field_definition->isFieldTranslatable()) {
|
||||
$normalized_field_items = $this->normalizeFieldItems($field, $format, $context);
|
||||
}
|
||||
// Otherwise, the languages have to be extracted from the entity and passed
|
||||
|
|
|
@ -41,7 +41,7 @@ class LocaleTypedConfig extends Element {
|
|||
/**
|
||||
* Constructs a configuration wrapper object.
|
||||
*
|
||||
* @param array $definition
|
||||
* @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
|
||||
* The data definition.
|
||||
* @param string $name
|
||||
* The configuration object name.
|
||||
|
@ -50,7 +50,7 @@ class LocaleTypedConfig extends Element {
|
|||
* @param \Drupal\locale\LocaleConfigManager $localeConfig;
|
||||
* The locale configuration manager object.
|
||||
*/
|
||||
public function __construct(array $definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
|
||||
public function __construct($definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) {
|
||||
parent::__construct($definition, $name);
|
||||
$this->langcode = $langcode;
|
||||
$this->localeConfig = $localeConfig;
|
||||
|
|
|
@ -9,8 +9,7 @@ namespace Drupal\serialization\Tests\Normalizer;
|
|||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\Normalizer\ListNormalizer;
|
||||
use Drupal\Core\TypedData\ItemList;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Integer;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
|
||||
|
||||
/**
|
||||
* Tests the ListNormalizer class.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinition;
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
|
@ -626,10 +627,8 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
|
|||
*
|
||||
* @return array
|
||||
* An array of entity field information having the following optional entries:
|
||||
* - definitions: An array of field definitions to add all entities of this
|
||||
* type, keyed by field name. See
|
||||
* \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions() for a list of
|
||||
* supported keys in field definitions.
|
||||
* - definitions: An array of field definitions to add to all entities of this
|
||||
* type, keyed by field name.
|
||||
* - optional: An array of field definitions for optional entity fields, keyed
|
||||
* by field name. Optional fields are fields that only exist for certain
|
||||
* bundles of the entity type.
|
||||
|
@ -637,29 +636,26 @@ function hook_entity_form_display_alter(\Drupal\entity\Entity\EntityFormDisplay
|
|||
* optional fields that entities of this bundle have.
|
||||
*
|
||||
* @see hook_entity_field_info_alter()
|
||||
* @see \Drupal\Core\Field\FieldDefinitionInterface
|
||||
* @see \Drupal\Core\Entity\EntityManagerInterface::getFieldDefinitions()
|
||||
* @see \Drupal\Core\TypedData\TypedDataManager::create()
|
||||
*/
|
||||
function hook_entity_field_info($entity_type) {
|
||||
if (mymodule_uses_entity_type($entity_type)) {
|
||||
$info = array();
|
||||
$info['definitions']['mymodule_text'] = array(
|
||||
'type' => 'string_item',
|
||||
'list' => TRUE,
|
||||
'label' => t('The text'),
|
||||
'description' => t('A text property added by mymodule.'),
|
||||
'computed' => TRUE,
|
||||
'class' => '\Drupal\mymodule\EntityComputedText',
|
||||
);
|
||||
$info['definitions']['mymodule_text'] = FieldDefinition::create('string')
|
||||
->setLabel(t('The text'))
|
||||
->setDescription(t('A text property added by mymodule.'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\mymodule\EntityComputedText');
|
||||
|
||||
if ($entity_type == 'node') {
|
||||
// Add a property only to entities of the 'article' bundle.
|
||||
$info['optional']['mymodule_text_more'] = array(
|
||||
'type' => 'string_item',
|
||||
'list' => TRUE,
|
||||
'label' => t('More text'),
|
||||
'computed' => TRUE,
|
||||
'class' => '\Drupal\mymodule\EntityComputedMoreText',
|
||||
);
|
||||
$info['optional']['mymodule_text_more'] = FieldDefinition::create('string')
|
||||
->setLabel(t('More text'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\mymodule\EntityComputedMoreText');
|
||||
|
||||
$info['bundle map']['article'][0] = 'mymodule_text_more';
|
||||
}
|
||||
return $info;
|
||||
|
@ -678,8 +674,8 @@ function hook_entity_field_info($entity_type) {
|
|||
*/
|
||||
function hook_entity_field_info_alter(&$info, $entity_type) {
|
||||
if (!empty($info['definitions']['mymodule_text'])) {
|
||||
// Alter the mymodule_text property to use a custom class.
|
||||
$info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
|
||||
// Alter the mymodule_text field to use a custom class.
|
||||
$info['definitions']['mymodule_text']->setClass('\Drupal\anothermodule\EntityComputedText');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -353,21 +353,19 @@ class EntityFieldTest extends EntityUnitTestBase {
|
|||
*/
|
||||
protected function checkIntrospection($entity_type) {
|
||||
// Test getting metadata upfront.
|
||||
// @todo: Make this work without having to create entity objects.
|
||||
$entity = entity_create($entity_type, array());
|
||||
$definitions = $entity->getPropertyDefinitions();
|
||||
$this->assertEqual($definitions['name']['type'], 'field_item:string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type);
|
||||
$this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
|
||||
|
||||
// Test introspecting an entity object.
|
||||
// @todo: Add bundles and test bundles as well.
|
||||
$entity = entity_create($entity_type, array());
|
||||
|
||||
$definitions = $entity->getPropertyDefinitions();
|
||||
$this->assertEqual($definitions['name']['type'], 'field_item:string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']['type'], 'field_item:entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']['type'], 'field_item:text', $entity_type .': Test-text-field field found.');
|
||||
$this->assertEqual($definitions['name']->getFieldType(), 'string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']->getFieldType(), 'entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']->getFieldType(), 'text', $entity_type .': Test-text-field field found.');
|
||||
|
||||
$name_properties = $entity->name->getPropertyDefinitions();
|
||||
$this->assertEqual($name_properties['value']['type'], 'string', $entity_type .': String value property of the name found.');
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\TypedData;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\TypedData\ListDefinition;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
|
||||
|
@ -313,10 +313,7 @@ class TypedDataTest extends DrupalUnitTestBase {
|
|||
public function testTypedDataLists() {
|
||||
// Test working with an existing list of strings.
|
||||
$value = array('one', 'two', 'three');
|
||||
$typed_data = $this->createTypedData(array(
|
||||
'type' => 'string',
|
||||
'list' => TRUE,
|
||||
), $value);
|
||||
$typed_data = $this->createTypedData(ListDefinition::create('string'), $value);
|
||||
$this->assertEqual($typed_data->getValue(), $value, 'List value has been set.');
|
||||
// Test iterating.
|
||||
$count = 0;
|
||||
|
|
|
@ -75,7 +75,7 @@ function entity_test_entity_info_alter(&$info) {
|
|||
function entity_test_entity_field_info_alter(&$info, $entity_type) {
|
||||
if ($entity_type == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
|
||||
foreach ($names as $name => $value) {
|
||||
$info['definitions'][$name]['translatable'] = $value;
|
||||
$info['definitions'][$name]->setTranslatable($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ use Drupal\Core\Entity\ContentEntityBase;
|
|||
use Drupal\Core\Entity\Annotation\EntityType;
|
||||
use Drupal\Core\Entity\EntityStorageControllerInterface;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
use Drupal\Core\Field\FieldDefinition;
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\taxonomy\TermInterface;
|
||||
|
||||
/**
|
||||
|
@ -200,67 +202,55 @@ class Term extends ContentEntityBase implements TermInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions($entity_type) {
|
||||
$properties['tid'] = array(
|
||||
'label' => t('Term ID'),
|
||||
'description' => t('The term ID.'),
|
||||
'type' => 'integer_field',
|
||||
'read-only' => TRUE,
|
||||
);
|
||||
$properties['uuid'] = array(
|
||||
'label' => t('UUID'),
|
||||
'description' => t('The term UUID.'),
|
||||
'type' => 'uuid_field',
|
||||
'read-only' => TRUE,
|
||||
);
|
||||
$properties['vid'] = array(
|
||||
'label' => t('Vocabulary ID'),
|
||||
'description' => t('The ID of the vocabulary to which the term is assigned.'),
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$properties['langcode'] = array(
|
||||
'label' => t('Language code'),
|
||||
'description' => t('The term language code.'),
|
||||
'type' => 'language_field',
|
||||
);
|
||||
$properties['name'] = array(
|
||||
'label' => t('Name'),
|
||||
'description' => t('The term name.'),
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$properties['description'] = array(
|
||||
'label' => t('Description'),
|
||||
'description' => t('A description of the term'),
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$fields['tid'] = FieldDefinition::create('integer')
|
||||
->setLabel(t('Term ID'))
|
||||
->setDescription(t('The term ID.'))
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['uuid'] = FieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The term UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
|
||||
$fields['vid'] = FieldDefinition::create('string')
|
||||
->setLabel(t('Vocabulary ID'))
|
||||
->setDescription(t('The ID of the vocabulary to which the term is assigned.'));
|
||||
|
||||
$fields['langcode'] = FieldDefinition::create('language')
|
||||
->setLabel(t('Language code'))
|
||||
->setDescription(t('The term language code.'));
|
||||
|
||||
$fields['name'] = FieldDefinition::create('string')
|
||||
->setLabel(t('Name'))
|
||||
->setDescription(t('The term name.'));
|
||||
|
||||
$fields['description'] = FieldDefinition::create('string')
|
||||
->setLabel(t('Description'))
|
||||
->setDescription(t('A description of the term.'));
|
||||
|
||||
// @todo Combine with description.
|
||||
$properties['format'] = array(
|
||||
'label' => t('Description format'),
|
||||
'description' => t('The filter format ID of the description.'),
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$properties['weight'] = array(
|
||||
'label' => t('Weight'),
|
||||
'description' => t('The weight of this term in relation to other terms.'),
|
||||
'type' => 'integer_field',
|
||||
'settings' => array('default_value' => 0),
|
||||
);
|
||||
$properties['parent'] = array(
|
||||
'label' => t('Term Parents'),
|
||||
'description' => t('The parents of this term.'),
|
||||
'type' => 'integer_field',
|
||||
$fields['format'] = FieldDefinition::create('string')
|
||||
->setLabel(t('Description format'))
|
||||
->setDescription(t('The filter format ID of the description.'));
|
||||
|
||||
$fields['weight'] = FieldDefinition::create('integer')
|
||||
->setLabel(t('Weight'))
|
||||
->setDescription(t('The weight of this term in relation to other terms.'))
|
||||
->setFieldSetting('default_value', 0);
|
||||
|
||||
$fields['parent'] = FieldDefinition::create('integer')
|
||||
->setLabel(t('Term Parents'))
|
||||
->setDescription(t('The parents of this term.'))
|
||||
// Save new terms with no parents by default.
|
||||
'settings' => array('default_value' => 0),
|
||||
'computed' => TRUE,
|
||||
);
|
||||
$properties['changed'] = array(
|
||||
'label' => t('Changed'),
|
||||
'description' => t('The time that the term was last edited.'),
|
||||
'type' => 'integer_field',
|
||||
'property_constraints' => array(
|
||||
'value' => array('EntityChanged' => array()),
|
||||
),
|
||||
);
|
||||
return $properties;
|
||||
->setFieldSetting('default_value', 0)
|
||||
->setComputed(TRUE);
|
||||
|
||||
$fields['changed'] = FieldDefinition::create('integer')
|
||||
->setLabel(t('Changed'))
|
||||
->setDescription(t('The time that the term was last edited.'))
|
||||
->setPropertyConstraints('value', array('EntityChanged' => array()));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,7 +29,7 @@ class TextProcessed extends TypedData {
|
|||
/**
|
||||
* Overrides TypedData::__construct().
|
||||
*/
|
||||
public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
public function __construct($definition, $name = NULL, TypedDataInterface $parent = NULL) {
|
||||
parent::__construct($definition, $name, $parent);
|
||||
|
||||
if (!isset($definition['settings']['text source'])) {
|
||||
|
|
|
@ -25,6 +25,34 @@ class FieldDefinitionTest extends UnitTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Prepare a container with a mock typed data object, that returns no
|
||||
// type definitions.
|
||||
// @todo: Overhaul how field definitions deal with dependencies and improve
|
||||
// unit tests. See https://drupal.org/node/2143555.
|
||||
$typed_data = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$typed_data
|
||||
->expects($this->any())
|
||||
->method('getDefinition')
|
||||
->will($this->returnValue(NULL));
|
||||
|
||||
$container = $this->getMock('Drupal\Core\DependencyInjection\Container');
|
||||
$container
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnValue($typed_data));
|
||||
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field name methods.
|
||||
*/
|
||||
|
@ -59,10 +87,9 @@ class FieldDefinitionTest extends UnitTestCase {
|
|||
* Tests field type methods.
|
||||
*/
|
||||
public function testFieldType() {
|
||||
$definition = new FieldDefinition();
|
||||
$field_name = $this->randomName();
|
||||
$definition->setFieldType($field_name);
|
||||
$this->assertEquals($field_name, $definition->getFieldType());
|
||||
$field_type = $this->randomName();
|
||||
$definition = FieldDefinition::create($field_type);
|
||||
$this->assertEquals($field_type, $definition->getFieldType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue