Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
151
core/modules/field/src/ConfigImporterFieldPurger.php
Normal file
151
core/modules/field/src/ConfigImporterFieldPurger.php
Normal file
|
@ -0,0 +1,151 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\ConfigImporterFieldPurger.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Processes field purges before a configuration synchronization.
|
||||
*/
|
||||
class ConfigImporterFieldPurger {
|
||||
|
||||
/**
|
||||
* Processes fields targeted for purge as part of a configuration sync.
|
||||
*
|
||||
* This takes care of deleting the field if necessary, and purging the data on
|
||||
* the fly.
|
||||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The config importer.
|
||||
*/
|
||||
public static function process(array &$context, ConfigImporter $config_importer) {
|
||||
if (!isset($context['sandbox']['field'])) {
|
||||
static::initializeSandbox($context, $config_importer);
|
||||
}
|
||||
|
||||
// Get the list of field storages to purge.
|
||||
$field_storages = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
||||
// Get the first field storage to process.
|
||||
$field_storage = reset($field_storages);
|
||||
if (!isset($context['sandbox']['field']['current_storage_id']) || $context['sandbox']['field']['current_storage_id'] != $field_storage->id()) {
|
||||
$context['sandbox']['field']['current_storage_id'] = $field_storage->id();
|
||||
// If the storage has not been deleted yet we need to do that. This is the
|
||||
// case when the storage deletion is staged.
|
||||
if (!$field_storage->isDeleted()) {
|
||||
$field_storage->delete();
|
||||
}
|
||||
}
|
||||
field_purge_batch($context['sandbox']['field']['purge_batch_size'], $field_storage->uuid());
|
||||
$context['sandbox']['field']['current_progress']++;
|
||||
$fields_to_delete_count = count(static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')));
|
||||
if ($fields_to_delete_count == 0) {
|
||||
$context['finished'] = 1;
|
||||
}
|
||||
else {
|
||||
$context['finished'] = $context['sandbox']['field']['current_progress'] / $context['sandbox']['field']['steps_to_delete'];
|
||||
$context['message'] = \Drupal::translation()->translate('Purging field @field_label', array('@field_label' => $field_storage->label()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the batch context sandbox for processing field deletions.
|
||||
*
|
||||
* This calculates the number of steps necessary to purge all the field data
|
||||
* and saves data for later use.
|
||||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The config importer.
|
||||
*/
|
||||
protected static function initializeSandbox(array &$context, ConfigImporter $config_importer) {
|
||||
$context['sandbox']['field']['purge_batch_size'] = \Drupal::config('field.settings')->get('purge_batch_size');
|
||||
// Save the future list of installed extensions to limit the amount of times
|
||||
// the configuration is read from disk.
|
||||
$context['sandbox']['field']['extensions'] = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension');
|
||||
|
||||
$context['sandbox']['field']['steps_to_delete'] = 0;
|
||||
$fields = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
||||
foreach ($fields as $field) {
|
||||
$row_count = \Drupal::entityManager()->getStorage($field->getTargetEntityTypeId())
|
||||
->countFieldData($field);
|
||||
if ($row_count > 0) {
|
||||
// The number of steps to delete each field is determined by the
|
||||
// purge_batch_size setting. For example if the field has 9 rows and the
|
||||
// batch size is 10 then this will add 1 step to $number_of_steps.
|
||||
$how_many_steps = ceil($row_count / $context['sandbox']['field']['purge_batch_size']);
|
||||
$context['sandbox']['field']['steps_to_delete'] += $how_many_steps;
|
||||
}
|
||||
}
|
||||
// Each field possibly needs one last field_purge_batch() call to remove the
|
||||
// last field and the field storage itself.
|
||||
$context['sandbox']['field']['steps_to_delete'] += count($fields);
|
||||
|
||||
$context['sandbox']['field']['current_progress'] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of fields to purge before configuration synchronization.
|
||||
*
|
||||
* If, during a configuration synchronization, a field is being deleted and
|
||||
* the module that provides the field type is being uninstalled then the field
|
||||
* data must be purged before the module is uninstalled. Also, if deleted
|
||||
* fields exist whose field types are provided by modules that are being
|
||||
* uninstalled their data need to be purged too.
|
||||
*
|
||||
* @param array $extensions
|
||||
* The list of extensions that will be enabled after the configuration
|
||||
* synchronization has finished.
|
||||
* @param array $deletes
|
||||
* The configuration that will be deleted by the configuration
|
||||
* synchronization.
|
||||
*
|
||||
* @return \Drupal\field\Entity\FieldStorageConfig[]
|
||||
* An array of field storages that need purging before configuration can be
|
||||
* synchronized.
|
||||
*/
|
||||
public static function getFieldStoragesToPurge(array $extensions, array $deletes) {
|
||||
$providers = array_keys($extensions['module']);
|
||||
$providers[] = 'core';
|
||||
$storages_to_delete = array();
|
||||
|
||||
// Gather fields that will be deleted during configuration synchronization
|
||||
// where the module that provides the field type is also being uninstalled.
|
||||
$field_storage_ids = array();
|
||||
foreach ($deletes as $config_name) {
|
||||
$field_storage_config_prefix = \Drupal::entityManager()->getDefinition('field_storage_config')->getConfigPrefix();
|
||||
if (strpos($config_name, $field_storage_config_prefix . '.') === 0) {
|
||||
$field_storage_ids[] = ConfigEntityStorage::getIDFromConfigName($config_name, $field_storage_config_prefix);
|
||||
}
|
||||
}
|
||||
if (!empty($field_storage_ids)) {
|
||||
$field_storages = \Drupal::entityQuery('field_storage_config')
|
||||
->condition('id', $field_storage_ids, 'IN')
|
||||
->condition('module', $providers, 'NOT IN')
|
||||
->execute();
|
||||
if (!empty($field_storages)) {
|
||||
$storages_to_delete = FieldStorageConfig::loadMultiple($field_storages);
|
||||
}
|
||||
}
|
||||
|
||||
// Gather deleted fields from modules that are being uninstalled.
|
||||
/** @var \Drupal\field\FieldStorageConfigInterface[] $field_storages */
|
||||
$field_storages = entity_load_multiple_by_properties('field_storage_config', array('deleted' => TRUE, 'include_deleted' => TRUE));
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!in_array($field_storage->getTypeProvider(), $providers)) {
|
||||
$storages_to_delete[$field_storage->id()] = $field_storage;
|
||||
}
|
||||
}
|
||||
return $storages_to_delete;
|
||||
}
|
||||
|
||||
}
|
348
core/modules/field/src/Entity/FieldConfig.php
Normal file
348
core/modules/field/src/Entity/FieldConfig.php
Normal file
|
@ -0,0 +1,348 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Entity\FieldConfig.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Field\FieldConfigBase;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
|
||||
/**
|
||||
* Defines the Field entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "field_config",
|
||||
* label = @Translation("Field"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\field\FieldConfigAccessControlHandler",
|
||||
* "storage" = "Drupal\field\FieldConfigStorage"
|
||||
* },
|
||||
* config_prefix = "field",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "field_name",
|
||||
* "entity_type",
|
||||
* "bundle",
|
||||
* "label",
|
||||
* "description",
|
||||
* "required",
|
||||
* "translatable",
|
||||
* "default_value",
|
||||
* "default_value_callback",
|
||||
* "settings",
|
||||
* "field_type",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is deleted.
|
||||
*
|
||||
* The delete() method marks the field as "deleted" and removes the
|
||||
* corresponding entry from the config storage, but keeps its definition in
|
||||
* the state storage while field data is purged by a separate
|
||||
* garbage-collection process.
|
||||
*
|
||||
* Deleted fields stay out of the regular entity lifecycle (notably, their
|
||||
* values are not populated in loaded entities, and are not saved back).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $deleted = FALSE;
|
||||
|
||||
/**
|
||||
* The associated FieldStorageConfig entity.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* Constructs a FieldConfig object.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
* entity_create('field_config', $values), where $values is the same
|
||||
* parameter as in this constructor.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of field properties, keyed by property name. The
|
||||
* storage associated to the field can be specified either with:
|
||||
* - field_storage: the FieldStorageConfigInterface object,
|
||||
* or by referring to an existing field storage in the current configuration
|
||||
* with:
|
||||
* - field_name: The field name.
|
||||
* - entity_type: The entity type.
|
||||
* Additionally, a 'bundle' property is required to indicate the entity
|
||||
* bundle to which the field is attached to. Other array elements will be
|
||||
* used to set the corresponding properties on the class; see the class
|
||||
* property documentation for details.
|
||||
*
|
||||
* @see entity_create()
|
||||
*/
|
||||
public function __construct(array $values, $entity_type = 'field_config') {
|
||||
// Allow either an injected FieldStorageConfig object, or a field_name and
|
||||
// entity_type.
|
||||
if (isset($values['field_storage'])) {
|
||||
if (!$values['field_storage'] instanceof FieldStorageConfigInterface) {
|
||||
throw new FieldException('Attempt to create a configurable field for a non-configurable field storage.');
|
||||
}
|
||||
$field_storage = $values['field_storage'];
|
||||
$values['field_name'] = $field_storage->getName();
|
||||
$values['entity_type'] = $field_storage->getTargetEntityTypeId();
|
||||
// The internal property is fieldStorage, not field_storage.
|
||||
unset($values['field_storage']);
|
||||
$values['fieldStorage'] = $field_storage;
|
||||
}
|
||||
else {
|
||||
if (empty($values['field_name'])) {
|
||||
throw new FieldException('Attempt to create a field without a field_name.');
|
||||
}
|
||||
if (empty($values['entity_type'])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name without an entity_type.', array('@field_name' => $values['field_name'])));
|
||||
}
|
||||
}
|
||||
// 'bundle' is required in either case.
|
||||
if (empty($values['bundle'])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name without a bundle.', array('@field_name' => $values['field_name'])));
|
||||
}
|
||||
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageInterface $storage) {
|
||||
parent::postCreate($storage);
|
||||
|
||||
// Validate that we have a valid storage for this field. This throws an
|
||||
// exception if the storage is invalid.
|
||||
$this->getFieldStorageDefinition();
|
||||
|
||||
// 'Label' defaults to the field name (mostly useful for fields created in
|
||||
// tests).
|
||||
if (empty($this->label)) {
|
||||
$this->label = $this->getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\Entity::preSave().
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException
|
||||
* If the field definition is invalid.
|
||||
* @throws \Drupal\Core\Entity\EntityStorageException
|
||||
* In case of failures at the configuration storage level.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
$storage_definition = $this->getFieldStorageDefinition();
|
||||
|
||||
// Filter out unknown settings and make sure all settings are present, so
|
||||
// that a complete field definition is passed to the various hooks and
|
||||
// written to config.
|
||||
$default_settings = $field_type_manager->getDefaultFieldSettings($storage_definition->getType());
|
||||
$this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
|
||||
|
||||
if ($this->isNew()) {
|
||||
// Notify the entity storage.
|
||||
$entity_manager->onFieldDefinitionCreate($this);
|
||||
}
|
||||
else {
|
||||
// Some updates are always disallowed.
|
||||
if ($this->entity_type != $this->original->entity_type) {
|
||||
throw new FieldException("Cannot change an existing field's entity_type.");
|
||||
}
|
||||
if ($this->bundle != $this->original->bundle && empty($this->bundleRenameAllowed)) {
|
||||
throw new FieldException("Cannot change an existing field's bundle.");
|
||||
}
|
||||
if ($storage_definition->uuid() != $this->original->getFieldStorageDefinition()->uuid()) {
|
||||
throw new FieldException("Cannot change an existing field's storage.");
|
||||
}
|
||||
// Notify the entity storage.
|
||||
$entity_manager->onFieldDefinitionUpdate($this, $this->original);
|
||||
}
|
||||
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
// Mark the field_storage_config as a a dependency.
|
||||
$this->addDependency('config', $this->getFieldStorageDefinition()->getConfigDependencyName());
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $fields) {
|
||||
$state = \Drupal::state();
|
||||
|
||||
parent::preDelete($storage, $fields);
|
||||
// Keep the field definitions in the state storage so we can use them
|
||||
// later during field_purge_batch().
|
||||
$deleted_fields = $state->get('field.field.deleted') ?: array();
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
$config = $field->toArray();
|
||||
$config['deleted'] = TRUE;
|
||||
$config['field_storage_uuid'] = $field->getFieldStorageDefinition()->uuid();
|
||||
$deleted_fields[$field->uuid()] = $config;
|
||||
}
|
||||
}
|
||||
$state->set('field.field.deleted', $deleted_fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $fields) {
|
||||
// Clear the cache upfront, to refresh the results of getBundles().
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
|
||||
// Notify the entity storage.
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
\Drupal::entityManager()->onFieldDefinitionDelete($field);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is part of a configuration synchronization then the following
|
||||
// configuration updates are not necessary.
|
||||
$entity = reset($fields);
|
||||
if ($entity->isSyncing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the associated field storages if they are not used anymore and are
|
||||
// not persistent.
|
||||
$storages_to_delete = array();
|
||||
foreach ($fields as $field) {
|
||||
$storage_definition = $field->getFieldStorageDefinition();
|
||||
if (!$field->deleted && !$field->isUninstalling() && $storage_definition->isDeletable()) {
|
||||
// Key by field UUID to avoid deleting the same storage twice.
|
||||
$storages_to_delete[$storage_definition->uuid()] = $storage_definition;
|
||||
}
|
||||
}
|
||||
if ($storages_to_delete) {
|
||||
\Drupal::entityManager()->getStorage('field_storage_config')->delete($storages_to_delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function linkTemplates() {
|
||||
$link_templates = parent::linkTemplates();
|
||||
if (\Drupal::moduleHandler()->moduleExists('field_ui')) {
|
||||
$link_templates["{$this->entity_type}-field-edit-form"] = 'entity.field_config.' . $this->entity_type . '_field_edit_form';
|
||||
$link_templates["{$this->entity_type}-storage-edit-form"] = 'entity.field_config.' . $this->entity_type . '_storage_edit_form';
|
||||
$link_templates["{$this->entity_type}-field-delete-form"] = 'entity.field_config.' . $this->entity_type . '_field_delete_form';
|
||||
|
||||
if (isset($link_templates['config-translation-overview'])) {
|
||||
$link_templates["config-translation-overview.{$this->entity_type}"] = "entity.field_config.config_translation_overview.{$this->entity_type}";
|
||||
}
|
||||
}
|
||||
return $link_templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function urlRouteParameters($rel) {
|
||||
$parameters = parent::urlRouteParameters($rel);
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
|
||||
$parameters[$entity_type->getBundleEntityType()] = $this->bundle;
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeleted() {
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldStorageDefinition() {
|
||||
if (!$this->fieldStorage) {
|
||||
$fields = $this->entityManager()->getFieldStorageDefinitions($this->entity_type);
|
||||
if (!isset($fields[$this->field_name])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field @field_name that does not exist on entity type @entity_type.', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type))); }
|
||||
if (!$fields[$this->field_name] instanceof FieldStorageConfigInterface) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a configurable field of non-configurable field storage @field_name.', array('@field_name' => $this->field_name, '@entity_type' => $this->entity_type)));
|
||||
}
|
||||
$this->fieldStorage = $fields[$this->field_name];
|
||||
}
|
||||
|
||||
return $this->fieldStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDisplayConfigurable($context) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDisplayOptions($display_context) {
|
||||
// Hide configurable fields by default.
|
||||
return array('type' => 'hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadOnly() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComputed() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a field config entity based on the entity type and field name.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* ID of the entity type.
|
||||
* @param string $bundle
|
||||
* Bundle name.
|
||||
* @param string $field_name
|
||||
* Name of the field.
|
||||
*
|
||||
* @return static
|
||||
* The field config entity if one exists for the provided field
|
||||
* name, otherwise NULL.
|
||||
*/
|
||||
public static function loadByName($entity_type_id, $bundle, $field_name) {
|
||||
return \Drupal::entityManager()->getStorage('field_config')->load($entity_type_id . '.' . $bundle . '.' . $field_name);
|
||||
}
|
||||
|
||||
}
|
823
core/modules/field/src/Entity/FieldStorageConfig.php
Normal file
823
core/modules/field/src/Entity/FieldStorageConfig.php
Normal file
|
@ -0,0 +1,823 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Entity\FieldStorageConfig.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\OptionsProviderInterface;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
|
||||
/**
|
||||
* Defines the Field storage configuration entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "field_storage_config",
|
||||
* label = @Translation("Field storage"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\field\FieldStorageConfigStorage"
|
||||
* },
|
||||
* config_prefix = "storage",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "id"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "field_name",
|
||||
* "entity_type",
|
||||
* "type",
|
||||
* "settings",
|
||||
* "module",
|
||||
* "locked",
|
||||
* "cardinality",
|
||||
* "translatable",
|
||||
* "indexes",
|
||||
* "persist_with_no_fields",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigInterface {
|
||||
|
||||
/**
|
||||
* The maximum length of the field name, in characters.
|
||||
*
|
||||
* For fields created through Field UI, this includes the 'field_' prefix.
|
||||
*/
|
||||
const NAME_MAX_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* The field ID.
|
||||
*
|
||||
* The ID consists of 2 parts: the entity type and the field name.
|
||||
*
|
||||
* Example: node.body, user.field_main_image.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The field name.
|
||||
*
|
||||
* This is the name of the property under which the field values are placed in
|
||||
* an entity: $entity->{$field_name}. The maximum length is
|
||||
* Field:NAME_MAX_LENGTH.
|
||||
*
|
||||
* Example: body, field_main_image.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $field_name;
|
||||
|
||||
/**
|
||||
* The name of the entity type the field can be attached to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entity_type;
|
||||
|
||||
/**
|
||||
* The field type.
|
||||
*
|
||||
* Example: text, integer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The name of the module that provides the field type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $module;
|
||||
|
||||
/**
|
||||
* Field-type specific settings.
|
||||
*
|
||||
* An array of key/value pairs, The keys and default values are defined by the
|
||||
* field type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* The field cardinality.
|
||||
*
|
||||
* The maximum number of values the field can hold. Possible values are
|
||||
* positive integers or
|
||||
* FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED. Defaults to 1.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $cardinality = 1;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is translatable.
|
||||
*
|
||||
* Defaults to TRUE.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $translatable = TRUE;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is available for editing.
|
||||
*
|
||||
* If TRUE, some actions not available though the UI (but are still possible
|
||||
* through direct API manipulation):
|
||||
* - field settings cannot be changed,
|
||||
* - new fields cannot be created
|
||||
* - existing fields cannot be deleted.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $locked = FALSE;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field storage should be deleted when orphaned.
|
||||
*
|
||||
* By default field storages for configurable fields are removed when there
|
||||
* are no remaining fields using them. If multiple modules provide bundles
|
||||
* which need to use the same field storage then setting this to TRUE will
|
||||
* preserve the field storage regardless of what happens to the bundles. The
|
||||
* classic use case for this is node body field storage since Book, Forum, the
|
||||
* Standard profile and bundle (node type) creation through the UI all use
|
||||
* same field storage.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $persist_with_no_fields = FALSE;
|
||||
|
||||
/**
|
||||
* The custom storage indexes for the field data storage.
|
||||
*
|
||||
* This set of indexes is merged with the "default" indexes specified by the
|
||||
* field type in hook_field_schema() to determine the actual set of indexes
|
||||
* that get created.
|
||||
*
|
||||
* The indexes are defined using the same definition format as Schema API
|
||||
* index specifications. Only columns that are part of the field schema, as
|
||||
* defined by the field type in hook_field_schema(), are allowed.
|
||||
*
|
||||
* Some storage backends might not support indexes, and discard that
|
||||
* information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $indexes = [];
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is deleted.
|
||||
*
|
||||
* The delete() method marks the field as "deleted" and removes the
|
||||
* corresponding entry from the config storage, but keeps its definition in
|
||||
* the state storage while field data is purged by a separate
|
||||
* garbage-collection process.
|
||||
*
|
||||
* Deleted fields stay out of the regular entity lifecycle (notably, their
|
||||
* values are not populated in loaded entities, and are not saved back).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $deleted = FALSE;
|
||||
|
||||
/**
|
||||
* The field schema.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* An array of field property definitions.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinitionInterface[]
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
|
||||
*/
|
||||
protected $propertyDefinitions;
|
||||
|
||||
/**
|
||||
* Static flag set to prevent recursion during field deletes.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $inDeletion = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a FieldStorageConfig object.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of field properties, keyed by property name. Most array
|
||||
* elements will be used to set the corresponding properties on the class;
|
||||
* see the class property documentation for details. Some array elements
|
||||
* have special meanings and a few are required. Special elements are:
|
||||
* - name: required. As a temporary Backwards Compatibility layer right now,
|
||||
* a 'field_name' property can be accepted in place of 'id'.
|
||||
* - entity_type: required.
|
||||
* - type: required.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
* entity_create('field_storage_config', $values)), where $values is the same
|
||||
* parameter as in this constructor.
|
||||
*
|
||||
* @see entity_create()
|
||||
*/
|
||||
public function __construct(array $values, $entity_type = 'field_storage_config') {
|
||||
// Check required properties.
|
||||
if (empty($values['field_name'])) {
|
||||
throw new FieldException('Attempt to create a field storage without a field name.');
|
||||
}
|
||||
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character', array('@field_name' => $values['field_name'])));
|
||||
}
|
||||
if (empty($values['type'])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with no type.', array('@field_name' => $values['field_name'])));
|
||||
}
|
||||
if (empty($values['entity_type'])) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field storage @field_name with no entity_type.', array('@field_name' => $values['field_name'])));
|
||||
}
|
||||
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->getTargetEntityTypeId() . '.' . $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\Entity::preSave().
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException
|
||||
* If the field definition is invalid.
|
||||
* @throws \Drupal\Core\Entity\EntityStorageException
|
||||
* In case of failures at the configuration storage level.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
// Clear the derived data about the field.
|
||||
unset($this->schema);
|
||||
|
||||
// Filter out unknown settings and make sure all settings are present, so
|
||||
// that a complete field definition is passed to the various hooks and
|
||||
// written to config.
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
$default_settings = $field_type_manager->getDefaultStorageSettings($this->type);
|
||||
$this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
|
||||
|
||||
if ($this->isNew()) {
|
||||
$this->preSaveNew($storage);
|
||||
}
|
||||
else {
|
||||
$this->preSaveUpdated($storage);
|
||||
}
|
||||
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares saving a new field definition.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage.
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException If the field definition is invalid.
|
||||
*/
|
||||
protected function preSaveNew(EntityStorageInterface $storage) {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
// Assign the ID.
|
||||
$this->id = $this->id();
|
||||
|
||||
// Field name cannot be longer than FieldStorageConfig::NAME_MAX_LENGTH characters.
|
||||
// We use Unicode::strlen() because the DB layer assumes that column widths
|
||||
// are given in characters rather than bytes.
|
||||
if (Unicode::strlen($this->getName()) > static::NAME_MAX_LENGTH) {
|
||||
throw new FieldException(SafeMarkup::format(
|
||||
'Attempt to create a field storage with an name longer than @max characters: %name', array(
|
||||
'@max' => static::NAME_MAX_LENGTH,
|
||||
'%name' => $this->getName(),
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
// Disallow reserved field names.
|
||||
$disallowed_field_names = array_keys($entity_manager->getBaseFieldDefinitions($this->getTargetEntityTypeId()));
|
||||
if (in_array($this->getName(), $disallowed_field_names)) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create field storage %name which is reserved by entity type %type.', array('%name' => $this->getName(), '%type' => $this->getTargetEntityTypeId())));
|
||||
}
|
||||
|
||||
// Check that the field type is known.
|
||||
$field_type = $field_type_manager->getDefinition($this->getType(), FALSE);
|
||||
if (!$field_type) {
|
||||
throw new FieldException(SafeMarkup::format('Attempt to create a field storage of unknown type %type.', array('%type' => $this->getType())));
|
||||
}
|
||||
$this->module = $field_type['provider'];
|
||||
|
||||
// Notify the entity manager.
|
||||
$entity_manager->onFieldStorageDefinitionCreate($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
// Ensure the field is dependent on the providing module.
|
||||
$this->addDependency('module', $this->getTypeProvider());
|
||||
// Ensure the field is dependent on the provider of the entity type.
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
|
||||
$this->addDependency('module', $entity_type->getProvider());
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares saving an updated field definition.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage.
|
||||
*/
|
||||
protected function preSaveUpdated(EntityStorageInterface $storage) {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
|
||||
// Some updates are always disallowed.
|
||||
if ($this->getType() != $this->original->getType()) {
|
||||
throw new FieldException("Cannot change the field type for an existing field storage.");
|
||||
}
|
||||
if ($this->getTargetEntityTypeId() != $this->original->getTargetEntityTypeId()) {
|
||||
throw new FieldException("Cannot change the entity type for an existing field storage.");
|
||||
}
|
||||
|
||||
// See if any module forbids the update by throwing an exception. This
|
||||
// invokes hook_field_storage_config_update_forbid().
|
||||
$module_handler->invokeAll('field_storage_config_update_forbid', array($this, $this->original));
|
||||
|
||||
// Notify the entity manager. A listener can reject the definition
|
||||
// update as invalid by raising an exception, which stops execution before
|
||||
// the definition is written to config.
|
||||
$entity_manager->onFieldStorageDefinitionUpdate($this, $this->original);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
if ($update) {
|
||||
// Invalidate the render cache for all affected entities.
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$entity_type = $this->getTargetEntityTypeId();
|
||||
if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
|
||||
$entity_manager->getViewBuilder($entity_type)->resetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $field_storages) {
|
||||
$state = \Drupal::state();
|
||||
|
||||
// Set the static flag so that we don't delete field storages whilst
|
||||
// deleting fields.
|
||||
static::$inDeletion = TRUE;
|
||||
|
||||
// Delete or fix any configuration that is dependent, for example, fields.
|
||||
parent::preDelete($storage, $field_storages);
|
||||
|
||||
// Keep the field definitions in the state storage so we can use them later
|
||||
// during field_purge_batch().
|
||||
$deleted_storages = $state->get('field.storage.deleted') ?: array();
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!$field_storage->deleted) {
|
||||
$config = $field_storage->toArray();
|
||||
$config['deleted'] = TRUE;
|
||||
$config['bundles'] = $field_storage->getBundles();
|
||||
$deleted_storages[$field_storage->uuid()] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
$state->set('field.storage.deleted', $deleted_storages);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $fields) {
|
||||
// Notify the storage.
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
\Drupal::entityManager()->onFieldStorageDefinitionDelete($field);
|
||||
$field->deleted = TRUE;
|
||||
}
|
||||
}
|
||||
// Unset static flag.
|
||||
static::$inDeletion = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSchema() {
|
||||
if (!isset($this->schema)) {
|
||||
// Get the schema from the field item class.
|
||||
$class = $this->getFieldItemClass();
|
||||
$schema = $class::schema($this);
|
||||
// Fill in default values for optional entries.
|
||||
$schema += array(
|
||||
'unique keys' => array(),
|
||||
'indexes' => array(),
|
||||
'foreign keys' => array(),
|
||||
);
|
||||
|
||||
// Merge custom indexes with those specified by the field type. Custom
|
||||
// indexes prevail.
|
||||
$schema['indexes'] = $this->indexes + $schema['indexes'];
|
||||
|
||||
$this->schema = $schema;
|
||||
}
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCustomStorage() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isBaseField() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getColumns() {
|
||||
$schema = $this->getSchema();
|
||||
// A typical use case for the method is to iterate on the columns, while
|
||||
// some other use cases rely on identifying the first column with the key()
|
||||
// function. Since the schema is persisted in the Field object, we take care
|
||||
// of resetting the array pointer so that the former does not interfere with
|
||||
// the latter.
|
||||
reset($schema['columns']);
|
||||
return $schema['columns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBundles() {
|
||||
if (!$this->isDeleted()) {
|
||||
$map = \Drupal::entityManager()->getFieldMap();
|
||||
if (isset($map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'])) {
|
||||
return $map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'];
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->field_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeleted() {
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeProvider() {
|
||||
return $this->module;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSettings() {
|
||||
// @todo FieldTypePluginManager maintains its own static cache. However, do
|
||||
// some CPU and memory profiling to see if it's worth statically caching
|
||||
// $field_type_info, or the default field storage and field settings,
|
||||
// within $this.
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
$settings = $field_type_manager->getDefaultStorageSettings($this->getType());
|
||||
return $this->settings + $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSetting($setting_name) {
|
||||
// @todo See getSettings() about potentially statically caching this.
|
||||
// We assume here that one call to array_key_exists() is more efficient
|
||||
// than calling getSettings() when all we need is a single setting.
|
||||
if (array_key_exists($setting_name, $this->settings)) {
|
||||
return $this->settings[$setting_name];
|
||||
}
|
||||
$settings = $this->getSettings();
|
||||
if (array_key_exists($setting_name, $settings)) {
|
||||
return $settings[$setting_name];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSetting($setting_name, $value) {
|
||||
$this->settings[$setting_name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSettings(array $settings) {
|
||||
$this->settings = $settings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTranslatable() {
|
||||
return $this->translatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRevisionable() {
|
||||
// All configurable fields are revisionable.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTranslatable($translatable) {
|
||||
$this->translatable = $translatable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProvider() {
|
||||
return 'field';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCardinality() {
|
||||
return $this->cardinality;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCardinality($cardinality) {
|
||||
$this->cardinality = $cardinality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
|
||||
// If the field item class implements the interface, create an orphaned
|
||||
// runtime item object, so that it can be used as the options provider
|
||||
// without modifying the entity being worked on.
|
||||
if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) {
|
||||
$items = $entity->get($this->getName());
|
||||
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
|
||||
}
|
||||
// @todo: Allow setting custom options provider, see
|
||||
// https://www.drupal.org/node/2002138.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isMultiple() {
|
||||
$cardinality = $this->getCardinality();
|
||||
return ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLocked($locked) {
|
||||
$this->locked = $locked;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTargetEntityTypeId() {
|
||||
return $this->entity_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isQueryable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a field has any data.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field has data for any entity; FALSE otherwise.
|
||||
*/
|
||||
public function hasData() {
|
||||
return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the magic __sleep() method.
|
||||
*
|
||||
* Using the Serialize interface and serialize() / unserialize() methods
|
||||
* breaks entity forms in PHP 5.4.
|
||||
* @todo Investigate in https://www.drupal.org/node/2074253.
|
||||
*/
|
||||
public function __sleep() {
|
||||
// Only serialize necessary properties, excluding those that can be
|
||||
// recalculated.
|
||||
$properties = get_object_vars($this);
|
||||
unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
|
||||
return array_keys($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraint($constraint_name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinition($name) {
|
||||
if (!isset($this->propertyDefinitions)) {
|
||||
$this->getPropertyDefinitions();
|
||||
}
|
||||
if (isset($this->propertyDefinitions[$name])) {
|
||||
return $this->propertyDefinitions[$name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinitions() {
|
||||
if (!isset($this->propertyDefinitions)) {
|
||||
$class = $this->getFieldItemClass();
|
||||
$this->propertyDefinitions = $class::propertyDefinitions($this);
|
||||
}
|
||||
return $this->propertyDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyNames() {
|
||||
return array_keys($this->getPropertyDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMainPropertyName() {
|
||||
$class = $this->getFieldItemClass();
|
||||
return $class::mainPropertyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUniqueStorageIdentifier() {
|
||||
return $this->uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to retrieve the field item class.
|
||||
*/
|
||||
protected function getFieldItemClass() {
|
||||
$type_definition = \Drupal::typedDataManager()
|
||||
->getDefinition('field_item:' . $this->getType());
|
||||
return $type_definition['class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a field config entity based on the entity type and field name.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* ID of the entity type.
|
||||
* @param string $field_name
|
||||
* Name of the field.
|
||||
*
|
||||
* @return static
|
||||
* The field config entity if one exists for the provided field name,
|
||||
* otherwise NULL.
|
||||
*/
|
||||
public static function loadByName($entity_type_id, $field_name) {
|
||||
return \Drupal::entityManager()->getStorage('field_storage_config')->load($entity_type_id . '.' . $field_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeletable() {
|
||||
// The field storage is not deleted, is configured to be removed when there
|
||||
// are no fields, the field storage has no bundles, and field storages are
|
||||
// not in the process of being deleted.
|
||||
return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIndexes() {
|
||||
return $this->indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setIndexes(array $indexes) {
|
||||
$this->indexes = $indexes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
38
core/modules/field/src/FieldConfigAccessControlHandler.php
Normal file
38
core/modules/field/src/FieldConfigAccessControlHandler.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldConfigAccessControlHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the field entity type.
|
||||
*
|
||||
* @see \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
class FieldConfigAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
|
||||
if ($operation == 'delete') {
|
||||
$field_storage_entity = $entity->getFieldStorageDefinition();
|
||||
if ($field_storage_entity->isLocked()) {
|
||||
return AccessResult::forbidden()->cacheUntilEntityChanges($field_storage_entity);
|
||||
}
|
||||
else {
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' fields')->cacheUntilEntityChanges($field_storage_entity);
|
||||
}
|
||||
}
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' fields');
|
||||
}
|
||||
|
||||
}
|
26
core/modules/field/src/FieldConfigInterface.php
Normal file
26
core/modules/field/src/FieldConfigInterface.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldConfigInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a field entity.
|
||||
*/
|
||||
interface FieldConfigInterface extends ConfigEntityInterface, FieldDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Gets the deleted flag of the field.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the field is deleted.
|
||||
*/
|
||||
public function isDeleted();
|
||||
|
||||
}
|
185
core/modules/field/src/FieldConfigStorage.php
Normal file
185
core/modules/field/src/FieldConfigStorage.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldConfigStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldConfigStorageBase;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
|
||||
/**
|
||||
* Controller class for fields.
|
||||
*/
|
||||
class FieldConfigStorage extends FieldConfigStorageBase {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The state keyvalue collection.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldConfigStorage object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
|
||||
* The UUID service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager);
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->state = $state;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('config.factory'),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('state'),
|
||||
$container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importDelete($name, Config $new_config, Config $old_config) {
|
||||
// If the field storage has been deleted in the same import, the field will
|
||||
// be deleted by then, and there is nothing left to do. Just return TRUE so
|
||||
// that the file does not get written to active store.
|
||||
if (!$old_config->get()) {
|
||||
return TRUE;
|
||||
}
|
||||
return parent::importDelete($name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadByProperties(array $conditions = array()) {
|
||||
// Include deleted fields if specified in the $conditions parameters.
|
||||
$include_deleted = isset($conditions['include_deleted']) ? $conditions['include_deleted'] : FALSE;
|
||||
unset($conditions['include_deleted']);
|
||||
|
||||
$fields = array();
|
||||
|
||||
// Get fields stored in configuration. If we are explicitly looking for
|
||||
// deleted fields only, this can be skipped, because they will be
|
||||
// retrieved from state below.
|
||||
if (empty($conditions['deleted'])) {
|
||||
if (isset($conditions['entity_type']) && isset($conditions['bundle']) && isset($conditions['field_name'])) {
|
||||
// Optimize for the most frequent case where we do have a specific ID.
|
||||
$id = $conditions['entity_type'] . '.' . $conditions['bundle'] . '.' . $conditions['field_name'];
|
||||
$fields = $this->loadMultiple(array($id));
|
||||
}
|
||||
else {
|
||||
// No specific ID, we need to examine all existing fields.
|
||||
$fields = $this->loadMultiple();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge deleted fields (stored in state) if needed.
|
||||
if ($include_deleted || !empty($conditions['deleted'])) {
|
||||
$deleted_fields = $this->state->get('field.field.deleted') ?: array();
|
||||
$deleted_storages = $this->state->get('field.storage.deleted') ?: array();
|
||||
foreach ($deleted_fields as $id => $config) {
|
||||
// If the field storage itself is deleted, inject it directly in the field.
|
||||
if (isset($deleted_storages[$config['field_storage_uuid']])) {
|
||||
$config['field_storage'] = $this->entityManager->getStorage('field_storage_config')->create($deleted_storages[$config['field_storage_uuid']]);
|
||||
}
|
||||
$fields[$id] = $this->create($config);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect matching fields.
|
||||
$matching_fields = array();
|
||||
foreach ($fields as $field) {
|
||||
// Some conditions are checked against the field storage.
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
|
||||
// Only keep the field if it matches all conditions.
|
||||
foreach ($conditions as $key => $value) {
|
||||
// Extract the actual value against which the condition is checked.
|
||||
switch ($key) {
|
||||
case 'field_name':
|
||||
$checked_value = $field_storage->getName();
|
||||
break;
|
||||
|
||||
case 'field_id':
|
||||
case 'field_storage_uuid':
|
||||
$checked_value = $field_storage->uuid();
|
||||
break;
|
||||
|
||||
case 'uuid';
|
||||
$checked_value = $field->uuid();
|
||||
break;
|
||||
|
||||
case 'deleted';
|
||||
$checked_value = $field->isDeleted();
|
||||
break;
|
||||
|
||||
default:
|
||||
$checked_value = $field->get($key);
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip to the next field as soon as one condition does not match.
|
||||
if ($checked_value != $value) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// When returning deleted fields, key the results by UUID since they
|
||||
// can include several fields with the same ID.
|
||||
$key = $include_deleted ? $field->uuid() : $field->id();
|
||||
$matching_fields[$key] = $field;
|
||||
}
|
||||
|
||||
return $matching_fields;
|
||||
}
|
||||
|
||||
}
|
136
core/modules/field/src/FieldStorageConfigInterface.php
Normal file
136
core/modules/field/src/FieldStorageConfigInterface.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldStorageConfigInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a field storage entity.
|
||||
*/
|
||||
interface FieldStorageConfigInterface extends ConfigEntityInterface, FieldStorageDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Returns the field type.
|
||||
*
|
||||
* @return string
|
||||
* The field type, i.e. the id of a field type plugin. For example 'text'.
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Returns the name of the module providing the field type.
|
||||
*
|
||||
* @return string
|
||||
* The name of the module that provides the field type.
|
||||
*/
|
||||
public function getTypeProvider();
|
||||
|
||||
/**
|
||||
* Returns the list of bundles where the field storage has fields.
|
||||
*
|
||||
* @return array
|
||||
* An array of bundle names.
|
||||
*/
|
||||
public function getBundles();
|
||||
|
||||
/**
|
||||
* Returns whether the field is deleted or not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field is deleted.
|
||||
*/
|
||||
public function isDeleted();
|
||||
|
||||
/**
|
||||
* Checks if the field storage can be deleted.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field storage can be deleted.
|
||||
*/
|
||||
public function isDeletable();
|
||||
|
||||
/**
|
||||
* Returns whether the field storage is locked or not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field storage is locked.
|
||||
*/
|
||||
public function isLocked();
|
||||
|
||||
/**
|
||||
* Sets the locked flag.
|
||||
*
|
||||
* @param bool $locked
|
||||
* Sets value of locked flag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocked($locked);
|
||||
|
||||
/**
|
||||
* Sets the maximum number of items allowed for the field.
|
||||
*
|
||||
* @param int $cardinality
|
||||
* The cardinality value.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCardinality($cardinality);
|
||||
|
||||
/**
|
||||
* Sets the value for a field setting by name.
|
||||
*
|
||||
* @param string $setting_name
|
||||
* The name of the setting.
|
||||
* @param mixed $value
|
||||
* The value of the setting.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSetting($setting_name, $value);
|
||||
|
||||
/**
|
||||
* Sets field settings (overwrites existing settings).
|
||||
*
|
||||
* @param array $settings
|
||||
* The array of field settings.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSettings(array $settings);
|
||||
|
||||
/**
|
||||
* Sets whether the field is translatable.
|
||||
*
|
||||
* @param bool $translatable
|
||||
* Whether the field is translatable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTranslatable($translatable);
|
||||
|
||||
/**
|
||||
* Returns the custom storage indexes for the field data storage.
|
||||
*
|
||||
* @return array
|
||||
* An array of custom indexes.
|
||||
*/
|
||||
public function getIndexes();
|
||||
|
||||
/**
|
||||
* Sets the custom storage indexes for the field data storage..
|
||||
*
|
||||
* @param array $indexes
|
||||
* The array of custom indexes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIndexes(array $indexes);
|
||||
|
||||
}
|
175
core/modules/field/src/FieldStorageConfigStorage.php
Normal file
175
core/modules/field/src/FieldStorageConfigStorage.php
Normal file
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldStorageConfigStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
|
||||
/**
|
||||
* Controller class for "field storage" configuration entities.
|
||||
*/
|
||||
class FieldStorageConfigStorage extends ConfigEntityStorage {
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The state keyvalue collection.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldStorageConfigStorage object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
|
||||
* The UUID service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface\FieldTypePluginManagerInterface
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager);
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->state = $state;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('config.factory'),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('state'),
|
||||
$container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadByProperties(array $conditions = array()) {
|
||||
// Include deleted fields if specified in the $conditions parameters.
|
||||
$include_deleted = isset($conditions['include_deleted']) ? $conditions['include_deleted'] : FALSE;
|
||||
unset($conditions['include_deleted']);
|
||||
|
||||
/** @var \Drupal\field\FieldStorageConfigInterface[] $storages */
|
||||
$storages = array();
|
||||
|
||||
// Get field storages living in configuration. If we are explicitly looking
|
||||
// for deleted storages only, this can be skipped, because they will be
|
||||
// retrieved from state below.
|
||||
if (empty($conditions['deleted'])) {
|
||||
if (isset($conditions['entity_type']) && isset($conditions['field_name'])) {
|
||||
// Optimize for the most frequent case where we do have a specific ID.
|
||||
$id = $conditions['entity_type'] . $conditions['field_name'];
|
||||
$storages = $this->loadMultiple(array($id));
|
||||
}
|
||||
else {
|
||||
// No specific ID, we need to examine all existing storages.
|
||||
$storages = $this->loadMultiple();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge deleted field storages (living in state) if needed.
|
||||
if ($include_deleted || !empty($conditions['deleted'])) {
|
||||
$deleted_storages = $this->state->get('field.storage.deleted') ?: array();
|
||||
foreach ($deleted_storages as $id => $config) {
|
||||
$storages[$id] = $this->create($config);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect matching fields.
|
||||
$matches = array();
|
||||
foreach ($storages as $field) {
|
||||
foreach ($conditions as $key => $value) {
|
||||
// Extract the actual value against which the condition is checked.
|
||||
$checked_value = $field->get($key);
|
||||
// Skip to the next field as soon as one condition does not match.
|
||||
if ($checked_value != $value) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// When returning deleted fields, key the results by UUID since they can
|
||||
// include several fields with the same ID.
|
||||
$key = $include_deleted ? $field->uuid() : $field->id();
|
||||
$matches[$key] = $field;
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapFromStorageRecords(array $records) {
|
||||
foreach ($records as &$record) {
|
||||
$class = $this->fieldTypeManager->getPluginClass($record['type']);
|
||||
$record['settings'] = $class::storageSettingsFromConfigData($record['settings']);
|
||||
}
|
||||
return parent::mapFromStorageRecords($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapToStorageRecord(EntityInterface $entity) {
|
||||
$record = parent::mapToStorageRecord($entity);
|
||||
$class = $this->fieldTypeManager->getPluginClass($record['type']);
|
||||
$record['settings'] = $class::storageSettingsToConfigData($record['settings']);
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldStorageConfigUpdateForbiddenException.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Field\FieldException;
|
||||
|
||||
/**
|
||||
* Exception class thrown by hook_field_storage_config_update_forbid().
|
||||
*/
|
||||
class FieldStorageConfigUpdateForbiddenException extends FieldException {}
|
80
core/modules/field/src/FieldUninstallValidator.php
Normal file
80
core/modules/field/src/FieldUninstallValidator.php
Normal file
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\FieldUninstallValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Prevents uninstallation of modules providing active field storage.
|
||||
*/
|
||||
class FieldUninstallValidator implements ModuleUninstallValidatorInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The field storage config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $fieldStorageConfigStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldUninstallValidator.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) {
|
||||
$this->fieldStorageConfigStorage = $entity_manager->getStorage('field_storage_config');
|
||||
$this->stringTranslation = $string_translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($module) {
|
||||
$reasons = [];
|
||||
if ($field_storages = $this->getFieldStoragesByModule($module)) {
|
||||
// Provide an explanation message (only mention pending deletions if there
|
||||
// remain no actual, non-deleted fields.)
|
||||
$non_deleted = FALSE;
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!$field_storage->isDeleted()) {
|
||||
$non_deleted = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($non_deleted) {
|
||||
$reasons[] = $this->t('Fields type(s) in use');
|
||||
}
|
||||
else {
|
||||
$reasons[] = $this->t('Fields pending deletion');
|
||||
}
|
||||
}
|
||||
return $reasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all field storages for a specified module.
|
||||
*
|
||||
* @param string $module
|
||||
* The module to filter field storages by.
|
||||
*
|
||||
* @return \Drupal\field\FieldStorageConfigInterface[]
|
||||
* An array of field storages for a specified module.
|
||||
*/
|
||||
protected function getFieldStoragesByModule($module) {
|
||||
return $this->fieldStorageConfigStorage->loadByProperties(['module' => $module, 'include_deleted' => TRUE]);
|
||||
}
|
||||
|
||||
}
|
185
core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
Normal file
185
core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Boolean\BooleanFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests boolean field functionality.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_test', 'field_ui', 'options');
|
||||
|
||||
/**
|
||||
* A field to use in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field used in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer entity_test form display',
|
||||
'administer entity_test fields',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests boolean field.
|
||||
*/
|
||||
function testBooleanField() {
|
||||
$on = $this->randomMachineName();
|
||||
$off = $this->randomMachineName();
|
||||
$label = $this->randomMachineName();
|
||||
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'boolean',
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $label,
|
||||
'required' => TRUE,
|
||||
'settings' => array(
|
||||
'on_label' => $on,
|
||||
'off_label' => $off,
|
||||
),
|
||||
));
|
||||
$this->field->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean_checkbox',
|
||||
))
|
||||
->save();
|
||||
// Create a display for the full view mode.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[value]", '', 'Widget found.');
|
||||
$this->assertRaw($on);
|
||||
|
||||
// Submit and ensure it is accepted.
|
||||
$edit = array(
|
||||
"{$field_name}[value]" => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
|
||||
|
||||
// Verify that boolean value is displayed.
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertRaw('<div class="field-item">' . $on . '</div>');
|
||||
|
||||
// Test if we can change the on label.
|
||||
$on = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'settings[on_label]' => $on,
|
||||
);
|
||||
$this->drupalPostForm('entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field_name, $edit, t('Save settings'));
|
||||
// Check if we see the updated labels in the creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertRaw($on);
|
||||
|
||||
// Test the display_label option.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => array(
|
||||
'display_label' => TRUE,
|
||||
)
|
||||
))
|
||||
->save();
|
||||
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[value]", '', 'Widget found.');
|
||||
$this->assertNoRaw($on);
|
||||
$this->assertText($this->field->label());
|
||||
|
||||
// Go to the form display page and check if the default settings works as
|
||||
// expected.
|
||||
$fieldEditUrl = 'entity_test/structure/entity_test/form-display';
|
||||
$this->drupalGet($fieldEditUrl);
|
||||
|
||||
// Click on the widget settings button to open the widget settings form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), $field_name . "_settings_edit");
|
||||
|
||||
$this->assertText(
|
||||
'Use field label instead of the "On label" as label',
|
||||
t('Display setting checkbox available.')
|
||||
);
|
||||
|
||||
// Enable setting.
|
||||
$edit = array('fields[' . $field_name . '][settings_edit_form][settings][display_label]' => 1);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, $field_name . "_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, NULL, 'Save');
|
||||
|
||||
// Go again to the form display page and check if the setting
|
||||
// is stored and has the expected effect.
|
||||
$this->drupalGet($fieldEditUrl);
|
||||
$this->assertText('Use field label: Yes', 'Checking the display settings checkbox updated the value.');
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), $field_name . "_settings_edit");
|
||||
$this->assertText(
|
||||
'Use field label instead of the "On label" as label',
|
||||
t('Display setting checkbox is available')
|
||||
);
|
||||
$this->assertFieldByXPath(
|
||||
'*//input[starts-with(@id, "edit-fields-' . $field_name . '-settings-edit-form-settings-display-label") and @value="1"]',
|
||||
TRUE,
|
||||
t('Display label changes label of the checkbox')
|
||||
);
|
||||
|
||||
// Test the boolean field settings.
|
||||
$this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field_name);
|
||||
$this->assertFieldById('edit-settings-on-label', $on);
|
||||
$this->assertFieldById('edit-settings-off-label', $off);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Boolean\BooleanFormatterSettingsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Boolean field formatter settings.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanFormatterSettingsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'field_ui', 'text', 'node', 'user'];
|
||||
|
||||
/**
|
||||
* The name of the entity bundle that is created in the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* The name of the Boolean field to use for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a content type. Use Node because it has Field UI pages that work.
|
||||
$type_name = Unicode::strtolower($this->randomMachineName(8)) . '_test';
|
||||
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
|
||||
$this->bundle = $type->id();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node display', 'bypass node access', 'administer nodes'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName(8));
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
]);
|
||||
$instance->save();
|
||||
|
||||
$display = entity_get_display('node', $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'boolean',
|
||||
'settings' => [],
|
||||
]);
|
||||
$display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formatter settings page for the Boolean formatter.
|
||||
*/
|
||||
function testBooleanFormatterSettings() {
|
||||
// List the options we expect to see on the settings form. Omit the one
|
||||
// with the Unicode check/x characters, which does not appear to work
|
||||
// well in WebTestBase.
|
||||
$options = array(
|
||||
'Yes / No',
|
||||
'True / False',
|
||||
'On / Off',
|
||||
'Enabled / Disabled',
|
||||
'1 / 0',
|
||||
'Custom',
|
||||
);
|
||||
|
||||
// Define what the "default" option should look like, depending on the
|
||||
// field settings.
|
||||
$default = 'Field settings (@on / @off)';
|
||||
|
||||
// For several different values of the field settings, test that the
|
||||
// options, including default, are shown correctly.
|
||||
$settings = array(
|
||||
array('Yes', 'No'),
|
||||
array('On', 'Off'),
|
||||
array('TRUE', 'FALSE'),
|
||||
);
|
||||
|
||||
foreach ($settings as $values) {
|
||||
// Set up the field settings.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->bundle . '/fields/node.' . $this->bundle . '.' . $this->fieldName);
|
||||
$this->drupalPostForm(NULL, array(
|
||||
'settings[on_label]' => $values[0],
|
||||
'settings[off_label]' => $values[1],
|
||||
), 'Save settings');
|
||||
|
||||
// Open the Manage Display page and trigger the field settings form.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->bundle . '/display');
|
||||
$this->drupalPostAjaxForm(NULL, array(), $this->fieldName . '_settings_edit');
|
||||
|
||||
// Test that the settings options are present in the correct format.
|
||||
foreach ($options as $string) {
|
||||
$this->assertText($string);
|
||||
}
|
||||
$this->assertText(SafeMarkup::format($default, array('@on' => $values[0], '@off' => $values[1])));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
144
core/modules/field/src/Tests/Boolean/BooleanFormatterTest.php
Normal file
144
core/modules/field/src/Tests/Boolean/BooleanFormatterTest.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Boolean\BooleanFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the boolean formatter.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanFormatterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'text', 'entity_test', 'user'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['field']);
|
||||
$this->installEntitySchema('entity_test');
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
]);
|
||||
$instance->save();
|
||||
|
||||
$this->display = entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'boolean',
|
||||
'settings' => [],
|
||||
]);
|
||||
$this->display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders fields of a given entity with a given display.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object with attached fields to render.
|
||||
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
|
||||
* The display to render the fields in.
|
||||
*
|
||||
* @return string
|
||||
* The rendered entity fields.
|
||||
*/
|
||||
protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
|
||||
$content = $display->build($entity);
|
||||
$content = $this->render($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests boolean formatter output.
|
||||
*/
|
||||
public function testBooleanFormatter() {
|
||||
$data = [];
|
||||
$data[] = [0, [], 'Off'];
|
||||
$data[] = [1, [], 'On'];
|
||||
|
||||
$format = ['format' => 'enabled-disabled'];
|
||||
$data[] = [0, $format, 'Disabled'];
|
||||
$data[] = [1, $format, 'Enabled'];
|
||||
|
||||
$format = ['format' => 'unicode-yes-no'];
|
||||
$data[] = [1, $format, '✔'];
|
||||
$data[] = [0, $format, '✖'];
|
||||
|
||||
$format = [
|
||||
'format' => 'custom',
|
||||
'format_custom_false' => 'FALSE',
|
||||
'format_custom_true' => 'TRUE'
|
||||
];
|
||||
$data[] = [0, $format, 'FALSE'];
|
||||
$data[] = [1, $format, 'TRUE'];
|
||||
|
||||
foreach ($data as $test_data) {
|
||||
list($value, $settings, $expected) = $test_data;
|
||||
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['settings'] = $settings;
|
||||
$this->display->setComponent($this->fieldName, $component);
|
||||
|
||||
$entity = EntityTest::create([]);
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
// Verify that all HTML is escaped and newlines are retained.
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertRaw($expected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
82
core/modules/field/src/Tests/Boolean/BooleanItemTest.php
Normal file
82
core/modules/field/src/Tests/Boolean/BooleanItemTest.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Boolean\BooleanItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\field\Tests\FieldUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the boolean field type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanItemTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a boolean field and storage for validation.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_boolean',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'boolean',
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_boolean',
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent('field_boolean', array(
|
||||
'type' => 'boolean_checkbox',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the boolean field type.
|
||||
*/
|
||||
public function testBooleanItem() {
|
||||
// Verify entity creation.
|
||||
$entity = entity_create('entity_test');
|
||||
$value = '1';
|
||||
$entity->field_boolean = $value;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->field_boolean instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_boolean[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_boolean->value, $value);
|
||||
$this->assertEqual($entity->field_boolean[0]->value, $value);
|
||||
|
||||
// Verify changing the boolean value.
|
||||
$new_value = 0;
|
||||
$entity->field_boolean->value = $new_value;
|
||||
$this->assertEqual($entity->field_boolean->value, $new_value);
|
||||
|
||||
// Read changed entity and assert changed values.
|
||||
$entity->save();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_boolean->value, $new_value);
|
||||
|
||||
// Test sample item generation.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_boolean->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
}
|
||||
|
||||
}
|
361
core/modules/field/src/Tests/BulkDeleteTest.php
Normal file
361
core/modules/field/src/Tests/BulkDeleteTest.php
Normal file
|
@ -0,0 +1,361 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\BulkDeleteTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Bulk delete storages and fields, and clean up afterwards.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BulkDeleteTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* The fields to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorages;
|
||||
|
||||
/**
|
||||
* The entities to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* The entities to use in this test, keyed by bundle.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entitiesByBundles;
|
||||
|
||||
/**
|
||||
* The bundles for the entities used in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $bundles;
|
||||
|
||||
/**
|
||||
* The entity type to be used in the test classes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId = 'entity_test';
|
||||
|
||||
/**
|
||||
* Tests that the expected hooks have been invoked on the expected entities.
|
||||
*
|
||||
* @param $expected_hooks
|
||||
* An array keyed by hook name, with one entry per expected invocation.
|
||||
* Each entry is the value of the "$entity" parameter the hook is expected
|
||||
* to have been passed.
|
||||
* @param $actual_hooks
|
||||
* The array of actual hook invocations recorded by field_test_memorize().
|
||||
*/
|
||||
function checkHooksInvocations($expected_hooks, $actual_hooks) {
|
||||
foreach ($expected_hooks as $hook => $invocations) {
|
||||
$actual_invocations = $actual_hooks[$hook];
|
||||
|
||||
// Check that the number of invocations is correct.
|
||||
$this->assertEqual(count($actual_invocations), count($invocations), "$hook() was called the expected number of times.");
|
||||
|
||||
// Check that the hook was called for each expected argument.
|
||||
foreach ($invocations as $argument) {
|
||||
$found = FALSE;
|
||||
foreach ($actual_invocations as $actual_arguments) {
|
||||
// The argument we are looking for is either an array of entities as
|
||||
// the second argument or a single entity object as the first.
|
||||
if ($argument instanceof EntityInterface && $actual_arguments[0]->id() == $argument->id()) {
|
||||
$found = TRUE;
|
||||
break;
|
||||
}
|
||||
// In case of an array, compare the array size and make sure it
|
||||
// contains the same elements.
|
||||
elseif (is_array($argument) && count($actual_arguments[1]) == count($argument) && count(array_diff_key($actual_arguments[1], $argument)) == 0) {
|
||||
$found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found, "$hook() was called on expected argument");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldStorages = array();
|
||||
$this->entities = array();
|
||||
$this->entitiesByBundles = array();
|
||||
|
||||
// Create two bundles.
|
||||
$this->bundles = array('bb_1' => 'bb_1', 'bb_2' => 'bb_2');
|
||||
foreach ($this->bundles as $name => $desc) {
|
||||
entity_test_create_bundle($name, $desc);
|
||||
}
|
||||
|
||||
// Create two field storages.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'bf_1',
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1
|
||||
));
|
||||
$field_storage->save();
|
||||
$this->fieldStorages[] = $field_storage;
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'bf_2',
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4
|
||||
));
|
||||
$field_storage->save();
|
||||
$this->fieldStorages[] = $field_storage;
|
||||
|
||||
// For each bundle, create each field, and 10 entities with values for the
|
||||
// fields.
|
||||
foreach ($this->bundles as $bundle) {
|
||||
foreach ($this->fieldStorages as $field_storage) {
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
))->save();
|
||||
}
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$entity = entity_create($this->entityTypeId, array('type' => $bundle));
|
||||
foreach ($this->fieldStorages as $field_storage) {
|
||||
$entity->{$field_storage->getName()}->setValue($this->_generateTestFieldValues($field_storage->getCardinality()));
|
||||
}
|
||||
$entity->save();
|
||||
}
|
||||
}
|
||||
$this->entities = entity_load_multiple($this->entityTypeId);
|
||||
foreach ($this->entities as $entity) {
|
||||
// This test relies on the entities having stale field definitions
|
||||
// so that the deleted field can be accessed on them. Access the field
|
||||
// now, so that they are always loaded.
|
||||
$entity->bf_1->value;
|
||||
|
||||
// Also keep track of the entities per bundle.
|
||||
$this->entitiesByBundles[$entity->bundle()][$entity->id()] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that deleting a field leaves the field data items in the database
|
||||
* and that the appropriate Field API functions can operate on the deleted
|
||||
* data and field definition.
|
||||
*
|
||||
* This tests how EntityFieldQuery interacts with field deletion and could be
|
||||
* moved to FieldCrudTestCase, but depends on this class's setUp().
|
||||
*/
|
||||
function testDeleteField() {
|
||||
$bundle = reset($this->bundles);
|
||||
$field_storage = reset($this->fieldStorages);
|
||||
$field_name = $field_storage->getName();
|
||||
$factory = \Drupal::service('entity.query');
|
||||
|
||||
// There are 10 entities of this bundle.
|
||||
$found = $factory->get('entity_test')
|
||||
->condition('type', $bundle)
|
||||
->execute();
|
||||
$this->assertEqual(count($found), 10, 'Correct number of entities found before deleting');
|
||||
|
||||
// Delete the field.
|
||||
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
|
||||
$field->delete();
|
||||
|
||||
// The field still exists, deleted.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('field_storage_uuid' => $field_storage->uuid(), 'deleted' => TRUE, 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($fields), 1, 'There is one deleted field');
|
||||
$field = $fields[$field->uuid()];
|
||||
$this->assertEqual($field->getTargetBundle(), $bundle, 'The deleted field is for the correct bundle');
|
||||
|
||||
// Check that the actual stored content did not change during delete.
|
||||
$storage = \Drupal::entityManager()->getStorage($this->entityTypeId);
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $storage->getTableMapping();
|
||||
$table = $table_mapping->getDedicatedDataTableName($field_storage);
|
||||
$column = $table_mapping->getFieldColumnName($field_storage, 'value');
|
||||
$result = db_select($table, 't')
|
||||
->fields('t')
|
||||
->execute();
|
||||
foreach ($result as $row) {
|
||||
$this->assertEqual($this->entities[$row->entity_id]->{$field_name}->value, $row->$column);
|
||||
}
|
||||
|
||||
// There are 0 entities of this bundle with non-deleted data.
|
||||
$found = $factory->get('entity_test')
|
||||
->condition('type', $bundle)
|
||||
->condition("$field_name.deleted", 0)
|
||||
->execute();
|
||||
$this->assertFalse($found, 'No entities found after deleting');
|
||||
|
||||
// There are 10 entities of this bundle when deleted fields are allowed, and
|
||||
// their values are correct.
|
||||
$found = $factory->get('entity_test')
|
||||
->condition('type', $bundle)
|
||||
->condition("$field_name.deleted", 1)
|
||||
->sort('id')
|
||||
->execute();
|
||||
$this->assertEqual(count($found), 10, 'Correct number of entities found after deleting');
|
||||
$this->assertFalse(array_diff($found, array_keys($this->entities)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that field data items and fields are purged when a field storage is
|
||||
* deleted.
|
||||
*/
|
||||
function testPurgeField() {
|
||||
// Start recording hook invocations.
|
||||
field_test_memorize();
|
||||
|
||||
$bundle = reset($this->bundles);
|
||||
$field_storage = reset($this->fieldStorages);
|
||||
$field_name = $field_storage->getName();
|
||||
|
||||
// Delete the field.
|
||||
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
|
||||
$field->delete();
|
||||
|
||||
// No field hooks were called.
|
||||
$mem = field_test_memorize();
|
||||
$this->assertEqual(count($mem), 0, 'No field hooks were called');
|
||||
|
||||
$batch_size = 2;
|
||||
for ($count = 8; $count >= 0; $count -= $batch_size) {
|
||||
// Purge two entities.
|
||||
field_purge_batch($batch_size);
|
||||
|
||||
// There are $count deleted entities left.
|
||||
$found = \Drupal::entityQuery('entity_test')
|
||||
->condition('type', $bundle)
|
||||
->condition($field_name . '.deleted', 1)
|
||||
->execute();
|
||||
$this->assertEqual(count($found), $count, 'Correct number of entities found after purging 2');
|
||||
}
|
||||
|
||||
// Check hooks invocations.
|
||||
// FieldItemInterface::delete() should have been called once for each entity in the
|
||||
// bundle.
|
||||
$actual_hooks = field_test_memorize();
|
||||
$hooks = array();
|
||||
$entities = $this->entitiesByBundles[$bundle];
|
||||
foreach ($entities as $id => $entity) {
|
||||
$hooks['field_test_field_delete'][] = $entity;
|
||||
}
|
||||
$this->checkHooksInvocations($hooks, $actual_hooks);
|
||||
|
||||
// The field still exists, deleted.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('field_storage_uuid' => $field_storage->uuid(), 'deleted' => TRUE, 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($fields), 1, 'There is one deleted field');
|
||||
|
||||
// Purge the field.
|
||||
field_purge_batch($batch_size);
|
||||
|
||||
// The field is gone.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('field_storage_uuid' => $field_storage->uuid(), 'deleted' => TRUE, 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($fields), 0, 'The field is gone');
|
||||
|
||||
// The field storage still exists, not deleted, because it has a second
|
||||
// field.
|
||||
$storages = entity_load_multiple_by_properties('field_storage_config', array('uuid' => $field_storage->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertTrue(isset($storages[$field_storage->uuid()]), 'The field storage exists and is not deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that field storages are preserved and purged correctly as multiple
|
||||
* fields are deleted and purged.
|
||||
*/
|
||||
function testPurgeFieldStorage() {
|
||||
// Start recording hook invocations.
|
||||
field_test_memorize();
|
||||
|
||||
$field_storage = reset($this->fieldStorages);
|
||||
$field_name = $field_storage->getName();
|
||||
|
||||
// Delete the first field.
|
||||
$bundle = reset($this->bundles);
|
||||
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
|
||||
$field->delete();
|
||||
|
||||
// Assert that FieldItemInterface::delete() was not called yet.
|
||||
$mem = field_test_memorize();
|
||||
$this->assertEqual(count($mem), 0, 'No field hooks were called.');
|
||||
|
||||
// Purge the data.
|
||||
field_purge_batch(10);
|
||||
|
||||
// Check hooks invocations.
|
||||
// FieldItemInterface::delete() should have been called once for each entity in the
|
||||
// bundle.
|
||||
$actual_hooks = field_test_memorize();
|
||||
$hooks = array();
|
||||
$entities = $this->entitiesByBundles[$bundle];
|
||||
foreach ($entities as $id => $entity) {
|
||||
$hooks['field_test_field_delete'][] = $entity;
|
||||
}
|
||||
$this->checkHooksInvocations($hooks, $actual_hooks);
|
||||
|
||||
// The field still exists, deleted.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertTrue(isset($fields[$field->uuid()]) && $fields[$field->uuid()]->isDeleted(), 'The field exists and is deleted');
|
||||
|
||||
// Purge again to purge the field.
|
||||
field_purge_batch(0);
|
||||
|
||||
// The field is gone.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($fields), 0, 'The field is purged.');
|
||||
// The field storage still exists, not deleted.
|
||||
$storages = entity_load_multiple_by_properties('field_storage_config', array('uuid' => $field_storage->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertTrue(isset($storages[$field_storage->uuid()]) && !$storages[$field_storage->uuid()]->isDeleted(), 'The field storage exists and is not deleted');
|
||||
|
||||
// Delete the second field.
|
||||
$bundle = next($this->bundles);
|
||||
$field = FieldConfig::loadByName($this->entityTypeId, $bundle, $field_name);
|
||||
$field->delete();
|
||||
|
||||
// Assert that FieldItemInterface::delete() was not called yet.
|
||||
$mem = field_test_memorize();
|
||||
$this->assertEqual(count($mem), 0, 'No field hooks were called.');
|
||||
|
||||
// Purge the data.
|
||||
field_purge_batch(10);
|
||||
|
||||
// Check hooks invocations (same as above, for the 2nd bundle).
|
||||
$actual_hooks = field_test_memorize();
|
||||
$hooks = array();
|
||||
$entities = $this->entitiesByBundles[$bundle];
|
||||
foreach ($entities as $id => $entity) {
|
||||
$hooks['field_test_field_delete'][] = $entity;
|
||||
}
|
||||
$this->checkHooksInvocations($hooks, $actual_hooks);
|
||||
|
||||
// The field and the storage still exist, deleted.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertTrue(isset($fields[$field->uuid()]) && $fields[$field->uuid()]->isDeleted(), 'The field exists and is deleted');
|
||||
$storages = entity_load_multiple_by_properties('field_storage_config', array('uuid' => $field_storage->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertTrue(isset($storages[$field_storage->uuid()]) && $storages[$field_storage->uuid()]->isDeleted(), 'The field storage exists and is deleted');
|
||||
|
||||
// Purge again to purge the field and the storage.
|
||||
field_purge_batch(0);
|
||||
|
||||
// The field and the storage are gone.
|
||||
$fields = entity_load_multiple_by_properties('field_config', array('uuid' => $field->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($fields), 0, 'The field is purged.');
|
||||
$storages = entity_load_multiple_by_properties('field_storage_config', array('uuid' => $field_storage->uuid(), 'include_deleted' => TRUE));
|
||||
$this->assertEqual(count($storages), 0, 'The field storage is purged.');
|
||||
}
|
||||
|
||||
}
|
76
core/modules/field/src/Tests/ConfigFieldDefinitionTest.php
Normal file
76
core/modules/field/src/Tests/ConfigFieldDefinitionTest.php
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\ConfigFieldDefinitionTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Tests exposing field definitions for configurable fields.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class ConfigFieldDefinitionTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* The entity manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface;
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $bundle;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a field and a storage of type 'test_field', on the 'entity_test'
|
||||
// entity type.
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = 'entity_test';
|
||||
$this->createFieldWithStorage('', $this->entityType, $this->bundle);
|
||||
$this->entityManager = $this->container->get('entity.manager');
|
||||
|
||||
// Create a second field on 'entity_test_rev'.
|
||||
$this->createFieldWithStorage('_rev', 'entity_test_rev', 'entity_test_rev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a field definition is exposed for a configurable field.
|
||||
*/
|
||||
public function testBundleFieldDefinition() {
|
||||
$definitions = $this->entityManager->getFieldDefinitions($this->entityType, $this->bundle);
|
||||
$this->assertTrue(isset($definitions[$this->fieldTestData->field->getName()]));
|
||||
$this->assertTrue($definitions[$this->fieldTestData->field->getName()] instanceof FieldDefinitionInterface);
|
||||
// Make sure fields on other entity types are not exposed.
|
||||
$this->assertFalse(isset($definitions[$this->fieldTestData->field_rev->getName()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure a field storage definition is exposed for a configurable field.
|
||||
*/
|
||||
public function testFieldStorageDefinition() {
|
||||
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityType);
|
||||
$this->assertTrue(isset($field_storage_definitions[$this->fieldTestData->field->getName()]));
|
||||
$this->assertTrue($field_storage_definitions[$this->fieldTestData->field->getName()] instanceof FieldStorageDefinitionInterface);
|
||||
// Make sure storages on other entity types are not exposed.
|
||||
$this->assertFalse(isset($field_storage_definitions[$this->fieldTestData->field_rev->getName()]));
|
||||
}
|
||||
|
||||
}
|
305
core/modules/field/src/Tests/DisplayApiTest.php
Normal file
305
core/modules/field/src/Tests/DisplayApiTest.php
Normal file
|
@ -0,0 +1,305 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\DisplayApiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
|
||||
/**
|
||||
* Tests the field display API.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class DisplayApiTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* The field name to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The field label to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
/**
|
||||
* The field cardinality to use in this test.
|
||||
*
|
||||
* @var number
|
||||
*/
|
||||
protected $cardinality;
|
||||
|
||||
/**
|
||||
* The field display options to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $displayOptions;
|
||||
|
||||
/**
|
||||
* The test entity.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* An array of random values, in the format expected for field values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a field and its storage.
|
||||
$this->fieldName = 'test_field';
|
||||
$this->label = $this->randomMachineName();
|
||||
$this->cardinality = 4;
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => $this->cardinality,
|
||||
);
|
||||
$field = array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->label,
|
||||
);
|
||||
|
||||
$this->displayOptions = array(
|
||||
'default' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomMachineName(),
|
||||
),
|
||||
),
|
||||
'teaser' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomMachineName(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $field)->save();
|
||||
// Create a display for the default view mode.
|
||||
entity_get_display($field['entity_type'], $field['bundle'], 'default')
|
||||
->setComponent($this->fieldName, $this->displayOptions['default'])
|
||||
->save();
|
||||
// Create a display for the teaser view mode.
|
||||
EntityViewMode::create(array('id' => 'entity_test.teaser', 'targetEntityType' => 'entity_test'))->save();
|
||||
entity_get_display($field['entity_type'], $field['bundle'], 'teaser')
|
||||
->setComponent($this->fieldName, $this->displayOptions['teaser'])
|
||||
->save();
|
||||
|
||||
// Create an entity with values.
|
||||
$this->values = $this->_generateTestFieldValues($this->cardinality);
|
||||
$this->entity = entity_create('entity_test');
|
||||
$this->entity->{$this->fieldName}->setValue($this->values);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the FieldItemListInterface::view() method.
|
||||
*/
|
||||
function testFieldItemListView() {
|
||||
$items = $this->entity->get($this->fieldName);
|
||||
|
||||
// No display settings: check that default display settings are used.
|
||||
$build = $items->view();
|
||||
$this->render($build);
|
||||
$settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings('field_test_default');
|
||||
$setting = $settings['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// Display settings: Check hidden field.
|
||||
$display = array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'field_test_multiple',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_multiple' => $this->randomMachineName(),
|
||||
'alter' => TRUE,
|
||||
),
|
||||
);
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
$this->assertNoText($this->label, 'Label was not displayed.');
|
||||
$this->assertText('field_test_entity_display_build_alter', 'Alter fired, display passed.');
|
||||
$this->assertText('entity language is en', 'Language is placed onto the context.');
|
||||
$array = array();
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$array[] = $delta . ':' . $value['value'];
|
||||
}
|
||||
$this->assertText($setting . '|' . implode('|', $array), 'Values were displayed with expected setting.');
|
||||
|
||||
// Display settings: Check visually_hidden field.
|
||||
$display = array(
|
||||
'label' => 'visually_hidden',
|
||||
'type' => 'field_test_multiple',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_multiple' => $this->randomMachineName(),
|
||||
'alter' => TRUE,
|
||||
),
|
||||
);
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
$this->assertRaw('visually-hidden', 'Label was visually hidden.');
|
||||
$this->assertText('field_test_entity_display_build_alter', 'Alter fired, display passed.');
|
||||
$this->assertText('entity language is en', 'Language is placed onto the context.');
|
||||
$array = array();
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$array[] = $delta . ':' . $value['value'];
|
||||
}
|
||||
$this->assertText($setting . '|' . implode('|', $array), 'Values were displayed with expected setting.');
|
||||
|
||||
// Check the prepare_view steps are invoked.
|
||||
$display = array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'field_test_with_prepare_view',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_additional' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_additional'];
|
||||
$this->assertNoText($this->label, 'Label was not displayed.');
|
||||
$this->assertNoText('field_test_entity_display_build_alter', 'Alter not fired.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// View mode: check that display settings specified in the display object
|
||||
// are used.
|
||||
$build = $items->view('teaser');
|
||||
$this->render($build);
|
||||
$setting = $this->displayOptions['teaser']['settings']['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// Unknown view mode: check that display settings for 'default' view mode
|
||||
// are used.
|
||||
$build = $items->view('unknown_view_mode');
|
||||
$this->render($build);
|
||||
$setting = $this->displayOptions['default']['settings']['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the FieldItemInterface::view() method.
|
||||
*/
|
||||
function testFieldItemView() {
|
||||
// No display settings: check that default display settings are used.
|
||||
$settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings('field_test_default');
|
||||
$setting = $settings['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->fieldName}[$delta];
|
||||
$build = $item->view();
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// Check that explicit display settings are used.
|
||||
$display = array(
|
||||
'type' => 'field_test_multiple',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_multiple' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->fieldName}[$delta];
|
||||
$build = $item->view($display);
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|0:' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// Check that prepare_view steps are invoked.
|
||||
$display = array(
|
||||
'type' => 'field_test_with_prepare_view',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_additional' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
$setting = $display['settings']['test_formatter_setting_additional'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->fieldName}[$delta];
|
||||
$build = $item->view($display);
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// View mode: check that display settings specified in the field are used.
|
||||
$setting = $this->displayOptions['teaser']['settings']['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->fieldName}[$delta];
|
||||
$build = $item->view('teaser');
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
// Unknown view mode: check that display settings for 'default' view mode
|
||||
// are used.
|
||||
$setting = $this->displayOptions['default']['settings']['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->fieldName}[$delta];
|
||||
$build = $item->view('unknown_view_mode');
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the prepareView() formatter method still fires for empty values.
|
||||
*/
|
||||
function testFieldEmpty() {
|
||||
// Uses \Drupal\field_test\Plugin\Field\FieldFormatter\TestFieldEmptyFormatter.
|
||||
$display = array(
|
||||
'label' => 'hidden',
|
||||
'type' => 'field_empty_test',
|
||||
'settings' => array(
|
||||
'test_empty_string' => '**EMPTY FIELD**' . $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
// $this->entity is set by the setUp() method and by default contains 4
|
||||
// numeric values. We only want to test the display of this one field.
|
||||
$build = $this->entity->get($this->fieldName)->view($display);
|
||||
$this->render($build);
|
||||
// The test field by default contains values, so should not display the
|
||||
// default "empty" text.
|
||||
$this->assertNoText($display['settings']['test_empty_string']);
|
||||
|
||||
// Now remove the values from the test field and retest.
|
||||
$this->entity->{$this->fieldName} = array();
|
||||
$this->entity->save();
|
||||
$build = $this->entity->get($this->fieldName)->view($display);
|
||||
$this->render($build);
|
||||
// This time, as the field values have been removed, we *should* show the
|
||||
// default "empty" text.
|
||||
$this->assertText($display['settings']['test_empty_string']);
|
||||
}
|
||||
}
|
109
core/modules/field/src/Tests/Email/EmailFieldTest.php
Normal file
109
core/modules/field/src/Tests/Email/EmailFieldTest.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Email\EmailFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Email;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests email field functionality.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class EmailFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'entity_test', 'field_ui');
|
||||
|
||||
/**
|
||||
* A field storage to use in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field used in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer content types',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests email field.
|
||||
*/
|
||||
function testEmailField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'email',
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
$this->field = entity_create('field_config', array(
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$this->field->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'email_default',
|
||||
'settings' => array(
|
||||
'placeholder' => 'example@example.com',
|
||||
),
|
||||
))
|
||||
->save();
|
||||
// Create a display for the full view mode.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'email_mailto',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget found.');
|
||||
$this->assertRaw('placeholder="example@example.com"');
|
||||
|
||||
// Submit a valid email address and ensure it is accepted.
|
||||
$value = 'test@example.com';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
|
||||
$this->assertRaw($value);
|
||||
|
||||
// Verify that a mailto link is displayed.
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertLinkByHref('mailto:test@example.com');
|
||||
}
|
||||
|
||||
}
|
79
core/modules/field/src/Tests/Email/EmailItemTest.php
Normal file
79
core/modules/field/src/Tests/Email/EmailItemTest.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Email\EmailItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Email;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\field\Tests\FieldUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the email field type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class EmailItemTest extends FieldUnitTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an email field storage and field for validation.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_email',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'email',
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_email',
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent('field_email', array(
|
||||
'type' => 'email_default',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the email field type.
|
||||
*/
|
||||
public function testEmailItem() {
|
||||
// Verify entity creation.
|
||||
$entity = entity_create('entity_test');
|
||||
$value = 'test@example.com';
|
||||
$entity->field_email = $value;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->field_email instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_email[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_email->value, $value);
|
||||
$this->assertEqual($entity->field_email[0]->value, $value);
|
||||
|
||||
// Verify changing the email value.
|
||||
$new_value = $this->randomMachineName();
|
||||
$entity->field_email->value = $new_value;
|
||||
$this->assertEqual($entity->field_email->value, $new_value);
|
||||
|
||||
// Read changed entity and assert changed values.
|
||||
$entity->save();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_email->value, $new_value);
|
||||
|
||||
// Test sample item generation.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_email->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\EntityReference\EntityReferenceFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the formatters functionality.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceFormatterTest extends EntityUnitTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* The entity to be referenced in this test.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected $referencedEntity;
|
||||
|
||||
/**
|
||||
* The entity that is not yet saved to its persistent storage to be referenced
|
||||
* in this test.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected $unsavedReferencedEntity;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Grant the 'view test entity' permission.
|
||||
$this->installConfig(array('user'));
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->grantPermission('view test entity')
|
||||
->save();
|
||||
|
||||
// The label formatter rendering generates links, so build the router.
|
||||
$this->installSchema('system', 'router');
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
|
||||
$this->createEntityReferenceField($this->entityType, $this->bundle, $this->fieldName, 'Field test', $this->entityType, 'default', array(), FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
// Set up a field, so that the entity that'll be referenced bubbles up a
|
||||
// cache tag when rendering it entirely.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => 'body',
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'text',
|
||||
'settings' => array(),
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => $this->entityType,
|
||||
'bundle' => $this->bundle,
|
||||
'field_name' => 'body',
|
||||
'label' => 'Body',
|
||||
))->save();
|
||||
entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent('body', array(
|
||||
'type' => 'text_default',
|
||||
'settings' => array(),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_create('filter_format', array(
|
||||
'format' => 'full_html',
|
||||
'name' => 'Full HTML',
|
||||
))->save();
|
||||
|
||||
// Create the entity to be referenced.
|
||||
$this->referencedEntity = entity_create($this->entityType, array('name' => $this->randomMachineName()));
|
||||
$this->referencedEntity->body = array(
|
||||
'value' => '<p>Hello, world!</p>',
|
||||
'format' => 'full_html',
|
||||
);
|
||||
$this->referencedEntity->save();
|
||||
|
||||
// Create another entity to be referenced but do not save it.
|
||||
$this->unsavedReferencedEntity = entity_create($this->entityType, array('name' => $this->randomMachineName()));
|
||||
$this->unsavedReferencedEntity->body = array(
|
||||
'value' => '<p>Hello, unsaved world!</p>',
|
||||
'format' => 'full_html',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert inaccessible items don't change the data of the fields.
|
||||
*/
|
||||
public function testAccess() {
|
||||
// Revoke the 'view test entity' permission for this test.
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->revokePermission('view test entity')
|
||||
->save();
|
||||
|
||||
$field_name = $this->fieldName;
|
||||
|
||||
$referencing_entity = entity_create($this->entityType, array('name' => $this->randomMachineName()));
|
||||
$referencing_entity->save();
|
||||
$referencing_entity->{$field_name}->entity = $this->referencedEntity;
|
||||
|
||||
// Assert user doesn't have access to the entity.
|
||||
$this->assertFalse($this->referencedEntity->access('view'), 'Current user does not have access to view the referenced entity.');
|
||||
|
||||
$formatter_manager = $this->container->get('plugin.manager.field.formatter');
|
||||
|
||||
// Get all the existing formatters.
|
||||
foreach ($formatter_manager->getOptions('entity_reference') as $formatter => $name) {
|
||||
// Set formatter type for the 'full' view mode.
|
||||
entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => $formatter,
|
||||
))
|
||||
->save();
|
||||
|
||||
// Invoke entity view.
|
||||
entity_view($referencing_entity, 'default');
|
||||
|
||||
// Verify the un-accessible item still exists.
|
||||
$this->assertEqual($referencing_entity->{$field_name}->target_id, $this->referencedEntity->id(), format_string('The un-accessible item still exists after @name formatter was executed.', array('@name' => $name)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the ID formatter.
|
||||
*/
|
||||
public function testIdFormatter() {
|
||||
$formatter = 'entity_reference_entity_id';
|
||||
$build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter);
|
||||
|
||||
$this->assertEqual($build[0]['#markup'], $this->referencedEntity->id(), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
|
||||
$this->assertEqual($build[0]['#cache']['tags'], $this->referencedEntity->getCacheTags(), sprintf('The %s formatter has the expected cache tags.', $formatter));
|
||||
$this->assertTrue(!isset($build[1]), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the entity formatter.
|
||||
*/
|
||||
public function testEntityFormatter() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$formatter = 'entity_reference_entity_view';
|
||||
$build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter);
|
||||
|
||||
// Test the first field item.
|
||||
$expected_rendered_name_field_1 = '<div class="field field-entity-test--name field-name-name field-type-string field-label-hidden">
|
||||
<div class="field-items">
|
||||
<div class="field-item">' . $this->referencedEntity->label() . '</div>
|
||||
</div>
|
||||
</div>
|
||||
';
|
||||
$expected_rendered_body_field_1 = '<div class="field field-entity-test--body field-name-body field-type-text field-label-above">
|
||||
<div class="field-label">Body</div>
|
||||
<div class="field-items">
|
||||
<div class="field-item"><p>Hello, world!</p></div>
|
||||
</div>
|
||||
</div>
|
||||
';
|
||||
$renderer->renderRoot($build[0]);
|
||||
$this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() . $expected_rendered_name_field_1 . $expected_rendered_body_field_1, sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
|
||||
$expected_cache_tags = Cache::mergeTags(
|
||||
\Drupal::entityManager()->getViewBuilder($this->entityType)->getCacheTags(),
|
||||
$this->referencedEntity->getCacheTags(),
|
||||
FilterFormat::load('full_html')->getCacheTags()
|
||||
);
|
||||
$this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
|
||||
|
||||
// Test the second field item.
|
||||
$renderer->renderRoot($build[1]);
|
||||
$this->assertEqual($build[1]['#markup'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the label formatter.
|
||||
*/
|
||||
public function testLabelFormatter() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$formatter = 'entity_reference_label';
|
||||
|
||||
// The 'link' settings is TRUE by default.
|
||||
$build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter);
|
||||
|
||||
$expected_field_cacheability = [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => Cache::PERMANENT,
|
||||
];
|
||||
$this->assertEqual($build['#cache'], $expected_field_cacheability, 'The field render array contains the entity access cacheability metadata');
|
||||
$expected_item_1 = array(
|
||||
'#type' => 'link',
|
||||
'#title' => $this->referencedEntity->label(),
|
||||
'#url' => $this->referencedEntity->urlInfo(),
|
||||
'#options' => $this->referencedEntity->urlInfo()->getOptions(),
|
||||
'#cache' => array(
|
||||
'contexts' => [
|
||||
'user.permissions',
|
||||
],
|
||||
'tags' => $this->referencedEntity->getCacheTags(),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($renderer->renderRoot($build[0]), $renderer->renderRoot($expected_item_1), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
|
||||
$this->assertEqual(CacheableMetadata::createFromRenderArray($build[0]), CacheableMetadata::createFromRenderArray($expected_item_1));
|
||||
|
||||
// The second referenced entity is "autocreated", therefore not saved and
|
||||
// lacking any URL info.
|
||||
$expected_item_2 = array(
|
||||
'#markup' => $this->unsavedReferencedEntity->label(),
|
||||
'#cache' => array(
|
||||
'contexts' => [
|
||||
'user.permissions',
|
||||
],
|
||||
'tags' => $this->unsavedReferencedEntity->getCacheTags(),
|
||||
'max-age' => Cache::PERMANENT,
|
||||
),
|
||||
);
|
||||
$this->assertEqual($build[1], $expected_item_2, sprintf('The render array returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
|
||||
|
||||
// Test with the 'link' setting set to FALSE.
|
||||
$build = $this->buildRenderArray([$this->referencedEntity, $this->unsavedReferencedEntity], $formatter, array('link' => FALSE));
|
||||
$this->assertEqual($build[0]['#markup'], $this->referencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a saved entity.', $formatter));
|
||||
$this->assertEqual($build[1]['#markup'], $this->unsavedReferencedEntity->label(), sprintf('The markup returned by the %s formatter is correct for an item with a unsaved entity.', $formatter));
|
||||
|
||||
// Test an entity type that doesn't have any link templates, which means
|
||||
// \Drupal\Core\Entity\EntityInterface::urlInfo() will throw an exception
|
||||
// and the label formatter will output only the label instead of a link.
|
||||
$field_storage_config = FieldStorageConfig::loadByName($this->entityType, $this->fieldName);
|
||||
$field_storage_config->setSetting('target_type', 'entity_test_label');
|
||||
$field_storage_config->save();
|
||||
|
||||
$referenced_entity_with_no_link_template = entity_create('entity_test_label', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
));
|
||||
$referenced_entity_with_no_link_template->save();
|
||||
|
||||
$build = $this->buildRenderArray([$referenced_entity_with_no_link_template], $formatter, array('link' => TRUE));
|
||||
$this->assertEqual($build[0]['#markup'], $referenced_entity_with_no_link_template->label(), sprintf('The markup returned by the %s formatter is correct for an entity type with no valid link template.', $formatter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets field values and returns a render array as built by
|
||||
* \Drupal\Core\Field\FieldItemListInterface::view().
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface[] $referenced_entities
|
||||
* An array of entity objects that will be referenced.
|
||||
* @param string $formatter
|
||||
* The formatted plugin that will be used for building the render array.
|
||||
* @param array $formatter_options
|
||||
* Settings specific to the formatter. Defaults to the formatter's default
|
||||
* settings.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
protected function buildRenderArray(array $referenced_entities, $formatter, $formatter_options = array()) {
|
||||
// Create the entity that will have the entity reference field.
|
||||
$referencing_entity = entity_create($this->entityType, array('name' => $this->randomMachineName()));
|
||||
|
||||
$items = $referencing_entity->get($this->fieldName);
|
||||
|
||||
// Assign the referenced entities.
|
||||
foreach ($referenced_entities as $referenced_entity) {
|
||||
$items[] = ['entity' => $referenced_entity];
|
||||
}
|
||||
|
||||
// Build the renderable array for the field.
|
||||
return $items->view(array('type' => $formatter, 'settings' => $formatter_options));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\EntityReference\EntityReferenceItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\entity_test\Entity\EntityTestStringId;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Tests\FieldUnitTestBase;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the entity reference field type.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceItemTest extends FieldUnitTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference', 'taxonomy', 'text', 'filter', 'views');
|
||||
|
||||
/**
|
||||
* The taxonomy vocabulary to test with.
|
||||
*
|
||||
* @var \Drupal\taxonomy\VocabularyInterface
|
||||
*/
|
||||
protected $vocabulary;
|
||||
|
||||
/**
|
||||
* The taxonomy term to test with.
|
||||
*
|
||||
* @var \Drupal\taxonomy\TermInterface
|
||||
*/
|
||||
protected $term;
|
||||
|
||||
/**
|
||||
* The test entity with a string ID.
|
||||
*
|
||||
* @var \Drupal\entity_test\Entity\EntityTestStringId
|
||||
*/
|
||||
protected $entityStringId;
|
||||
|
||||
/**
|
||||
* Sets up the test.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_string_id');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
$this->vocabulary = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$this->vocabulary->save();
|
||||
|
||||
$this->term = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $this->vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$this->term->save();
|
||||
|
||||
$this->entityStringId = EntityTestStringId::create([
|
||||
'id' => $this->randomMachineName(),
|
||||
]);
|
||||
$this->entityStringId->save();
|
||||
|
||||
// Use the util to create an instance.
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_term', 'Test content entity reference', 'taxonomy_term');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_entity_test_string_id', 'Test content entity reference with string ID', 'entity_test_string_id');
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'field_test_taxonomy_vocabulary', 'Test config entity reference', 'taxonomy_vocabulary');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the entity reference field type for referencing content entities.
|
||||
*/
|
||||
public function testContentEntityReferenceItem() {
|
||||
$tid = $this->term->id();
|
||||
|
||||
// Just being able to create the entity like this verifies a lot of code.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_test_taxonomy_term->target_id = $tid;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
$entity = entity_load('entity_test', $entity->id());
|
||||
$this->assertTrue($entity->field_test_taxonomy_term instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_test_taxonomy_term[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->target_id, $tid);
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $this->term->getName());
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $tid);
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->uuid(), $this->term->uuid());
|
||||
|
||||
// Change the name of the term via the reference.
|
||||
$new_name = $this->randomMachineName();
|
||||
$entity->field_test_taxonomy_term->entity->setName($new_name);
|
||||
$entity->field_test_taxonomy_term->entity->save();
|
||||
// Verify it is the correct name.
|
||||
$term = Term::load($tid);
|
||||
$this->assertEqual($term->getName(), $new_name);
|
||||
|
||||
// Make sure the computed term reflects updates to the term id.
|
||||
$term2 = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $this->term->bundle(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$term2->save();
|
||||
|
||||
// Test all the possible ways of assigning a value.
|
||||
$entity->field_test_taxonomy_term->target_id = $term->id();
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term->getName());
|
||||
|
||||
$entity->field_test_taxonomy_term = [['target_id' => $term2->id()]];
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term2->id());
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term2->getName());
|
||||
|
||||
// Test value assignment via the computed 'entity' property.
|
||||
$entity->field_test_taxonomy_term->entity = $term;
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->target_id, $term->id());
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term->getName());
|
||||
|
||||
$entity->field_test_taxonomy_term = [['entity' => $term2]];
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->target_id, $term2->id());
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->getName(), $term2->getName());
|
||||
|
||||
// Test assigning an invalid item throws an exception.
|
||||
try {
|
||||
$entity->field_test_taxonomy_term = ['target_id' => 'invalid', 'entity' => $term2];
|
||||
$this->fail('Assigning an invalid item throws an exception.');
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass('Assigning an invalid item throws an exception.');
|
||||
}
|
||||
|
||||
// Delete terms so we have nothing to reference and try again
|
||||
$term->delete();
|
||||
$term2->delete();
|
||||
$entity = entity_create('entity_test', array('name' => $this->randomMachineName()));
|
||||
$entity->save();
|
||||
|
||||
// Test the generateSampleValue() method.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_test_taxonomy_term->generateSampleItems();
|
||||
$entity->field_test_taxonomy_vocabulary->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests referencing content entities with string IDs.
|
||||
*/
|
||||
public function testContentEntityReferenceItemWithStringId() {
|
||||
$entity = EntityTest::create();
|
||||
$entity->field_test_entity_test_string_id->target_id = $this->entityStringId->id();
|
||||
$entity->save();
|
||||
$storage = \Drupal::entityManager()->getStorage('entity_test');
|
||||
$storage->resetCache();
|
||||
$this->assertEqual($this->entityStringId->id(), $storage->load($entity->id())->field_test_entity_test_string_id->target_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the entity reference field type for referencing config entities.
|
||||
*/
|
||||
public function testConfigEntityReferenceItem() {
|
||||
$referenced_entity_id = $this->vocabulary->id();
|
||||
|
||||
// Just being able to create the entity like this verifies a lot of code.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_test_taxonomy_vocabulary->target_id = $referenced_entity_id;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
$entity = entity_load('entity_test', $entity->id());
|
||||
$this->assertTrue($entity->field_test_taxonomy_vocabulary instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_test_taxonomy_vocabulary[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->target_id, $referenced_entity_id);
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->label(), $this->vocabulary->label());
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->id(), $referenced_entity_id);
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->uuid(), $this->vocabulary->uuid());
|
||||
|
||||
// Change the name of the term via the reference.
|
||||
$new_name = $this->randomMachineName();
|
||||
$entity->field_test_taxonomy_vocabulary->entity->set('name', $new_name);
|
||||
$entity->field_test_taxonomy_vocabulary->entity->save();
|
||||
// Verify it is the correct name.
|
||||
$vocabulary = Vocabulary::load($referenced_entity_id);
|
||||
$this->assertEqual($vocabulary->label(), $new_name);
|
||||
|
||||
// Make sure the computed term reflects updates to the term id.
|
||||
$vocabulary2 = entity_create('taxonomy_vocabulary', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$vocabulary2->save();
|
||||
|
||||
$entity->field_test_taxonomy_vocabulary->target_id = $vocabulary2->id();
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->id(), $vocabulary2->id());
|
||||
$this->assertEqual($entity->field_test_taxonomy_vocabulary->entity->label(), $vocabulary2->label());
|
||||
|
||||
// Delete terms so we have nothing to reference and try again
|
||||
$this->vocabulary->delete();
|
||||
$vocabulary2->delete();
|
||||
$entity = entity_create('entity_test', array('name' => $this->randomMachineName()));
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test saving order sequence doesn't matter.
|
||||
*/
|
||||
public function testEntitySaveOrder() {
|
||||
// The term entity is unsaved here.
|
||||
$term = entity_create('taxonomy_term', array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $this->term->bundle(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
$entity = entity_create('entity_test');
|
||||
// Now assign the unsaved term to the field.
|
||||
$entity->field_test_taxonomy_term->entity = $term;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
// Now save the term.
|
||||
$term->save();
|
||||
// And then the entity.
|
||||
$entity->save();
|
||||
$this->assertEqual($entity->field_test_taxonomy_term->entity->id(), $term->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the 'handler' field setting stores the proper plugin ID.
|
||||
*/
|
||||
public function testSelectionHandlerSettings() {
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'entity_reference',
|
||||
'settings' => array(
|
||||
'target_type' => 'entity_test'
|
||||
),
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
// Do not specify any value for the 'handler' setting in order to verify
|
||||
// that the default value is properly used.
|
||||
$field = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
$field = FieldConfig::load($field->id());
|
||||
$this->assertTrue($field->getSetting('handler') == 'default:entity_test');
|
||||
|
||||
$field->setSetting('handler', 'views');
|
||||
$field->save();
|
||||
$field = FieldConfig::load($field->id());
|
||||
$this->assertTrue($field->getSetting('handler') == 'views');
|
||||
}
|
||||
|
||||
}
|
93
core/modules/field/src/Tests/FieldAccessTest.php
Normal file
93
core/modules/field/src/Tests/FieldAccessTest.php
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldAccessTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Tests Field access.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldAccessTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test');
|
||||
|
||||
/**
|
||||
* Node entity to use in this test.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* Field value to test display on nodes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testViewFieldValue;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test_view_field content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create content type.
|
||||
$content_type_info = $this->drupalCreateContentType();
|
||||
$content_type = $content_type_info->id();
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => 'test_view_field',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
$field = array(
|
||||
'field_name' => $field_storage['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $content_type,
|
||||
);
|
||||
entity_create('field_config', $field)->save();
|
||||
|
||||
// Assign display properties for the 'default' and 'teaser' view modes.
|
||||
foreach (array('default', 'teaser') as $view_mode) {
|
||||
entity_get_display('node', $content_type, $view_mode)
|
||||
->setComponent($field_storage['field_name'])
|
||||
->save();
|
||||
}
|
||||
|
||||
// Create test node.
|
||||
$this->testViewFieldValue = 'This is some text';
|
||||
$settings = array();
|
||||
$settings['type'] = $content_type;
|
||||
$settings['title'] = 'Field view access test';
|
||||
$settings['test_view_field'] = array(array('value' => $this->testViewFieldValue));
|
||||
$this->node = $this->drupalCreateNode($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that hook_entity_field_access() is called.
|
||||
*/
|
||||
function testFieldAccess() {
|
||||
|
||||
// Assert the text is visible.
|
||||
$this->drupalGet('node/' . $this->node->id());
|
||||
$this->assertText($this->testViewFieldValue);
|
||||
|
||||
// Assert the text is not visible for anonymous users.
|
||||
// The field_test module implements hook_entity_field_access() which will
|
||||
// specifically target the 'test_view_field' field.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('node/' . $this->node->id());
|
||||
$this->assertNoText($this->testViewFieldValue);
|
||||
}
|
||||
}
|
380
core/modules/field/src/Tests/FieldAttachOtherTest.php
Normal file
380
core/modules/field/src/Tests/FieldAttachOtherTest.php
Normal file
|
@ -0,0 +1,380 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldAttachOtherTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
||||
/**
|
||||
* Tests other Field API functions.
|
||||
*
|
||||
* @group field
|
||||
* @todo move this to the Entity module
|
||||
*/
|
||||
class FieldAttachOtherTest extends FieldUnitTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->createFieldWithStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test rendering fields with EntityDisplay build().
|
||||
*/
|
||||
function testEntityDisplayBuild() {
|
||||
$this->createFieldWithStorage('_2');
|
||||
|
||||
$entity_type = 'entity_test';
|
||||
$entity_init = entity_create($entity_type);
|
||||
|
||||
// Populate values to be displayed.
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage->getCardinality());
|
||||
$entity_init->{$this->fieldTestData->field_name}->setValue($values);
|
||||
$values_2 = $this->_generateTestFieldValues($this->fieldTestData->field_storage_2->getCardinality());
|
||||
$entity_init->{$this->fieldTestData->field_name_2}->setValue($values_2);
|
||||
|
||||
// Simple formatter, label displayed.
|
||||
$entity = clone($entity_init);
|
||||
$display = entity_get_display($entity_type, $entity->bundle(), 'full');
|
||||
|
||||
$formatter_setting = $this->randomMachineName();
|
||||
$display_options = array(
|
||||
'label' => 'above',
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $formatter_setting,
|
||||
),
|
||||
);
|
||||
$display->setComponent($this->fieldTestData->field_name, $display_options);
|
||||
|
||||
$formatter_setting_2 = $this->randomMachineName();
|
||||
$display_options_2 = array(
|
||||
'label' => 'above',
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $formatter_setting_2,
|
||||
),
|
||||
);
|
||||
$display->setComponent($this->fieldTestData->field_name_2, $display_options_2);
|
||||
|
||||
// View all fields.
|
||||
$content = $display->build($entity);
|
||||
$this->render($content);
|
||||
$this->assertRaw($this->fieldTestData->field->getLabel(), "First field's label is displayed.");
|
||||
foreach ($values as $delta => $value) {
|
||||
$this->assertRaw("$formatter_setting|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
|
||||
}
|
||||
$this->assertRaw($this->fieldTestData->field_2->getLabel(), "Second field's label is displayed.");
|
||||
foreach ($values_2 as $delta => $value) {
|
||||
$this->assertRaw("$formatter_setting_2|{$value['value']}", "Value $delta is displayed, formatter settings are applied.");
|
||||
}
|
||||
|
||||
// Label hidden.
|
||||
$entity = clone($entity_init);
|
||||
$display_options['label'] = 'hidden';
|
||||
$display->setComponent($this->fieldTestData->field_name, $display_options);
|
||||
$content = $display->build($entity);
|
||||
$this->render($content);
|
||||
$this->assertNoRaw($this->fieldTestData->field->getLabel(), "Hidden label: label is not displayed.");
|
||||
|
||||
// Field hidden.
|
||||
$entity = clone($entity_init);
|
||||
$display->removeComponent($this->fieldTestData->field_name);
|
||||
$content = $display->build($entity);
|
||||
$this->render($content);
|
||||
$this->assertNoRaw($this->fieldTestData->field->getLabel(), "Hidden field: label is not displayed.");
|
||||
foreach ($values as $delta => $value) {
|
||||
$this->assertNoRaw("$formatter_setting|{$value['value']}", "Hidden field: value $delta is not displayed.");
|
||||
}
|
||||
|
||||
// Multiple formatter.
|
||||
$entity = clone($entity_init);
|
||||
$formatter_setting = $this->randomMachineName();
|
||||
$display->setComponent($this->fieldTestData->field_name, array(
|
||||
'label' => 'above',
|
||||
'type' => 'field_test_multiple',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_multiple' => $formatter_setting,
|
||||
),
|
||||
));
|
||||
$content = $display->build($entity);
|
||||
$this->render($content);
|
||||
$expected_output = $formatter_setting;
|
||||
foreach ($values as $delta => $value) {
|
||||
$expected_output .= "|$delta:{$value['value']}";
|
||||
}
|
||||
$this->assertRaw($expected_output, "Multiple formatter: all values are displayed, formatter settings are applied.");
|
||||
|
||||
// Test a formatter that uses hook_field_formatter_prepare_view().
|
||||
$entity = clone($entity_init);
|
||||
$formatter_setting = $this->randomMachineName();
|
||||
$display->setComponent($this->fieldTestData->field_name, array(
|
||||
'label' => 'above',
|
||||
'type' => 'field_test_with_prepare_view',
|
||||
'settings' => array(
|
||||
'test_formatter_setting_additional' => $formatter_setting,
|
||||
),
|
||||
));
|
||||
$content = $display->build($entity);
|
||||
$this->render($content);
|
||||
foreach ($values as $delta => $value) {
|
||||
$expected = $formatter_setting . '|' . $value['value'] . '|' . ($value['value'] + 1);
|
||||
$this->assertRaw($expected, "Value $delta is displayed, formatter settings are applied.");
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - check display order with several fields
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rendering fields with EntityDisplay::buildMultiple().
|
||||
*/
|
||||
function testEntityDisplayViewMultiple() {
|
||||
// Use a formatter that has a prepareView() step.
|
||||
$display = entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($this->fieldTestData->field_name, array(
|
||||
'type' => 'field_test_with_prepare_view',
|
||||
));
|
||||
|
||||
// Create two entities.
|
||||
$entity1 = entity_create('entity_test', array('id' => 1, 'type' => 'entity_test'));
|
||||
$entity1->{$this->fieldTestData->field_name}->setValue($this->_generateTestFieldValues(1));
|
||||
$entity2 = entity_create('entity_test', array('id' => 2, 'type' => 'entity_test'));
|
||||
$entity2->{$this->fieldTestData->field_name}->setValue($this->_generateTestFieldValues(1));
|
||||
|
||||
// Run buildMultiple(), and check that the entities come out as expected.
|
||||
$display->buildMultiple(array($entity1, $entity2));
|
||||
$item1 = $entity1->{$this->fieldTestData->field_name}[0];
|
||||
$this->assertEqual($item1->additional_formatter_value, $item1->value + 1, 'Entity 1 ran through the prepareView() formatter method.');
|
||||
$item2 = $entity2->{$this->fieldTestData->field_name}[0];
|
||||
$this->assertEqual($item2->additional_formatter_value, $item2->value + 1, 'Entity 2 ran through the prepareView() formatter method.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test entity cache.
|
||||
*
|
||||
* Complements unit test coverage in
|
||||
* \Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTest.
|
||||
*/
|
||||
function testEntityCache() {
|
||||
// Initialize random values and a test entity.
|
||||
$entity_init = entity_create('entity_test', array('type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage->getCardinality());
|
||||
|
||||
// Non-cacheable entity type.
|
||||
$entity_type = 'entity_test';
|
||||
$cid = "values:$entity_type:" . $entity_init->id();
|
||||
|
||||
// Check that no initial cache entry is present.
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no initial cache entry');
|
||||
|
||||
// Save, and check that no cache entry is present.
|
||||
$entity = clone($entity_init);
|
||||
$entity->{$this->fieldTestData->field_name}->setValue($values);
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$cid = "values:$entity_type:" . $entity->id();
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no cache entry on insert and load');
|
||||
|
||||
// Cacheable entity type.
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('_2', $entity_type);
|
||||
|
||||
$entity_init = entity_create($entity_type, array(
|
||||
'type' => $entity_type,
|
||||
));
|
||||
|
||||
// Check that no initial cache entry is present.
|
||||
$cid = "values:$entity_type:" . $entity->id();
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no initial cache entry');
|
||||
|
||||
// Save, and check that no cache entry is present.
|
||||
$entity = clone($entity_init);
|
||||
$entity->{$this->fieldTestData->field_name_2} = $values;
|
||||
$entity->save();
|
||||
$cid = "values:$entity_type:" . $entity->id();
|
||||
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry on insert');
|
||||
// Load, and check that a cache entry is present with the expected values.
|
||||
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
$cached_entity = $controller->load($entity->id());
|
||||
$cache = \Drupal::cache('entity')->get($cid);
|
||||
$this->assertEqual($cache->data, $cached_entity, 'Cached: correct cache entry on load');
|
||||
|
||||
// Update with different values, and check that the cache entry is wiped.
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage_2->getCardinality());
|
||||
$entity->{$this->fieldTestData->field_name_2} = $values;
|
||||
$entity->save();
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry on update');
|
||||
|
||||
// Load, and check that a cache entry is present with the expected values.
|
||||
$controller->resetCache();
|
||||
$cached_entity = $controller->load($entity->id());
|
||||
$cache = \Drupal::cache('entity')->get($cid);
|
||||
$this->assertEqual($cache->data, $cached_entity, 'Cached: correct cache entry on load');
|
||||
|
||||
// Create a new revision, and check that the cache entry is wiped.
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage_2->getCardinality());
|
||||
$entity->{$this->fieldTestData->field_name_2} = $values;
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry on new revision creation');
|
||||
|
||||
// Load, and check that a cache entry is present with the expected values.
|
||||
$controller->resetCache();
|
||||
$cached_entity = $controller->load($entity->id());
|
||||
$cache = \Drupal::cache('entity')->get($cid);
|
||||
$this->assertEqual($cache->data, $cached_entity, 'Cached: correct cache entry on load');
|
||||
|
||||
// Delete, and check that the cache entry is wiped.
|
||||
$entity->delete();
|
||||
$this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry after delete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Entity\Display\EntityFormDisplayInterface::buildForm().
|
||||
*
|
||||
* This could be much more thorough, but it does verify that the correct
|
||||
* widgets show up.
|
||||
*/
|
||||
function testEntityFormDisplayBuildForm() {
|
||||
$this->createFieldWithStorage('_2');
|
||||
|
||||
$entity_type = 'entity_test';
|
||||
$entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
|
||||
// Test generating widgets for all fields.
|
||||
$display = entity_get_form_display($entity_type, $this->fieldTestData->field->getTargetBundle(), 'default');
|
||||
$form = array();
|
||||
$form_state = new FormState();
|
||||
$display->buildForm($entity, $form, $form_state);
|
||||
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name]['widget']['#title'], $this->fieldTestData->field->getLabel(), "First field's form title is {$this->fieldTestData->field->getLabel()}");
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name_2]['widget']['#title'], $this->fieldTestData->field_2->getLabel(), "Second field's form title is {$this->fieldTestData->field_2->getLabel()}");
|
||||
for ($delta = 0; $delta < $this->fieldTestData->field_storage->getCardinality(); $delta++) {
|
||||
// field_test_widget uses 'textfield'
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name]['widget'][$delta]['value']['#type'], 'textfield', "First field's form delta $delta widget is textfield");
|
||||
}
|
||||
for ($delta = 0; $delta < $this->fieldTestData->field_storage_2->getCardinality(); $delta++) {
|
||||
// field_test_widget uses 'textfield'
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name_2]['widget'][$delta]['value']['#type'], 'textfield', "Second field's form delta $delta widget is textfield");
|
||||
}
|
||||
|
||||
// Test generating widgets for all fields.
|
||||
$display = entity_get_form_display($entity_type, $this->fieldTestData->field->getTargetBundle(), 'default');
|
||||
foreach ($display->getComponents() as $name => $options) {
|
||||
if ($name != $this->fieldTestData->field_name_2) {
|
||||
$display->removeComponent($name);
|
||||
}
|
||||
}
|
||||
$form = array();
|
||||
$form_state = new FormState();
|
||||
$display->buildForm($entity, $form, $form_state);
|
||||
|
||||
$this->assertFalse(isset($form[$this->fieldTestData->field_name]), 'The first field does not exist in the form');
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name_2]['widget']['#title'], $this->fieldTestData->field_2->getLabel(), "Second field's form title is {$this->fieldTestData->field_2->getLabel()}");
|
||||
for ($delta = 0; $delta < $this->fieldTestData->field_storage_2->getCardinality(); $delta++) {
|
||||
// field_test_widget uses 'textfield'
|
||||
$this->assertEqual($form[$this->fieldTestData->field_name_2]['widget'][$delta]['value']['#type'], 'textfield', "Second field's form delta $delta widget is textfield");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Entity\Display\EntityFormDisplayInterface::extractFormValues().
|
||||
*/
|
||||
function testEntityFormDisplayExtractFormValues() {
|
||||
$this->createFieldWithStorage('_2');
|
||||
|
||||
$entity_type = 'entity_test';
|
||||
$entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
|
||||
// Build the form for all fields.
|
||||
$display = entity_get_form_display($entity_type, $this->fieldTestData->field->getTargetBundle(), 'default');
|
||||
$form = array();
|
||||
$form_state = new FormState();
|
||||
$display->buildForm($entity_init, $form, $form_state);
|
||||
|
||||
// Simulate incoming values.
|
||||
// First field.
|
||||
$values = array();
|
||||
$weights = array();
|
||||
for ($delta = 0; $delta < $this->fieldTestData->field_storage->getCardinality(); $delta++) {
|
||||
$values[$delta]['value'] = mt_rand(1, 127);
|
||||
// Assign random weight.
|
||||
do {
|
||||
$weight = mt_rand(0, $this->fieldTestData->field_storage->getCardinality());
|
||||
} while (in_array($weight, $weights));
|
||||
$weights[$delta] = $weight;
|
||||
$values[$delta]['_weight'] = $weight;
|
||||
}
|
||||
// Leave an empty value. 'field_test' fields are empty if empty().
|
||||
$values[1]['value'] = 0;
|
||||
// Second field.
|
||||
$values_2 = array();
|
||||
$weights_2 = array();
|
||||
for ($delta = 0; $delta < $this->fieldTestData->field_storage_2->getCardinality(); $delta++) {
|
||||
$values_2[$delta]['value'] = mt_rand(1, 127);
|
||||
// Assign random weight.
|
||||
do {
|
||||
$weight = mt_rand(0, $this->fieldTestData->field_storage_2->getCardinality());
|
||||
} while (in_array($weight, $weights_2));
|
||||
$weights_2[$delta] = $weight;
|
||||
$values_2[$delta]['_weight'] = $weight;
|
||||
}
|
||||
// Leave an empty value. 'field_test' fields are empty if empty().
|
||||
$values_2[1]['value'] = 0;
|
||||
|
||||
// Pretend the form has been built.
|
||||
$form_state->setFormObject(\Drupal::entityManager()->getFormObject($entity_type, 'default'));
|
||||
\Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state);
|
||||
\Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state);
|
||||
$form_state->setValue($this->fieldTestData->field_name, $values);
|
||||
$form_state->setValue($this->fieldTestData->field_name_2, $values_2);
|
||||
|
||||
// Extract values for all fields.
|
||||
$entity = clone($entity_init);
|
||||
$display->extractFormValues($entity, $form, $form_state);
|
||||
|
||||
asort($weights);
|
||||
asort($weights_2);
|
||||
$expected_values = array();
|
||||
$expected_values_2 = array();
|
||||
foreach ($weights as $key => $value) {
|
||||
if ($key != 1) {
|
||||
$expected_values[] = array('value' => $values[$key]['value']);
|
||||
}
|
||||
}
|
||||
$this->assertIdentical($entity->{$this->fieldTestData->field_name}->getValue(), $expected_values, 'Submit filters empty values');
|
||||
foreach ($weights_2 as $key => $value) {
|
||||
if ($key != 1) {
|
||||
$expected_values_2[] = array('value' => $values_2[$key]['value']);
|
||||
}
|
||||
}
|
||||
$this->assertIdentical($entity->{$this->fieldTestData->field_name_2}->getValue(), $expected_values_2, 'Submit filters empty values');
|
||||
|
||||
// Call EntityFormDisplayInterface::extractFormValues() for a single field (the second field).
|
||||
foreach ($display->getComponents() as $name => $options) {
|
||||
if ($name != $this->fieldTestData->field_name_2) {
|
||||
$display->removeComponent($name);
|
||||
}
|
||||
}
|
||||
$entity = clone($entity_init);
|
||||
$display->extractFormValues($entity, $form, $form_state);
|
||||
$expected_values_2 = array();
|
||||
foreach ($weights_2 as $key => $value) {
|
||||
if ($key != 1) {
|
||||
$expected_values_2[] = array('value' => $values_2[$key]['value']);
|
||||
}
|
||||
}
|
||||
$this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'The first field is empty in the entity object');
|
||||
$this->assertIdentical($entity->{$this->fieldTestData->field_name_2}->getValue(), $expected_values_2, 'Submit filters empty values');
|
||||
}
|
||||
|
||||
}
|
378
core/modules/field/src/Tests/FieldAttachStorageTest.php
Normal file
378
core/modules/field/src/Tests/FieldAttachStorageTest.php
Normal file
|
@ -0,0 +1,378 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldAttachStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Tests storage-related Field Attach API functions.
|
||||
*
|
||||
* @group field
|
||||
* @todo move this to the Entity module
|
||||
*/
|
||||
class FieldAttachStorageTest extends FieldUnitTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check field values insert, update and load.
|
||||
*
|
||||
* Works independently of the underlying field storage backend. Inserts or
|
||||
* updates random field data and then loads and verifies the data.
|
||||
*/
|
||||
function testFieldAttachSaveLoad() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
$cardinality = $this->fieldTestData->field_storage->getCardinality();
|
||||
|
||||
// TODO : test empty values filtering and "compression" (store consecutive deltas).
|
||||
// Preparation: create three revisions and store them in $revision array.
|
||||
$values = array();
|
||||
$entity = entity_create($entity_type);
|
||||
for ($revision_id = 0; $revision_id < 3; $revision_id++) {
|
||||
// Note: we try to insert one extra value.
|
||||
$current_values = $this->_generateTestFieldValues($cardinality + 1);
|
||||
$entity->{$this->fieldTestData->field_name}->setValue($current_values);
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$entity_id = $entity->id();
|
||||
$current_revision = $entity->getRevisionId();
|
||||
$values[$current_revision] = $current_values;
|
||||
}
|
||||
|
||||
$storage = $this->container->get('entity.manager')->getStorage($entity_type);
|
||||
$storage->resetCache();
|
||||
$entity = $storage->load($entity_id);
|
||||
// Confirm current revision loads the correct data.
|
||||
// Number of values per field loaded equals the field cardinality.
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, 'Current revision: expected number of values');
|
||||
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||
// The field value loaded matches the one inserted or updated.
|
||||
$this->assertEqual($entity->{$this->fieldTestData->field_name}[$delta]->value , $values[$current_revision][$delta]['value'], format_string('Current revision: expected value %delta was found.', array('%delta' => $delta)));
|
||||
}
|
||||
|
||||
// Confirm each revision loads the correct data.
|
||||
foreach (array_keys($values) as $revision_id) {
|
||||
$entity = $storage->loadRevision($revision_id);
|
||||
// Number of values per field loaded equals the field cardinality.
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
|
||||
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||
// The field value loaded matches the one inserted or updated.
|
||||
$this->assertEqual($entity->{$this->fieldTestData->field_name}[$delta]->value, $values[$revision_id][$delta]['value'], format_string('Revision %revision_id: expected value %delta was found.', array('%revision_id' => $revision_id, '%delta' => $delta)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the 'multiple' load feature.
|
||||
*/
|
||||
function testFieldAttachLoadMultiple() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
|
||||
// Define 2 bundles.
|
||||
$bundles = array(
|
||||
1 => 'test_bundle_1',
|
||||
2 => 'test_bundle_2',
|
||||
);
|
||||
entity_test_create_bundle($bundles[1]);
|
||||
entity_test_create_bundle($bundles[2]);
|
||||
// Define 3 fields:
|
||||
// - field_1 is in bundle_1 and bundle_2,
|
||||
// - field_2 is in bundle_1,
|
||||
// - field_3 is in bundle_2.
|
||||
$field_bundles_map = array(
|
||||
1 => array(1, 2),
|
||||
2 => array(1),
|
||||
3 => array(2),
|
||||
);
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$field_names[$i] = 'field_' . $i;
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => $field_names[$i],
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'test_field',
|
||||
));
|
||||
$field_storage->save();
|
||||
$field_ids[$i] = $field_storage->uuid();
|
||||
foreach ($field_bundles_map[$i] as $bundle) {
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $field_names[$i],
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $bundles[$bundle],
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
// Create one test entity per bundle, with random values.
|
||||
foreach ($bundles as $index => $bundle) {
|
||||
$entities[$index] = entity_create($entity_type, array('id' => $index, 'revision_id' => $index, 'type' => $bundle));
|
||||
$entity = clone($entities[$index]);
|
||||
foreach ($field_names as $field_name) {
|
||||
if (!$entity->hasField($field_name)) {
|
||||
continue;
|
||||
}
|
||||
$values[$index][$field_name] = mt_rand(1, 127);
|
||||
$entity->$field_name->setValue(array('value' => $values[$index][$field_name]));
|
||||
}
|
||||
$entity->enforceIsnew();
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
// Check that a single load correctly loads field values for both entities.
|
||||
$controller = \Drupal::entityManager()->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
$entities = $controller->loadMultiple();
|
||||
foreach ($entities as $index => $entity) {
|
||||
foreach ($field_names as $field_name) {
|
||||
if (!$entity->hasField($field_name)) {
|
||||
continue;
|
||||
}
|
||||
// The field value loaded matches the one inserted.
|
||||
$this->assertEqual($entity->{$field_name}->value, $values[$index][$field_name], format_string('Entity %index: expected value was found.', array('%index' => $index)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests insert and update with empty or NULL fields.
|
||||
*/
|
||||
function testFieldAttachSaveEmptyData() {
|
||||
$entity_type = 'entity_test';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
|
||||
$entity_init = entity_create($entity_type, array('id' => 1));
|
||||
|
||||
// Insert: Field is NULL.
|
||||
$entity = clone $entity_init;
|
||||
$entity->{$this->fieldTestData->field_name} = NULL;
|
||||
$entity->enforceIsNew();
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
|
||||
|
||||
// All saves after this point should be updates, not inserts.
|
||||
$entity_init->enforceIsNew(FALSE);
|
||||
|
||||
// Add some real data.
|
||||
$entity = clone($entity_init);
|
||||
$values = $this->_generateTestFieldValues(1);
|
||||
$entity->{$this->fieldTestData->field_name} = $values;
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $values, 'Field data saved');
|
||||
|
||||
// Update: Field is NULL. Data should be wiped.
|
||||
$entity = clone($entity_init);
|
||||
$entity->{$this->fieldTestData->field_name} = NULL;
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Update: NULL field removes existing values');
|
||||
|
||||
// Re-add some data.
|
||||
$entity = clone($entity_init);
|
||||
$values = $this->_generateTestFieldValues(1);
|
||||
$entity->{$this->fieldTestData->field_name} = $values;
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $values, 'Field data saved');
|
||||
|
||||
// Update: Field is empty array. Data should be wiped.
|
||||
$entity = clone($entity_init);
|
||||
$entity->{$this->fieldTestData->field_name} = array();
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Update: empty array removes existing values');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test insert with empty or NULL fields, with default value.
|
||||
*/
|
||||
function testFieldAttachSaveEmptyDataDefaultValue() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
|
||||
// Add a default value function.
|
||||
$this->fieldTestData->field->set('default_value_callback', 'field_test_default_value');
|
||||
$this->fieldTestData->field->save();
|
||||
|
||||
// Verify that fields are populated with default values.
|
||||
$entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
|
||||
$default = field_test_default_value($entity_init, $this->fieldTestData->field);
|
||||
$this->assertEqual($entity_init->{$this->fieldTestData->field_name}->getValue(), $default, 'Default field value correctly populated.');
|
||||
|
||||
// Insert: Field is NULL.
|
||||
$entity = clone($entity_init);
|
||||
$entity->{$this->fieldTestData->field_name} = NULL;
|
||||
$entity->enforceIsNew();
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertTrue($entity->{$this->fieldTestData->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
|
||||
|
||||
// Verify that prepopulated field values are not overwritten by defaults.
|
||||
$value = array(array('value' => $default[0]['value'] - mt_rand(1, 127)));
|
||||
$entity = entity_create($entity_type, array('type' => $entity_init->bundle(), $this->fieldTestData->field_name => $value));
|
||||
$this->assertEqual($entity->{$this->fieldTestData->field_name}->getValue(), $value, 'Prepopulated field value correctly maintained.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test entity deletion.
|
||||
*/
|
||||
function testFieldAttachDelete() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
$cardinality = $this->fieldTestData->field_storage->getCardinality();
|
||||
$entity = entity_create($entity_type, array('type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
$vids = array();
|
||||
|
||||
// Create revision 0
|
||||
$values = $this->_generateTestFieldValues($cardinality);
|
||||
$entity->{$this->fieldTestData->field_name} = $values;
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
|
||||
// Create revision 1
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
|
||||
// Create revision 2
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$vids[] = $entity->getRevisionId();
|
||||
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
|
||||
// Confirm each revision loads
|
||||
foreach ($vids as $vid) {
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertEqual(count($revision->{$this->fieldTestData->field_name}), $cardinality, "The test entity revision $vid has $cardinality values.");
|
||||
}
|
||||
|
||||
// Delete revision 1, confirm the other two still load.
|
||||
$controller->deleteRevision($vids[1]);
|
||||
$controller->resetCache();
|
||||
foreach (array(0, 2) as $key) {
|
||||
$vid = $vids[$key];
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertEqual(count($revision->{$this->fieldTestData->field_name}), $cardinality, "The test entity revision $vid has $cardinality values.");
|
||||
}
|
||||
|
||||
// Confirm the current revision still loads
|
||||
$controller->resetCache();
|
||||
$current = $controller->load($entity->id());
|
||||
$this->assertEqual(count($current->{$this->fieldTestData->field_name}), $cardinality, "The test entity current revision has $cardinality values.");
|
||||
|
||||
// Delete all field data, confirm nothing loads
|
||||
$entity->delete();
|
||||
$controller->resetCache();
|
||||
foreach (array(0, 1, 2) as $vid) {
|
||||
$revision = $controller->loadRevision($vid);
|
||||
$this->assertFalse($revision);
|
||||
}
|
||||
$this->assertFalse($controller->load($entity->id()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test entity_bundle_create() and entity_bundle_rename().
|
||||
*/
|
||||
function testEntityCreateRenameBundle() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
$cardinality = $this->fieldTestData->field_storage->getCardinality();
|
||||
|
||||
// Create a new bundle.
|
||||
$new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
|
||||
entity_test_create_bundle($new_bundle, NULL, $entity_type);
|
||||
|
||||
// Add a field to that bundle.
|
||||
$this->fieldTestData->field_definition['bundle'] = $new_bundle;
|
||||
entity_create('field_config', $this->fieldTestData->field_definition)->save();
|
||||
|
||||
// Save an entity with data in the field.
|
||||
$entity = entity_create($entity_type, array('type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
$values = $this->_generateTestFieldValues($cardinality);
|
||||
$entity->{$this->fieldTestData->field_name} = $values;
|
||||
|
||||
// Verify the field data is present on load.
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, "Data is retrieved for the new bundle");
|
||||
|
||||
// Rename the bundle.
|
||||
$new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
|
||||
entity_test_rename_bundle($this->fieldTestData->field_definition['bundle'], $new_bundle, $entity_type);
|
||||
|
||||
// Check that the field definition has been updated.
|
||||
$this->fieldTestData->field = FieldConfig::loadByName($entity_type, $new_bundle, $this->fieldTestData->field_name);
|
||||
$this->assertIdentical($this->fieldTestData->field->getTargetBundle(), $new_bundle, "Bundle name has been updated in the field.");
|
||||
|
||||
// Verify the field data is present on load.
|
||||
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
$entity = $controller->load($entity->id());
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), $cardinality, "Bundle name has been updated in the field storage");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test entity_bundle_delete().
|
||||
*/
|
||||
function testEntityDeleteBundle() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('', $entity_type);
|
||||
|
||||
// Create a new bundle.
|
||||
$new_bundle = 'test_bundle_' . Unicode::strtolower($this->randomMachineName());
|
||||
entity_test_create_bundle($new_bundle, NULL, $entity_type);
|
||||
|
||||
// Add a field to that bundle.
|
||||
$this->fieldTestData->field_definition['bundle'] = $new_bundle;
|
||||
entity_create('field_config', $this->fieldTestData->field_definition)->save();
|
||||
|
||||
// Create a second field for the test bundle
|
||||
$field_name = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
$field_storage = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $this->fieldTestData->field->getTargetBundle(),
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => $this->randomMachineName() . '_description',
|
||||
'weight' => mt_rand(0, 127),
|
||||
);
|
||||
entity_create('field_config', $field)->save();
|
||||
|
||||
// Save an entity with data for both fields
|
||||
$entity = entity_create($entity_type, array('type' => $this->fieldTestData->field->getTargetBundle()));
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage->getCardinality());
|
||||
$entity->{$this->fieldTestData->field_name} = $values;
|
||||
$entity->{$field_name} = $this->_generateTestFieldValues(1);
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
|
||||
// Verify the fields are present on load
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name}), 4, 'First field got loaded');
|
||||
$this->assertEqual(count($entity->{$field_name}), 1, 'Second field got loaded');
|
||||
|
||||
// Delete the bundle.
|
||||
entity_test_delete_bundle($this->fieldTestData->field->getTargetBundle(), $entity_type);
|
||||
|
||||
// Verify no data gets loaded
|
||||
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
$entity= $controller->load($entity->id());
|
||||
|
||||
$this->assertTrue(empty($entity->{$this->fieldTestData->field_name}), 'No data for first field');
|
||||
$this->assertTrue(empty($entity->{$field_name}), 'No data for second field');
|
||||
|
||||
// Verify that the fields are gone.
|
||||
$this->assertFalse(FieldConfig::load('entity_test.' . $this->fieldTestData->field->getTargetBundle() . '.' . $this->fieldTestData->field_name), "First field is deleted");
|
||||
$this->assertFalse(FieldConfig::load('entity_test.' . $field['bundle']. '.' . $field_name), "Second field is deleted");
|
||||
}
|
||||
|
||||
}
|
269
core/modules/field/src/Tests/FieldCrudTest.php
Normal file
269
core/modules/field/src/Tests/FieldCrudTest.php
Normal file
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldCrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Create field entities by attaching fields to entities.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldCrudTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* The field storage entity.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field entity definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageDefinition;
|
||||
|
||||
/**
|
||||
* The field entity definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldDefinition;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldStorageDefinition = array(
|
||||
'field_name' => Unicode::strtolower($this->randomMachineName()),
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$this->fieldStorage = entity_create('field_storage_config', $this->fieldStorageDefinition);
|
||||
$this->fieldStorage->save();
|
||||
$this->fieldDefinition = array(
|
||||
'field_name' => $this->fieldStorage->getName(),
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
);
|
||||
}
|
||||
|
||||
// TODO : test creation with
|
||||
// - a full fledged $field structure, check that all the values are there
|
||||
// - a minimal $field structure, check all default values are set
|
||||
// defer actual $field comparison to a helper function, used for the two cases above,
|
||||
// and for testUpdateField
|
||||
|
||||
/**
|
||||
* Test the creation of a field.
|
||||
*/
|
||||
function testCreateField() {
|
||||
// Set a state flag so that field_test.module knows to add an in-memory
|
||||
// constraint for this field.
|
||||
\Drupal::state()->set('field_test_add_constraint', $this->fieldStorage->getName());
|
||||
/** @var \Drupal\Core\Field\FieldConfigInterface $field */
|
||||
$field = entity_create('field_config', $this->fieldDefinition);
|
||||
$field->save();
|
||||
|
||||
$field = FieldConfig::load($field->id());
|
||||
$this->assertTrue($field->getSetting('field_setting_from_config_data'));
|
||||
$this->assertNull($field->getSetting('config_data_from_field_setting'));
|
||||
|
||||
// Read the configuration. Check against raw configuration data rather than
|
||||
// the loaded ConfigEntity, to be sure we check that the defaults are
|
||||
// applied on write.
|
||||
$config = $this->config('field.field.' . $field->id())->get();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
$this->assertTrue($config['settings']['config_data_from_field_setting']);
|
||||
$this->assertTrue(!isset($config['settings']['field_setting_from_config_data']));
|
||||
|
||||
// Since we are working with raw configuration, this needs to be unset
|
||||
// manually.
|
||||
// @see Drupal\field_test\Plugin\Field\FieldType\TestItem::fieldSettingsFromConfigData()
|
||||
unset($config['settings']['config_data_from_field_setting']);
|
||||
|
||||
// Check that default values are set.
|
||||
$this->assertEqual($config['required'], FALSE, 'Required defaults to false.');
|
||||
$this->assertIdentical($config['label'], $this->fieldDefinition['field_name'], 'Label defaults to field name.');
|
||||
$this->assertIdentical($config['description'], '', 'Description defaults to empty string.');
|
||||
|
||||
// Check that default settings are set.
|
||||
$this->assertEqual($config['settings'], $field_type_manager->getDefaultFieldSettings($this->fieldStorageDefinition['type']) , 'Default field settings have been written.');
|
||||
|
||||
// Check that the denormalized 'field_type' was properly written.
|
||||
$this->assertEqual($config['field_type'], $this->fieldStorageDefinition['type']);
|
||||
|
||||
// Test constraints are applied. A Range constraint is added dynamically to
|
||||
// limit the field to values between 0 and 32.
|
||||
// @see field_test_entity_bundle_field_info_alter()
|
||||
$this->doFieldValidationTests();
|
||||
|
||||
// Test FieldConfigBase::setPropertyConstraints().
|
||||
\Drupal::state()->set('field_test_set_constraint', $this->fieldStorage->getName());
|
||||
\Drupal::state()->set('field_test_add_constraint', FALSE);
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
$this->doFieldValidationTests();
|
||||
|
||||
// Guarantee that the field/bundle combination is unique.
|
||||
try {
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
$this->fail(t('Cannot create two fields with the same field / bundle combination.'));
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass(t('Cannot create two fields with the same field / bundle combination.'));
|
||||
}
|
||||
|
||||
// Check that the specified field exists.
|
||||
try {
|
||||
$this->fieldDefinition['field_name'] = $this->randomMachineName();
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
$this->fail(t('Cannot create a field with a non-existing storage.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field with a non-existing storage.'));
|
||||
}
|
||||
|
||||
// TODO: test other failures.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading back a field definition.
|
||||
*/
|
||||
function testReadField() {
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
|
||||
// Read the field back.
|
||||
$field = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
|
||||
$this->assertTrue($this->fieldDefinition['field_name'] == $field->getName(), 'The field was properly read.');
|
||||
$this->assertTrue($this->fieldDefinition['entity_type'] == $field->getTargetEntityTypeId(), 'The field was properly read.');
|
||||
$this->assertTrue($this->fieldDefinition['bundle'] == $field->getTargetBundle(), 'The field was properly read.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the update of a field.
|
||||
*/
|
||||
function testUpdateField() {
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
|
||||
// Check that basic changes are saved.
|
||||
$field = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
|
||||
$field->setRequired(!$field->isRequired());
|
||||
$field->setLabel($this->randomMachineName());
|
||||
$field->set('description', $this->randomMachineName());
|
||||
$field->setSetting('test_field_setting', $this->randomMachineName());
|
||||
$field->save();
|
||||
|
||||
$field_new = FieldConfig::load('entity_test.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
|
||||
$this->assertEqual($field->isRequired(), $field_new->isRequired(), '"required" change is saved');
|
||||
$this->assertEqual($field->getLabel(), $field_new->getLabel(), '"label" change is saved');
|
||||
$this->assertEqual($field->getDescription(), $field_new->getDescription(), '"description" change is saved');
|
||||
|
||||
// TODO: test failures.
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the deletion of a field.
|
||||
*/
|
||||
function testDeleteField() {
|
||||
// TODO: Test deletion of the data stored in the field also.
|
||||
// Need to check that data for a 'deleted' field / storage doesn't get loaded
|
||||
// Need to check data marked deleted is cleaned on cron (not implemented yet...)
|
||||
|
||||
// Create two fields for the same field storage so we can test that only one
|
||||
// is deleted.
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
$another_field_definition = $this->fieldDefinition;
|
||||
$another_field_definition['bundle'] .= '_another_bundle';
|
||||
entity_test_create_bundle($another_field_definition['bundle']);
|
||||
entity_create('field_config', $another_field_definition)->save();
|
||||
|
||||
// Test that the first field is not deleted, and then delete it.
|
||||
$field = current(entity_load_multiple_by_properties('field_config', array('entity_type' => 'entity_test', 'field_name' => $this->fieldDefinition['field_name'], 'bundle' => $this->fieldDefinition['bundle'], 'include_deleted' => TRUE)));
|
||||
$this->assertTrue(!empty($field) && empty($field->deleted), 'A new field is not marked for deletion.');
|
||||
$field->delete();
|
||||
|
||||
// Make sure the field is marked as deleted when it is specifically loaded.
|
||||
$field = current(entity_load_multiple_by_properties('field_config', array('entity_type' => 'entity_test', 'field_name' => $this->fieldDefinition['field_name'], 'bundle' => $this->fieldDefinition['bundle'], 'include_deleted' => TRUE)));
|
||||
$this->assertTrue($field->isDeleted(), 'A deleted field is marked for deletion.');
|
||||
|
||||
// Try to load the field normally and make sure it does not show up.
|
||||
$field = FieldConfig::load('entity_test.' . '.' . $this->fieldDefinition['bundle'] . '.' . $this->fieldDefinition['field_name']);
|
||||
$this->assertTrue(empty($field), 'A deleted field is not loaded by default.');
|
||||
|
||||
// Make sure the other field is not deleted.
|
||||
$another_field = FieldConfig::load('entity_test.' . $another_field_definition['bundle'] . '.' . $another_field_definition['field_name']);
|
||||
$this->assertTrue(!empty($another_field) && empty($another_field->deleted), 'A non-deleted field is not marked for deletion.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cross deletion behavior between field storages and fields.
|
||||
*/
|
||||
function testDeleteFieldCrossDeletion() {
|
||||
$field_definition_2 = $this->fieldDefinition;
|
||||
$field_definition_2['bundle'] .= '_another_bundle';
|
||||
entity_test_create_bundle($field_definition_2['bundle']);
|
||||
|
||||
// Check that deletion of a field storage deletes its fields.
|
||||
$field_storage = $this->fieldStorage;
|
||||
entity_create('field_config', $this->fieldDefinition)->save();
|
||||
entity_create('field_config', $field_definition_2)->save();
|
||||
$field_storage->delete();
|
||||
$this->assertFalse(FieldConfig::loadByName('entity_test', $this->fieldDefinition['bundle'], $field_storage->getName()));
|
||||
$this->assertFalse(FieldConfig::loadByName('entity_test', $field_definition_2['bundle'], $field_storage->getName()));
|
||||
|
||||
// Check that deletion of the last field deletes the storage.
|
||||
$field_storage = entity_create('field_storage_config', $this->fieldStorageDefinition);
|
||||
$field_storage->save();
|
||||
$field = entity_create('field_config', $this->fieldDefinition);
|
||||
$field->save();
|
||||
$field_2 = entity_create('field_config', $field_definition_2);
|
||||
$field_2->save();
|
||||
$field->delete();
|
||||
$this->assertTrue(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
|
||||
$field_2->delete();
|
||||
$this->assertFalse(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
|
||||
|
||||
// Check that deletion of all fields using a storage simultaneously deletes
|
||||
// the storage.
|
||||
$field_storage = entity_create('field_storage_config', $this->fieldStorageDefinition);
|
||||
$field_storage->save();
|
||||
$field = entity_create('field_config', $this->fieldDefinition);
|
||||
$field->save();
|
||||
$field_2 = entity_create('field_config', $field_definition_2);
|
||||
$field_2->save();
|
||||
$this->container->get('entity.manager')->getStorage('field_config')->delete(array($field, $field_2));
|
||||
$this->assertFalse(FieldStorageConfig::loadByName('entity_test', $field_storage->getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configurable field validation.
|
||||
*
|
||||
* @see field_test_entity_bundle_field_info_alter()
|
||||
*/
|
||||
protected function doFieldValidationTests() {
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->set($this->fieldStorage->getName(), 1);
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual(count($violations), 0, 'No violations found when in-range value passed.');
|
||||
|
||||
$entity->set($this->fieldStorage->getName(), 33);
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violations found when using value outside the range.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), $this->fieldStorage->getName() . '.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value should be %limit or less.', [
|
||||
'%limit' => 32,
|
||||
]));
|
||||
}
|
||||
|
||||
}
|
139
core/modules/field/src/Tests/FieldDataCountTest.php
Normal file
139
core/modules/field/src/Tests/FieldDataCountTest.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldDataCountTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
|
||||
/**
|
||||
* Tests counting field data records and the hasData() method on
|
||||
* FieldStorageConfig entity.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\Core\Entity\FieldableEntityStorageInterface::countFieldData()
|
||||
* @see \Drupal\field\Entity\FieldStorageConfig::hasData()
|
||||
*/
|
||||
class FieldDataCountTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface
|
||||
*/
|
||||
protected $storageRev;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
$this->storage = \Drupal::entityManager()->getStorage('entity_test');
|
||||
$this->storageRev = \Drupal::entityManager()->getStorage('entity_test_rev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entityCount() and hadData() methods.
|
||||
*/
|
||||
public function testEntityCountAndHasData() {
|
||||
// Create a field with a cardinality of 2 to show that we are counting
|
||||
// entities and not rows in a table.
|
||||
/** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_int',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'integer',
|
||||
'cardinality' => 2,
|
||||
));
|
||||
$field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
$this->assertIdentical($field_storage->hasdata(), FALSE, 'There are no entities with field data.');
|
||||
$this->assertIdentical($this->storage->countFieldData($field_storage), 0, 'There are 0 entities with field data.');
|
||||
|
||||
// Create 1 entity without the field.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
$this->assertIdentical($field_storage->hasdata(), FALSE, 'There are no entities with field data.');
|
||||
$this->assertIdentical($this->storage->countFieldData($field_storage), 0, 'There are 0 entities with field data.');
|
||||
|
||||
// Create 12 entities to ensure that the purging works as expected.
|
||||
for ($i=0; $i < 12; $i++) {
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_int[] = mt_rand(1,99);
|
||||
$entity->field_int[] = mt_rand(1,99);
|
||||
$entity->name[] = $this->randomMachineName();
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
$storage = \Drupal::entityManager()->getStorage('entity_test');
|
||||
if ($storage instanceof SqlContentEntityStorage) {
|
||||
// Count the actual number of rows in the field table.
|
||||
$table_mapping = $storage->getTableMapping();
|
||||
$field_table_name = $table_mapping->getDedicatedDataTableName($field_storage);
|
||||
$result = db_select($field_table_name, 't')
|
||||
->fields('t')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($result, 24, 'The field table has 24 rows.');
|
||||
}
|
||||
|
||||
$this->assertIdentical($field_storage->hasdata(), TRUE, 'There are entities with field data.');
|
||||
$this->assertEqual($this->storage->countFieldData($field_storage), 12, 'There are 12 entities with field data.');
|
||||
|
||||
// Ensure the methods work on deleted fields.
|
||||
$field_storage->delete();
|
||||
$this->assertIdentical($field_storage->hasdata(), TRUE, 'There are entities with deleted field data.');
|
||||
$this->assertEqual($this->storage->countFieldData($field_storage), 12, 'There are 12 entities with deleted field data.');
|
||||
|
||||
field_purge_batch(6);
|
||||
$this->assertIdentical($field_storage->hasdata(), TRUE, 'There are entities with deleted field data.');
|
||||
$this->assertEqual($this->storage->countFieldData($field_storage), 6, 'There are 6 entities with deleted field data.');
|
||||
|
||||
$entity_type = 'entity_test_rev';
|
||||
$this->createFieldWithStorage('_2', $entity_type);
|
||||
|
||||
$entity_init = entity_create($entity_type, array(
|
||||
'type' => $entity_type,
|
||||
));
|
||||
$cardinality = $this->fieldTestData->field_storage_2->getCardinality();
|
||||
|
||||
$this->assertIdentical($this->fieldTestData->field_storage_2->hasData(), FALSE, 'There are no entities with field data.');
|
||||
$this->assertIdentical($this->storageRev->countFieldData($this->fieldTestData->field_storage_2), 0, 'There are 0 entities with field data.');
|
||||
|
||||
// Create 1 entity with the field.
|
||||
$entity = clone($entity_init);
|
||||
$values = $this->_generateTestFieldValues($this->fieldTestData->field_storage_2->getCardinality());
|
||||
$entity->{$this->fieldTestData->field_name_2} = $values;
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
$first_revision = $entity->getRevisionId();
|
||||
|
||||
$this->assertIdentical($this->fieldTestData->field_storage_2->hasData(), TRUE, 'There are entities with field data.');
|
||||
$this->assertIdentical($this->storageRev->countFieldData($this->fieldTestData->field_storage_2), 1, 'There is 1 entity with field data.');
|
||||
|
||||
$entity->{$this->fieldTestData->field_name_2} = array();
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
|
||||
$this->assertIdentical($this->fieldTestData->field_storage_2->hasData(), TRUE, 'There are entities with field data.');
|
||||
|
||||
$storage = $this->container->get('entity.manager')->getStorage($entity_type);
|
||||
$entity = $storage->loadRevision($first_revision);
|
||||
$this->assertEqual(count($entity->{$this->fieldTestData->field_name_2}), $cardinality, format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $first_revision)));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldDefinitionIntegrityTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Extension\Extension;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the integrity of field API plugin definitions.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldDefinitionIntegrityTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable all core modules that provide field plugins.
|
||||
$modules = system_rebuild_module_data();
|
||||
$modules = array_filter($modules, function (Extension $module) {
|
||||
// Filter contrib, hidden, already enabled modules and modules in the
|
||||
// Testing package.
|
||||
if ($module->origin === 'core'
|
||||
&& empty($module->info['hidden'])
|
||||
&& $module->status == FALSE
|
||||
&& $module->info['package'] !== 'Testing'
|
||||
&& is_readable($module->getPath() . '/src/Plugin/Field')) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
});
|
||||
$this->enableModules(array_keys($modules));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the integrity of field plugin definitions.
|
||||
*/
|
||||
public function testFieldPluginDefinitionIntegrity() {
|
||||
// Load the IDs of all available field type plugins.
|
||||
$available_field_type_ids = [];
|
||||
/** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_type_manager */
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
foreach ($field_type_manager->getDefinitions() as $definition) {
|
||||
$available_field_type_ids[] = $definition['id'];
|
||||
}
|
||||
|
||||
// Test the field widget plugins.
|
||||
/** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_widget_manager */
|
||||
$field_widget_manager = \Drupal::service('plugin.manager.field.widget');
|
||||
foreach ($field_widget_manager->getDefinitions() as $definition) {
|
||||
$missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids);
|
||||
if ($missing_field_type_ids) {
|
||||
$this->fail(sprintf('Field widget %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids)));
|
||||
}
|
||||
else {
|
||||
$this->pass(sprintf('Field widget %s integrates with existing field types.', $definition['id']));
|
||||
}
|
||||
}
|
||||
|
||||
// Test the field formatter plugins.
|
||||
/** @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface $field_formatter_manager */
|
||||
$field_formatter_manager = \Drupal::service('plugin.manager.field.formatter');
|
||||
foreach ($field_formatter_manager->getDefinitions() as $definition) {
|
||||
$missing_field_type_ids = array_diff($definition['field_types'], $available_field_type_ids);
|
||||
if ($missing_field_type_ids) {
|
||||
$this->fail(sprintf('Field formatter %s integrates with non-existent field types: %s', $definition['id'], implode(', ', $missing_field_type_ids)));
|
||||
}
|
||||
else {
|
||||
$this->pass(sprintf('Field formatter %s integrates with existing field types.', $definition['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
64
core/modules/field/src/Tests/FieldHelpTest.php
Normal file
64
core/modules/field/src/Tests/FieldHelpTest.php
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldHelpTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests help display for the Field module.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldHelpTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array.
|
||||
*/
|
||||
public static $modules = array('field', 'help');
|
||||
|
||||
// Tests field help implementation without optional core modules enabled.
|
||||
protected $profile = 'minimal';
|
||||
|
||||
/**
|
||||
* The admin user that will be created.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the admin user.
|
||||
$this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Field module's help page.
|
||||
*/
|
||||
public function testFieldHelp() {
|
||||
// Login the admin user.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Visit the Help page and make sure no warnings or notices are thrown.
|
||||
$this->drupalGet('admin/help/field');
|
||||
|
||||
// Enable the Options, Email and Field API Test modules.
|
||||
\Drupal::service('module_installer')->install(array('options', 'field_test'));
|
||||
$this->resetAll();
|
||||
\Drupal::service('plugin.manager.field.widget')->clearCachedDefinitions();
|
||||
\Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
|
||||
|
||||
$this->drupalGet('admin/help/field');
|
||||
$this->assertLink('Options', 0, 'Options module is listed on the Field help page.');
|
||||
$this->assertText('Field API Test', 'Modules with field types that do not implement hook_help are listed.');
|
||||
$this->assertNoLink('Field API Test', 'Modules with field types that do not implement hook_help are not linked.');
|
||||
$this->assertNoLink('Link', 'Modules that have not been installed, are not listed.');
|
||||
}
|
||||
|
||||
}
|
57
core/modules/field/src/Tests/FieldImportChangeTest.php
Normal file
57
core/modules/field/src/Tests/FieldImportChangeTest.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldImportChangeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
/**
|
||||
* Update field storage and fields during config change method invocation.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldImportChangeTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* The default configuration provided by field_test_config is imported by
|
||||
* \Drupal\field\Tests\FieldUnitTestBase::setUp() when it installs field
|
||||
* configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test_config');
|
||||
|
||||
/**
|
||||
* Tests importing an updated field.
|
||||
*/
|
||||
function testImportChange() {
|
||||
$this->installConfig(['field_test_config']);
|
||||
$field_storage_id = 'field_test_import';
|
||||
$field_id = "entity_test.entity_test.$field_storage_id";
|
||||
$field_config_name = "field.field.$field_id";
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Save as files in the staging directory.
|
||||
$field = $active->read($field_config_name);
|
||||
$new_label = 'Test update import field';
|
||||
$field['label'] = $new_label;
|
||||
$staging->write($field_config_name, $field);
|
||||
|
||||
// Import the content of the staging directory.
|
||||
$this->configImporter()->import();
|
||||
|
||||
// Check that the updated config was correctly imported.
|
||||
$field = FieldConfig::load($field_id);
|
||||
$this->assertEqual($field->getLabel(), $new_label, 'field label updated');
|
||||
}
|
||||
}
|
||||
|
124
core/modules/field/src/Tests/FieldImportCreateTest.php
Normal file
124
core/modules/field/src/Tests/FieldImportCreateTest.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldImportCreateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Create field storages and fields during config create method invocation.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldImportCreateTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Tests creating field storages and fields during default config import.
|
||||
*/
|
||||
function testImportCreateDefault() {
|
||||
$field_name = 'field_test_import';
|
||||
$field_storage_id = "entity_test.$field_name";
|
||||
$field_id = "entity_test.entity_test.$field_name";
|
||||
$field_name_2 = 'field_test_import_2';
|
||||
$field_storage_id_2 = "entity_test.$field_name_2";
|
||||
$field_id_2a = "entity_test.entity_test.$field_name_2";
|
||||
$field_id_2b = "entity_test.test_bundle.$field_name_2";
|
||||
|
||||
// Check that the field storages and fields do not exist yet.
|
||||
$this->assertFalse(FieldStorageConfig::load($field_storage_id));
|
||||
$this->assertFalse(FieldConfig::load($field_id));
|
||||
$this->assertFalse(FieldStorageConfig::load($field_storage_id_2));
|
||||
$this->assertFalse(FieldConfig::load($field_id_2a));
|
||||
$this->assertFalse(FieldConfig::load($field_id_2b));
|
||||
|
||||
// Create a second bundle for the 'Entity test' entity type.
|
||||
entity_test_create_bundle('test_bundle');
|
||||
|
||||
// Enable field_test_config module and check that the field and storage
|
||||
// shipped in the module's default config were created.
|
||||
\Drupal::service('module_installer')->install(array('field_test_config'));
|
||||
|
||||
// A field storage with one single field.
|
||||
$field_storage = FieldStorageConfig::load($field_storage_id);
|
||||
$this->assertTrue($field_storage, 'The field was created.');
|
||||
$field = FieldConfig::load($field_id);
|
||||
$this->assertTrue($field, 'The field was deleted.');
|
||||
|
||||
// A field storage with two fields.
|
||||
$field_storage_2 = FieldStorageConfig::load($field_storage_id_2);
|
||||
$this->assertTrue($field_storage_2, 'The second field was created.');
|
||||
$this->assertTrue($field->getTargetBundle(), 'test_bundle', 'The second field was created on bundle test_bundle.');
|
||||
$this->assertTrue($field->getTargetBundle(), 'test_bundle_2', 'The second field was created on bundle test_bundle_2.');
|
||||
|
||||
// Tests fields.
|
||||
$ids = \Drupal::entityQuery('field_config')
|
||||
->condition('entity_type', 'entity_test')
|
||||
->condition('bundle', 'entity_test')
|
||||
->execute();
|
||||
$this->assertEqual(count($ids), 2);
|
||||
$this->assertTrue(isset($ids['entity_test.entity_test.field_test_import']));
|
||||
$this->assertTrue(isset($ids['entity_test.entity_test.field_test_import_2']));
|
||||
$ids = \Drupal::entityQuery('field_config')
|
||||
->condition('entity_type', 'entity_test')
|
||||
->condition('bundle', 'test_bundle')
|
||||
->execute();
|
||||
$this->assertEqual(count($ids), 1);
|
||||
$this->assertTrue(isset($ids['entity_test.test_bundle.field_test_import_2']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating field storages and fields during config import.
|
||||
*/
|
||||
function testImportCreate() {
|
||||
// A field storage with one single field.
|
||||
$field_name = 'field_test_import_staging';
|
||||
$field_storage_id = "entity_test.$field_name";
|
||||
$field_id = "entity_test.entity_test.$field_name";
|
||||
$field_storage_config_name = "field.storage.$field_storage_id";
|
||||
$field_config_name = "field.field.$field_id";
|
||||
|
||||
// A field storage with two fields.
|
||||
$field_name_2 = 'field_test_import_staging_2';
|
||||
$field_storage_id_2 = "entity_test.$field_name_2";
|
||||
$field_id_2a = "entity_test.test_bundle.$field_name_2";
|
||||
$field_id_2b = "entity_test.test_bundle_2.$field_name_2";
|
||||
$field_storage_config_name_2 = "field.storage.$field_storage_id_2";
|
||||
$field_config_name_2a = "field.field.$field_id_2a";
|
||||
$field_config_name_2b = "field.field.$field_id_2b";
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Add the new files to the staging directory.
|
||||
$src_dir = drupal_get_path('module', 'field_test_config') . '/staging';
|
||||
$target_dir = $this->configDirectories[CONFIG_STAGING_DIRECTORY];
|
||||
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_storage_config_name.yml", "$target_dir/$field_storage_config_name.yml"));
|
||||
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name.yml", "$target_dir/$field_config_name.yml"));
|
||||
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_storage_config_name_2.yml", "$target_dir/$field_storage_config_name_2.yml"));
|
||||
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2a.yml", "$target_dir/$field_config_name_2a.yml"));
|
||||
$this->assertTrue(file_unmanaged_copy("$src_dir/$field_config_name_2b.yml", "$target_dir/$field_config_name_2b.yml"));
|
||||
|
||||
// Import the content of the staging directory.
|
||||
$this->configImporter()->import();
|
||||
|
||||
// Check that the field and storage were created.
|
||||
$field_storage = FieldStorageConfig::load($field_storage_id);
|
||||
$this->assertTrue($field_storage, 'Test import storage field from staging exists');
|
||||
$field = FieldConfig::load($field_id);
|
||||
$this->assertTrue($field, 'Test import field from staging exists');
|
||||
$field_storage = FieldStorageConfig::load($field_storage_id_2);
|
||||
$this->assertTrue($field_storage, 'Test import storage field 2 from staging exists');
|
||||
$field = FieldConfig::load($field_id_2a);
|
||||
$this->assertTrue($field, 'Test import field 2a from staging exists');
|
||||
$field = FieldConfig::load($field_id_2b);
|
||||
$this->assertTrue($field, 'Test import field 2b from staging exists');
|
||||
}
|
||||
}
|
||||
|
117
core/modules/field/src/Tests/FieldImportDeleteTest.php
Normal file
117
core/modules/field/src/Tests/FieldImportDeleteTest.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldImportDeleteTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Delete field storages and fields during config delete method invocation.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldImportDeleteTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* The default configuration provided by field_test_config is imported by
|
||||
* \Drupal\field\Tests\FieldUnitTestBase::setUp() when it installs field
|
||||
* configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test_config');
|
||||
|
||||
/**
|
||||
* Tests deleting field storages and fields as part of config import.
|
||||
*/
|
||||
public function testImportDelete() {
|
||||
$this->installConfig(['field_test_config']);
|
||||
// At this point there are 5 field configuration objects in the active
|
||||
// storage.
|
||||
// - field.storage.entity_test.field_test_import
|
||||
// - field.storage.entity_test.field_test_import_2
|
||||
// - field.field.entity_test.entity_test.field_test_import
|
||||
// - field.field.entity_test.entity_test.field_test_import_2
|
||||
// - field.field.entity_test.test_bundle.field_test_import_2
|
||||
|
||||
$field_name = 'field_test_import';
|
||||
$field_storage_id = "entity_test.$field_name";
|
||||
$field_name_2 = 'field_test_import_2';
|
||||
$field_storage_id_2 = "entity_test.$field_name_2";
|
||||
$field_id = "entity_test.entity_test.$field_name";
|
||||
$field_id_2a = "entity_test.entity_test.$field_name_2";
|
||||
$field_id_2b = "entity_test.test_bundle.$field_name_2";
|
||||
$field_storage_config_name = "field.storage.$field_storage_id";
|
||||
$field_storage_config_name_2 = "field.storage.$field_storage_id_2";
|
||||
$field_config_name = "field.field.$field_id";
|
||||
$field_config_name_2a = "field.field.$field_id_2a";
|
||||
$field_config_name_2b = "field.field.$field_id_2b";
|
||||
|
||||
// Create a second bundle for the 'Entity test' entity type.
|
||||
entity_test_create_bundle('test_bundle');
|
||||
|
||||
// Get the uuid's for the field storages.
|
||||
$field_storage_uuid = FieldStorageConfig::load($field_storage_id)->uuid();
|
||||
$field_storage_uuid_2 = FieldStorageConfig::load($field_storage_id_2)->uuid();
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
$this->assertTrue($staging->delete($field_storage_config_name), SafeMarkup::format('Deleted field storage: !field_storage', array('!field_storage' => $field_storage_config_name)));
|
||||
$this->assertTrue($staging->delete($field_storage_config_name_2), SafeMarkup::format('Deleted field storage: !field_storage', array('!field_storage' => $field_storage_config_name_2)));
|
||||
$this->assertTrue($staging->delete($field_config_name), SafeMarkup::format('Deleted field: !field', array('!field' => $field_config_name)));
|
||||
$this->assertTrue($staging->delete($field_config_name_2a), SafeMarkup::format('Deleted field: !field', array('!field' => $field_config_name_2a)));
|
||||
$this->assertTrue($staging->delete($field_config_name_2b), SafeMarkup::format('Deleted field: !field', array('!field' => $field_config_name_2b)));
|
||||
|
||||
$deletes = $this->configImporter()->getUnprocessedConfiguration('delete');
|
||||
$this->assertEqual(count($deletes), 5, 'Importing configuration will delete 3 fields and 2 field storages.');
|
||||
|
||||
// Import the content of the staging directory.
|
||||
$this->configImporter()->import();
|
||||
|
||||
// Check that the field storages and fields are gone.
|
||||
\Drupal::entityManager()->getStorage('field_storage_config')->resetCache(array($field_storage_id));
|
||||
$field_storage = FieldStorageConfig::load($field_storage_id);
|
||||
$this->assertFalse($field_storage, 'The field storage was deleted.');
|
||||
\Drupal::entityManager()->getStorage('field_storage_config')->resetCache(array($field_storage_id_2));
|
||||
$field_storage_2 = FieldStorageConfig::load($field_storage_id_2);
|
||||
$this->assertFalse($field_storage_2, 'The second field storage was deleted.');
|
||||
\Drupal::entityManager()->getStorage('field_config')->resetCache(array($field_id));
|
||||
$field = FieldConfig::load($field_id);
|
||||
$this->assertFalse($field, 'The field was deleted.');
|
||||
\Drupal::entityManager()->getStorage('field_config')->resetCache(array($field_id_2a));
|
||||
$field_2a = FieldConfig::load($field_id_2a);
|
||||
$this->assertFalse($field_2a, 'The second field on test bundle was deleted.');
|
||||
\Drupal::entityManager()->getStorage('field_config')->resetCache(array($field_id_2b));
|
||||
$field_2b = FieldConfig::load($field_id_2b);
|
||||
$this->assertFalse($field_2b, 'The second field on test bundle 2 was deleted.');
|
||||
|
||||
// Check that all config files are gone.
|
||||
$active = $this->container->get('config.storage');
|
||||
$this->assertIdentical($active->listAll($field_storage_config_name), array());
|
||||
$this->assertIdentical($active->listAll($field_storage_config_name_2), array());
|
||||
$this->assertIdentical($active->listAll($field_config_name), array());
|
||||
$this->assertIdentical($active->listAll($field_config_name_2a), array());
|
||||
$this->assertIdentical($active->listAll($field_config_name_2b), array());
|
||||
|
||||
// Check that the storage definition is preserved in state.
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertTrue(isset($deleted_storages[$field_storage_uuid]));
|
||||
$this->assertTrue(isset($deleted_storages[$field_storage_uuid_2]));
|
||||
|
||||
// Purge field data, and check that the storage definition has been
|
||||
// completely removed once the data is purged.
|
||||
field_purge_batch(10);
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertTrue(empty($deleted_storages), 'Fields are deleted');
|
||||
}
|
||||
}
|
||||
|
169
core/modules/field/src/Tests/FieldImportDeleteUninstallTest.php
Normal file
169
core/modules/field/src/Tests/FieldImportDeleteUninstallTest.php
Normal file
|
@ -0,0 +1,169 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldImportDeleteUninstallTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Delete field storages and fields during config synchronization and uninstall
|
||||
* module that provides the field type.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\field\ConfigImporterFieldPurger
|
||||
* @see field_config_import_steps_alter()
|
||||
*/
|
||||
class FieldImportDeleteUninstallTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('telephone');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Module uninstall requires the router and users_data tables.
|
||||
// @see drupal_flush_all_caches()
|
||||
// @see user_modules_uninstalled()
|
||||
$this->installSchema('user', array('users_data'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting field storages and fields as part of config import.
|
||||
*/
|
||||
public function testImportDeleteUninstall() {
|
||||
// Create a field to delete to prove that
|
||||
// \Drupal\field\ConfigImporterFieldPurger does not purge fields that are
|
||||
// not related to the configuration synchronization.
|
||||
$unrelated_field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_int',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'integer',
|
||||
));
|
||||
$unrelated_field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $unrelated_field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create a telephone field for validation.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
$entity = entity_create('entity_test');
|
||||
$value = '+0123456789';
|
||||
$entity->field_test = $value;
|
||||
$entity->field_int = '99';
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_test->value, $value);
|
||||
$this->assertEqual($entity->field_test[0]->value, $value);
|
||||
$this->assertEqual($entity->field_int->value, '99');
|
||||
|
||||
// Delete unrelated field before copying configuration and running the
|
||||
// synchronization.
|
||||
$unrelated_field_storage->delete();
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Stage uninstall of the Telephone module.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['telephone']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
// Stage the field deletion
|
||||
$staging->delete('field.storage.entity_test.field_test');
|
||||
$staging->delete('field.field.entity_test.entity_test.field_test');
|
||||
|
||||
$steps = $this->configImporter()->initialize();
|
||||
$this->assertIdentical($steps[0], array('\Drupal\field\ConfigImporterFieldPurger', 'process'), 'The additional process configuration synchronization step has been added.');
|
||||
|
||||
// This will purge all the data, delete the field and uninstall the
|
||||
// Telephone module.
|
||||
$this->configImporter()->import();
|
||||
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('telephone'));
|
||||
$this->assertFalse(\Drupal::entityManager()->loadEntityByUuid('field_storage_config', $field_storage->uuid()), 'The test field has been deleted by the configuration synchronization');
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage->uuid()]), 'Telephone field has been completed removed from the system.');
|
||||
$this->assertTrue(isset($deleted_storages[$unrelated_field_storage->uuid()]), 'Unrelated field not purged by configuration synchronization.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests purging already deleted field storages and fields during a config
|
||||
* import.
|
||||
*/
|
||||
public function testImportAlreadyDeletedUninstall() {
|
||||
// Create a telephone field for validation.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
$field_storage_uuid = $field_storage->uuid();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create 12 entities to ensure that the purging works as expected.
|
||||
for ($i=0; $i < 12; $i++) {
|
||||
$entity = entity_create('entity_test');
|
||||
$value = '+0123456789';
|
||||
$entity->field_test = $value;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_test->value, $value);
|
||||
}
|
||||
|
||||
// Delete the field.
|
||||
$field_storage->delete();
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Stage uninstall of the Telephone module.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['telephone']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertTrue(isset($deleted_storages[$field_storage_uuid]), 'Field has been deleted and needs purging before configuration synchronization.');
|
||||
|
||||
$steps = $this->configImporter()->initialize();
|
||||
$this->assertIdentical($steps[0], array('\Drupal\field\ConfigImporterFieldPurger', 'process'), 'The additional process configuration synchronization step has been added.');
|
||||
|
||||
// This will purge all the data, delete the field and uninstall the
|
||||
// Telephone module.
|
||||
$this->configImporter()->import();
|
||||
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('telephone'));
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage_uuid]), 'Field has been completed removed from the system.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldImportDeleteUninstallUiTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Delete field storages and fields during config synchronization and uninstall
|
||||
* module that provides the field type through the UI.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\field\ConfigImporterFieldPurger
|
||||
* @see field_config_import_steps_alter()
|
||||
* @see field_form_config_admin_import_form_alter()
|
||||
*/
|
||||
class FieldImportDeleteUninstallUiTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_test', 'telephone', 'config', 'filter', 'datetime');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('synchronize configuration')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting field storages and fields as part of config import.
|
||||
*/
|
||||
public function testImportDeleteUninstall() {
|
||||
// Create a telephone field.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_tel',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create a text field.
|
||||
$date_field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_date',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'datetime',
|
||||
));
|
||||
$date_field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $date_field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
// Create an entity which has values for the telephone and text field.
|
||||
$entity = entity_create('entity_test');
|
||||
$value = '+0123456789';
|
||||
$entity->field_tel = $value;
|
||||
$entity->field_date = time();
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Delete the text field before exporting configuration so that we can test
|
||||
// that deleted fields that are provided by modules that will be uninstalled
|
||||
// are also purged and that the UI message includes such fields.
|
||||
$date_field_storage->delete();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_tel->value, $value);
|
||||
$this->assertEqual($entity->field_tel[0]->value, $value);
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$staging = $this->container->get('config.storage.staging');
|
||||
$this->copyConfig($active, $staging);
|
||||
|
||||
// Stage uninstall of the Telephone module.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['telephone']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
// Stage the field deletion
|
||||
$staging->delete('field.storage.entity_test.field_tel');
|
||||
$staging->delete('field.field.entity_test.entity_test.field_tel');
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
// Test that the message for one field being purged during a configuration
|
||||
// synchronization is correct.
|
||||
$this->assertText('This synchronization will delete data from the field entity_test.field_tel.');
|
||||
|
||||
// Stage an uninstall of the datetime module to test the message for
|
||||
// multiple fields.
|
||||
unset($core_extension['module']['datetime']);
|
||||
$staging->write('core.extension', $core_extension);
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText('This synchronization will delete data from the fields: entity_test.field_tel, entity_test.field_date.');
|
||||
|
||||
// This will purge all the data, delete the field and uninstall the
|
||||
// Telephone and Text modules.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertNoText('Field data will be deleted by this synchronization.');
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('telephone'));
|
||||
$this->assertFalse(\Drupal::entityManager()->loadEntityByUuid('field_storage_config', $field_storage->uuid()), 'The telephone field has been deleted by the configuration synchronization');
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage->uuid()]), 'Telephone field has been completed removed from the system.');
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage->uuid()]), 'Text field has been completed removed from the system.');
|
||||
}
|
||||
|
||||
}
|
461
core/modules/field/src/Tests/FieldStorageCrudTest.php
Normal file
461
core/modules/field/src/Tests/FieldStorageCrudTest.php
Normal file
|
@ -0,0 +1,461 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldStorageCrudTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests field storage create, read, update, and delete.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldStorageCrudTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array();
|
||||
|
||||
// TODO : test creation with
|
||||
// - a full fledged $field structure, check that all the values are there
|
||||
// - a minimal $field structure, check all default values are set
|
||||
// defer actual $field comparison to a helper function, used for the two cases above
|
||||
|
||||
/**
|
||||
* Test the creation of a field storage.
|
||||
*/
|
||||
function testCreate() {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'field_2',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
field_test_memorize();
|
||||
$field_storage = entity_create('field_storage_config', $field_storage_definition);
|
||||
$field_storage->save();
|
||||
|
||||
$field_storage = FieldStorageConfig::load($field_storage->id());
|
||||
$this->assertTrue($field_storage->getSetting('storage_setting_from_config_data'));
|
||||
$this->assertNull($field_storage->getSetting('config_data_from_storage_setting'));
|
||||
|
||||
$mem = field_test_memorize();
|
||||
$this->assertIdentical($mem['field_test_field_storage_config_create'][0][0]->getName(), $field_storage_definition['field_name'], 'hook_entity_create() called with correct arguments.');
|
||||
$this->assertIdentical($mem['field_test_field_storage_config_create'][0][0]->getType(), $field_storage_definition['type'], 'hook_entity_create() called with correct arguments.');
|
||||
|
||||
// Read the configuration. Check against raw configuration data rather than
|
||||
// the loaded ConfigEntity, to be sure we check that the defaults are
|
||||
// applied on write.
|
||||
$field_storage_config = $this->config('field.storage.' . $field_storage->id())->get();
|
||||
|
||||
$this->assertTrue($field_storage_config['settings']['config_data_from_storage_setting']);
|
||||
$this->assertTrue(!isset($field_storage_config['settings']['storage_setting_from_config_data']));
|
||||
|
||||
// Since we are working with raw configuration, this needs to be unset
|
||||
// manually.
|
||||
// @see Drupal\field_test\Plugin\Field\FieldType\TestItem::storageSettingsFromConfigData()
|
||||
unset($field_storage_config['settings']['config_data_from_storage_setting']);
|
||||
|
||||
// Ensure that basic properties are preserved.
|
||||
$this->assertEqual($field_storage_config['field_name'], $field_storage_definition['field_name'], 'The field name is properly saved.');
|
||||
$this->assertEqual($field_storage_config['entity_type'], $field_storage_definition['entity_type'], 'The field entity type is properly saved.');
|
||||
$this->assertEqual($field_storage_config['id'], $field_storage_definition['entity_type'] . '.' . $field_storage_definition['field_name'], 'The field id is properly saved.');
|
||||
$this->assertEqual($field_storage_config['type'], $field_storage_definition['type'], 'The field type is properly saved.');
|
||||
|
||||
// Ensure that cardinality defaults to 1.
|
||||
$this->assertEqual($field_storage_config['cardinality'], 1, 'Cardinality defaults to 1.');
|
||||
|
||||
// Ensure that default settings are present.
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
$this->assertEqual($field_storage_config['settings'], $field_type_manager->getDefaultStorageSettings($field_storage_definition['type']), 'Default storage settings have been written.');
|
||||
|
||||
// Guarantee that the name is unique.
|
||||
try {
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create two fields with the same name.'));
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass(t('Cannot create two fields with the same name.'));
|
||||
}
|
||||
|
||||
// Check that field type is required.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'field_1',
|
||||
'entity_type' => 'entity_type',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create a field with no type.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field with no type.'));
|
||||
}
|
||||
|
||||
// Check that field name is required.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'type' => 'test_field',
|
||||
'entity_type' => 'entity_test',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create an unnamed field.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create an unnamed field.'));
|
||||
}
|
||||
// Check that entity type is required.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'test_field',
|
||||
'type' => 'test_field'
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail('Cannot create a field without an entity type.');
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass('Cannot create a field without an entity type.');
|
||||
}
|
||||
|
||||
// Check that field name must start with a letter or _.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => '2field_2',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create a field with a name starting with a digit.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field with a name starting with a digit.'));
|
||||
}
|
||||
|
||||
// Check that field name must only contain lowercase alphanumeric or _.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'field#_3',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create a field with a name containing an illegal character.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field with a name containing an illegal character.'));
|
||||
}
|
||||
|
||||
// Check that field name cannot be longer than 32 characters long.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => '_12345678901234567890123456789012',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create a field with a name longer than 32 characters.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field with a name longer than 32 characters.'));
|
||||
}
|
||||
|
||||
// Check that field name can not be an entity key.
|
||||
// "id" is known as an entity key from the "entity_test" type.
|
||||
try {
|
||||
$field_storage_definition = array(
|
||||
'type' => 'test_field',
|
||||
'field_name' => 'id',
|
||||
'entity_type' => 'entity_test',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$this->fail(t('Cannot create a field bearing the name of an entity key.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot create a field bearing the name of an entity key.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an explicit schema can be provided on creation.
|
||||
*
|
||||
* This behavior is needed to allow field storage creation within updates,
|
||||
* since plugin classes (and thus the field type schema) cannot be accessed.
|
||||
*/
|
||||
function testCreateWithExplicitSchema() {
|
||||
$schema = array(
|
||||
'dummy' => 'foobar'
|
||||
);
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_2',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'schema' => $schema,
|
||||
));
|
||||
$this->assertEqual($field_storage->getSchema(), $schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reading field storage definitions.
|
||||
*/
|
||||
function testRead() {
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'field_1',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$field_storage = entity_create('field_storage_config', $field_storage_definition);
|
||||
$field_storage->save();
|
||||
$id = $field_storage->id();
|
||||
|
||||
// Check that 'single column' criteria works.
|
||||
$fields = entity_load_multiple_by_properties('field_storage_config', array('field_name' => $field_storage_definition['field_name']));
|
||||
$this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
|
||||
|
||||
// Check that 'multi column' criteria works.
|
||||
$fields = entity_load_multiple_by_properties('field_storage_config', array('field_name' => $field_storage_definition['field_name'], 'type' => $field_storage_definition['type']));
|
||||
$this->assertTrue(count($fields) == 1 && isset($fields[$id]), 'The field was properly read.');
|
||||
$fields = entity_load_multiple_by_properties('field_storage_config', array('field_name' => $field_storage_definition['field_name'], 'type' => 'foo'));
|
||||
$this->assertTrue(empty($fields), 'No field was found.');
|
||||
|
||||
// Create a field from the field storage.
|
||||
$field_definition = array(
|
||||
'field_name' => $field_storage_definition['field_name'],
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
);
|
||||
entity_create('field_config', $field_definition)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creation of indexes on data column.
|
||||
*/
|
||||
function testIndexes() {
|
||||
// Check that indexes specified by the field type are used by default.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_1',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
));
|
||||
$field_storage->save();
|
||||
$field_storage = FieldStorageConfig::load($field_storage->id());
|
||||
$schema = $field_storage->getSchema();
|
||||
$expected_indexes = array('value' => array('value'));
|
||||
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field type indexes saved by default');
|
||||
|
||||
// Check that indexes specified by the field definition override the field
|
||||
// type indexes.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_2',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'indexes' => array(
|
||||
'value' => array(),
|
||||
),
|
||||
));
|
||||
$field_storage->save();
|
||||
$field_storage = FieldStorageConfig::load($field_storage->id());
|
||||
$schema = $field_storage->getSchema();
|
||||
$expected_indexes = array('value' => array());
|
||||
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes override field type indexes');
|
||||
|
||||
// Check that indexes specified by the field definition add to the field
|
||||
// type indexes.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_3',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'indexes' => array(
|
||||
'value_2' => array('value'),
|
||||
),
|
||||
));
|
||||
$field_storage->save();
|
||||
$id = $field_storage->id();
|
||||
$field_storage = FieldStorageConfig::load($id);
|
||||
$schema = $field_storage->getSchema();
|
||||
$expected_indexes = array('value' => array('value'), 'value_2' => array('value'));
|
||||
$this->assertEqual($schema['indexes'], $expected_indexes, 'Field definition indexes are merged with field type indexes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the deletion of a field storage.
|
||||
*/
|
||||
function testDelete() {
|
||||
// TODO: Also test deletion of the data stored in the field ?
|
||||
|
||||
// Create two fields (so we can test that only one is deleted).
|
||||
$field_storage_definition = array(
|
||||
'field_name' => 'field_1',
|
||||
'type' => 'test_field',
|
||||
'entity_type' => 'entity_test',
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
$another_field_storage_definition = array(
|
||||
'field_name' => 'field_2',
|
||||
'type' => 'test_field',
|
||||
'entity_type' => 'entity_test',
|
||||
);
|
||||
entity_create('field_storage_config', $another_field_storage_definition)->save();
|
||||
|
||||
// Create fields for each.
|
||||
$field_definition = array(
|
||||
'field_name' => $field_storage_definition['field_name'],
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
);
|
||||
entity_create('field_config', $field_definition)->save();
|
||||
$another_field_definition = $field_definition;
|
||||
$another_field_definition['field_name'] = $another_field_storage_definition['field_name'];
|
||||
entity_create('field_config', $another_field_definition)->save();
|
||||
|
||||
// Test that the first field is not deleted, and then delete it.
|
||||
$field_storage = current(entity_load_multiple_by_properties('field_storage_config', array('field_name' => $field_storage_definition['field_name'], 'include_deleted' => TRUE)));
|
||||
$this->assertTrue(!empty($field_storage) && !$field_storage->isDeleted(), 'A new storage is not marked for deletion.');
|
||||
FieldStorageConfig::loadByName('entity_test', $field_storage_definition['field_name'])->delete();
|
||||
|
||||
// Make sure that the field is marked as deleted when it is specifically
|
||||
// loaded.
|
||||
$field_storage = current(entity_load_multiple_by_properties('field_storage_config', array('field_name' => $field_storage_definition['field_name'], 'include_deleted' => TRUE)));
|
||||
$this->assertTrue($field_storage->isDeleted(), 'A deleted storage is marked for deletion.');
|
||||
|
||||
// Make sure that this field is marked as deleted when it is
|
||||
// specifically loaded.
|
||||
$field = current(entity_load_multiple_by_properties('field_config', array('entity_type' => 'entity_test', 'field_name' => $field_definition['field_name'], 'bundle' => $field_definition['bundle'], 'include_deleted' => TRUE)));
|
||||
$this->assertTrue($field->isDeleted(), 'A field whose storage was deleted is marked for deletion.');
|
||||
|
||||
// Try to load the storage normally and make sure it does not show up.
|
||||
$field_storage = FieldStorageConfig::load('entity_test.' . $field_storage_definition['field_name']);
|
||||
$this->assertTrue(empty($field_storage), 'A deleted storage is not loaded by default.');
|
||||
|
||||
// Try to load the field normally and make sure it does not show up.
|
||||
$field = FieldConfig::load('entity_test.' . '.' . $field_definition['bundle'] . '.' . $field_definition['field_name']);
|
||||
$this->assertTrue(empty($field), 'A field whose storage was deleted is not loaded by default.');
|
||||
|
||||
// Make sure the other field and its storage are not deleted.
|
||||
$another_field_storage = FieldStorageConfig::load('entity_test.' . $another_field_storage_definition['field_name']);
|
||||
$this->assertTrue(!empty($another_field_storage) && !$another_field_storage->isDeleted(), 'A non-deleted storage is not marked for deletion.');
|
||||
$another_field = FieldConfig::load('entity_test.' . $another_field_definition['bundle'] . '.' . $another_field_definition['field_name']);
|
||||
$this->assertTrue(!empty($another_field) && !$another_field->isDeleted(), 'A field whose storage was not deleted is not marked for deletion.');
|
||||
|
||||
// Try to create a new field the same name as a deleted field and
|
||||
// write data into it.
|
||||
entity_create('field_storage_config', $field_storage_definition)->save();
|
||||
entity_create('field_config', $field_definition)->save();
|
||||
$field_storage = FieldStorageConfig::load('entity_test.' . $field_storage_definition['field_name']);
|
||||
$this->assertTrue(!empty($field_storage) && !$field_storage->isDeleted(), 'A new storage with a previously used name is created.');
|
||||
$field = FieldConfig::load('entity_test.' . $field_definition['bundle'] . '.' . $field_definition['field_name'] );
|
||||
$this->assertTrue(!empty($field) && !$field->isDeleted(), 'A new field for a previously used field name is created.');
|
||||
|
||||
// Save an entity with data for the field
|
||||
$entity = entity_create('entity_test');
|
||||
$values[0]['value'] = mt_rand(1, 127);
|
||||
$entity->{$field_storage->getName()}->value = $values[0]['value'];
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
|
||||
// Verify the field is present on load
|
||||
$this->assertIdentical(count($entity->{$field_storage->getName()}), count($values), "Data in previously deleted field saves and loads correctly");
|
||||
foreach ($values as $delta => $value) {
|
||||
$this->assertEqual($entity->{$field_storage->getName()}[$delta]->value, $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
|
||||
}
|
||||
}
|
||||
|
||||
function testUpdateFieldType() {
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_type',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'decimal',
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
try {
|
||||
$field_storage->set('type', 'integer');
|
||||
$field_storage->save();
|
||||
$this->fail(t('Cannot update a field to a different type.'));
|
||||
}
|
||||
catch (FieldException $e) {
|
||||
$this->pass(t('Cannot update a field to a different type.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test updating a field storage.
|
||||
*/
|
||||
function testUpdate() {
|
||||
// Create a field with a defined cardinality, so that we can ensure it's
|
||||
// respected. Since cardinality enforcement is consistent across database
|
||||
// systems, it makes a good test case.
|
||||
$cardinality = 4;
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_update',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => $cardinality,
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
do {
|
||||
$entity = entity_create('entity_test');
|
||||
// Fill in the entity with more values than $cardinality.
|
||||
for ($i = 0; $i < 20; $i++) {
|
||||
// We can not use $i here because 0 values are filtered out.
|
||||
$entity->field_update[] = $i + 1;
|
||||
}
|
||||
// Load back and assert there are $cardinality number of values.
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
$this->assertEqual(count($entity->field_update), $field_storage->getCardinality());
|
||||
// Now check the values themselves.
|
||||
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||
$this->assertEqual($entity->field_update[$delta]->value, $delta + 1);
|
||||
}
|
||||
// Increase $cardinality and set the field cardinality to the new value.
|
||||
$field_storage->setCardinality(++$cardinality);
|
||||
$field_storage->save();
|
||||
} while ($cardinality < 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test field type modules forbidding an update.
|
||||
*/
|
||||
function testUpdateForbid() {
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'forbidden',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'settings' => array(
|
||||
'changeable' => 0,
|
||||
'unchangeable' => 0
|
||||
)));
|
||||
$field_storage->save();
|
||||
$field_storage->setSetting('changeable', $field_storage->getSetting('changeable') + 1);
|
||||
try {
|
||||
$field_storage->save();
|
||||
$this->pass(t("A changeable setting can be updated."));
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->fail(t("An unchangeable setting cannot be updated."));
|
||||
}
|
||||
$field_storage->setSetting('unchangeable', $field_storage->getSetting('unchangeable') + 1);
|
||||
try {
|
||||
$field_storage->save();
|
||||
$this->fail(t("An unchangeable setting can be updated."));
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass(t("An unchangeable setting cannot be updated."));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
core/modules/field/src/Tests/FieldTestBase.php
Normal file
66
core/modules/field/src/Tests/FieldTestBase.php
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Parent class for Field API tests.
|
||||
*/
|
||||
abstract class FieldTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Generate random values for a field_test field.
|
||||
*
|
||||
* @param $cardinality
|
||||
* Number of values to generate.
|
||||
* @return
|
||||
* An array of random values, in the format expected for field values.
|
||||
*/
|
||||
function _generateTestFieldValues($cardinality) {
|
||||
$values = array();
|
||||
for ($i = 0; $i < $cardinality; $i++) {
|
||||
// field_test fields treat 0 as 'empty value'.
|
||||
$values[$i]['value'] = mt_rand(1, 127);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a field has the expected values in an entity.
|
||||
*
|
||||
* This function only checks a single column in the field values.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity to test.
|
||||
* @param $field_name
|
||||
* The name of the field to test
|
||||
* @param $expected_values
|
||||
* The array of expected values.
|
||||
* @param $langcode
|
||||
* (Optional) The language code for the values. Defaults to
|
||||
* \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_SPECIFIED.
|
||||
* @param $column
|
||||
* (Optional) The name of the column to check. Defaults to 'value'.
|
||||
*/
|
||||
function assertFieldValues(EntityInterface $entity, $field_name, $expected_values, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED, $column = 'value') {
|
||||
// Re-load the entity to make sure we have the latest changes.
|
||||
\Drupal::entityManager()->getStorage($entity->getEntityTypeId())->resetCache(array($entity->id()));
|
||||
$e = entity_load($entity->getEntityTypeId(), $entity->id());
|
||||
$field = $values = $e->getTranslation($langcode)->$field_name;
|
||||
// Filter out empty values so that they don't mess with the assertions.
|
||||
$field->filterEmptyItems();
|
||||
$values = $field->getValue();
|
||||
$this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
|
||||
foreach ($expected_values as $key => $value) {
|
||||
$this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
|
||||
}
|
||||
}
|
||||
}
|
92
core/modules/field/src/Tests/FieldTypePluginManagerTest.php
Normal file
92
core/modules/field/src/Tests/FieldTypePluginManagerTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldTypePluginManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Tests the field type manager.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldTypePluginManagerTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Tests the default settings convenience methods.
|
||||
*/
|
||||
function testDefaultSettings() {
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
foreach (array('test_field', 'shape', 'hidden_test_field') as $type) {
|
||||
$definition = $field_type_manager->getDefinition($type);
|
||||
$this->assertIdentical($field_type_manager->getDefaultStorageSettings($type), $definition['class']::defaultStorageSettings(), format_string("%type storage settings were returned", array('%type' => $type)));
|
||||
$this->assertIdentical($field_type_manager->getDefaultFieldSettings($type), $definition['class']::defaultFieldSettings(), format_string(" %type field settings were returned", array('%type' => $type)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of field item instances.
|
||||
*/
|
||||
public function testCreateInstance() {
|
||||
/** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
foreach (array('test_field', 'shape', 'hidden_test_field') as $type) {
|
||||
$definition = $field_type_manager->getDefinition($type);
|
||||
|
||||
$class = $definition['class'];
|
||||
$field_name = 'field_' . $type;
|
||||
|
||||
$field_definition = BaseFieldDefinition::create($type);
|
||||
|
||||
$configuration = array(
|
||||
'field_definition' => $field_definition,
|
||||
'name' => $field_name,
|
||||
'parent' => NULL,
|
||||
);
|
||||
|
||||
$instance = $field_type_manager->createInstance($type, $configuration);
|
||||
|
||||
$this->assertTrue($instance instanceof $class, SafeMarkup::format('Created a @class instance', array('@class' => $class)));
|
||||
$this->assertEqual($field_name, $instance->getName(), SafeMarkup::format('Instance name is @name', array('@name' => $field_name)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of field item instances.
|
||||
*/
|
||||
public function testCreateInstanceWithConfig() {
|
||||
/** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
$type = 'test_field';
|
||||
$definition = $field_type_manager->getDefinition($type);
|
||||
|
||||
$class = $definition['class'];
|
||||
$field_name = 'field_' . $type;
|
||||
|
||||
$field_definition = BaseFieldDefinition::create($type)
|
||||
->setLabel('Jenny')
|
||||
->setDefaultValue(8675309);
|
||||
|
||||
$configuration = array(
|
||||
'field_definition' => $field_definition,
|
||||
'name' => $field_name,
|
||||
'parent' => NULL,
|
||||
);
|
||||
|
||||
$entity = EntityTest::create();
|
||||
|
||||
$instance = $field_type_manager->createInstance($type, $configuration);
|
||||
|
||||
$this->assertTrue($instance instanceof $class, SafeMarkup::format('Created a @class instance', array('@class' => $class)));
|
||||
$this->assertEqual($field_name, $instance->getName(), SafeMarkup::format('Instance name is @name', array('@name' => $field_name)));
|
||||
$this->assertEqual($instance->getFieldDefinition()->getLabel(), 'Jenny', 'Instance label is Jenny');
|
||||
$this->assertEqual($instance->getFieldDefinition()->getDefaultValue($entity), [['value' => 8675309]], 'Instance default_value is 8675309');
|
||||
}
|
||||
|
||||
}
|
206
core/modules/field/src/Tests/FieldUnitTestBase.php
Normal file
206
core/modules/field/src/Tests/FieldUnitTestBase.php
Normal file
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldUnitTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Parent class for Field API unit tests.
|
||||
*/
|
||||
abstract class FieldUnitTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('user', 'system', 'field', 'text', 'entity_test', 'field_test', 'entity_reference');
|
||||
|
||||
/**
|
||||
* Bag of created field storages and fields.
|
||||
*
|
||||
* Allows easy access to test field storage/field names/IDs/objects via:
|
||||
* - $this->fieldTestData->field_name[suffix]
|
||||
* - $this->fieldTestData->field_storage[suffix]
|
||||
* - $this->fieldTestData->field_storage_uuid[suffix]
|
||||
* - $this->fieldTestData->field[suffix]
|
||||
* - $this->fieldTestData->field_definition[suffix]
|
||||
*
|
||||
* @see \Drupal\field\Tests\FieldUnitTestBase::createFieldWithStorage()
|
||||
*
|
||||
* @var \ArrayObject
|
||||
*/
|
||||
protected $fieldTestData;
|
||||
|
||||
/**
|
||||
* Set the default field storage backend for fields created during tests.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldTestData = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS);
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('system', ['router', 'sequences', 'key_value']);
|
||||
|
||||
// Set default storage backend and configure the theme system.
|
||||
$this->installConfig(array('field', 'system'));
|
||||
|
||||
// Create user 1.
|
||||
$storage = \Drupal::entityManager()->getStorage('user');
|
||||
$storage
|
||||
->create(array(
|
||||
'uid' => 1,
|
||||
'name' => 'entity-test',
|
||||
'mail' => 'entity@localhost',
|
||||
'status' => TRUE,
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a field and an associated field storage.
|
||||
*
|
||||
* @param string $suffix
|
||||
* (optional) A string that should only contain characters that are valid in
|
||||
* PHP variable names as well.
|
||||
* @param string $entity_type
|
||||
* (optional) The entity type on which the field should be created.
|
||||
* Defaults to "entity_test".
|
||||
* @param string $bundle
|
||||
* (optional) The entity type on which the field should be created.
|
||||
* Defaults to the default bundle of the entity type.
|
||||
*/
|
||||
protected function createFieldWithStorage($suffix = '', $entity_type = 'entity_test', $bundle = NULL) {
|
||||
if (empty($bundle)) {
|
||||
$bundle = $entity_type;
|
||||
}
|
||||
$field_name = 'field_name' . $suffix;
|
||||
$field_storage = 'field_storage' . $suffix;
|
||||
$field_storage_uuid = 'field_storage_uuid' . $suffix;
|
||||
$field = 'field' . $suffix;
|
||||
$field_definition = 'field_definition' . $suffix;
|
||||
|
||||
$this->fieldTestData->$field_name = Unicode::strtolower($this->randomMachineName() . '_field_name' . $suffix);
|
||||
$this->fieldTestData->$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => $this->fieldTestData->$field_name,
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
));
|
||||
$this->fieldTestData->$field_storage->save();
|
||||
$this->fieldTestData->$field_storage_uuid = $this->fieldTestData->$field_storage->uuid();
|
||||
$this->fieldTestData->$field_definition = array(
|
||||
'field_storage' => $this->fieldTestData->$field_storage,
|
||||
'bundle' => $bundle,
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => $this->randomMachineName() . '_description',
|
||||
'settings' => array(
|
||||
'test_field_setting' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
$this->fieldTestData->$field = entity_create('field_config', $this->fieldTestData->$field_definition);
|
||||
$this->fieldTestData->$field->save();
|
||||
|
||||
entity_get_form_display($entity_type, $bundle, 'default')
|
||||
->setComponent($this->fieldTestData->$field_name, array(
|
||||
'type' => 'test_field_widget',
|
||||
'settings' => array(
|
||||
'test_widget_setting' => $this->randomMachineName(),
|
||||
)
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves and reloads an entity.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to save.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The entity, freshly reloaded from storage.
|
||||
*/
|
||||
protected function entitySaveReload(EntityInterface $entity) {
|
||||
$entity->save();
|
||||
$controller = $this->container->get('entity.manager')->getStorage($entity->getEntityTypeId());
|
||||
$controller->resetCache();
|
||||
return $controller->load($entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save entity. Fail if violations are found.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity to save.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function entityValidateAndSave(EntityInterface $entity) {
|
||||
$violations = $entity->validate();
|
||||
if ($violations->count()) {
|
||||
$this->fail($violations);
|
||||
}
|
||||
else {
|
||||
$entity->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random values for a field_test field.
|
||||
*
|
||||
* @param $cardinality
|
||||
* Number of values to generate.
|
||||
* @return
|
||||
* An array of random values, in the format expected for field values.
|
||||
*/
|
||||
protected function _generateTestFieldValues($cardinality) {
|
||||
$values = array();
|
||||
for ($i = 0; $i < $cardinality; $i++) {
|
||||
// field_test fields treat 0 as 'empty value'.
|
||||
$values[$i]['value'] = mt_rand(1, 127);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a field has the expected values in an entity.
|
||||
*
|
||||
* This function only checks a single column in the field values.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity to test.
|
||||
* @param $field_name
|
||||
* The name of the field to test
|
||||
* @param $expected_values
|
||||
* The array of expected values.
|
||||
* @param $langcode
|
||||
* (Optional) The language code for the values. Defaults to
|
||||
* \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_SPECIFIED.
|
||||
* @param $column
|
||||
* (Optional) The name of the column to check. Defaults to 'value'.
|
||||
*/
|
||||
protected function assertFieldValues(EntityInterface $entity, $field_name, $expected_values, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED, $column = 'value') {
|
||||
// Re-load the entity to make sure we have the latest changes.
|
||||
\Drupal::entityManager()->getStorage($entity->getEntityTypeId())->resetCache(array($entity->id()));
|
||||
$e = entity_load($entity->getEntityTypeId(), $entity->id());
|
||||
$field = $values = $e->getTranslation($langcode)->$field_name;
|
||||
// Filter out empty values so that they don't mess with the assertions.
|
||||
$field->filterEmptyItems();
|
||||
$values = $field->getValue();
|
||||
$this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
|
||||
foreach ($expected_values as $key => $value) {
|
||||
$this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
102
core/modules/field/src/Tests/FieldValidationTest.php
Normal file
102
core/modules/field/src/Tests/FieldValidationTest.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FieldValidationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Tests field validation.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldValidationTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $bundle;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
private $entity;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a field and storage of type 'test_field', on the 'entity_test'
|
||||
// entity type.
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = 'entity_test';
|
||||
$this->createFieldWithStorage('', $this->entityType, $this->bundle);
|
||||
|
||||
// Create an 'entity_test' entity.
|
||||
$this->entity = entity_create($this->entityType, array(
|
||||
'type' => $this->bundle,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the number of values is validated against the field cardinality.
|
||||
*/
|
||||
function testCardinalityConstraint() {
|
||||
$cardinality = $this->fieldTestData->field_storage->getCardinality();
|
||||
$entity = $this->entity;
|
||||
|
||||
for ($delta = 0; $delta < $cardinality + 1; $delta++) {
|
||||
$entity->{$this->fieldTestData->field_name}[] = array('value' => 1);
|
||||
}
|
||||
|
||||
// Validate the field.
|
||||
$violations = $entity->{$this->fieldTestData->field_name}->validate();
|
||||
|
||||
// Check that the expected constraint violations are reported.
|
||||
$this->assertEqual(count($violations), 1);
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), '');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('%name: this field cannot hold more than @count values.', array('%name' => $this->fieldTestData->field->getLabel(), '@count' => $cardinality)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that constraints defined by the field type are validated.
|
||||
*/
|
||||
function testFieldConstraints() {
|
||||
$cardinality = $this->fieldTestData->field_storage->getCardinality();
|
||||
$entity = $this->entity;
|
||||
|
||||
// The test is only valid if the field cardinality is greater than 2.
|
||||
$this->assertTrue($cardinality >= 2);
|
||||
|
||||
// Set up values for the field.
|
||||
$expected_violations = array();
|
||||
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||
// All deltas except '1' have incorrect values.
|
||||
if ($delta == 1) {
|
||||
$value = 1;
|
||||
}
|
||||
else {
|
||||
$value = -1;
|
||||
$expected_violations[$delta . '.value'][] = t('%name does not accept the value -1.', array('%name' => $this->fieldTestData->field->getLabel()));
|
||||
}
|
||||
$entity->{$this->fieldTestData->field_name}[] = $value;
|
||||
}
|
||||
|
||||
// Validate the field.
|
||||
$violations = $entity->{$this->fieldTestData->field_name}->validate();
|
||||
|
||||
// Check that the expected constraint violations are reported.
|
||||
$violations_by_path = array();
|
||||
foreach ($violations as $violation) {
|
||||
$violations_by_path[$violation->getPropertyPath()][] = $violation->getMessage();
|
||||
}
|
||||
$this->assertEqual($violations_by_path, $expected_violations);
|
||||
}
|
||||
|
||||
}
|
652
core/modules/field/src/Tests/FormTest.php
Normal file
652
core/modules/field/src/Tests/FormTest.php
Normal file
|
@ -0,0 +1,652 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FormTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests field form handling.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FormTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test', 'options', 'entity_test');
|
||||
|
||||
/**
|
||||
* An array of values defining a field single.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageSingle;
|
||||
|
||||
/**
|
||||
* An array of values defining a field multiple.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageMultiple;
|
||||
|
||||
/**
|
||||
* An array of values defining a field with unlimited cardinality.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageUnlimited;
|
||||
|
||||
/**
|
||||
* An array of values defining a field.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->fieldStorageSingle = array(
|
||||
'field_name' => 'field_single',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$this->fieldStorageMultiple = array(
|
||||
'field_name' => 'field_multiple',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
);
|
||||
$this->fieldStorageUnlimited = array(
|
||||
'field_name' => 'field_unlimited',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
);
|
||||
|
||||
$this->field = array(
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => '[site:name]_description',
|
||||
'weight' => mt_rand(0, 127),
|
||||
'settings' => array(
|
||||
'test_field_setting' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function testFieldFormSingle() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Create token value expected for description.
|
||||
$token_description = SafeMarkup::checkPlain($this->config('system.site')->get('name')) . '_description';
|
||||
$this->assertText($token_description, 'Token replacement for description is displayed');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Check that hook_field_widget_form_alter() does not believe this is the
|
||||
// default value form.
|
||||
$this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
|
||||
|
||||
// Submit with invalid value (field-level validation).
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => -1
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->field['label'])), 'Field validation fails with invalid input.');
|
||||
// TODO : check that the correct field is flagged for error.
|
||||
|
||||
// Create an entity
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was saved');
|
||||
|
||||
// Display edit form.
|
||||
$this->drupalGet('entity_test/manage/' . $id);
|
||||
$this->assertFieldByName("{$field_name}[0][value]", $value, 'Widget is displayed with the correct default value');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Update the entity.
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
$this->container->get('entity.manager')->getStorage('entity_test')->resetCache(array($id));
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was updated');
|
||||
|
||||
// Empty the field.
|
||||
$value = '';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value
|
||||
);
|
||||
$this->drupalPostForm('entity_test/manage/' . $id, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
$this->container->get('entity.manager')->getStorage('entity_test')->resetCache(array($id));
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->{$field_name}->isEmpty(), 'Field was emptied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field widget default values on entity forms.
|
||||
*/
|
||||
function testFieldFormDefaultValue() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$default = rand(1, 127);
|
||||
$this->field['default_value'] = array(array('value' => $default));
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
// Test that the default value is displayed correctly.
|
||||
$this->assertFieldByXpath("//input[@name='{$field_name}[0][value]' and @value='$default']");
|
||||
|
||||
// Try to submit an empty value.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => '',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created.');
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->{$field_name}->isEmpty(), 'Field is now empty.');
|
||||
}
|
||||
|
||||
function testFieldFormSingleRequired() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['required'] = TRUE;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Submit with missing required value.
|
||||
$edit = array();
|
||||
$this->drupalPostForm('entity_test/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('!name field is required.', array('!name' => $this->field['label'])), 'Required field with no value fails validation');
|
||||
|
||||
// Create an entity
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was saved');
|
||||
|
||||
// Edit with missing required value.
|
||||
$value = '';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm('entity_test/manage/' . $id, $edit, t('Save'));
|
||||
$this->assertRaw(t('!name field is required.', array('!name' => $this->field['label'])), 'Required field with no value fails validation');
|
||||
}
|
||||
|
||||
// function testFieldFormMultiple() {
|
||||
// $this->field = $this->field_multiple;
|
||||
// $field_name = $this->field['field_name'];
|
||||
// $this->instance['field_name'] = $field_name;
|
||||
// entity_create('field_storage_config', $this->field)->save();
|
||||
// entity_create('field_config', $this->instance)->save();
|
||||
// }
|
||||
|
||||
function testFieldFormUnlimited() {
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Press 'add more' button -> 2 widgets.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertFieldByName("{$field_name}[1][value]", '', 'New widget is displayed');
|
||||
$this->assertNoField("{$field_name}[2][value]", 'No extraneous widget is displayed');
|
||||
// TODO : check that non-field inputs are preserved ('title'), etc.
|
||||
|
||||
// Yet another time so that we can play with more values -> 3 widgets.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Prepare values and weights.
|
||||
$count = 3;
|
||||
$delta_range = $count - 1;
|
||||
$values = $weights = $pattern = $expected_values = array();
|
||||
$edit = array();
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
// Assign unique random values and weights.
|
||||
do {
|
||||
$value = mt_rand(1, 127);
|
||||
} while (in_array($value, $values));
|
||||
do {
|
||||
$weight = mt_rand(-$delta_range, $delta_range);
|
||||
} while (in_array($weight, $weights));
|
||||
$edit["{$field_name}[$delta][value]"] = $value;
|
||||
$edit["{$field_name}[$delta][_weight]"] = $weight;
|
||||
// We'll need three slightly different formats to check the values.
|
||||
$values[$delta] = $value;
|
||||
$weights[$delta] = $weight;
|
||||
$field_values[$weight]['value'] = (string) $value;
|
||||
$pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
|
||||
}
|
||||
|
||||
// Press 'add more' button -> 4 widgets
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
|
||||
}
|
||||
ksort($pattern);
|
||||
$pattern = implode('.*', array_values($pattern));
|
||||
$this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", '', "New widget is displayed");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $delta, "New widget has the right weight");
|
||||
$this->assertNoField("{$field_name}[" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
|
||||
|
||||
// Submit the form and create the entity.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = entity_load('entity_test', $id);
|
||||
ksort($field_values);
|
||||
$field_values = array_values($field_values);
|
||||
$this->assertIdentical($entity->{$field_name}->getValue(), $field_values, 'Field values were saved in the correct order');
|
||||
|
||||
// Display edit form: check that the expected number of widgets is
|
||||
// displayed, with correct values change values, reorder, leave an empty
|
||||
// value in the middle.
|
||||
// Submit: check that the entity is updated with correct values
|
||||
// Re-submit: check that the field can be emptied.
|
||||
|
||||
// Test with several multiple fields in a form
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the position of the required label.
|
||||
*/
|
||||
public function testFieldFormUnlimitedRequired() {
|
||||
$field_name = $this->fieldStorageUnlimited['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['required'] = TRUE;
|
||||
FieldStorageConfig::create($this->fieldStorageUnlimited)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
// Check that the Required symbol is present for the multifield label.
|
||||
$this->assertRaw(SafeMarkup::format('<h4 class="label form-required">@label</h4>', array('@label' => $this->field['label'])),
|
||||
'Required symbol added field label.');
|
||||
// Check that the label of the field input is visually hidden and contains
|
||||
// the field title and an indication of the delta for a11y.
|
||||
$this->assertRaw(SafeMarkup::format('<label for="edit-field-unlimited-0-value" class="visually-hidden form-required">@label (value 1)</label>', array('@label' => $this->field['label'])),
|
||||
'Required symbol not added for field input.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests widget handling of multiple required radios.
|
||||
*/
|
||||
function testFieldFormMultivalueWithRequiredRadio() {
|
||||
// Create a multivalue test field.
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Add a required radio field.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => 'required_radio_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'list_string',
|
||||
'settings' => array(
|
||||
'allowed_values' => array('yes' => 'yes', 'no' => 'no'),
|
||||
),
|
||||
))->save();
|
||||
$field = array(
|
||||
'field_name' => 'required_radio_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'required' => TRUE,
|
||||
);
|
||||
entity_create('field_config', $field)->save();
|
||||
entity_get_form_display($field['entity_type'], $field['bundle'], 'default')
|
||||
->setComponent($field['field_name'], array(
|
||||
'type' => 'options_buttons',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Press the 'Add more' button.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Verify that no error is thrown by the radio element.
|
||||
$this->assertNoFieldByXpath('//div[contains(@class, "error")]', FALSE, 'No error message is displayed.');
|
||||
|
||||
// Verify that the widget is added.
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertFieldByName("{$field_name}[1][value]", '', 'New widget is displayed');
|
||||
$this->assertNoField("{$field_name}[2][value]", 'No extraneous widget is displayed');
|
||||
}
|
||||
|
||||
function testFieldFormJSAddMore() {
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Press 'add more' button a couple times -> 3 widgets.
|
||||
// drupalPostAjaxForm() will not work iteratively, so we add those through
|
||||
// non-JS submission.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Prepare values and weights.
|
||||
$count = 3;
|
||||
$delta_range = $count - 1;
|
||||
$values = $weights = $pattern = $expected_values = $edit = array();
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
// Assign unique random values and weights.
|
||||
do {
|
||||
$value = mt_rand(1, 127);
|
||||
} while (in_array($value, $values));
|
||||
do {
|
||||
$weight = mt_rand(-$delta_range, $delta_range);
|
||||
} while (in_array($weight, $weights));
|
||||
$edit["{$field_name}[$delta][value]"] = $value;
|
||||
$edit["{$field_name}[$delta][_weight]"] = $weight;
|
||||
// We'll need three slightly different formats to check the values.
|
||||
$values[$delta] = $value;
|
||||
$weights[$delta] = $weight;
|
||||
$field_values[$weight]['value'] = (string) $value;
|
||||
$pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
|
||||
}
|
||||
// Press 'add more' button through Ajax, and place the expected HTML result
|
||||
// as the tested content.
|
||||
$commands = $this->drupalPostAjaxForm(NULL, $edit, $field_name . '_add_more');
|
||||
$this->setRawContent($commands[2]['data']);
|
||||
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
|
||||
}
|
||||
ksort($pattern);
|
||||
$pattern = implode('.*', array_values($pattern));
|
||||
$this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", '', "New widget is displayed");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $delta, "New widget has the right weight");
|
||||
$this->assertNoField("{$field_name}[" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests widgets handling multiple values.
|
||||
*/
|
||||
function testFieldFormMultipleWidget() {
|
||||
// Create a field with fixed cardinality, configure the form to use a
|
||||
// "multiple" widget.
|
||||
$field_storage = $this->fieldStorageMultiple;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'test_field_widget_multiple',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName($field_name, '', 'Widget is displayed.');
|
||||
|
||||
// Create entity with three values.
|
||||
$edit = array(
|
||||
$field_name => '1, 2, 3',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
|
||||
// Check that the values were saved.
|
||||
$entity_init = entity_load('entity_test', $id);
|
||||
$this->assertFieldValues($entity_init, $field_name, array(1, 2, 3));
|
||||
|
||||
// Display the form, check that the values are correctly filled in.
|
||||
$this->drupalGet('entity_test/manage/' . $id);
|
||||
$this->assertFieldByName($field_name, '1, 2, 3', 'Widget is displayed.');
|
||||
|
||||
// Submit the form with more values than the field accepts.
|
||||
$edit = array($field_name => '1, 2, 3, 4, 5');
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('this field cannot hold more than 4 values', 'Form validation failed.');
|
||||
// Check that the field values were not submitted.
|
||||
$this->assertFieldValues($entity_init, $field_name, array(1, 2, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fields with no 'edit' access.
|
||||
*/
|
||||
function testFieldFormAccess() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
// Create a "regular" field.
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_storage['entity_type'] = $entity_type;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$field = $this->field;
|
||||
$field['field_name'] = $field_name;
|
||||
$field['entity_type'] = $entity_type;
|
||||
$field['bundle'] = $entity_type;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
entity_create('field_config', $field)->save();
|
||||
entity_get_form_display($entity_type, $entity_type, 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Create a field with no edit access. See
|
||||
// field_test_entity_field_access().
|
||||
$field_storage_no_access = array(
|
||||
'field_name' => 'field_no_edit_access',
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$field_name_no_access = $field_storage_no_access['field_name'];
|
||||
$field_no_access = array(
|
||||
'field_name' => $field_name_no_access,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $entity_type,
|
||||
'default_value' => array(0 => array('value' => 99)),
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage_no_access)->save();
|
||||
entity_create('field_config', $field_no_access)->save();
|
||||
entity_get_form_display($field_no_access['entity_type'], $field_no_access['bundle'], 'default')
|
||||
->setComponent($field_name_no_access)
|
||||
->save();
|
||||
|
||||
// Test that the form structure includes full information for each delta
|
||||
// apart from #access.
|
||||
$entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0));
|
||||
|
||||
$display = entity_get_form_display($entity_type, $entity_type, 'default');
|
||||
$form = array();
|
||||
$form_state = new FormState();
|
||||
$display->buildForm($entity, $form, $form_state);
|
||||
|
||||
$this->assertFalse($form[$field_name_no_access]['#access'], 'Field #access is FALSE for the field without edit access.');
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet($entity_type . '/add');
|
||||
$this->assertNoFieldByName("{$field_name_no_access}[0][value]", '', 'Widget is not displayed if field access is denied.');
|
||||
|
||||
// Create entity.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match("|$entity_type/manage/(\d+)|", $this->url, $match);
|
||||
$id = $match[1];
|
||||
|
||||
// Check that the default value was saved.
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'Default value was saved for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 1, 'Entered value vas saved for the field with edit access.');
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 2,
|
||||
'revision' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm($entity_type . '/manage/' . $id, $edit, t('Save'));
|
||||
|
||||
// Check that the new revision has the expected values.
|
||||
$this->container->get('entity.manager')->getStorage($entity_type)->resetCache(array($id));
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'New revision has the expected value for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 2, 'New revision has the expected value for the field with edit access.');
|
||||
|
||||
// Check that the revision is also saved in the revisions table.
|
||||
// $entity = entity_revision_load($entity_type, $entity->getRevisionId());
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'New revision has the expected value for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 2, 'New revision has the expected value for the field with edit access.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hiding a field in a form.
|
||||
*/
|
||||
function testHiddenField() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_storage['entity_type'] = $entity_type;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['default_value'] = array(0 => array('value' => 99));
|
||||
$this->field['entity_type'] = $entity_type;
|
||||
$this->field['bundle'] = $entity_type;
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
$this->field = entity_create('field_config', $this->field);
|
||||
$this->field->save();
|
||||
// We explicitly do not assign a widget in a form display, so the field
|
||||
// stays hidden in forms.
|
||||
|
||||
// Display the entity creation form.
|
||||
$this->drupalGet($entity_type . '/add');
|
||||
|
||||
// Create an entity and test that the default value is assigned correctly to
|
||||
// the field that uses the hidden widget.
|
||||
$this->assertNoField("{$field_name}[0][value]", 'The field does not appear in the form');
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
preg_match('|' . $entity_type . '/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test_rev @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, 99, 'Default value was saved');
|
||||
|
||||
// Update the field to remove the default value, and switch to the default
|
||||
// widget.
|
||||
$this->field->default_value = array();
|
||||
$this->field->save();
|
||||
entity_get_form_display($entity_type, $this->field->getTargetBundle(), 'default')
|
||||
->setComponent($this->field->getName(), array(
|
||||
'type' => 'test_field_widget',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display edit form.
|
||||
$this->drupalGet($entity_type . '/manage/' . $id);
|
||||
$this->assertFieldByName("{$field_name}[0][value]", 99, 'Widget is displayed with the correct default value');
|
||||
|
||||
// Update the entity.
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array("{$field_name}[0][value]" => $value);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test_rev @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
\Drupal::entityManager()->getStorage($entity_type)->resetCache(array($id));
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was updated');
|
||||
|
||||
// Set the field back to hidden.
|
||||
entity_get_form_display($entity_type, $this->field->getTargetBundle(), 'default')
|
||||
->removeComponent($this->field->getName())
|
||||
->save();
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array('revision' => TRUE);
|
||||
$this->drupalPostForm($entity_type . '/manage/' . $id, $edit, t('Save'));
|
||||
|
||||
// Check that the expected value has been carried over to the new revision.
|
||||
\Drupal::entityManager()->getStorage($entity_type)->resetCache(array($id));
|
||||
$entity = entity_load($entity_type, $id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'New revision has the expected value for the field with the Hidden widget');
|
||||
}
|
||||
|
||||
}
|
52
core/modules/field/src/Tests/FormatterPluginManagerTest.php
Normal file
52
core/modules/field/src/Tests/FormatterPluginManagerTest.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\FormatterPluginManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FormatterPluginManager;
|
||||
|
||||
/**
|
||||
* Tests the field formatter plugin manager.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FormatterPluginManagerTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Tests that getInstance falls back on default if current is not applicable.
|
||||
*
|
||||
* @see \Drupal\field\Tests\WidgetPluginManagerTest::testNotApplicableFallback()
|
||||
*/
|
||||
public function testNotApplicableFallback() {
|
||||
/** @var FormatterPluginManager $formatter_plugin_manager */
|
||||
$formatter_plugin_manager = \Drupal::service('plugin.manager.field.formatter');
|
||||
|
||||
$base_field_definition = BaseFieldDefinition::create('test_field')
|
||||
// Set a name that will make isApplicable() return TRUE.
|
||||
->setName('field_test_field');
|
||||
|
||||
$formatter_options = array(
|
||||
'field_definition' => $base_field_definition,
|
||||
'view_mode' => 'default',
|
||||
'configuration' => array(
|
||||
'type' => 'field_test_applicable',
|
||||
),
|
||||
);
|
||||
|
||||
$instance = $formatter_plugin_manager->getInstance($formatter_options);
|
||||
$this->assertEqual($instance->getPluginId(), 'field_test_applicable');
|
||||
|
||||
// Now set name to something that makes isApplicable() return FALSE.
|
||||
$base_field_definition->setName('deny_applicable');
|
||||
$instance = $formatter_plugin_manager->getInstance($formatter_options);
|
||||
|
||||
// Instance should be default widget.
|
||||
$this->assertNotEqual($instance->getPluginId(), 'field_test_applicable');
|
||||
$this->assertEqual($instance->getPluginId(), 'field_test_default');
|
||||
}
|
||||
|
||||
}
|
168
core/modules/field/src/Tests/NestedFormTest.php
Normal file
168
core/modules/field/src/Tests/NestedFormTest.php
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\NestedFormTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Tests field elements in nested forms.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class NestedFormTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test', 'entity_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->fieldStorageSingle = array(
|
||||
'field_name' => 'field_single',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$this->fieldStorageUnlimited = array(
|
||||
'field_name' => 'field_unlimited',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
);
|
||||
|
||||
$this->field = array(
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => '[site:name]_description',
|
||||
'weight' => mt_rand(0, 127),
|
||||
'settings' => array(
|
||||
'test_field_setting' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API form integration within a subform.
|
||||
*/
|
||||
function testNestedFieldForm() {
|
||||
// Add two fields on the 'entity_test'
|
||||
entity_create('field_storage_config', $this->fieldStorageSingle)->save();
|
||||
entity_create('field_storage_config', $this->fieldStorageUnlimited)->save();
|
||||
$this->field['field_name'] = 'field_single';
|
||||
$this->field['label'] = 'Single field';
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($this->field['field_name'])
|
||||
->save();
|
||||
$this->field['field_name'] = 'field_unlimited';
|
||||
$this->field['label'] = 'Unlimited field';
|
||||
entity_create('field_config', $this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($this->field['field_name'])
|
||||
->save();
|
||||
|
||||
// Create two entities.
|
||||
$entity_type = 'entity_test';
|
||||
$entity_1 = entity_create($entity_type, array('id' => 1));
|
||||
$entity_1->enforceIsNew();
|
||||
$entity_1->field_single->value = 0;
|
||||
$entity_1->field_unlimited->value = 1;
|
||||
$entity_1->save();
|
||||
|
||||
$entity_2 = entity_create($entity_type, array('id' => 2));
|
||||
$entity_2->enforceIsNew();
|
||||
$entity_2->field_single->value = 10;
|
||||
$entity_2->field_unlimited->value = 11;
|
||||
$entity_2->save();
|
||||
|
||||
// Display the 'combined form'.
|
||||
$this->drupalGet('test-entity/nested/1/2');
|
||||
$this->assertFieldByName('field_single[0][value]', 0, 'Entity 1: field_single value appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[0][value]', 1, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_single][0][value]', 10, 'Entity 2: field_single value appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][0][value]', 11, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
|
||||
|
||||
// Submit the form and check that the entities are updated accordingly.
|
||||
$edit = array(
|
||||
'field_single[0][value]' => 1,
|
||||
'field_unlimited[0][value]' => 2,
|
||||
'field_unlimited[1][value]' => 3,
|
||||
'entity_2[field_single][0][value]' => 11,
|
||||
'entity_2[field_unlimited][0][value]' => 12,
|
||||
'entity_2[field_unlimited][1][value]' => 13,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$entity_1 = entity_load($entity_type, 1);
|
||||
$entity_2 = entity_load($entity_type, 2);
|
||||
$this->assertFieldValues($entity_1, 'field_single', array(1));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(2, 3));
|
||||
$this->assertFieldValues($entity_2, 'field_single', array(11));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(12, 13));
|
||||
|
||||
// Submit invalid values and check that errors are reported on the
|
||||
// correct widgets.
|
||||
$edit = array(
|
||||
'field_unlimited[1][value]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 1: the field validation error was reported.');
|
||||
$error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-1-value'));
|
||||
$this->assertTrue($error_field, 'Entity 1: the error was flagged on the correct element.');
|
||||
$edit = array(
|
||||
'entity_2[field_unlimited][1][value]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 2: the field validation error was reported.');
|
||||
$error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-1-value'));
|
||||
$this->assertTrue($error_field, 'Entity 2: the error was flagged on the correct element.');
|
||||
|
||||
// Test that reordering works on both entities.
|
||||
$edit = array(
|
||||
'field_unlimited[0][_weight]' => 0,
|
||||
'field_unlimited[1][_weight]' => -1,
|
||||
'entity_2[field_unlimited][0][_weight]' => 0,
|
||||
'entity_2[field_unlimited][1][_weight]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(13, 12));
|
||||
|
||||
// Test the 'add more' buttons. Only Ajax submission is tested, because
|
||||
// the two 'add more' buttons present in the form have the same #value,
|
||||
// which confuses drupalPostForm().
|
||||
// 'Add more' button in the first entity:
|
||||
$this->drupalGet('test-entity/nested/1/2');
|
||||
$this->drupalPostAjaxForm(NULL, array(), 'field_unlimited_add_more');
|
||||
$this->assertFieldByName('field_unlimited[0][value]', 3, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[1][value]', 2, 'Entity 1: field_unlimited value 1 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[2][value]', '', 'Entity 1: field_unlimited value 2 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[3][value]', '', 'Entity 1: an empty widget was added for field_unlimited value 3.');
|
||||
// 'Add more' button in the first entity (changing field values):
|
||||
$edit = array(
|
||||
'entity_2[field_unlimited][0][value]' => 13,
|
||||
'entity_2[field_unlimited][1][value]' => 14,
|
||||
'entity_2[field_unlimited][2][value]' => 15,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'entity_2_field_unlimited_add_more');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][0][value]', 13, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][1][value]', 14, 'Entity 2: field_unlimited value 1 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][2][value]', 15, 'Entity 2: field_unlimited value 2 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][3][value]', '', 'Entity 2: an empty widget was added for field_unlimited value 3.');
|
||||
// Save the form and check values are saved correctly.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(13, 14, 15));
|
||||
}
|
||||
|
||||
}
|
530
core/modules/field/src/Tests/Number/NumberFieldTest.php
Normal file
530
core/modules/field/src/Tests/Number/NumberFieldTest.php
Normal file
|
@ -0,0 +1,530 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Number\NumberFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Number;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the creation of numeric fields.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class NumberFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'entity_test', 'field_ui');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
'administer node display',
|
||||
'bypass node access',
|
||||
'administer entity_test fields',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decimal field.
|
||||
*/
|
||||
function testNumberDecimalField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'decimal',
|
||||
'settings' => array(
|
||||
'precision' => 8, 'scale' => 4,
|
||||
)
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="0.00"');
|
||||
|
||||
// Submit a signed decimal value within the allowed precision and scale.
|
||||
$value = '-1234.5678';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->assertRaw($value, 'Value is displayed.');
|
||||
|
||||
// Try to create entries with more than one decimal separator; assert fail.
|
||||
$wrong_entries = array(
|
||||
'3.14.159',
|
||||
'0..45469',
|
||||
'..4589',
|
||||
'6.459.52',
|
||||
'6.3..25',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save decimal value with more than one decimal point.');
|
||||
}
|
||||
|
||||
// Try to create entries with minus sign not in the first position.
|
||||
$wrong_entries = array(
|
||||
'3-3',
|
||||
'4-',
|
||||
'1.3-',
|
||||
'1.2-4',
|
||||
'-10-10',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save decimal value with minus sign in the wrong position.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test integer field.
|
||||
*/
|
||||
function testNumberIntegerField() {
|
||||
$minimum = rand(-4000, -2000);
|
||||
$maximum = rand(2000, 4000);
|
||||
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$storage = entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'integer',
|
||||
));
|
||||
$storage->save();
|
||||
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'settings' => array(
|
||||
'min' => $minimum, 'max' => $maximum,
|
||||
)
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '4'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_integer',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Check the storage schema.
|
||||
$expected = array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => '',
|
||||
'size' => 'normal'
|
||||
),
|
||||
),
|
||||
'unique keys' => array(),
|
||||
'indexes' => array(),
|
||||
'foreign keys' => array()
|
||||
);
|
||||
$this->assertEqual($storage->getSchema(), $expected);
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="4"');
|
||||
|
||||
// Submit a valid integer
|
||||
$value = rand($minimum, $maximum);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Try to set a value below the minimum value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $minimum - 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be higher than or equal to %minimum.', array('%name' => $field_name, '%minimum' => $minimum)), 'Correctly failed to save integer value less than minimum allowed value.');
|
||||
|
||||
// Try to set a decimal value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 1.5,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name is not a valid number.', array('%name' => $field_name)), 'Correctly failed to save decimal value to integer field.');
|
||||
|
||||
// Try to set a value above the maximum value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $maximum + 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be lower than or equal to %maximum.', array('%name' => $field_name, '%maximum' => $maximum)), 'Correctly failed to save integer value greater than maximum allowed value.');
|
||||
|
||||
// Test with valid entries.
|
||||
$valid_entries = array(
|
||||
'-1234',
|
||||
'0',
|
||||
'1234',
|
||||
);
|
||||
|
||||
foreach ($valid_entries as $valid_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $valid_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->assertRaw($valid_entry, 'Value is displayed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test float field.
|
||||
*/
|
||||
function testNumberFloatField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="0.00"');
|
||||
|
||||
// Submit a signed decimal value within the allowed precision and scale.
|
||||
$value = '-1234.5678';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->assertRaw(round($value, 2), 'Value is displayed.');
|
||||
|
||||
// Try to create entries with more than one decimal separator; assert fail.
|
||||
$wrong_entries = array(
|
||||
'3.14.159',
|
||||
'0..45469',
|
||||
'..4589',
|
||||
'6.459.52',
|
||||
'6.3..25',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save float value with more than one decimal point.');
|
||||
}
|
||||
|
||||
// Try to create entries with minus sign not in the first position.
|
||||
$wrong_entries = array(
|
||||
'3-3',
|
||||
'4-',
|
||||
'1.3-',
|
||||
'1.2-4',
|
||||
'-10-10',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save float value with minus sign in the wrong position.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test default formatter behavior
|
||||
*/
|
||||
function testNumberFormatter() {
|
||||
$type = Unicode::strtolower($this->randomMachineName());
|
||||
$float_field = Unicode::strtolower($this->randomMachineName());
|
||||
$integer_field = Unicode::strtolower($this->randomMachineName());
|
||||
$thousand_separators = array('', '.', ',', ' ', chr(8201), "'");
|
||||
$decimal_separators = array('.', ',');
|
||||
$prefix = $this->randomMachineName();
|
||||
$suffix = $this->randomMachineName();
|
||||
$random_float = rand(0,pow(10,6));
|
||||
$random_integer = rand(0, pow(10,6));
|
||||
|
||||
// Create a content type containing float and integer fields.
|
||||
$this->drupalCreateContentType(array('type' => $type));
|
||||
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $float_field,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $integer_field,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'integer',
|
||||
))->save();
|
||||
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $float_field,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type,
|
||||
'settings' => array(
|
||||
'prefix' => $prefix,
|
||||
'suffix' => $suffix
|
||||
),
|
||||
))->save();
|
||||
|
||||
entity_create('field_config', array(
|
||||
'field_name' => $integer_field,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type,
|
||||
'settings' => array(
|
||||
'prefix' => $prefix,
|
||||
'suffix' => $suffix
|
||||
),
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('node', $type, 'default')
|
||||
->setComponent($float_field, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('node', $type, 'default')
|
||||
->setComponent($float_field, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number_unformatted',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Create a node to test formatters.
|
||||
$node = entity_create('node', array(
|
||||
'type' => $type,
|
||||
'title' => $this->randomMachineName(),
|
||||
$float_field => array(
|
||||
'value' => $random_float,
|
||||
),
|
||||
$integer_field => array(
|
||||
'value' => $random_integer,
|
||||
),
|
||||
));
|
||||
$node->save();
|
||||
|
||||
// Go to manage display page.
|
||||
$this->drupalGet("admin/structure/types/manage/$type/display");
|
||||
|
||||
// Configure number_decimal formatter for the 'float' field type.
|
||||
$thousand_separator = $thousand_separators[array_rand($thousand_separators)];
|
||||
$decimal_separator = $decimal_separators[array_rand($decimal_separators)];
|
||||
$scale = rand(0, 10);
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), "${float_field}_settings_edit");
|
||||
$edit = array(
|
||||
"fields[${float_field}][settings_edit_form][settings][prefix_suffix]" => TRUE,
|
||||
"fields[${float_field}][settings_edit_form][settings][scale]" => $scale,
|
||||
"fields[${float_field}][settings_edit_form][settings][decimal_separator]" => $decimal_separator,
|
||||
"fields[${float_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "${float_field}_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Check number_decimal and number_unformatted formatters behavior.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$float_formatted = number_format($random_float, $scale, $decimal_separator, $thousand_separator);
|
||||
$this->assertRaw("$prefix$float_formatted$suffix", 'Prefix and suffix added');
|
||||
$this->assertRaw((string) $random_integer);
|
||||
|
||||
// Configure the number_decimal formatter.
|
||||
entity_get_display('node', $type, 'default')
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number_integer',
|
||||
))
|
||||
->save();
|
||||
$this->drupalGet("admin/structure/types/manage/$type/display");
|
||||
|
||||
$thousand_separator = $thousand_separators[array_rand($thousand_separators)];
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), "${integer_field}_settings_edit");
|
||||
$edit = array(
|
||||
"fields[${integer_field}][settings_edit_form][settings][prefix_suffix]" => FALSE,
|
||||
"fields[${integer_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "${integer_field}_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Check number_integer formatter behavior.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
$integer_formatted = number_format($random_integer, 0, '', $thousand_separator);
|
||||
$this->assertRaw($integer_formatted, 'Random integer formatted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the minimum value of a float field through the interface.
|
||||
*/
|
||||
function testCreateNumberFloatField() {
|
||||
// Create a float field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
$field = entity_create('field_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Set the minimum value to a float value.
|
||||
$this->assertSetMinimumValue($field, 0.0001);
|
||||
// Set the minimum value to an integer value.
|
||||
$this->assertSetMinimumValue($field, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the minimum value of a decimal field through the interface.
|
||||
*/
|
||||
function testCreateNumberDecimalField() {
|
||||
// Create a decimal field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'decimal',
|
||||
))->save();
|
||||
|
||||
$field = entity_create('field_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Set the minimum value to a decimal value.
|
||||
$this->assertSetMinimumValue($field, 0.1);
|
||||
// Set the minimum value to an integer value.
|
||||
$this->assertSetMinimumValue($field, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the minimum value of a field.
|
||||
*/
|
||||
function assertSetMinimumValue($field, $minimum_value) {
|
||||
$field_configuration_url = 'entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field->getName();
|
||||
|
||||
// Set the minimum value.
|
||||
$edit = array(
|
||||
'settings[min]' => $minimum_value,
|
||||
);
|
||||
$this->drupalPostForm($field_configuration_url, $edit, t('Save settings'));
|
||||
// Check if an error message is shown.
|
||||
$this->assertNoRaw(t('%name is not a valid number.', array('%name' => t('Minimum'))), 'Saved ' . gettype($minimum_value) .' value as minimal value on a ' . $field->getType() . ' field');
|
||||
// Check if a success message is shown.
|
||||
$this->assertRaw(t('Saved %label configuration.', array('%label' => $field->getLabel())));
|
||||
// Check if the minimum value was actually set.
|
||||
$this->drupalGet($field_configuration_url);
|
||||
$this->assertFieldById('edit-settings-min', $minimum_value, 'Minimal ' . gettype($minimum_value) .' value was set on a ' . $field->getType() . ' field.');
|
||||
}
|
||||
}
|
103
core/modules/field/src/Tests/Number/NumberItemTest.php
Normal file
103
core/modules/field/src/Tests/Number/NumberItemTest.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Number\NumberItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Number;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\field\Tests\FieldUnitTestBase;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the number field type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class NumberItemTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array();
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create number field storages and fields for validation.
|
||||
foreach (array('integer', 'float', 'decimal') as $type) {
|
||||
entity_create('field_storage_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_' . $type,
|
||||
'type' => $type,
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'field_' . $type,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the number field type.
|
||||
*/
|
||||
public function testNumberItem() {
|
||||
// Verify entity creation.
|
||||
$entity = entity_create('entity_test');
|
||||
$integer = rand(0, 10);
|
||||
$entity->field_integer = $integer;
|
||||
$float = 3.14;
|
||||
$entity->field_float = $float;
|
||||
$decimal = '31.3';
|
||||
$entity->field_decimal = $decimal;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->field_integer instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_integer[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_integer->value, $integer);
|
||||
$this->assertEqual($entity->field_integer[0]->value, $integer);
|
||||
$this->assertTrue($entity->field_float instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_float[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_float->value, $float);
|
||||
$this->assertEqual($entity->field_float[0]->value, $float);
|
||||
$this->assertTrue($entity->field_decimal instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->field_decimal[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->field_decimal->value, $decimal);
|
||||
$this->assertEqual($entity->field_decimal[0]->value, $decimal);
|
||||
|
||||
// Verify changing the number value.
|
||||
$new_integer = rand(11, 20);
|
||||
$new_float = rand(1001, 2000) / 100;
|
||||
$new_decimal = '18.2';
|
||||
$entity->field_integer->value = $new_integer;
|
||||
$this->assertEqual($entity->field_integer->value, $new_integer);
|
||||
$entity->field_float->value = $new_float;
|
||||
$this->assertEqual($entity->field_float->value, $new_float);
|
||||
$entity->field_decimal->value = $new_decimal;
|
||||
$this->assertEqual($entity->field_decimal->value, $new_decimal);
|
||||
|
||||
// Read changed entity and assert changed values.
|
||||
$entity->save();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->field_integer->value, $new_integer);
|
||||
$this->assertEqual($entity->field_float->value, $new_float);
|
||||
$this->assertEqual($entity->field_decimal->value, $new_decimal);
|
||||
|
||||
/// Test sample item generation.
|
||||
$entity = entity_create('entity_test');
|
||||
$entity->field_integer->generateSampleItems();
|
||||
$entity->field_float->generateSampleItems();
|
||||
$entity->field_decimal->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
}
|
||||
|
||||
}
|
88
core/modules/field/src/Tests/ShapeItemTest.php
Normal file
88
core/modules/field/src/Tests/ShapeItemTest.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\ShapeItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the shape field type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class ShapeItemTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_shape';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a 'shape' field and storage for validation.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'shape',
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the field field type.
|
||||
*/
|
||||
public function testShapeItem() {
|
||||
// Verify entity creation.
|
||||
$entity = entity_create('entity_test');
|
||||
$shape = 'cube';
|
||||
$color = 'blue';
|
||||
$entity->{$this->fieldName}->shape = $shape;
|
||||
$entity->{$this->fieldName}->color = $color;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->{$this->fieldName} instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->{$this->fieldName}[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->{$this->fieldName}->shape, $shape);
|
||||
$this->assertEqual($entity->{$this->fieldName}->color, $color);
|
||||
$this->assertEqual($entity->{$this->fieldName}[0]->shape, $shape);
|
||||
$this->assertEqual($entity->{$this->fieldName}[0]->color, $color);
|
||||
|
||||
// Verify changing the field value.
|
||||
$new_shape = 'circle';
|
||||
$new_color = 'red';
|
||||
$entity->{$this->fieldName}->shape = $new_shape;
|
||||
$entity->{$this->fieldName}->color = $new_color;
|
||||
$this->assertEqual($entity->{$this->fieldName}->shape, $new_shape);
|
||||
$this->assertEqual($entity->{$this->fieldName}->color, $new_color);
|
||||
|
||||
// Read changed entity and assert changed values.
|
||||
$entity->save();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->{$this->fieldName}->shape, $new_shape);
|
||||
$this->assertEqual($entity->{$this->fieldName}->color, $new_color);
|
||||
}
|
||||
|
||||
}
|
129
core/modules/field/src/Tests/String/RawStringFormatterTest.php
Normal file
129
core/modules/field/src/Tests/String/RawStringFormatterTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\String\RawStringFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\String;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the raw string formatter
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class RawStringFormatterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field', 'text', 'entity_test', 'system', 'filter', 'user');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Configure the theme system.
|
||||
$this->installConfig(array('system', 'field'));
|
||||
$this->installSchema('system', 'router');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->installEntitySchema('entity_test');
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'string_long',
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$instance->save();
|
||||
|
||||
$this->display = entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'string',
|
||||
'settings' => array(),
|
||||
));
|
||||
$this->display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders fields of a given entity with a given display.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object with attached fields to render.
|
||||
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
|
||||
* The display to render the fields in.
|
||||
*
|
||||
* @return string
|
||||
* The rendered entity fields.
|
||||
*/
|
||||
protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
|
||||
$content = $display->build($entity);
|
||||
$content = $this->render($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string formatter output.
|
||||
*/
|
||||
public function testStringFormatter() {
|
||||
$value = $this->randomString();
|
||||
$value .= "\n\n<strong>" . $this->randomString() . '</strong>';
|
||||
$value .= "\n\n" . $this->randomString();
|
||||
|
||||
$entity = EntityTest::create(array());
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
// Verify that all HTML is escaped and newlines are retained.
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertNoRaw($value);
|
||||
$this->assertRaw(nl2br(SafeMarkup::checkPlain($value)));
|
||||
|
||||
// Verify the cache tags.
|
||||
$build = $entity->{$this->fieldName}->view();
|
||||
$this->assertTrue(!isset($build[0]['#cache']), format_string('The string formatter has no cache tags.'));
|
||||
}
|
||||
|
||||
}
|
103
core/modules/field/src/Tests/String/StringFieldTest.php
Normal file
103
core/modules/field/src/Tests/String/StringFieldTest.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\String\StringFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\String;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the creation of string fields.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class StringFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_test');
|
||||
|
||||
/**
|
||||
* A user without any special permissions.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
// Test fields.
|
||||
|
||||
/**
|
||||
* Test widgets.
|
||||
*/
|
||||
function testTextfieldWidgets() {
|
||||
$this->_testTextfieldWidgets('string', 'string_textfield');
|
||||
$this->_testTextfieldWidgets('string_long', 'string_textarea');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for testTextfieldWidgets().
|
||||
*/
|
||||
function _testTextfieldWidgets($field_type, $widget_type) {
|
||||
// Create a field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => $field_type
|
||||
));
|
||||
$field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
))->save();
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => $widget_type,
|
||||
'settings' => array(
|
||||
'placeholder' => 'A placeholder on ' . $widget_type,
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertNoFieldByName("{$field_name}[0][format]", '1', 'Format selector is not displayed');
|
||||
$this->assertRaw(format_string('placeholder="A placeholder on !widget_type"', array('!widget_type' => $widget_type)));
|
||||
|
||||
// Submit with some value.
|
||||
$value = $this->randomMachineName();
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Display the entity.
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertText($value, 'Filtered tags are not displayed');
|
||||
}
|
||||
}
|
165
core/modules/field/src/Tests/String/StringFormatterTest.php
Normal file
165
core/modules/field/src/Tests/String/StringFormatterTest.php
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\String\StringFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\String;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the creation of text fields.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class StringFormatterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field', 'text', 'entity_test', 'system', 'filter', 'user');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Configure the theme system.
|
||||
$this->installConfig(array('system', 'field'));
|
||||
$this->installSchema('system', 'router');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
$this->entityType = 'entity_test_rev';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'string',
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$instance->save();
|
||||
|
||||
$this->display = entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, array(
|
||||
'type' => 'string',
|
||||
'settings' => array(),
|
||||
));
|
||||
$this->display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders fields of a given entity with a given display.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object with attached fields to render.
|
||||
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
|
||||
* The display to render the fields in.
|
||||
*
|
||||
* @return string
|
||||
* The rendered entity fields.
|
||||
*/
|
||||
protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
|
||||
$content = $display->build($entity);
|
||||
$content = $this->render($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string formatter output.
|
||||
*/
|
||||
public function testStringFormatter() {
|
||||
$value = $this->randomString();
|
||||
$value .= "\n\n<strong>" . $this->randomString() . '</strong>';
|
||||
$value .= "\n\n" . $this->randomString();
|
||||
|
||||
$entity = EntityTestRev::create(array());
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
// Verify that all HTML is escaped and newlines are retained.
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertNoRaw($value);
|
||||
$this->assertRaw(nl2br(SafeMarkup::checkPlain($value)));
|
||||
|
||||
// Verify the cache tags.
|
||||
$build = $entity->{$this->fieldName}->view();
|
||||
$this->assertTrue(!isset($build[0]['#cache']), format_string('The string formatter has no cache tags.'));
|
||||
|
||||
$value = $this->randomMachineName();
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
$entity->save();
|
||||
|
||||
// Set the formatter to link to the entity.
|
||||
$this->display->setComponent($this->fieldName, [
|
||||
'type' => 'string',
|
||||
'settings' => [
|
||||
'link_to_entity' => TRUE,
|
||||
],
|
||||
]);
|
||||
$this->display->save();
|
||||
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertLink($value, 0);
|
||||
$this->assertLinkByHref($entity->url());
|
||||
|
||||
// $entity->url('revision') falls back to the canonical URL if this is no
|
||||
// revision.
|
||||
$this->assertLinkByHref($entity->url('revision'));
|
||||
|
||||
// Make the entity a new revision.
|
||||
$old_revision_id = $entity->getRevisionId();
|
||||
$entity->setNewRevision(TRUE);
|
||||
$value2 = $this->randomMachineName();
|
||||
$entity->{$this->fieldName}->value = $value2;
|
||||
$entity->save();
|
||||
$entity_new_revision = \Drupal::entityManager()->getStorage('entity_test_rev')->loadRevision($old_revision_id);
|
||||
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertLink($value2, 0);
|
||||
$this->assertLinkByHref($entity->url('revision'));
|
||||
|
||||
$this->renderEntityFields($entity_new_revision, $this->display);
|
||||
$this->assertLink($value, 0);
|
||||
$this->assertLinkByHref('/entity_test_rev/' . $entity_new_revision->id() . '/revision/' . $entity_new_revision->getRevisionId() . '/view');
|
||||
}
|
||||
}
|
58
core/modules/field/src/Tests/String/UuidFormatterTest.php
Normal file
58
core/modules/field/src/Tests/String/UuidFormatterTest.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\String\UuidFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\String;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
|
||||
/**
|
||||
* Tests the output of a UUID field.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class UuidFormatterTest extends KernelTestBase {
|
||||
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'entity_test', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['system', 'field']);
|
||||
$this->installSchema('system', 'router');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
$this->installEntitySchema('entity_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string formatter output.
|
||||
*/
|
||||
public function testUuidStringFormatter() {
|
||||
$entity = EntityTest::create([]);
|
||||
$entity->save();
|
||||
|
||||
$uuid_field = $entity->get('uuid');
|
||||
|
||||
$render_array = $uuid_field->view([]);
|
||||
$this->assertIdentical($render_array[0]['#markup'], $entity->uuid(), 'The rendered UUID matches the entity UUID.');
|
||||
|
||||
$render_array = $uuid_field->view(['settings' => ['link_to_entity' => TRUE]]);
|
||||
$this->assertIdentical($render_array[0]['#type'], 'link');
|
||||
$this->assertIdentical($render_array[0]['#title'], $entity->uuid());
|
||||
$this->assertIdentical($render_array[0]['#url']->toString(), $entity->url());
|
||||
}
|
||||
|
||||
}
|
98
core/modules/field/src/Tests/TestItemTest.php
Normal file
98
core/modules/field/src/Tests/TestItemTest.php
Normal file
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\TestItemTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the test field type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TestItemTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a 'test_field' field and storage for validation.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
))->save();
|
||||
entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => 'entity_test',
|
||||
))->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the field field type.
|
||||
*/
|
||||
public function testTestItem() {
|
||||
// Verify entity creation.
|
||||
$entity = entity_create('entity_test');
|
||||
$value = rand(1, 10);
|
||||
$entity->field_test = $value;
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertTrue($entity->{$this->fieldName} instanceof FieldItemListInterface, 'Field implements interface.');
|
||||
$this->assertTrue($entity->{$this->fieldName}[0] instanceof FieldItemInterface, 'Field item implements interface.');
|
||||
$this->assertEqual($entity->{$this->fieldName}->value, $value);
|
||||
$this->assertEqual($entity->{$this->fieldName}[0]->value, $value);
|
||||
|
||||
// Verify changing the field value.
|
||||
$new_value = rand(1, 10);
|
||||
$entity->field_test->value = $new_value;
|
||||
$this->assertEqual($entity->{$this->fieldName}->value, $new_value);
|
||||
|
||||
// Read changed entity and assert changed values.
|
||||
$entity->save();
|
||||
$entity = entity_load('entity_test', $id);
|
||||
$this->assertEqual($entity->{$this->fieldName}->value, $new_value);
|
||||
|
||||
// Test the schema for this field type.
|
||||
$expected_schema = array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
),
|
||||
),
|
||||
'unique keys' => array(),
|
||||
'indexes' => array(
|
||||
'value' => array('value'),
|
||||
),
|
||||
'foreign keys' => array(),
|
||||
);
|
||||
$field_schema = BaseFieldDefinition::create('test_field')->getSchema();
|
||||
$this->assertEqual($field_schema, $expected_schema);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\TestItemWithDependenciesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Tests the new entity API for the test field with dependencies type.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TestItemWithDependenciesTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
/**
|
||||
* Tests that field types can add dependencies to field config entities.
|
||||
*/
|
||||
public function testTestItemWithDepenencies() {
|
||||
// Create a 'test_field_with_dependencies' field and storage for validation.
|
||||
entity_create('field_storage_config', array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field_with_dependencies',
|
||||
))->save();
|
||||
$field = entity_create('field_config', array(
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => 'entity_test',
|
||||
));
|
||||
$field->save();
|
||||
|
||||
// Validate that the field configuration entity has the expected
|
||||
// dependencies.
|
||||
$this->assertEqual([
|
||||
'content' => ['node:article:uuid'],
|
||||
'config' => ['field.storage.entity_test.field_test'],
|
||||
'module' => ['field_test', 'test_module']
|
||||
], $field->getDependencies());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Timestamp\TimestampFormatterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Timestamp;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the timestamp formatters.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TimestampFormatterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'field', 'text', 'entity_test', 'user'];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
|
||||
*/
|
||||
protected $display;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['system']);
|
||||
$this->installConfig(['field']);
|
||||
$this->installEntitySchema('entity_test');
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
$this->bundle = $this->entityType;
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'timestamp',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
]);
|
||||
$instance->save();
|
||||
|
||||
$this->display = entity_get_display($this->entityType, $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'boolean',
|
||||
'settings' => [],
|
||||
]);
|
||||
$this->display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders fields of a given entity with a given display.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
|
||||
* The entity object with attached fields to render.
|
||||
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
|
||||
* The display to render the fields in.
|
||||
*
|
||||
* @return string
|
||||
* The rendered entity fields.
|
||||
*/
|
||||
protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
|
||||
$content = $display->build($entity);
|
||||
$content = $this->render($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests TimestampFormatter.
|
||||
*/
|
||||
protected function testTimestampFormatter() {
|
||||
$data = [];
|
||||
|
||||
// Test standard formats.
|
||||
$date_formats = array_keys(\Drupal::entityManager()->getStorage('date_format')->loadMultiple());
|
||||
|
||||
foreach ($date_formats as $date_format) {
|
||||
$data[] = ['date_format' => $date_format, 'custom_date_format' => '', 'timezone' => ''];
|
||||
}
|
||||
|
||||
$data[] = ['date_format' => 'custom', 'custom_date_format' => 'r', 'timezone' => ''];
|
||||
$data[] = ['date_format' => 'custom', 'custom_date_format' => 'e', 'timezone' => 'Asia/Tokyo'];
|
||||
|
||||
foreach ($data as $settings) {
|
||||
list($date_format, $custom_date_format, $timezone) = array_values($settings);
|
||||
if (empty($timezone)) {
|
||||
$timezone = NULL;
|
||||
}
|
||||
|
||||
$value = REQUEST_TIME - 87654321;
|
||||
$expected = \Drupal::service('date.formatter')->format($value, $date_format, $custom_date_format, $timezone);
|
||||
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['type'] = 'timestamp';
|
||||
$component['settings'] = $settings;
|
||||
$this->display->setComponent($this->fieldName, $component);
|
||||
|
||||
$entity = EntityTest::create([]);
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertRaw($expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests TimestampAgoFormatter.
|
||||
*/
|
||||
protected function testTimestampAgoFormatter() {
|
||||
$data = [];
|
||||
|
||||
foreach (array(1,2,3,4,5,6) as $granularity) {
|
||||
$data[] = [
|
||||
'future_format' => '@interval hence',
|
||||
'past_format' => '@interval ago',
|
||||
'granularity' => $granularity,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($data as $settings) {
|
||||
$future_format = $settings['future_format'];
|
||||
$past_format = $settings['past_format'];
|
||||
$granularity = $settings['granularity'];
|
||||
$request_time = \Drupal::requestStack()->getCurrentRequest()->server->get('REQUEST_TIME');
|
||||
|
||||
// Test a timestamp in the past
|
||||
$value = $request_time - 87654321;
|
||||
$expected = SafeMarkup::format($past_format, ['@interval' => \Drupal::service('date.formatter')->formatTimeDiffSince($value, ['granularity' => $granularity])]);
|
||||
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['type'] = 'timestamp_ago';
|
||||
$component['settings'] = $settings;
|
||||
$this->display->setComponent($this->fieldName, $component);
|
||||
|
||||
$entity = EntityTest::create([]);
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertRaw($expected);
|
||||
|
||||
// Test a timestamp in the future
|
||||
$value = $request_time + 87654321;
|
||||
$expected = SafeMarkup::format($future_format, ['@interval' => \Drupal::service('date.formatter')->formatTimeDiffUntil($value, ['granularity' => $granularity])]);
|
||||
|
||||
$component = $this->display->getComponent($this->fieldName);
|
||||
$component['type'] = 'timestamp_ago';
|
||||
$component['settings'] = $settings;
|
||||
$this->display->setComponent($this->fieldName, $component);
|
||||
|
||||
$entity = EntityTest::create([]);
|
||||
$entity->{$this->fieldName}->value = $value;
|
||||
|
||||
$this->renderEntityFields($entity, $this->display);
|
||||
$this->assertRaw($expected);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
205
core/modules/field/src/Tests/TranslationTest.php
Normal file
205
core/modules/field/src/Tests/TranslationTest.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\TranslationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests multilanguage fields logic.
|
||||
*
|
||||
* The following tests will check the multilanguage logic in field handling.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TranslationTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* node is required because the tests alter the node entity type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'node');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The name of the entity type to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'test_entity';
|
||||
|
||||
/**
|
||||
* An array defining the field storage to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageDefinition;
|
||||
|
||||
/**
|
||||
* An array defining the field to use in this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldDefinition;
|
||||
|
||||
/**
|
||||
* The field storage to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(array('language'));
|
||||
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName());
|
||||
|
||||
$this->entityType = 'entity_test';
|
||||
|
||||
$this->fieldStorageDefinition = array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityType,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
);
|
||||
$this->fieldStorage = entity_create('field_storage_config', $this->fieldStorageDefinition);
|
||||
$this->fieldStorage->save();
|
||||
|
||||
$this->fieldDefinition = array(
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => 'entity_test',
|
||||
);
|
||||
$this->field = entity_create('field_config', $this->fieldDefinition);
|
||||
$this->field->save();
|
||||
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => 'l' . $i,
|
||||
'label' => $this->randomString(),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test translatable fields storage/retrieval.
|
||||
*/
|
||||
function testTranslatableFieldSaveLoad() {
|
||||
// Enable field translations for nodes.
|
||||
field_test_entity_info_translatable('node', TRUE);
|
||||
$entity_type = \Drupal::entityManager()->getDefinition('node');
|
||||
$this->assertTrue($entity_type->isTranslatable(), 'Nodes are translatable.');
|
||||
|
||||
// Prepare the field translations.
|
||||
$entity_type_id = 'entity_test';
|
||||
field_test_entity_info_translatable($entity_type_id, TRUE);
|
||||
$entity = entity_create($entity_type_id, array('type' => $this->field->getTargetBundle()));
|
||||
$field_translations = array();
|
||||
$available_langcodes = array_keys($this->container->get('language_manager')->getLanguages());
|
||||
$entity->langcode->value = reset($available_langcodes);
|
||||
foreach ($available_langcodes as $langcode) {
|
||||
$field_translations[$langcode] = $this->_generateTestFieldValues($this->fieldStorage->getCardinality());
|
||||
$entity->getTranslation($langcode)->{$this->fieldName}->setValue($field_translations[$langcode]);
|
||||
}
|
||||
|
||||
// Save and reload the field translations.
|
||||
$entity = $this->entitySaveReload($entity);
|
||||
|
||||
// Check if the correct values were saved/loaded.
|
||||
foreach ($field_translations as $langcode => $items) {
|
||||
$result = TRUE;
|
||||
foreach ($items as $delta => $item) {
|
||||
$result = $result && $item['value'] == $entity->getTranslation($langcode)->{$this->fieldName}[$delta]->value;
|
||||
}
|
||||
$this->assertTrue($result, format_string('%language translation correctly handled.', array('%language' => $langcode)));
|
||||
}
|
||||
|
||||
// Test default values.
|
||||
$field_name_default = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
$field_storage_definition = $this->fieldStorageDefinition;
|
||||
$field_storage_definition['field_name'] = $field_name_default;
|
||||
$field_storage = entity_create('field_storage_config', $field_storage_definition);
|
||||
$field_storage->save();
|
||||
|
||||
$field_definition = $this->fieldDefinition;
|
||||
$field_definition['field_storage'] = $field_storage;
|
||||
$field_definition['default_value'] = array(array('value' => rand(1, 127)));
|
||||
$field = entity_create('field_config', $field_definition);
|
||||
$field->save();
|
||||
|
||||
$translation_langcodes = array_slice($available_langcodes, 0, 2);
|
||||
asort($translation_langcodes);
|
||||
$translation_langcodes = array_values($translation_langcodes);
|
||||
|
||||
$values = array('type' => $field->getTargetBundle(), 'langcode' => $translation_langcodes[0]);
|
||||
$entity = entity_create($entity_type_id, $values);
|
||||
foreach ($translation_langcodes as $langcode) {
|
||||
$values[$this->fieldName][$langcode] = $this->_generateTestFieldValues($this->fieldStorage->getCardinality());
|
||||
$entity->getTranslation($langcode, FALSE)->{$this->fieldName}->setValue($values[$this->fieldName][$langcode]);
|
||||
}
|
||||
|
||||
$field_langcodes = array_keys($entity->getTranslationLanguages());
|
||||
sort($field_langcodes);
|
||||
$this->assertEqual($translation_langcodes, $field_langcodes, 'Missing translations did not get a default value.');
|
||||
|
||||
// @todo Test every translation once the Entity Translation API allows for
|
||||
// multilingual defaults.
|
||||
$langcode = $entity->language()->getId();
|
||||
$this->assertEqual($entity->getTranslation($langcode)->{$field_name_default}->getValue(), $field->default_value, format_string('Default value correctly populated for language %language.', array('%language' => $langcode)));
|
||||
|
||||
// Check that explicit empty values are not overridden with default values.
|
||||
foreach (array(NULL, array()) as $empty_items) {
|
||||
$values = array('type' => $field->getTargetBundle(), 'langcode' => $translation_langcodes[0]);
|
||||
$entity = entity_create($entity_type_id, $values);
|
||||
foreach ($translation_langcodes as $langcode) {
|
||||
$values[$this->fieldName][$langcode] = $this->_generateTestFieldValues($this->fieldStorage->getCardinality());
|
||||
$entity->getTranslation($langcode)->{$this->fieldName}->setValue($values[$this->fieldName][$langcode]);
|
||||
$entity->getTranslation($langcode)->{$field_name_default}->setValue($empty_items);
|
||||
$values[$field_name_default][$langcode] = $empty_items;
|
||||
}
|
||||
|
||||
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
|
||||
$this->assertEqual($entity->getTranslation($langcode)->{$field_name_default}->getValue(), $empty_items, format_string('Empty value correctly populated for language %language.', array('%language' => $langcode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field access.
|
||||
*
|
||||
* Regression test to verify that fieldAccess() can be called while only
|
||||
* passing the required parameters.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2404739
|
||||
*/
|
||||
public function testFieldAccess() {
|
||||
$access_control_handler = \Drupal::entityManager()->getAccessControlHandler($this->entityType);
|
||||
$this->assertTrue($access_control_handler->fieldAccess('view', $this->field));
|
||||
}
|
||||
|
||||
}
|
135
core/modules/field/src/Tests/TranslationWebTest.php
Normal file
135
core/modules/field/src/Tests/TranslationWebTest.php
Normal file
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\TranslationWebTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests multilanguage fields logic that require a full environment.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TranslationWebTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'field_test', 'entity_test');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The name of the entity type to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId = 'entity_test_mulrev';
|
||||
|
||||
/**
|
||||
* The field storage to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
);
|
||||
entity_create('field_storage_config', $field_storage)->save();
|
||||
$this->fieldStorage = FieldStorageConfig::load($this->entityTypeId . '.' . $this->fieldName);
|
||||
|
||||
$field = array(
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => $this->entityTypeId,
|
||||
);
|
||||
entity_create('field_config', $field)->save();
|
||||
$this->field = FieldConfig::load($this->entityTypeId . '.' . $field['bundle'] . '.' . $this->fieldName);
|
||||
|
||||
entity_get_form_display($this->entityTypeId, $this->entityTypeId, 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => 'l' . $i,
|
||||
'label' => $this->randomString(),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field translations when creating a new revision.
|
||||
*/
|
||||
function testFieldFormTranslationRevisions() {
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Prepare the field translations.
|
||||
field_test_entity_info_translatable($this->entityTypeId, TRUE);
|
||||
$entity = entity_create($this->entityTypeId);
|
||||
$available_langcodes = array_flip(array_keys($this->container->get('language_manager')->getLanguages()));
|
||||
$field_name = $this->fieldStorage->getName();
|
||||
|
||||
// Store the field translations.
|
||||
ksort($available_langcodes);
|
||||
$entity->langcode->value = key($available_langcodes);
|
||||
foreach ($available_langcodes as $langcode => $value) {
|
||||
$entity->getTranslation($langcode)->{$field_name}->value = $value + 1;
|
||||
}
|
||||
$entity->save();
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $entity->{$field_name}->value,
|
||||
'revision' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm($this->entityTypeId . '/manage/' . $entity->id(), $edit, t('Save'));
|
||||
|
||||
// Check translation revisions.
|
||||
$this->checkTranslationRevisions($entity->id(), $entity->getRevisionId(), $available_langcodes);
|
||||
$this->checkTranslationRevisions($entity->id(), $entity->getRevisionId() + 1, $available_langcodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the field translation attached to the entity revision identified
|
||||
* by the passed arguments were correctly stored.
|
||||
*/
|
||||
private function checkTranslationRevisions($id, $revision_id, $available_langcodes) {
|
||||
$field_name = $this->fieldStorage->getName();
|
||||
$entity = entity_revision_load($this->entityTypeId, $revision_id);
|
||||
foreach ($available_langcodes as $langcode => $value) {
|
||||
$passed = $entity->getTranslation($langcode)->{$field_name}->value == $value + 1;
|
||||
$this->assertTrue($passed, format_string('The @language translation for revision @revision was correctly stored', array('@language' => $langcode, '@revision' => $entity->getRevisionId())));
|
||||
}
|
||||
}
|
||||
}
|
89
core/modules/field/src/Tests/Views/FieldTestBase.php
Normal file
89
core/modules/field/src/Tests/Views/FieldTestBase.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Views\FieldTestBase.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @TODO
|
||||
* - Test on a generic entity not on a node.
|
||||
*
|
||||
* What has to be tested:
|
||||
* - Make sure that every wanted field is added to the according entity type.
|
||||
* - Make sure the joins are done correctly.
|
||||
* - Use basic fields and make sure that the full wanted object is built.
|
||||
* - Use relationships between different entity types, for example node and
|
||||
* the node author(user).
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Provides some helper methods for testing fieldapi integration into views.
|
||||
*/
|
||||
abstract class FieldTestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test_views');
|
||||
|
||||
/**
|
||||
* Stores the field definitions used by the test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fieldStorages;
|
||||
|
||||
/**
|
||||
* Stores the fields of the field storage. They have the same keys as the
|
||||
* field storages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fields;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Ensure the page node type exists.
|
||||
entity_create('node_type', array(
|
||||
'type' => 'page',
|
||||
'name' => 'page',
|
||||
))->save();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('field_test_views'));
|
||||
}
|
||||
|
||||
function setUpFieldStorages($amount = 3, $type = 'string') {
|
||||
// Create three fields.
|
||||
$field_names = array();
|
||||
for ($i = 0; $i < $amount; $i++) {
|
||||
$field_names[$i] = 'field_name_' . $i;
|
||||
$this->fieldStorages[$i] = entity_create('field_storage_config', array(
|
||||
'field_name' => $field_names[$i],
|
||||
'entity_type' => 'node',
|
||||
'type' => $type,
|
||||
));
|
||||
$this->fieldStorages[$i]->save();
|
||||
}
|
||||
return $field_names;
|
||||
}
|
||||
|
||||
function setUpFields($bundle = 'page') {
|
||||
foreach ($this->fieldStorages as $key => $field_storage) {
|
||||
$this->fields[$key] = entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
));
|
||||
$this->fields[$key]->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
115
core/modules/field/src/Tests/Views/FieldUITest.php
Normal file
115
core/modules/field/src/Tests/Views/FieldUITest.php
Normal file
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Views\FieldUITest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of the field field handler.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\field\Plugin\views\field\Field
|
||||
*/
|
||||
class FieldUITest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_fieldapi');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui');
|
||||
|
||||
/**
|
||||
* A user with the 'administer views' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->account = $this->drupalCreateUser(array('administer views'));
|
||||
$this->drupalLogin($this->account);
|
||||
|
||||
$this->setUpFieldStorages(1, 'text');
|
||||
$this->setUpFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic field handler settings in the UI.
|
||||
*/
|
||||
public function testHandlerUI() {
|
||||
$url = "admin/structure/views/nojs/handler/test_view_fieldapi/default/field/field_name_0";
|
||||
$this->drupalGet($url);
|
||||
|
||||
// Tests the available formatter options.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-options-type'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
// @todo Replace this sort by assertArray once it's in.
|
||||
sort($options, SORT_STRING);
|
||||
$this->assertEqual($options, array('text_default', 'text_trimmed'), 'The text formatters for a simple text field appear as expected.');
|
||||
|
||||
$this->drupalPostForm(NULL, array('options[type]' => 'text_trimmed'), t('Apply'));
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertOptionSelected('edit-options-type', 'text_trimmed');
|
||||
|
||||
$random_number = rand(100, 400);
|
||||
$this->drupalPostForm(NULL, array('options[settings][trim_length]' => $random_number), t('Apply'));
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldByName('options[settings][trim_length]', $random_number, 'The formatter setting got saved.');
|
||||
|
||||
// Save the view and test whether the settings are saved.
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view_fieldapi', array(), t('Save'));
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$view->initHandlers();
|
||||
$this->assertEqual($view->field['field_name_0']->options['type'], 'text_trimmed');
|
||||
$this->assertEqual($view->field['field_name_0']->options['settings']['trim_length'], $random_number);
|
||||
|
||||
// Ensure that the view depends on the field storage.
|
||||
$dependencies = \Drupal::service('config.manager')->findConfigEntityDependents('config', [$this->fieldStorages[0]->getConfigDependencyName()]);
|
||||
$this->assertTrue(isset($dependencies['views.view.test_view_fieldapi']), 'The view is dependent on the field storage.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic field handler form when aggregation is enabled.
|
||||
*/
|
||||
public function testHandlerUIAggregation() {
|
||||
// Enable aggregation.
|
||||
$edit = array('group_by' => '1');
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view_fieldapi/default/group_by', $edit, t('Apply'));
|
||||
|
||||
$url = "admin/structure/views/nojs/handler/test_view_fieldapi/default/field/field_name_0";
|
||||
$this->drupalGet($url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Test the click sort column options.
|
||||
// Tests the available formatter options.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-options-click-sort-column'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
sort($options, SORT_STRING);
|
||||
|
||||
$this->assertEqual($options, array('format', 'value'), 'The expected sort field options were found.');
|
||||
}
|
||||
|
||||
}
|
314
core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
Normal file
314
core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
Normal file
|
@ -0,0 +1,314 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\Views\HandlerFieldFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the field itself of the Field integration.
|
||||
*
|
||||
* @group field
|
||||
* @TODO
|
||||
* Check a entity-type with bundles
|
||||
* Check a entity-type without bundles
|
||||
* Check locale:disabled, locale:enabled and locale:enabled with another language
|
||||
* Check revisions
|
||||
*/
|
||||
class HandlerFieldFieldTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('node', 'field_test');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_fieldapi');
|
||||
|
||||
/**
|
||||
* Test nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
public $nodes;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup basic fields.
|
||||
$this->setUpFieldStorages(3);
|
||||
|
||||
// Setup a field with cardinality > 1.
|
||||
$this->fieldStorages[3] = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_name_3',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
));
|
||||
$this->fieldStorages[3]->save();
|
||||
// Setup a field that will have no value.
|
||||
$this->fieldStorages[4] = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_name_4',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
));
|
||||
$this->fieldStorages[4]->save();
|
||||
|
||||
// Setup a text field.
|
||||
$this->fieldStorages[5] = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_name_5',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
));
|
||||
$this->fieldStorages[5]->save();
|
||||
|
||||
// Setup a text field with access control.
|
||||
// @see field_test_entity_field_access()
|
||||
$this->fieldStorages[6] = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_no_view_access',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
));
|
||||
$this->fieldStorages[6]->save();
|
||||
|
||||
$this->setUpFields();
|
||||
|
||||
// Create some nodes.
|
||||
$this->nodes = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$edit = array('type' => 'page');
|
||||
|
||||
foreach (array(0, 1, 2, 5) as $key) {
|
||||
$field_storage = $this->fieldStorages[$key];
|
||||
$edit[$field_storage->getName()][0]['value'] = $this->randomMachineName(8);
|
||||
}
|
||||
// Add a hidden value for the no-view field.
|
||||
$edit[$this->fieldStorages[6]->getName()][0]['value'] = 'ssh secret squirrel';
|
||||
for ($j = 0; $j < 5; $j++) {
|
||||
$edit[$this->fieldStorages[3]->getName()][$j]['value'] = $this->randomMachineName(8);
|
||||
}
|
||||
// Set this field to be empty.
|
||||
$edit[$this->fieldStorages[4]->getName()] = array(array('value' => NULL));
|
||||
|
||||
$this->nodes[$i] = $this->drupalCreateNode($edit);
|
||||
}
|
||||
|
||||
$this->container->get('views.views_data')->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the testing view with random field data.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to add field data to.
|
||||
*/
|
||||
protected function prepareView(ViewExecutable $view) {
|
||||
$view->storage->invalidateCaches();
|
||||
$view->initDisplay();
|
||||
foreach ($this->fieldStorages as $field_storage) {
|
||||
$field_name = $field_storage->getName();
|
||||
$view->display_handler->options['fields'][$field_name]['id'] = $field_name;
|
||||
$view->display_handler->options['fields'][$field_name]['table'] = 'node__' . $field_name;
|
||||
$view->display_handler->options['fields'][$field_name]['field'] = $field_name;
|
||||
}
|
||||
}
|
||||
|
||||
public function testFieldRender() {
|
||||
$this->_testSimpleFieldRender();
|
||||
$this->_testInaccessibleFieldRender();
|
||||
$this->_testFormatterSimpleFieldRender();
|
||||
$this->_testMultipleFieldRender();
|
||||
}
|
||||
|
||||
public function _testSimpleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$this->executeView($view);
|
||||
|
||||
// Tests that the rendered fields match the actual value of the fields.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
for ($key = 0; $key < 2; $key++) {
|
||||
$field_name = $this->fieldStorages[$key]->getName();
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$expected_field = $this->nodes[$i]->$field_name->value;
|
||||
$this->assertEqual($rendered_field, $expected_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function _testInaccessibleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$this->executeView($view);
|
||||
|
||||
// Check that the field handler for the hidden field is correctly removed
|
||||
// from the display.
|
||||
// @see https://www.drupal.org/node/2382931
|
||||
$this->assertFalse(array_key_exists('field_no_view_access', $view->field));
|
||||
|
||||
// Check that the access-denied field is not visible.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$field_name = $this->fieldStorages[6]->getName();
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$this->assertFalse($rendered_field, 'Hidden field not rendered');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that fields with formatters runs as expected.
|
||||
*/
|
||||
public function _testFormatterSimpleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$this->fieldStorages[5]->getName()]['type'] = 'text_trimmed';
|
||||
$view->displayHandlers->get('default')->options['fields'][$this->fieldStorages[5]->getName()]['settings'] = array(
|
||||
'trim_length' => 3,
|
||||
);
|
||||
$this->executeView($view);
|
||||
|
||||
// Make sure that the formatter works as expected.
|
||||
// @TODO: actually there should be a specific formatter.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $this->fieldStorages[5]->getName());
|
||||
$this->assertEqual(strlen(html_entity_decode($rendered_field)), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public function _testMultipleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$field_name = $this->fieldStorages[3]->getName();
|
||||
|
||||
// Test delta limit.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited.');
|
||||
}
|
||||
|
||||
// Test that an empty field is rendered without error.
|
||||
$view->style_plugin->getField(4, $this->fieldStorages[4]->getName());
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + offset
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_offset'] = 1;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 1, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and the offset is correct.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + reverse.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_offset'] = 0;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_reversed'] = TRUE;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
array_splice($pure_items, 0, -3);
|
||||
$pure_items = array_reverse($pure_items);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and they are reversed.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta first last.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 0;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_first_last'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_reversed'] = FALSE;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$items[] = $pure_items[0]['value'];
|
||||
$items[] = $pure_items[4]['value'];
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'Items are limited to first and last.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + custom separator.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_first_last'] = FALSE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['separator'] = ':';
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(':', $items), 'The amount of items is limited and the custom separator is correct.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test separator with HTML, ensure it is escaped.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['separator'] = '<h2>test</h2>';
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = [];
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode('<h2>test</h2>', $items), 'The custom separator is correctly escaped.');
|
||||
}
|
||||
$view->destroy();
|
||||
}
|
||||
|
||||
}
|
63
core/modules/field/src/Tests/WidgetPluginManagerTest.php
Normal file
63
core/modules/field/src/Tests/WidgetPluginManagerTest.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\WidgetPluginManagerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\WidgetPluginManager;
|
||||
|
||||
/**
|
||||
* Tests the field widget manager.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class WidgetPluginManagerTest extends FieldUnitTestBase {
|
||||
|
||||
/**
|
||||
* Tests that the widget definitions alter hook works.
|
||||
*/
|
||||
function testWidgetDefinitionAlter() {
|
||||
$widget_definition = \Drupal::service('plugin.manager.field.widget')->getDefinition('test_field_widget_multiple');
|
||||
|
||||
// Test if hook_field_widget_info_alter is being called.
|
||||
$this->assertTrue(in_array('test_field', $widget_definition['field_types']), "The 'test_field_widget_multiple' widget is enabled for the 'test_field' field type in field_test_field_widget_info_alter().");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getInstance falls back on default if current is not applicable.
|
||||
*
|
||||
* @see \Drupal\field\Tests\FormatterPluginManagerTest::testNotApplicableFallback()
|
||||
*/
|
||||
public function testNotApplicableFallback() {
|
||||
/** @var WidgetPluginManager $widget_plugin_manager */
|
||||
$widget_plugin_manager = \Drupal::service('plugin.manager.field.widget');
|
||||
|
||||
$base_field_definition = BaseFieldDefinition::create('test_field')
|
||||
// Set a name that will make isApplicable() return TRUE.
|
||||
->setName('field_multiwidgetfield');
|
||||
|
||||
$widget_options = array(
|
||||
'field_definition' => $base_field_definition,
|
||||
'form_mode' => 'default',
|
||||
'configuration' => array(
|
||||
'type' => 'test_field_widget_multiple',
|
||||
),
|
||||
);
|
||||
|
||||
$instance = $widget_plugin_manager->getInstance($widget_options);
|
||||
$this->assertEqual($instance->getPluginId(), 'test_field_widget_multiple');
|
||||
|
||||
// Now do the same but with machine name field_onewidgetfield, because that
|
||||
// makes isApplicable() return FALSE.
|
||||
$base_field_definition->setName('field_onewidgetfield');
|
||||
$instance = $widget_plugin_manager->getInstance($widget_options);
|
||||
|
||||
// Instance should be default widget.
|
||||
$this->assertNotEqual($instance->getPluginId(), 'test_field_widget_multiple');
|
||||
$this->assertEqual($instance->getPluginId(), 'test_field_widget');
|
||||
}
|
||||
|
||||
}
|
106
core/modules/field/src/Tests/reEnableModuleFieldTest.php
Normal file
106
core/modules/field/src/Tests/reEnableModuleFieldTest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\field\Tests\reEnableModuleFieldTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field module after being disabled and re-enabled.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class reEnableModuleFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'field',
|
||||
'node',
|
||||
// We use telephone module instead of test_field because test_field is
|
||||
// hidden and does not display on the admin/modules page.
|
||||
'telephone'
|
||||
);
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'create article content',
|
||||
'edit own article content',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior of a field module after being disabled and re-enabled.
|
||||
*
|
||||
* @see field_system_info_alter()
|
||||
*/
|
||||
function testReEnabledField() {
|
||||
|
||||
// Add a telephone field to the article content type.
|
||||
$field_storage = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_telephone',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
entity_create('field_config', array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'article',
|
||||
'label' => 'Telephone Number',
|
||||
))->save();
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent('field_telephone', array(
|
||||
'type' => 'telephone_default',
|
||||
'settings' => array(
|
||||
'placeholder' => '123-456-7890',
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent('field_telephone', array(
|
||||
'type' => 'telephone_link',
|
||||
'weight' => 1,
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display the article node form and verify the telephone widget is present.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertFieldByName("field_telephone[0][value]", '', 'Widget found.');
|
||||
|
||||
// Submit an article node with a telephone field so data exist for the
|
||||
// field.
|
||||
$edit = array(
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
'field_telephone[0][value]' => "123456789",
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('<a href="tel:123456789">');
|
||||
|
||||
// Test that the module can't be uninstalled from the UI while there is data
|
||||
// for it's fields.
|
||||
$admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('admin/modules/uninstall');
|
||||
$this->assertText('Fields type(s) in use');
|
||||
$field_storage->delete();
|
||||
$this->drupalGet('admin/modules/uninstall');
|
||||
$this->assertText('Fields pending deletion');
|
||||
$this->cronRun();
|
||||
$this->assertNoText('Fields type(s) in use');
|
||||
$this->assertNoText('Fields pending deletion');
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue