Issue #2232477 by plach, yched, tstoeckler, amateescu: Fatal when adding new fields with NOT NULL constraints in a base table that contains existing entities
parent
fb4f25aac7
commit
057b0cabaf
|
@ -55,6 +55,17 @@ class EntityReference extends DataReferenceBase {
|
|||
return $this->definition->getTargetDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the target entity has not been saved yet.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the entity is new, FALSE otherwise.
|
||||
*/
|
||||
public function isTargetNew() {
|
||||
// If only an ID is given, the reference cannot be a new entity.
|
||||
return !isset($this->id) && isset($this->target) && $this->target->getValue()->isNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -1019,6 +1019,7 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
|
|||
if ($schema['fields'][$key]['type'] == 'int') {
|
||||
$schema['fields'][$key]['type'] = 'serial';
|
||||
}
|
||||
$schema['fields'][$key]['not null'] = TRUE;
|
||||
unset($schema['fields'][$key]['default']);
|
||||
}
|
||||
|
||||
|
@ -1386,27 +1387,36 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
|
|||
|
||||
$field_name = $storage_definition->getName();
|
||||
$field_description = $storage_definition->getDescription();
|
||||
$base_table = $this->storage->getBaseTable();
|
||||
|
||||
// A shared table contains rows for entities where the field is empty
|
||||
// (since other fields stored in the same table might not be empty), thus
|
||||
// the only columns that can be 'not null' are those for required
|
||||
// properties of required fields. However, even those would break in the
|
||||
// case where a new field is added to a table that contains existing rows.
|
||||
// For now, we only hardcode 'not null' to a couple "entity keys", in order
|
||||
// to keep their indexes optimized.
|
||||
// @todo Revisit once we have support for 'initial' in
|
||||
// https://www.drupal.org/node/2346019.
|
||||
$not_null_keys = $this->entityType->getKeys();
|
||||
// Label fields are not necessarily required.
|
||||
unset($not_null_keys['label']);
|
||||
// Because entity ID and revision ID are both serial fields in the base and
|
||||
// revision table respectively, the revision ID is not known yet, when
|
||||
// inserting data into the base table. Instead the revision ID in the base
|
||||
// table is updated after the data has been inserted into the revision
|
||||
// table. For this reason the revision ID field cannot be marked as NOT
|
||||
// NULL.
|
||||
if ($table_name == $base_table) {
|
||||
unset($not_null_keys['revision']);
|
||||
}
|
||||
|
||||
foreach ($column_mapping as $field_column_name => $schema_field_name) {
|
||||
$column_schema = $field_schema['columns'][$field_column_name];
|
||||
|
||||
$schema['fields'][$schema_field_name] = $column_schema;
|
||||
$schema['fields'][$schema_field_name]['description'] = $field_description;
|
||||
// Only entity keys are required.
|
||||
$keys = $this->entityType->getKeys();
|
||||
// The label is an entity key, but label fields are not necessarily
|
||||
// required.
|
||||
// Because entity ID and revision ID are both serial fields in the base
|
||||
// and revision table respectively, the revision ID is not known yet, when
|
||||
// inserting data into the base table. Instead the revision ID in the base
|
||||
// table is updated after the data has been inserted into the revision
|
||||
// table. For this reason the revision ID field cannot be marked as NOT
|
||||
// NULL.
|
||||
unset($keys['label'], $keys['revision']);
|
||||
// Key fields may not be NULL.
|
||||
if (in_array($field_name, $keys)) {
|
||||
$schema['fields'][$schema_field_name]['not null'] = TRUE;
|
||||
}
|
||||
$schema['fields'][$schema_field_name]['not null'] = in_array($field_name, $not_null_keys);
|
||||
}
|
||||
|
||||
if (!empty($field_schema['indexes'])) {
|
||||
|
@ -1596,6 +1606,7 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
|
|||
|
||||
// Check that the schema does not include forbidden column names.
|
||||
$schema = $storage_definition->getSchema();
|
||||
$properties = $storage_definition->getPropertyDefinitions();
|
||||
$table_mapping = $this->storage->getTableMapping();
|
||||
if (array_intersect(array_keys($schema['columns']), $table_mapping->getReservedColumns())) {
|
||||
throw new FieldException(format_string('Illegal field column names on @field_name', array('@field_name' => $storage_definition->getName())));
|
||||
|
@ -1605,6 +1616,10 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
|
|||
foreach ($schema['columns'] as $column_name => $attributes) {
|
||||
$real_name = $table_mapping->getFieldColumnName($storage_definition, $column_name);
|
||||
$data_schema['fields'][$real_name] = $attributes;
|
||||
// A dedicated table only contain rows for actual field values, and no
|
||||
// rows for entities where the field is empty. Thus, we can safely
|
||||
// enforce 'not null' on the columns for the field's required properties.
|
||||
$data_schema['fields'][$real_name]['not null'] = $properties[$column_name]->isRequired();
|
||||
}
|
||||
|
||||
// Add indexes.
|
||||
|
|
|
@ -27,12 +27,12 @@ class EntityReferenceFieldItemList extends FieldItemList implements EntityRefere
|
|||
// "autocreate" entities that are already populated in $item->entity.
|
||||
$target_entities = $ids = array();
|
||||
foreach ($this->list as $delta => $item) {
|
||||
if ($item->target_id !== NULL) {
|
||||
$ids[$delta] = $item->target_id;
|
||||
}
|
||||
elseif ($item->hasNewEntity()) {
|
||||
if ($item->hasNewEntity()) {
|
||||
$target_entities[$delta] = $item->entity;
|
||||
}
|
||||
elseif ($item->target_id !== NULL) {
|
||||
$ids[$delta] = $item->target_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Load and add the existing entities.
|
||||
|
|
|
@ -159,13 +159,21 @@ interface FieldDefinitionInterface extends ListDataDefinitionInterface {
|
|||
public function getDisplayOptions($display_context);
|
||||
|
||||
/**
|
||||
* Returns whether at least one non-empty item is required for this field.
|
||||
* Returns whether the field can be empty.
|
||||
*
|
||||
* Currently, required-ness is only enforced at the Form API level in entity
|
||||
* edit forms, not during direct API saves.
|
||||
* If a field is required, an entity needs to have at least a valid,
|
||||
* non-empty item in that field's FieldItemList in order to pass validation.
|
||||
*
|
||||
* An item is considered empty if its isEmpty() method returns TRUE.
|
||||
* Typically, that is if at least one of its required properties is empty.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field is required.
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\ItemList::isEmpty()
|
||||
* @see \Drupal\Core\Field\FieldItemInterface::isEmpty()
|
||||
* @see \Drupal\Core\TypedData\DataDefinitionInterface:isRequired()
|
||||
* @see \Drupal\Core\TypedData\TypedDataManager::getDefaultConstraints()
|
||||
*/
|
||||
public function isRequired();
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ interface FieldItemInterface extends ComplexDataInterface {
|
|||
/**
|
||||
* Defines field item properties.
|
||||
*
|
||||
* Properties that are required to constitute a valid, non-empty item should
|
||||
* be denoted with \Drupal\Core\TypedData\DataDefinition::setRequired().
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\DataDefinitionInterface[]
|
||||
* An array of property definitions of contained properties, keyed by
|
||||
* property name.
|
||||
|
@ -67,10 +70,12 @@ interface FieldItemInterface extends ComplexDataInterface {
|
|||
* following key/value pairs:
|
||||
* - columns: An array of Schema API column specifications, keyed by column
|
||||
* name. The columns need to be a subset of the properties defined in
|
||||
* propertyDefinitions(). It is recommended to avoid having the column
|
||||
* definitions depend on field settings when possible. No assumptions
|
||||
* should be made on how storage engines internally use the original
|
||||
* column name to structure their storage.
|
||||
* propertyDefinitions(). The 'not null' property is ignored if present,
|
||||
* as it is determined automatically by the storage controller depending
|
||||
* on the table layout and the property definitions. It is recommended to
|
||||
* avoid having the column definitions depend on field settings when
|
||||
* possible. No assumptions should be made on how storage engines
|
||||
* internally use the original column name to structure their storage.
|
||||
* - unique keys: (optional) An array of Schema API unique key definitions.
|
||||
* Only columns that appear in the 'columns' array are allowed.
|
||||
* - indexes: (optional) An array of Schema API index definitions. Only
|
||||
|
|
|
@ -43,7 +43,8 @@ class BooleanItem extends FieldItemBase implements OptionsProviderInterface {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('boolean')
|
||||
->setLabel(t('Boolean value'));
|
||||
->setLabel(t('Boolean value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -57,7 +58,6 @@ class BooleanItem extends FieldItemBase implements OptionsProviderInterface {
|
|||
'value' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -41,7 +41,8 @@ class DecimalItem extends NumericItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Decimal value'));
|
||||
->setLabel(t('Decimal value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -56,7 +57,6 @@ class DecimalItem extends NumericItemBase {
|
|||
'type' => 'numeric',
|
||||
'precision' => $field_definition->getSetting('precision'),
|
||||
'scale' => $field_definition->getSetting('scale'),
|
||||
'not null' => FALSE
|
||||
)
|
||||
),
|
||||
);
|
||||
|
|
|
@ -32,7 +32,8 @@ class EmailItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('email')
|
||||
->setLabel(t('E-mail'));
|
||||
->setLabel(t('E-mail'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -46,7 +47,6 @@ class EmailItem extends FieldItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => Email::EMAIL_MAX_LENGTH,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -11,8 +11,8 @@ use Drupal\Core\Entity\EntityInterface;
|
|||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\DataReferenceDefinition;
|
||||
|
||||
|
@ -36,6 +36,13 @@ use Drupal\Core\TypedData\DataReferenceDefinition;
|
|||
*/
|
||||
class EntityReferenceItem extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* Marker value to identify a newly created entity.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected static $NEW_ENTITY_MARKER = -1;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -73,7 +80,9 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
$target_id_definition = DataDefinition::create('string')
|
||||
->setLabel(t('@label ID', array($target_type_info->getLabel())));
|
||||
}
|
||||
$target_id_definition->setRequired(TRUE);
|
||||
$properties['target_id'] = $target_id_definition;
|
||||
|
||||
$properties['entity'] = DataReferenceDefinition::create('entity')
|
||||
->setLabel($target_type_info->getLabel())
|
||||
->setDescription(t('The referenced entity'))
|
||||
|
@ -109,7 +118,6 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
'description' => 'The ID of the target entity.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -155,8 +163,12 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
$this->onChange('entity', FALSE);
|
||||
}
|
||||
elseif (isset($values['target_id']) && isset($values['entity'])) {
|
||||
// If both properties are passed, verify the passed values match.
|
||||
if ($this->get('entity')->getTargetIdentifier() != $values['target_id']) {
|
||||
// If both properties are passed, verify the passed values match. The
|
||||
// only exception we allow is when we have a new entity: in this case
|
||||
// its actual id and target_id will be different, due to the new entity
|
||||
// marker.
|
||||
$entity_id = $this->get('entity')->getTargetIdentifier();
|
||||
if ($entity_id != $values['target_id'] && ($values['target_id'] != static::$NEW_ENTITY_MARKER || !$this->entity->isNew())) {
|
||||
throw new \InvalidArgumentException('The target id and entity passed to the entity reference item do not match.');
|
||||
}
|
||||
}
|
||||
|
@ -187,11 +199,13 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
*/
|
||||
public function onChange($property_name, $notify = TRUE) {
|
||||
// Make sure that the target ID and the target property stay in sync.
|
||||
if ($property_name == 'target_id') {
|
||||
$this->writePropertyValue('entity', $this->target_id);
|
||||
if ($property_name == 'entity') {
|
||||
$property = $this->get('entity');
|
||||
$target_id = $property->isTargetNew() ? static::$NEW_ENTITY_MARKER : $property->getTargetIdentifier();
|
||||
$this->writePropertyValue('target_id', $target_id);
|
||||
}
|
||||
elseif ($property_name == 'entity') {
|
||||
$this->writePropertyValue('target_id', $this->get('entity')->getTargetIdentifier());
|
||||
elseif ($property_name == 'target_id' && $this->target_id != static::$NEW_ENTITY_MARKER) {
|
||||
$this->writePropertyValue('entity', $this->target_id);
|
||||
}
|
||||
parent::onChange($property_name, $notify);
|
||||
}
|
||||
|
@ -215,13 +229,12 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
*/
|
||||
public function preSave() {
|
||||
if ($this->hasNewEntity()) {
|
||||
$this->entity->save();
|
||||
}
|
||||
// Handle the case where an unsaved entity was directly set using the public
|
||||
// 'entity' property and then saved before this entity. In this case
|
||||
// ::hasNewEntity() will return FALSE but $this->target_id will still be
|
||||
// empty.
|
||||
if (empty($this->target_id) && $this->entity) {
|
||||
// Save the entity if it has not already been saved by some other code.
|
||||
if ($this->entity->isNew()) {
|
||||
$this->entity->save();
|
||||
}
|
||||
// Make sure the parent knows we are updating this property so it can
|
||||
// react properly.
|
||||
$this->target_id = $this->entity->id();
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +262,7 @@ class EntityReferenceItem extends FieldItemBase {
|
|||
* TRUE if the item holds an unsaved entity.
|
||||
*/
|
||||
public function hasNewEntity() {
|
||||
return $this->target_id === NULL && ($entity = $this->entity) && $entity->isNew();
|
||||
return $this->target_id === static::$NEW_ENTITY_MARKER;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,7 +29,8 @@ class FloatItem extends NumericItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('float')
|
||||
->setLabel(t('Float'));
|
||||
->setLabel(t('Float'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -42,7 +43,6 @@ class FloatItem extends NumericItemBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -53,7 +53,8 @@ class IntegerItem extends NumericItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Integer value'));
|
||||
->setLabel(t('Integer value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -92,7 +93,6 @@ class IntegerItem extends NumericItemBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
// Expose the 'unsigned' setting in the field item schema.
|
||||
'unsigned' => $field_definition->getSetting('unsigned'),
|
||||
// Expose the 'size' setting in the field item schema. For instance,
|
||||
|
|
|
@ -37,7 +37,8 @@ class LanguageItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Language code'));
|
||||
->setLabel(t('Language code'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['language'] = DataReferenceDefinition::create('language')
|
||||
->setLabel(t('Language object'))
|
||||
|
@ -58,7 +59,6 @@ class LanguageItem extends FieldItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 12,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -43,7 +43,6 @@ class StringItem extends StringItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => (int) $field_definition->getSetting('max_length'),
|
||||
'not null' => FALSE,
|
||||
'binary' => $field_definition->getSetting('case_sensitive'),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -34,7 +34,8 @@ abstract class StringItemBase extends FieldItemBase {
|
|||
// early t() calls by using the TranslationWrapper.
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(new TranslationWrapper('Text value'))
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ class TimestampItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('timestamp')
|
||||
->setLabel(t('Timestamp value'));
|
||||
->setLabel(t('Timestamp value'))
|
||||
->setRequired(TRUE);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
|
@ -52,7 +53,6 @@ class TimestampItem extends FieldItemBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -42,7 +42,8 @@ class UriItem extends StringItem {
|
|||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('uri')
|
||||
->setLabel(t('URI value'))
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -56,7 +57,6 @@ class UriItem extends StringItem {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => (int) $field_definition->getSetting('max_length'),
|
||||
'not null' => TRUE,
|
||||
'binary' => $field_definition->getSetting('case_sensitive'),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -61,4 +61,5 @@ abstract class DataReferenceBase extends TypedData implements DataReferenceInter
|
|||
public function getString() {
|
||||
return (string) $this->getType() . ':' . $this->getTargetIdentifier();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ class CommentItem extends FieldItemBase implements CommentItemInterface {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['status'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Comment status'));
|
||||
->setLabel(t('Comment status'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['cid'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Last comment ID'));
|
||||
|
@ -87,7 +88,6 @@ class CommentItem extends FieldItemBase implements CommentItemInterface {
|
|||
'status' => array(
|
||||
'description' => 'Whether comments are allowed on this entity: 0 = no, 1 = closed (read only), 2 = open (read/write).',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -51,7 +51,8 @@ class DateTimeItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('datetime_iso8601')
|
||||
->setLabel(t('Date value'));
|
||||
->setLabel(t('Date value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['date'] = DataDefinition::create('any')
|
||||
->setLabel(t('Computed date'))
|
||||
|
@ -73,7 +74,6 @@ class DateTimeItem extends FieldItemBase {
|
|||
'description' => 'The date value.',
|
||||
'type' => 'varchar',
|
||||
'length' => 20,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\entity_reference\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
|
@ -73,10 +74,7 @@ class AutocompleteTagsWidget extends AutocompleteWidgetBase {
|
|||
elseif ($auto_create && (count($this->getSelectionHandlerSetting('target_bundles')) == 1 || count($bundles) == 1)) {
|
||||
// Auto-create item. See
|
||||
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
|
||||
$value[] = array(
|
||||
'target_id' => NULL,
|
||||
'entity' => $this->createNewEntity($input, $element['#autocreate_uid']),
|
||||
);
|
||||
$value[] = array('entity' => $this->createNewEntity($input, $element['#autocreate_uid']));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\entity_reference\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
|
@ -71,7 +72,6 @@ class AutocompleteWidget extends AutocompleteWidgetBase {
|
|||
// Auto-create item. See
|
||||
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
|
||||
$value = array(
|
||||
'target_id' => NULL,
|
||||
'entity' => $this->createNewEntity($element['#value'], $element['#autocreate_uid']),
|
||||
// Keep the weight property.
|
||||
'_weight' => $element['#weight'],
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\entity_reference\Tests\EntityReferenceFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\entity_reference\Tests;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Tests for the entity reference field.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceFieldTest extends EntityUnitTestBase {
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The entity type that is being referenced.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedEntityType = 'entity_test_rev';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* The name of the field used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
// Create a field.
|
||||
entity_reference_create_field(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$this->fieldName,
|
||||
'Field test',
|
||||
$this->referencedEntityType,
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reference field validation.
|
||||
*/
|
||||
public function testEntityReferenceFieldValidation() {
|
||||
// Test a valid reference.
|
||||
$referenced_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle));
|
||||
$referenced_entity->save();
|
||||
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
$entity->{$this->fieldName}->target_id = $referenced_entity->id();
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passes.');
|
||||
|
||||
// Test an invalid reference.
|
||||
$entity->{$this->fieldName}->target_id = 9999;
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999)));
|
||||
|
||||
// @todo Implement a test case for invalid bundle references after
|
||||
// https://drupal.org/node/2064191 is fixed
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the multiple target entities loader.
|
||||
*/
|
||||
public function testReferencedEntitiesMultipleLoad() {
|
||||
// Create the parent entity.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
|
||||
// Create three target entities and attach them to parent field.
|
||||
$target_entities = array();
|
||||
$reference_field = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$target_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle));
|
||||
$target_entity->save();
|
||||
$target_entities[] = $target_entity;
|
||||
$reference_field[]['target_id'] = $target_entity->id();
|
||||
}
|
||||
|
||||
// Also attach a non-existent entity and a NULL target id.
|
||||
$reference_field[3]['target_id'] = 99999;
|
||||
$target_entities[3] = NULL;
|
||||
$reference_field[4]['target_id'] = NULL;
|
||||
$target_entities[4] = NULL;
|
||||
|
||||
// Attach the first created target entity as the sixth item ($delta == 5) of
|
||||
// the parent entity field. We want to test the case when the same target
|
||||
// entity is referenced twice (or more times) in the same entity reference
|
||||
// field.
|
||||
$reference_field[5] = $reference_field[0];
|
||||
$target_entities[5] = $target_entities[0];
|
||||
|
||||
// Create a new target entity that is not saved, thus testing the
|
||||
// "autocreate" feature.
|
||||
$target_entity_unsaved = entity_create($this->referencedEntityType, array('type' => $this->bundle, 'name' => $this->randomString()));
|
||||
$reference_field[6]['entity'] = $target_entity_unsaved;
|
||||
$target_entities[6] = $target_entity_unsaved;
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$this->fieldName}->setValue($reference_field);
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$this->fieldName}->referencedEntities();
|
||||
|
||||
// Test returned entities:
|
||||
// - Deltas must be preserved.
|
||||
// - Non-existent entities must not be retrieved in target entities result.
|
||||
foreach ($target_entities as $delta => $target_entity) {
|
||||
if (!empty($target_entity)) {
|
||||
if (!$target_entity->isNew()) {
|
||||
// There must be an entity in the loaded set having the same id for
|
||||
// the same delta.
|
||||
$this->assertEqual($target_entity->id(), $entities[$delta]->id());
|
||||
}
|
||||
else {
|
||||
// For entities that were not yet saved, there must an entity in the
|
||||
// loaded set having the same label for the same delta.
|
||||
$this->assertEqual($target_entity->label(), $entities[$delta]->label());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A non-existent or NULL entity target id must not return any item in
|
||||
// the target entities set.
|
||||
$this->assertFalse(isset($loaded_entities[$delta]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests referencing entities with string IDs.
|
||||
*/
|
||||
public function testReferencedEntitiesStringId() {
|
||||
$field_name = 'entity_reference_string_id';
|
||||
$this->installEntitySchema('entity_test_string_id');
|
||||
entity_reference_create_field(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$field_name,
|
||||
'Field test',
|
||||
'entity_test_string_id',
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
// Create the parent entity.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
|
||||
// Create the default target entity.
|
||||
$target_entity = entity_create('entity_test_string_id', array('id' => $this->randomString(), 'type' => $this->bundle));
|
||||
$target_entity->save();
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$field_name}->setValue(array(array('target_id' => $target_entity->id())));
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
|
||||
// Test that a string ID works as a default value and the field's config
|
||||
// schema is correct.
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name);
|
||||
$field->setDefaultValue($target_entity->id());
|
||||
$field->save();
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray());
|
||||
|
||||
// Test that the default value works.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,24 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info().
|
||||
*/
|
||||
function entity_reference_test_entity_base_field_info(EntityTypeInterface $entity_type) {
|
||||
$fields = array();
|
||||
|
||||
if ($entity_type->id() === 'entity_test') {
|
||||
$fields['user_role'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('User role'))
|
||||
->setDescription(t('The role of the associated user.'))
|
||||
->setSetting('target_type', 'user_role')
|
||||
->setSetting('handler', 'default');
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_entity_base_field_info_alter().
|
||||
|
|
|
@ -169,7 +169,6 @@ class NumberFieldTest extends WebTestBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
'unsigned' => '',
|
||||
'size' => 'normal'
|
||||
),
|
||||
|
|
|
@ -83,7 +83,6 @@ class TestItemTest extends FieldUnitTestBase {
|
|||
'value' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'unique keys' => array(),
|
||||
|
|
|
@ -24,14 +24,4 @@ use Drupal\Core\TypedData\DataDefinition;
|
|||
*/
|
||||
class HiddenTestItem extends TestItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Test integer value'));
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ class TestItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Test integer value'));
|
||||
->setLabel(t('Test integer value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -64,7 +65,6 @@ class TestItem extends FieldItemBase {
|
|||
'value' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -265,7 +265,8 @@ class File extends ContentEntityBase implements FileInterface {
|
|||
|
||||
$fields['status'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Status'))
|
||||
->setDescription(t('The status of the file, temporary (FALSE) and permanent (TRUE).'));
|
||||
->setDescription(t('The status of the file, temporary (FALSE) and permanent (TRUE).'))
|
||||
->setDefaultValue(FALSE);
|
||||
|
||||
$fields['created'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Created'))
|
||||
|
|
|
@ -64,7 +64,6 @@ class FileItem extends EntityReferenceItem {
|
|||
'target_id' => array(
|
||||
'description' => 'The ID of the file entity.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'display' => array(
|
||||
|
@ -72,13 +71,11 @@ class FileItem extends EntityReferenceItem {
|
|||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => 'A description of the file.',
|
||||
'type' => 'text',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -96,20 +96,17 @@ class ImageItem extends FileItem {
|
|||
'target_id' => array(
|
||||
'description' => 'The ID of the file entity.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'unsigned' => TRUE,
|
||||
),
|
||||
'alt' => array(
|
||||
'description' => "Alternative image text, for the image's 'alt' attribute.",
|
||||
'type' => 'varchar',
|
||||
'length' => 512,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'title' => array(
|
||||
'description' => "Image title text, for the image's 'title' attribute.",
|
||||
'type' => 'varchar',
|
||||
'length' => 1024,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'width' => array(
|
||||
'description' => 'The width of the image in pixels.',
|
||||
|
|
|
@ -31,6 +31,8 @@ class EntityDefaultLanguageTest extends KernelTestBase {
|
|||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
// Activate Spanish language, so there are two languages activated.
|
||||
$language = $this->container->get('entity.manager')->getStorage('configurable_language')->create(array(
|
||||
'id' => 'es',
|
||||
|
|
|
@ -72,32 +72,27 @@ class LinkItem extends FieldItemBase implements LinkItemInterface {
|
|||
'description' => 'The URL of the link.',
|
||||
'type' => 'varchar',
|
||||
'length' => 2048,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'title' => array(
|
||||
'description' => 'The link text.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'route_name' => array(
|
||||
'description' => 'The machine name of a defined Route this link represents.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'route_parameters' => array(
|
||||
'description' => 'Serialized array of route parameters of the link.',
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'options' => array(
|
||||
'description' => 'Serialized array of options for the link.',
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -452,6 +452,7 @@ class Node extends ContentEntityBase implements NodeInterface {
|
|||
->setDescription(t('A boolean indicating whether the node should be displayed at the top of lists in which it appears.'))
|
||||
->setRevisionable(TRUE)
|
||||
->setTranslatable(TRUE)
|
||||
->setDefaultValue(FALSE)
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => array(
|
||||
|
|
|
@ -28,7 +28,8 @@ class ListFloatItem extends ListItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('float')
|
||||
->setLabel(t('Float value'));
|
||||
->setLabel(t('Float value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -41,7 +42,6 @@ class ListFloatItem extends ListItemBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'float',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -28,7 +28,8 @@ class ListIntegerItem extends ListItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('integer')
|
||||
->setLabel(t('Integer value'));
|
||||
->setLabel(t('Integer value'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -41,7 +42,6 @@ class ListIntegerItem extends ListItemBase {
|
|||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -30,7 +30,8 @@ class ListStringItem extends ListItemBase {
|
|||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Text value'))
|
||||
->addConstraint('Length', array('max' => 255));
|
||||
->addConstraint('Length', array('max' => 255))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -44,7 +45,6 @@ class ListStringItem extends ListItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -23,7 +23,8 @@ class ShortcutPathItem extends StringItem {
|
|||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('String value'))
|
||||
->setComputed(TRUE)
|
||||
->setClass('\Drupal\shortcut\ShortcutPathValue');
|
||||
->setClass('\Drupal\shortcut\ShortcutPathValue')
|
||||
->setRequired(TRUE);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class BundleConstraintValidatorTest extends KernelTestBase {
|
|||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->typedData = $this->container->get('typed_data_manager');
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
namespace Drupal\system\Tests\Entity;
|
||||
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\EntityTypeEvents;
|
||||
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionEvents;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\FieldStorageDefinition;
|
||||
|
||||
/**
|
||||
|
@ -236,14 +238,16 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
public function testBaseFieldCreateDeleteWithExistingEntities() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name));
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Add a base field and run the update. Ensure the base field's column is
|
||||
// created and the prior saved entity data is still there.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
|
||||
$schema_handler = $this->database->schema();
|
||||
$this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
|
||||
|
||||
|
@ -251,9 +255,33 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
// is deleted and the prior saved entity data is still there.
|
||||
$this->removeBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.');
|
||||
$this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
|
||||
|
||||
// Add a base field with a required property and run the update. Ensure
|
||||
// 'not null' is not applied and thus no exception is thrown.
|
||||
$this->addBaseField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assertTrue($assert, 'Columns created in shared table for new_base_field.');
|
||||
|
||||
// Recreate the field after emptying the base table and check that its
|
||||
// columns are not 'not null'.
|
||||
// @todo Revisit this test when allowing for required storage field
|
||||
// definitions. See https://www.drupal.org/node/2390495.
|
||||
$entity->delete();
|
||||
$this->removeBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assert($assert, 'Columns removed from the shared table for new_base_field.');
|
||||
$this->addBaseField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assertTrue($assert, 'Columns created again in shared table for new_base_field.');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
$this->pass('The new_base_field columns are still nullable');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,14 +295,16 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
public function testBundleFieldCreateDeleteWithExistingEntities() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name));
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Add a bundle field and run the update. Ensure the bundle field's table
|
||||
// is created and the prior saved entity data is still there.
|
||||
$this->addBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
|
||||
$schema_handler = $this->database->schema();
|
||||
$this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
|
||||
|
||||
|
@ -282,9 +312,40 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
|
|||
// table is deleted and the prior saved entity data is still there.
|
||||
$this->removeBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
|
||||
$this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
|
||||
|
||||
// Test that required columns are created as 'not null'.
|
||||
$this->addBundleField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$message = 'The new_bundle_field_shape column is not nullable.';
|
||||
$values = array(
|
||||
'bundle' => $entity->bundle(),
|
||||
'deleted'=> 0,
|
||||
'entity_id' => $entity->id(),
|
||||
'revision_id' => $entity->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'delta' => 0,
|
||||
'new_bundle_field_color' => $this->randomString(),
|
||||
);
|
||||
try {
|
||||
// Try to insert a record without providing a value for the 'not null'
|
||||
// column. This should fail.
|
||||
$this->database->insert('entity_test_update__new_bundle_field')
|
||||
->fields($values)
|
||||
->execute();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
// Now provide a value for the 'not null' column. This is expected to
|
||||
// succeed.
|
||||
$values['new_bundle_field_shape'] = $this->randomString();
|
||||
$this->database->insert('entity_test_update__new_bundle_field')
|
||||
->fields($values)
|
||||
->execute();
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,402 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Entity\EntityReferenceFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Entity;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Tests for the entity reference field.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityReferenceFieldTest extends EntityUnitTestBase {
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The entity type that is being referenced.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedEntityType = 'entity_test_rev';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* The name of the field used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
// Create a field.
|
||||
entity_reference_create_field(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$this->fieldName,
|
||||
'Field test',
|
||||
$this->referencedEntityType,
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reference field validation.
|
||||
*/
|
||||
public function testEntityReferenceFieldValidation() {
|
||||
// Test a valid reference.
|
||||
$referenced_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle));
|
||||
$referenced_entity->save();
|
||||
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
$entity->{$this->fieldName}->target_id = $referenced_entity->id();
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passes.');
|
||||
|
||||
// Test an invalid reference.
|
||||
$entity->{$this->fieldName}->target_id = 9999;
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999)));
|
||||
|
||||
// @todo Implement a test case for invalid bundle references after
|
||||
// https://drupal.org/node/2064191 is fixed
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the multiple target entities loader.
|
||||
*/
|
||||
public function testReferencedEntitiesMultipleLoad() {
|
||||
// Create the parent entity.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
|
||||
// Create three target entities and attach them to parent field.
|
||||
$target_entities = array();
|
||||
$reference_field = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$target_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle));
|
||||
$target_entity->save();
|
||||
$target_entities[] = $target_entity;
|
||||
$reference_field[]['target_id'] = $target_entity->id();
|
||||
}
|
||||
|
||||
// Also attach a non-existent entity and a NULL target id.
|
||||
$reference_field[3]['target_id'] = 99999;
|
||||
$target_entities[3] = NULL;
|
||||
$reference_field[4]['target_id'] = NULL;
|
||||
$target_entities[4] = NULL;
|
||||
|
||||
// Attach the first created target entity as the sixth item ($delta == 5) of
|
||||
// the parent entity field. We want to test the case when the same target
|
||||
// entity is referenced twice (or more times) in the same entity reference
|
||||
// field.
|
||||
$reference_field[5] = $reference_field[0];
|
||||
$target_entities[5] = $target_entities[0];
|
||||
|
||||
// Create a new target entity that is not saved, thus testing the
|
||||
// "autocreate" feature.
|
||||
$target_entity_unsaved = entity_create($this->referencedEntityType, array('type' => $this->bundle, 'name' => $this->randomString()));
|
||||
$reference_field[6]['entity'] = $target_entity_unsaved;
|
||||
$target_entities[6] = $target_entity_unsaved;
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$this->fieldName}->setValue($reference_field);
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$this->fieldName}->referencedEntities();
|
||||
|
||||
// Test returned entities:
|
||||
// - Deltas must be preserved.
|
||||
// - Non-existent entities must not be retrieved in target entities result.
|
||||
foreach ($target_entities as $delta => $target_entity) {
|
||||
if (!empty($target_entity)) {
|
||||
if (!$target_entity->isNew()) {
|
||||
// There must be an entity in the loaded set having the same id for
|
||||
// the same delta.
|
||||
$this->assertEqual($target_entity->id(), $entities[$delta]->id());
|
||||
}
|
||||
else {
|
||||
// For entities that were not yet saved, there must an entity in the
|
||||
// loaded set having the same label for the same delta.
|
||||
$this->assertEqual($target_entity->label(), $entities[$delta]->label());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A non-existent or NULL entity target id must not return any item in
|
||||
// the target entities set.
|
||||
$this->assertFalse(isset($loaded_entities[$delta]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests referencing entities with string IDs.
|
||||
*/
|
||||
public function testReferencedEntitiesStringId() {
|
||||
$field_name = 'entity_reference_string_id';
|
||||
$this->installEntitySchema('entity_test_string_id');
|
||||
entity_reference_create_field(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$field_name,
|
||||
'Field test',
|
||||
'entity_test_string_id',
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
// Create the parent entity.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
|
||||
// Create the default target entity.
|
||||
$target_entity = entity_create('entity_test_string_id', array('id' => $this->randomString(), 'type' => $this->bundle));
|
||||
$target_entity->save();
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$field_name}->setValue(array(array('target_id' => $target_entity->id())));
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
|
||||
// Test that a string ID works as a default value and the field's config
|
||||
// schema is correct.
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name);
|
||||
$field->setDefaultValue($target_entity->id());
|
||||
$field->save();
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray());
|
||||
|
||||
// Test that the default value works.
|
||||
$entity = entity_create($this->entityType, array('type' => $this->bundle));
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all the possible ways to autocreate an entity via the API.
|
||||
*/
|
||||
function testAutocreateApi() {
|
||||
$entity = $this->entityManager
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => $this->randomString()));
|
||||
|
||||
// Test content entity autocreation.
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->set('user_id', $user);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->set('user_id', $user, FALSE);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->setValue($user);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id[0]->get('entity')->setValue($user);
|
||||
$entity->user_id[0]->get('target_id')->setValue(-1);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->setValue(array('entity' => $user, 'target_id' => -1));
|
||||
});
|
||||
try {
|
||||
$message = 'Setting both the entity and an invalid target_id property fails.';
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$user->save();
|
||||
$entity->user_id->setValue(array('entity' => $user, 'target_id' => -1));
|
||||
});
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id = $user;
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->entity = $user;
|
||||
});
|
||||
|
||||
// Test config entity autocreation.
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->set('user_role', $role);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->set('user_role', $role, FALSE);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->setValue($role);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role[0]->get('entity')->setValue($role);
|
||||
$entity->user_role[0]->get('target_id')->setValue(-1);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->setValue(array('entity' => $role, 'target_id' => -1));
|
||||
});
|
||||
try {
|
||||
$message = 'Setting both the entity and an invalid target_id property fails.';
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$role->save();
|
||||
$entity->user_role->setValue(array('entity' => $role, 'target_id' => -1));
|
||||
});
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role = $role;
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->entity = $role;
|
||||
});
|
||||
|
||||
// Test target entity saving after setting it as new.
|
||||
$storage = $this->entityManager->getStorage('user');
|
||||
$user_id = $this->generateRandomEntityId();
|
||||
$user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString()));
|
||||
$entity->user_id = $user;
|
||||
$user->save();
|
||||
$entity->save();
|
||||
$this->assertEqual($entity->user_id->target_id, $user->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the setter callback performs autocreation for users.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The referencing entity.
|
||||
* @param $setter_callback
|
||||
* A callback setting the target entity on the referencing entity.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the user was autocreated, FALSE otherwise.
|
||||
*/
|
||||
protected function assertUserAutocreate(EntityInterface $entity, $setter_callback) {
|
||||
$storage = $this->entityManager->getStorage('user');
|
||||
$user_id = $this->generateRandomEntityId();
|
||||
$user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString()));
|
||||
$setter_callback($entity, $user);
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$user = User::load($user_id);
|
||||
return $this->assertEqual($entity->user_id->target_id, $user->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the setter callback performs autocreation for user roles.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The referencing entity.
|
||||
* @param $setter_callback
|
||||
* A callback setting the target entity on the referencing entity.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the user was autocreated, FALSE otherwise.
|
||||
*/
|
||||
protected function assertUserRoleAutocreate(EntityInterface $entity, $setter_callback) {
|
||||
$storage = $this->entityManager->getStorage('user_role');
|
||||
$role_id = $this->generateRandomEntityId(TRUE);
|
||||
$role = $storage->create(array('id' => $role_id, 'label' => $this->randomString()));
|
||||
$setter_callback($entity, $role);
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$role = Role::load($role_id);
|
||||
return $this->assertEqual($entity->user_role->target_id, $role->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the target entity is not unnecessarily loaded.
|
||||
*/
|
||||
public function testTargetEntityNoLoad() {
|
||||
// Setup a test entity type with an entity reference field to itself. We use
|
||||
// a special storage class throwing exceptions when a load operation is
|
||||
// triggered to be able to detect them.
|
||||
$entity_type = clone $this->entityManager->getDefinition('entity_test_update');
|
||||
$entity_type->setHandlerClass('storage', '\Drupal\entity_test\EntityTestNoLoadStorage');
|
||||
$this->state->set('entity_test_update.entity_type', $entity_type);
|
||||
$definitions = array(
|
||||
'target_reference' => BaseFieldDefinition::create('entity_reference')
|
||||
->setSetting('target_type', $entity_type->id())
|
||||
->setSetting('handler', 'default')
|
||||
);
|
||||
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$this->installEntitySchema($entity_type->id());
|
||||
|
||||
// Create the target entity.
|
||||
$storage = $this->entityManager->getStorage($entity_type->id());
|
||||
$target_id = $this->generateRandomEntityId();
|
||||
$target = $storage->create(array('id' => $target_id, 'name' => $this->randomString()));
|
||||
$target->save();
|
||||
$this->assertEqual($target_id, $target->id(), 'The target entity has a random identifier.');
|
||||
|
||||
// Check that populating the reference with an existing target id does not
|
||||
// trigger a load operation.
|
||||
$message = 'The target entity was not loaded.';
|
||||
try {
|
||||
$entity = $this->entityManager
|
||||
->getStorage($entity_type->id())
|
||||
->create(array('name' => $this->randomString()));
|
||||
$entity->target_reference = $target_id;
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
// Check that the storage actually triggers the expected exception when
|
||||
// trying to load the target entity.
|
||||
$message = 'An exception is thrown when trying to load the target entity';
|
||||
try {
|
||||
$storage->load($target_id);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -29,6 +29,13 @@ abstract class EntityUnitTestBase extends KernelTestBase {
|
|||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* A list of generated identifiers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $generatedIds = array();
|
||||
|
||||
/**
|
||||
* The state service.
|
||||
*
|
||||
|
@ -160,4 +167,23 @@ abstract class EntityUnitTestBase extends KernelTestBase {
|
|||
$this->state = $this->container->get('state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random ID avoiding collisions.
|
||||
*
|
||||
* @param bool $string
|
||||
* (optional) Whether the id should have string type. Defaults to FALSE.
|
||||
*
|
||||
* @return int|string
|
||||
* The entity identifier.
|
||||
*/
|
||||
protected function generateRandomEntityId($string = FALSE) {
|
||||
srand(time());
|
||||
do {
|
||||
$id = $string ? $this->randomMachineName() : mt_rand(1, 0xFFFFFFFF);
|
||||
}
|
||||
while (isset($this->generatedIds[$id]));
|
||||
$this->generatedIds[$id] = $id;
|
||||
return $id;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ class ContextPluginTest extends KernelTestBase {
|
|||
* Tests basic context definition and value getters and setters.
|
||||
*/
|
||||
function testContext() {
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$name = $this->randomMachineName();
|
||||
$manager = new MockBlockManager();
|
||||
$plugin = $manager->createInstance('user_name');
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\entity_test\EntityTestNoLoadStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\entity_test;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
|
||||
/**
|
||||
* Test storage class used to verify that no load operation is triggered.
|
||||
*/
|
||||
class EntityTestNoLoadStorage extends SqlContentEntityStorage {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load($id) {
|
||||
throw new EntityStorageException('No load operation is supposed to happen.');
|
||||
}
|
||||
|
||||
}
|
|
@ -65,12 +65,10 @@ class ShapeItem extends FieldItemBase {
|
|||
'shape' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'color' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 32,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
) + $foreign_keys;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\entity_test\Plugin\Field\FieldType\ShapeItemRequired.
|
||||
*/
|
||||
|
||||
namespace Drupal\entity_test\Plugin\Field\FieldType;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Defines the 'shape_required' field type.
|
||||
*
|
||||
* @FieldType(
|
||||
* id = "shape_required",
|
||||
* label = @Translation("Shape (required)"),
|
||||
* description = @Translation("Yet another dummy field type."),
|
||||
* )
|
||||
*/
|
||||
class ShapeItemRequired extends ShapeItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties = parent::propertyDefinitions($field_definition);
|
||||
$properties['shape']->setRequired(TRUE);
|
||||
return $properties;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Drupal\taxonomy\Plugin\Field\FieldFormatter;
|
|||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceItem;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'taxonomy_term_reference_link' formatter.
|
||||
|
@ -31,7 +32,7 @@ class LinkFormatter extends TaxonomyFormatterBase {
|
|||
// Terms without target_id do not exist yet, theme such terms as just their
|
||||
// name.
|
||||
foreach ($items as $delta => $item) {
|
||||
if (!$item->target_id) {
|
||||
if ($item->hasNewEntity()) {
|
||||
$elements[$delta] = array(
|
||||
'#markup' => String::checkPlain($item->entity->label()),
|
||||
);
|
||||
|
|
|
@ -68,8 +68,9 @@ class TaxonomyTermReferenceItem extends EntityReferenceItem implements OptionsPr
|
|||
public function getSettableValues(AccountInterface $account = NULL) {
|
||||
// Flatten options firstly, because Settable Options may contain group
|
||||
// arrays.
|
||||
$flatten_options = OptGroup::flattenOptions($this->getSettableOptions($account));
|
||||
return array_keys($flatten_options);
|
||||
$values = array_keys(OptGroup::flattenOptions($this->getSettableOptions($account)));
|
||||
$values[] = static::$NEW_ENTITY_MARKER;
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,7 +104,6 @@ class TaxonomyTermReferenceItem extends EntityReferenceItem implements OptionsPr
|
|||
'target_id' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\Core\Field\WidgetBase;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceItem;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -156,7 +157,7 @@ class TaxonomyAutocompleteWidget extends WidgetBase implements ContainerFactoryP
|
|||
'vid' => $vocabulary->id(),
|
||||
'name' => $value,
|
||||
));
|
||||
$item = array('target_id' => NULL, 'entity' => $term);
|
||||
$item = array('entity' => $term);
|
||||
}
|
||||
$items[] = $item;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ class TelephoneItem extends FieldItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 256,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -45,7 +44,8 @@ class TelephoneItem extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Telephone number'));
|
||||
->setLabel(t('Telephone number'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,10 @@ class TextItem extends TextItemBase {
|
|||
'value' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => $field_definition->getSetting('max_length'),
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -23,7 +23,8 @@ abstract class TextItemBase extends FieldItemBase {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(t('Text'));
|
||||
->setLabel(t('Text'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$properties['format'] = DataDefinition::create('filter_format')
|
||||
->setLabel(t('Text format'));
|
||||
|
|
|
@ -31,12 +31,10 @@ class TextLongItem extends TextItemBase {
|
|||
'value' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -61,17 +61,14 @@ class TextWithSummaryItem extends TextItemBase {
|
|||
'value' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'summary' => array(
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'format' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace Drupal\Tests\Core\Entity\Sql;
|
|||
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\Sql\DefaultTableMapping;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
|
@ -257,72 +256,89 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'description' => 'The name field.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'description__value' => array(
|
||||
'description' => 'The description field.',
|
||||
'type' => 'text',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'description__format' => array(
|
||||
'description' => 'The description field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'uuid' => array(
|
||||
'description' => 'The uuid field.',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'hash' => array(
|
||||
'description' => 'The hash field.',
|
||||
'type' => 'varchar',
|
||||
'length' => 20,
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'email__username' => array(
|
||||
'description' => 'The email field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'email__hostname' => array(
|
||||
'description' => 'The email field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'email__domain' => array(
|
||||
'description' => 'The email field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'owner' => array(
|
||||
'description' => 'The owner field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'translator' => array(
|
||||
'description' => 'The translator field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'location__country' => array(
|
||||
'description' => 'The location field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'location__state' => array(
|
||||
'description' => 'The location field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'location__city' => array(
|
||||
'description' => 'The location field.',
|
||||
'type' => 'varchar',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'editor' => array(
|
||||
'description' => 'The editor field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'editor_revision__target_id' => array(
|
||||
'description' => 'The editor_revision field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'editor_revision__target_revision_id' => array(
|
||||
'description' => 'The editor_revision field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'long_index_name' => array(
|
||||
'description' => 'The long_index_name field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'default_langcode' => array(
|
||||
'description' => 'Boolean indicating whether field values are in the default entity language.',
|
||||
|
@ -430,6 +446,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
)
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
|
@ -455,6 +472,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('revision_id'),
|
||||
|
@ -634,6 +652,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'int',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The langcode field.',
|
||||
|
@ -664,6 +683,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The langcode field.',
|
||||
|
@ -694,6 +714,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The langcode field.',
|
||||
|
@ -724,6 +745,7 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
'revision_id' => array(
|
||||
'description' => 'The revision_id field.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'langcode' => array(
|
||||
'description' => 'The langcode field.',
|
||||
|
@ -1231,6 +1253,19 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
|
|||
$this->storageDefinitions[$field_name]->expects($this->any())
|
||||
->method('getColumns')
|
||||
->will($this->returnValue($schema['columns']));
|
||||
// Add property definitions.
|
||||
if (!empty($schema['columns'])) {
|
||||
$property_definitions = array();
|
||||
foreach ($schema['columns'] as $column => $info) {
|
||||
$property_definitions[$column] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface');
|
||||
$property_definitions[$column]->expects($this->any())
|
||||
->method('isRequired')
|
||||
->will($this->returnValue(!empty($info['not null'])));
|
||||
}
|
||||
$this->storageDefinitions[$field_name]->expects($this->any())
|
||||
->method('getPropertyDefinitions')
|
||||
->will($this->returnValue($property_definitions));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue