annotate core/modules/field/src/Entity/FieldStorageConfig.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\field\Entity;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\Unicode;
Chris@0 6 use Drupal\Core\Config\Entity\ConfigEntityBase;
Chris@0 7 use Drupal\Core\Entity\EntityStorageInterface;
Chris@0 8 use Drupal\Core\Entity\FieldableEntityInterface;
Chris@0 9 use Drupal\Core\Entity\FieldableEntityStorageInterface;
Chris@0 10 use Drupal\Core\Field\FieldException;
Chris@0 11 use Drupal\Core\Field\FieldStorageDefinitionInterface;
Chris@0 12 use Drupal\Core\TypedData\OptionsProviderInterface;
Chris@0 13 use Drupal\field\FieldStorageConfigInterface;
Chris@0 14
Chris@0 15 /**
Chris@0 16 * Defines the Field storage configuration entity.
Chris@0 17 *
Chris@0 18 * @ConfigEntityType(
Chris@0 19 * id = "field_storage_config",
Chris@0 20 * label = @Translation("Field storage"),
Chris@0 21 * handlers = {
Chris@0 22 * "access" = "Drupal\field\FieldStorageConfigAccessControlHandler",
Chris@0 23 * "storage" = "Drupal\field\FieldStorageConfigStorage"
Chris@0 24 * },
Chris@0 25 * config_prefix = "storage",
Chris@0 26 * entity_keys = {
Chris@0 27 * "id" = "id",
Chris@0 28 * "label" = "id"
Chris@0 29 * },
Chris@0 30 * config_export = {
Chris@0 31 * "id",
Chris@0 32 * "field_name",
Chris@0 33 * "entity_type",
Chris@0 34 * "type",
Chris@0 35 * "settings",
Chris@0 36 * "module",
Chris@0 37 * "locked",
Chris@0 38 * "cardinality",
Chris@0 39 * "translatable",
Chris@0 40 * "indexes",
Chris@0 41 * "persist_with_no_fields",
Chris@0 42 * "custom_storage",
Chris@0 43 * }
Chris@0 44 * )
Chris@0 45 */
Chris@0 46 class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigInterface {
Chris@0 47
Chris@0 48 /**
Chris@0 49 * The maximum length of the field name, in characters.
Chris@0 50 *
Chris@0 51 * For fields created through Field UI, this includes the 'field_' prefix.
Chris@0 52 */
Chris@0 53 const NAME_MAX_LENGTH = 32;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * The field ID.
Chris@0 57 *
Chris@0 58 * The ID consists of 2 parts: the entity type and the field name.
Chris@0 59 *
Chris@0 60 * Example: node.body, user.field_main_image.
Chris@0 61 *
Chris@0 62 * @var string
Chris@0 63 */
Chris@0 64 protected $id;
Chris@0 65
Chris@0 66 /**
Chris@0 67 * The field name.
Chris@0 68 *
Chris@0 69 * This is the name of the property under which the field values are placed in
Chris@0 70 * an entity: $entity->{$field_name}. The maximum length is
Chris@0 71 * Field:NAME_MAX_LENGTH.
Chris@0 72 *
Chris@0 73 * Example: body, field_main_image.
Chris@0 74 *
Chris@0 75 * @var string
Chris@0 76 */
Chris@0 77 protected $field_name;
Chris@0 78
Chris@0 79 /**
Chris@0 80 * The name of the entity type the field can be attached to.
Chris@0 81 *
Chris@0 82 * @var string
Chris@0 83 */
Chris@0 84 protected $entity_type;
Chris@0 85
Chris@0 86 /**
Chris@0 87 * The field type.
Chris@0 88 *
Chris@0 89 * Example: text, integer.
Chris@0 90 *
Chris@0 91 * @var string
Chris@0 92 */
Chris@0 93 protected $type;
Chris@0 94
Chris@0 95 /**
Chris@0 96 * The name of the module that provides the field type.
Chris@0 97 *
Chris@0 98 * @var string
Chris@0 99 */
Chris@0 100 protected $module;
Chris@0 101
Chris@0 102 /**
Chris@0 103 * Field-type specific settings.
Chris@0 104 *
Chris@0 105 * An array of key/value pairs, The keys and default values are defined by the
Chris@0 106 * field type.
Chris@0 107 *
Chris@0 108 * @var array
Chris@0 109 */
Chris@0 110 protected $settings = [];
Chris@0 111
Chris@0 112 /**
Chris@0 113 * The field cardinality.
Chris@0 114 *
Chris@0 115 * The maximum number of values the field can hold. Possible values are
Chris@0 116 * positive integers or
Chris@0 117 * FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED. Defaults to 1.
Chris@0 118 *
Chris@0 119 * @var int
Chris@0 120 */
Chris@0 121 protected $cardinality = 1;
Chris@0 122
Chris@0 123 /**
Chris@0 124 * Flag indicating whether the field is translatable.
Chris@0 125 *
Chris@0 126 * Defaults to TRUE.
Chris@0 127 *
Chris@0 128 * @var bool
Chris@0 129 */
Chris@0 130 protected $translatable = TRUE;
Chris@0 131
Chris@0 132 /**
Chris@0 133 * Flag indicating whether the field is available for editing.
Chris@0 134 *
Chris@0 135 * If TRUE, some actions not available though the UI (but are still possible
Chris@0 136 * through direct API manipulation):
Chris@0 137 * - field settings cannot be changed,
Chris@0 138 * - new fields cannot be created
Chris@0 139 * - existing fields cannot be deleted.
Chris@0 140 * Defaults to FALSE.
Chris@0 141 *
Chris@0 142 * @var bool
Chris@0 143 */
Chris@0 144 protected $locked = FALSE;
Chris@0 145
Chris@0 146 /**
Chris@0 147 * Flag indicating whether the field storage should be deleted when orphaned.
Chris@0 148 *
Chris@0 149 * By default field storages for configurable fields are removed when there
Chris@0 150 * are no remaining fields using them. If multiple modules provide bundles
Chris@0 151 * which need to use the same field storage then setting this to TRUE will
Chris@0 152 * preserve the field storage regardless of what happens to the bundles. The
Chris@0 153 * classic use case for this is node body field storage since Book, Forum, the
Chris@0 154 * Standard profile and bundle (node type) creation through the UI all use
Chris@0 155 * same field storage.
Chris@0 156 *
Chris@0 157 * @var bool
Chris@0 158 */
Chris@0 159 protected $persist_with_no_fields = FALSE;
Chris@0 160
Chris@0 161 /**
Chris@0 162 * A boolean indicating whether or not the field item uses custom storage.
Chris@0 163 *
Chris@0 164 * @var bool
Chris@0 165 */
Chris@0 166 public $custom_storage = FALSE;
Chris@0 167
Chris@0 168 /**
Chris@0 169 * The custom storage indexes for the field data storage.
Chris@0 170 *
Chris@0 171 * This set of indexes is merged with the "default" indexes specified by the
Chris@0 172 * field type in hook_field_schema() to determine the actual set of indexes
Chris@0 173 * that get created.
Chris@0 174 *
Chris@0 175 * The indexes are defined using the same definition format as Schema API
Chris@0 176 * index specifications. Only columns that are part of the field schema, as
Chris@0 177 * defined by the field type in hook_field_schema(), are allowed.
Chris@0 178 *
Chris@0 179 * Some storage backends might not support indexes, and discard that
Chris@0 180 * information.
Chris@0 181 *
Chris@0 182 * @var array
Chris@0 183 */
Chris@0 184 protected $indexes = [];
Chris@0 185
Chris@0 186 /**
Chris@0 187 * Flag indicating whether the field is deleted.
Chris@0 188 *
Chris@0 189 * The delete() method marks the field as "deleted" and removes the
Chris@0 190 * corresponding entry from the config storage, but keeps its definition in
Chris@0 191 * the state storage while field data is purged by a separate
Chris@0 192 * garbage-collection process.
Chris@0 193 *
Chris@0 194 * Deleted fields stay out of the regular entity lifecycle (notably, their
Chris@0 195 * values are not populated in loaded entities, and are not saved back).
Chris@0 196 *
Chris@0 197 * @var bool
Chris@0 198 */
Chris@0 199 protected $deleted = FALSE;
Chris@0 200
Chris@0 201 /**
Chris@0 202 * The field schema.
Chris@0 203 *
Chris@0 204 * @var array
Chris@0 205 */
Chris@0 206 protected $schema;
Chris@0 207
Chris@0 208 /**
Chris@0 209 * An array of field property definitions.
Chris@0 210 *
Chris@0 211 * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
Chris@0 212 *
Chris@0 213 * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
Chris@0 214 */
Chris@0 215 protected $propertyDefinitions;
Chris@0 216
Chris@0 217 /**
Chris@0 218 * Static flag set to prevent recursion during field deletes.
Chris@0 219 *
Chris@0 220 * @var bool
Chris@0 221 */
Chris@0 222 protected static $inDeletion = FALSE;
Chris@0 223
Chris@0 224 /**
Chris@0 225 * Constructs a FieldStorageConfig object.
Chris@0 226 *
Chris@0 227 * In most cases, Field entities are created via
Chris@0 228 * FieldStorageConfig::create($values)), where $values is the same parameter
Chris@0 229 * as in this constructor.
Chris@0 230 *
Chris@0 231 * @param array $values
Chris@0 232 * An array of field properties, keyed by property name. Most array
Chris@0 233 * elements will be used to set the corresponding properties on the class;
Chris@0 234 * see the class property documentation for details. Some array elements
Chris@0 235 * have special meanings and a few are required. Special elements are:
Chris@0 236 * - name: required. As a temporary Backwards Compatibility layer right now,
Chris@0 237 * a 'field_name' property can be accepted in place of 'id'.
Chris@0 238 * - entity_type: required.
Chris@0 239 * - type: required.
Chris@0 240 *
Chris@0 241 * @see entity_create()
Chris@0 242 */
Chris@0 243 public function __construct(array $values, $entity_type = 'field_storage_config') {
Chris@0 244 // Check required properties.
Chris@0 245 if (empty($values['field_name'])) {
Chris@0 246 throw new FieldException('Attempt to create a field storage without a field name.');
Chris@0 247 }
Chris@0 248 if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) {
Chris@0 249 throw new FieldException("Attempt to create a field storage {$values['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");
Chris@0 250 }
Chris@0 251 if (empty($values['type'])) {
Chris@0 252 throw new FieldException("Attempt to create a field storage {$values['field_name']} with no type.");
Chris@0 253 }
Chris@0 254 if (empty($values['entity_type'])) {
Chris@0 255 throw new FieldException("Attempt to create a field storage {$values['field_name']} with no entity_type.");
Chris@0 256 }
Chris@0 257
Chris@0 258 parent::__construct($values, $entity_type);
Chris@0 259 }
Chris@0 260
Chris@0 261 /**
Chris@0 262 * {@inheritdoc}
Chris@0 263 */
Chris@0 264 public function id() {
Chris@0 265 return $this->getTargetEntityTypeId() . '.' . $this->getName();
Chris@0 266 }
Chris@0 267
Chris@0 268 /**
Chris@0 269 * Overrides \Drupal\Core\Entity\Entity::preSave().
Chris@0 270 *
Chris@0 271 * @throws \Drupal\Core\Field\FieldException
Chris@0 272 * If the field definition is invalid.
Chris@0 273 * @throws \Drupal\Core\Entity\EntityStorageException
Chris@0 274 * In case of failures at the configuration storage level.
Chris@0 275 */
Chris@0 276 public function preSave(EntityStorageInterface $storage) {
Chris@0 277 // Clear the derived data about the field.
Chris@0 278 unset($this->schema);
Chris@0 279
Chris@0 280 // Filter out unknown settings and make sure all settings are present, so
Chris@0 281 // that a complete field definition is passed to the various hooks and
Chris@0 282 // written to config.
Chris@0 283 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 284 $default_settings = $field_type_manager->getDefaultStorageSettings($this->type);
Chris@0 285 $this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
Chris@0 286
Chris@0 287 if ($this->isNew()) {
Chris@0 288 $this->preSaveNew($storage);
Chris@0 289 }
Chris@0 290 else {
Chris@0 291 $this->preSaveUpdated($storage);
Chris@0 292 }
Chris@0 293
Chris@0 294 parent::preSave($storage);
Chris@0 295 }
Chris@0 296
Chris@0 297 /**
Chris@0 298 * Prepares saving a new field definition.
Chris@0 299 *
Chris@0 300 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
Chris@0 301 * The entity storage.
Chris@0 302 *
Chris@0 303 * @throws \Drupal\Core\Field\FieldException
Chris@0 304 * If the field definition is invalid.
Chris@0 305 */
Chris@0 306 protected function preSaveNew(EntityStorageInterface $storage) {
Chris@0 307 $entity_manager = \Drupal::entityManager();
Chris@0 308 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 309
Chris@0 310 // Assign the ID.
Chris@0 311 $this->id = $this->id();
Chris@0 312
Chris@0 313 // Field name cannot be longer than FieldStorageConfig::NAME_MAX_LENGTH characters.
Chris@0 314 // We use Unicode::strlen() because the DB layer assumes that column widths
Chris@0 315 // are given in characters rather than bytes.
Chris@0 316 if (Unicode::strlen($this->getName()) > static::NAME_MAX_LENGTH) {
Chris@0 317 throw new FieldException('Attempt to create a field storage with an name longer than ' . static::NAME_MAX_LENGTH . ' characters: ' . $this->getName());
Chris@0 318 }
Chris@0 319
Chris@0 320 // Disallow reserved field names.
Chris@0 321 $disallowed_field_names = array_keys($entity_manager->getBaseFieldDefinitions($this->getTargetEntityTypeId()));
Chris@0 322 if (in_array($this->getName(), $disallowed_field_names)) {
Chris@0 323 throw new FieldException("Attempt to create field storage {$this->getName()} which is reserved by entity type {$this->getTargetEntityTypeId()}.");
Chris@0 324 }
Chris@0 325
Chris@0 326 // Check that the field type is known.
Chris@0 327 $field_type = $field_type_manager->getDefinition($this->getType(), FALSE);
Chris@0 328 if (!$field_type) {
Chris@0 329 throw new FieldException("Attempt to create a field storage of unknown type {$this->getType()}.");
Chris@0 330 }
Chris@0 331 $this->module = $field_type['provider'];
Chris@0 332
Chris@0 333 // Notify the entity manager.
Chris@0 334 $entity_manager->onFieldStorageDefinitionCreate($this);
Chris@0 335 }
Chris@0 336
Chris@0 337 /**
Chris@0 338 * {@inheritdoc}
Chris@0 339 */
Chris@0 340 public function calculateDependencies() {
Chris@0 341 parent::calculateDependencies();
Chris@0 342 // Ensure the field is dependent on the providing module.
Chris@0 343 $this->addDependency('module', $this->getTypeProvider());
Chris@0 344 // Ask the field type for any additional storage dependencies.
Chris@0 345 // @see \Drupal\Core\Field\FieldItemInterface::calculateStorageDependencies()
Chris@0 346 $definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getType(), FALSE);
Chris@0 347 $this->addDependencies($definition['class']::calculateStorageDependencies($this));
Chris@0 348
Chris@0 349 // Ensure the field is dependent on the provider of the entity type.
Chris@0 350 $entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
Chris@0 351 $this->addDependency('module', $entity_type->getProvider());
Chris@0 352 return $this;
Chris@0 353 }
Chris@0 354
Chris@0 355 /**
Chris@0 356 * Prepares saving an updated field definition.
Chris@0 357 *
Chris@0 358 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
Chris@0 359 * The entity storage.
Chris@0 360 */
Chris@0 361 protected function preSaveUpdated(EntityStorageInterface $storage) {
Chris@0 362 $module_handler = \Drupal::moduleHandler();
Chris@0 363 $entity_manager = \Drupal::entityManager();
Chris@0 364
Chris@0 365 // Some updates are always disallowed.
Chris@0 366 if ($this->getType() != $this->original->getType()) {
Chris@0 367 throw new FieldException("Cannot change the field type for an existing field storage.");
Chris@0 368 }
Chris@0 369 if ($this->getTargetEntityTypeId() != $this->original->getTargetEntityTypeId()) {
Chris@0 370 throw new FieldException("Cannot change the entity type for an existing field storage.");
Chris@0 371 }
Chris@0 372
Chris@0 373 // See if any module forbids the update by throwing an exception. This
Chris@0 374 // invokes hook_field_storage_config_update_forbid().
Chris@0 375 $module_handler->invokeAll('field_storage_config_update_forbid', [$this, $this->original]);
Chris@0 376
Chris@0 377 // Notify the entity manager. A listener can reject the definition
Chris@0 378 // update as invalid by raising an exception, which stops execution before
Chris@0 379 // the definition is written to config.
Chris@0 380 $entity_manager->onFieldStorageDefinitionUpdate($this, $this->original);
Chris@0 381 }
Chris@0 382
Chris@0 383 /**
Chris@0 384 * {@inheritdoc}
Chris@0 385 */
Chris@0 386 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
Chris@0 387 if ($update) {
Chris@0 388 // Invalidate the render cache for all affected entities.
Chris@0 389 $entity_manager = \Drupal::entityManager();
Chris@0 390 $entity_type = $this->getTargetEntityTypeId();
Chris@0 391 if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
Chris@0 392 $entity_manager->getViewBuilder($entity_type)->resetCache();
Chris@0 393 }
Chris@0 394 }
Chris@0 395 }
Chris@0 396
Chris@0 397 /**
Chris@0 398 * {@inheritdoc}
Chris@0 399 */
Chris@0 400 public static function preDelete(EntityStorageInterface $storage, array $field_storages) {
Chris@14 401 /** @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository */
Chris@14 402 $deleted_fields_repository = \Drupal::service('entity_field.deleted_fields_repository');
Chris@0 403
Chris@0 404 // Set the static flag so that we don't delete field storages whilst
Chris@0 405 // deleting fields.
Chris@0 406 static::$inDeletion = TRUE;
Chris@0 407
Chris@0 408 // Delete or fix any configuration that is dependent, for example, fields.
Chris@0 409 parent::preDelete($storage, $field_storages);
Chris@0 410
Chris@14 411 // Keep the field storage definitions in the deleted fields repository so we
Chris@14 412 // can use them later during field_purge_batch().
Chris@0 413 /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */
Chris@0 414 foreach ($field_storages as $field_storage) {
Chris@0 415 // Only mark a field for purging if there is data. Otherwise, just remove
Chris@0 416 // it.
Chris@0 417 $target_entity_storage = \Drupal::entityTypeManager()->getStorage($field_storage->getTargetEntityTypeId());
Chris@0 418 if (!$field_storage->deleted && $target_entity_storage instanceof FieldableEntityStorageInterface && $target_entity_storage->countFieldData($field_storage, TRUE)) {
Chris@14 419 $storage_definition = clone $field_storage;
Chris@14 420 $storage_definition->deleted = TRUE;
Chris@14 421 $deleted_fields_repository->addFieldStorageDefinition($storage_definition);
Chris@0 422 }
Chris@0 423 }
Chris@0 424 }
Chris@0 425
Chris@0 426 /**
Chris@0 427 * {@inheritdoc}
Chris@0 428 */
Chris@0 429 public static function postDelete(EntityStorageInterface $storage, array $fields) {
Chris@0 430 // Notify the storage.
Chris@0 431 foreach ($fields as $field) {
Chris@0 432 if (!$field->deleted) {
Chris@0 433 \Drupal::entityManager()->onFieldStorageDefinitionDelete($field);
Chris@0 434 $field->deleted = TRUE;
Chris@0 435 }
Chris@0 436 }
Chris@0 437 // Unset static flag.
Chris@0 438 static::$inDeletion = FALSE;
Chris@0 439 }
Chris@0 440
Chris@0 441 /**
Chris@0 442 * {@inheritdoc}
Chris@0 443 */
Chris@0 444 public function getSchema() {
Chris@0 445 if (!isset($this->schema)) {
Chris@0 446 // Get the schema from the field item class.
Chris@0 447 $class = $this->getFieldItemClass();
Chris@0 448 $schema = $class::schema($this);
Chris@0 449 // Fill in default values for optional entries.
Chris@0 450 $schema += [
Chris@0 451 'columns' => [],
Chris@0 452 'unique keys' => [],
Chris@0 453 'indexes' => [],
Chris@0 454 'foreign keys' => [],
Chris@0 455 ];
Chris@0 456
Chris@0 457 // Merge custom indexes with those specified by the field type. Custom
Chris@0 458 // indexes prevail.
Chris@0 459 $schema['indexes'] = $this->indexes + $schema['indexes'];
Chris@0 460
Chris@0 461 $this->schema = $schema;
Chris@0 462 }
Chris@0 463
Chris@0 464 return $this->schema;
Chris@0 465 }
Chris@0 466
Chris@0 467 /**
Chris@0 468 * {@inheritdoc}
Chris@0 469 */
Chris@0 470 public function hasCustomStorage() {
Chris@0 471 return $this->custom_storage;
Chris@0 472 }
Chris@0 473
Chris@0 474 /**
Chris@0 475 * {@inheritdoc}
Chris@0 476 */
Chris@0 477 public function isBaseField() {
Chris@0 478 return FALSE;
Chris@0 479 }
Chris@0 480
Chris@0 481 /**
Chris@0 482 * {@inheritdoc}
Chris@0 483 */
Chris@0 484 public function getColumns() {
Chris@0 485 $schema = $this->getSchema();
Chris@0 486 // A typical use case for the method is to iterate on the columns, while
Chris@0 487 // some other use cases rely on identifying the first column with the key()
Chris@0 488 // function. Since the schema is persisted in the Field object, we take care
Chris@0 489 // of resetting the array pointer so that the former does not interfere with
Chris@0 490 // the latter.
Chris@0 491 reset($schema['columns']);
Chris@0 492 return $schema['columns'];
Chris@0 493 }
Chris@0 494
Chris@0 495 /**
Chris@0 496 * {@inheritdoc}
Chris@0 497 */
Chris@0 498 public function getBundles() {
Chris@0 499 if (!$this->isDeleted()) {
Chris@0 500 $map = \Drupal::entityManager()->getFieldMap();
Chris@0 501 if (isset($map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'])) {
Chris@0 502 return $map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'];
Chris@0 503 }
Chris@0 504 }
Chris@0 505 return [];
Chris@0 506 }
Chris@0 507
Chris@0 508 /**
Chris@0 509 * {@inheritdoc}
Chris@0 510 */
Chris@0 511 public function getName() {
Chris@0 512 return $this->field_name;
Chris@0 513 }
Chris@0 514
Chris@0 515 /**
Chris@0 516 * {@inheritdoc}
Chris@0 517 */
Chris@0 518 public function isDeleted() {
Chris@0 519 return $this->deleted;
Chris@0 520 }
Chris@0 521
Chris@0 522 /**
Chris@0 523 * {@inheritdoc}
Chris@0 524 */
Chris@0 525 public function getTypeProvider() {
Chris@0 526 return $this->module;
Chris@0 527 }
Chris@0 528
Chris@0 529 /**
Chris@0 530 * {@inheritdoc}
Chris@0 531 */
Chris@0 532 public function getType() {
Chris@0 533 return $this->type;
Chris@0 534 }
Chris@0 535
Chris@0 536 /**
Chris@0 537 * {@inheritdoc}
Chris@0 538 */
Chris@0 539 public function getSettings() {
Chris@0 540 // @todo FieldTypePluginManager maintains its own static cache. However, do
Chris@0 541 // some CPU and memory profiling to see if it's worth statically caching
Chris@0 542 // $field_type_info, or the default field storage and field settings,
Chris@0 543 // within $this.
Chris@0 544 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 545
Chris@0 546 $settings = $field_type_manager->getDefaultStorageSettings($this->getType());
Chris@0 547 return $this->settings + $settings;
Chris@0 548 }
Chris@0 549
Chris@0 550 /**
Chris@0 551 * {@inheritdoc}
Chris@0 552 */
Chris@0 553 public function getSetting($setting_name) {
Chris@0 554 // @todo See getSettings() about potentially statically caching this.
Chris@0 555 // We assume here that one call to array_key_exists() is more efficient
Chris@0 556 // than calling getSettings() when all we need is a single setting.
Chris@0 557 if (array_key_exists($setting_name, $this->settings)) {
Chris@0 558 return $this->settings[$setting_name];
Chris@0 559 }
Chris@0 560 $settings = $this->getSettings();
Chris@0 561 if (array_key_exists($setting_name, $settings)) {
Chris@0 562 return $settings[$setting_name];
Chris@0 563 }
Chris@0 564 else {
Chris@0 565 return NULL;
Chris@0 566 }
Chris@0 567 }
Chris@0 568
Chris@0 569 /**
Chris@0 570 * {@inheritdoc}
Chris@0 571 */
Chris@0 572 public function setSetting($setting_name, $value) {
Chris@0 573 $this->settings[$setting_name] = $value;
Chris@0 574 return $this;
Chris@0 575 }
Chris@0 576
Chris@0 577 /**
Chris@0 578 * {@inheritdoc}
Chris@0 579 */
Chris@0 580 public function setSettings(array $settings) {
Chris@0 581 $this->settings = $settings + $this->settings;
Chris@0 582 return $this;
Chris@0 583 }
Chris@0 584
Chris@0 585 /**
Chris@0 586 * {@inheritdoc}
Chris@0 587 */
Chris@0 588 public function isTranslatable() {
Chris@0 589 return $this->translatable;
Chris@0 590 }
Chris@0 591
Chris@0 592 /**
Chris@0 593 * {@inheritdoc}
Chris@0 594 */
Chris@0 595 public function isRevisionable() {
Chris@0 596 // All configurable fields are revisionable.
Chris@0 597 return TRUE;
Chris@0 598 }
Chris@0 599
Chris@0 600 /**
Chris@0 601 * {@inheritdoc}
Chris@0 602 */
Chris@0 603 public function setTranslatable($translatable) {
Chris@0 604 $this->translatable = $translatable;
Chris@0 605 return $this;
Chris@0 606 }
Chris@0 607
Chris@0 608 /**
Chris@0 609 * {@inheritdoc}
Chris@0 610 */
Chris@0 611 public function getProvider() {
Chris@0 612 return 'field';
Chris@0 613 }
Chris@0 614
Chris@0 615 /**
Chris@0 616 * {@inheritdoc}
Chris@0 617 */
Chris@0 618 public function getLabel() {
Chris@0 619 return $this->label();
Chris@0 620 }
Chris@0 621
Chris@0 622 /**
Chris@0 623 * {@inheritdoc}
Chris@0 624 */
Chris@0 625 public function getDescription() {
Chris@0 626 return NULL;
Chris@0 627 }
Chris@0 628
Chris@0 629 /**
Chris@0 630 * {@inheritdoc}
Chris@0 631 */
Chris@0 632 public function getCardinality() {
Chris@0 633 /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
Chris@0 634 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 635 $definition = $field_type_manager->getDefinition($this->getType());
Chris@0 636 $enforced_cardinality = isset($definition['cardinality']) ? $definition['cardinality'] : NULL;
Chris@0 637
Chris@0 638 // Enforced cardinality is a positive integer or -1.
Chris@0 639 if ($enforced_cardinality !== NULL && $enforced_cardinality < 1 && $enforced_cardinality !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
Chris@0 640 throw new FieldException("Invalid enforced cardinality '$enforced_cardinality'. Allowed values: a positive integer or -1.");
Chris@0 641 }
Chris@0 642
Chris@0 643 return $enforced_cardinality ?: $this->cardinality;
Chris@0 644 }
Chris@0 645
Chris@0 646 /**
Chris@0 647 * {@inheritdoc}
Chris@0 648 */
Chris@0 649 public function setCardinality($cardinality) {
Chris@0 650 $this->cardinality = $cardinality;
Chris@0 651 return $this;
Chris@0 652 }
Chris@0 653
Chris@0 654 /**
Chris@0 655 * {@inheritdoc}
Chris@0 656 */
Chris@0 657 public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
Chris@0 658 // If the field item class implements the interface, create an orphaned
Chris@0 659 // runtime item object, so that it can be used as the options provider
Chris@0 660 // without modifying the entity being worked on.
Chris@0 661 if (is_subclass_of($this->getFieldItemClass(), OptionsProviderInterface::class)) {
Chris@0 662 $items = $entity->get($this->getName());
Chris@0 663 return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
Chris@0 664 }
Chris@0 665 // @todo: Allow setting custom options provider, see
Chris@0 666 // https://www.drupal.org/node/2002138.
Chris@0 667 }
Chris@0 668
Chris@0 669 /**
Chris@0 670 * {@inheritdoc}
Chris@0 671 */
Chris@0 672 public function isMultiple() {
Chris@0 673 $cardinality = $this->getCardinality();
Chris@0 674 return ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1);
Chris@0 675 }
Chris@0 676
Chris@0 677 /**
Chris@0 678 * {@inheritdoc}
Chris@0 679 */
Chris@0 680 public function isLocked() {
Chris@0 681 return $this->locked;
Chris@0 682 }
Chris@0 683
Chris@0 684 /**
Chris@0 685 * {@inheritdoc}
Chris@0 686 */
Chris@0 687 public function setLocked($locked) {
Chris@0 688 $this->locked = $locked;
Chris@0 689 return $this;
Chris@0 690 }
Chris@0 691
Chris@0 692 /**
Chris@0 693 * {@inheritdoc}
Chris@0 694 */
Chris@0 695 public function getTargetEntityTypeId() {
Chris@0 696 return $this->entity_type;
Chris@0 697 }
Chris@0 698
Chris@0 699 /**
Chris@0 700 * {@inheritdoc}
Chris@0 701 */
Chris@0 702 public function isQueryable() {
Chris@0 703 return TRUE;
Chris@0 704 }
Chris@0 705
Chris@0 706 /**
Chris@0 707 * Determines whether a field has any data.
Chris@0 708 *
Chris@0 709 * @return bool
Chris@0 710 * TRUE if the field has data for any entity; FALSE otherwise.
Chris@0 711 */
Chris@0 712 public function hasData() {
Chris@0 713 return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE);
Chris@0 714 }
Chris@0 715
Chris@0 716 /**
Chris@0 717 * Implements the magic __sleep() method.
Chris@0 718 *
Chris@0 719 * Using the Serialize interface and serialize() / unserialize() methods
Chris@0 720 * breaks entity forms in PHP 5.4.
Chris@0 721 * @todo Investigate in https://www.drupal.org/node/2074253.
Chris@0 722 */
Chris@0 723 public function __sleep() {
Chris@0 724 // Only serialize necessary properties, excluding those that can be
Chris@0 725 // recalculated.
Chris@0 726 $properties = get_object_vars($this);
Chris@0 727 unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
Chris@0 728 return array_keys($properties);
Chris@0 729 }
Chris@0 730
Chris@0 731 /**
Chris@0 732 * {@inheritdoc}
Chris@0 733 */
Chris@0 734 public function getConstraints() {
Chris@0 735 return [];
Chris@0 736 }
Chris@0 737
Chris@0 738 /**
Chris@0 739 * {@inheritdoc}
Chris@0 740 */
Chris@0 741 public function getConstraint($constraint_name) {
Chris@0 742 return NULL;
Chris@0 743 }
Chris@0 744
Chris@0 745 /**
Chris@0 746 * {@inheritdoc}
Chris@0 747 */
Chris@0 748 public function getPropertyDefinition($name) {
Chris@0 749 if (!isset($this->propertyDefinitions)) {
Chris@0 750 $this->getPropertyDefinitions();
Chris@0 751 }
Chris@0 752 if (isset($this->propertyDefinitions[$name])) {
Chris@0 753 return $this->propertyDefinitions[$name];
Chris@0 754 }
Chris@0 755 }
Chris@0 756
Chris@0 757 /**
Chris@0 758 * {@inheritdoc}
Chris@0 759 */
Chris@0 760 public function getPropertyDefinitions() {
Chris@0 761 if (!isset($this->propertyDefinitions)) {
Chris@0 762 $class = $this->getFieldItemClass();
Chris@0 763 $this->propertyDefinitions = $class::propertyDefinitions($this);
Chris@0 764 }
Chris@0 765 return $this->propertyDefinitions;
Chris@0 766 }
Chris@0 767
Chris@0 768 /**
Chris@0 769 * {@inheritdoc}
Chris@0 770 */
Chris@0 771 public function getPropertyNames() {
Chris@0 772 return array_keys($this->getPropertyDefinitions());
Chris@0 773 }
Chris@0 774
Chris@0 775 /**
Chris@0 776 * {@inheritdoc}
Chris@0 777 */
Chris@0 778 public function getMainPropertyName() {
Chris@0 779 $class = $this->getFieldItemClass();
Chris@0 780 return $class::mainPropertyName();
Chris@0 781 }
Chris@0 782
Chris@0 783 /**
Chris@0 784 * {@inheritdoc}
Chris@0 785 */
Chris@0 786 public function getUniqueStorageIdentifier() {
Chris@0 787 return $this->uuid();
Chris@0 788 }
Chris@0 789
Chris@0 790 /**
Chris@0 791 * Helper to retrieve the field item class.
Chris@0 792 */
Chris@0 793 protected function getFieldItemClass() {
Chris@0 794 $type_definition = \Drupal::typedDataManager()
Chris@0 795 ->getDefinition('field_item:' . $this->getType());
Chris@0 796 return $type_definition['class'];
Chris@0 797 }
Chris@0 798
Chris@0 799 /**
Chris@0 800 * Loads a field config entity based on the entity type and field name.
Chris@0 801 *
Chris@0 802 * @param string $entity_type_id
Chris@0 803 * ID of the entity type.
Chris@0 804 * @param string $field_name
Chris@0 805 * Name of the field.
Chris@0 806 *
Chris@0 807 * @return static
Chris@0 808 * The field config entity if one exists for the provided field name,
Chris@0 809 * otherwise NULL.
Chris@0 810 */
Chris@0 811 public static function loadByName($entity_type_id, $field_name) {
Chris@0 812 return \Drupal::entityManager()->getStorage('field_storage_config')->load($entity_type_id . '.' . $field_name);
Chris@0 813 }
Chris@0 814
Chris@0 815 /**
Chris@0 816 * {@inheritdoc}
Chris@0 817 */
Chris@0 818 public function isDeletable() {
Chris@0 819 // The field storage is not deleted, is configured to be removed when there
Chris@0 820 // are no fields, the field storage has no bundles, and field storages are
Chris@0 821 // not in the process of being deleted.
Chris@0 822 return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion;
Chris@0 823 }
Chris@0 824
Chris@0 825 /**
Chris@0 826 * {@inheritdoc}
Chris@0 827 */
Chris@0 828 public function getIndexes() {
Chris@0 829 return $this->indexes;
Chris@0 830 }
Chris@0 831
Chris@0 832 /**
Chris@0 833 * {@inheritdoc}
Chris@0 834 */
Chris@0 835 public function setIndexes(array $indexes) {
Chris@0 836 $this->indexes = $indexes;
Chris@0 837 return $this;
Chris@0 838 }
Chris@0 839
Chris@0 840 }