annotate core/modules/field/src/Entity/FieldStorageConfig.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
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@0 401 $state = \Drupal::state();
Chris@0 402
Chris@0 403 // Set the static flag so that we don't delete field storages whilst
Chris@0 404 // deleting fields.
Chris@0 405 static::$inDeletion = TRUE;
Chris@0 406
Chris@0 407 // Delete or fix any configuration that is dependent, for example, fields.
Chris@0 408 parent::preDelete($storage, $field_storages);
Chris@0 409
Chris@0 410 // Keep the field definitions in the state storage so we can use them later
Chris@0 411 // during field_purge_batch().
Chris@0 412 $deleted_storages = $state->get('field.storage.deleted') ?: [];
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@0 419 $config = $field_storage->toArray();
Chris@0 420 $config['deleted'] = TRUE;
Chris@0 421 $config['bundles'] = $field_storage->getBundles();
Chris@0 422 $deleted_storages[$field_storage->uuid()] = $config;
Chris@0 423 }
Chris@0 424 }
Chris@0 425
Chris@0 426 $state->set('field.storage.deleted', $deleted_storages);
Chris@0 427 }
Chris@0 428
Chris@0 429 /**
Chris@0 430 * {@inheritdoc}
Chris@0 431 */
Chris@0 432 public static function postDelete(EntityStorageInterface $storage, array $fields) {
Chris@0 433 // Notify the storage.
Chris@0 434 foreach ($fields as $field) {
Chris@0 435 if (!$field->deleted) {
Chris@0 436 \Drupal::entityManager()->onFieldStorageDefinitionDelete($field);
Chris@0 437 $field->deleted = TRUE;
Chris@0 438 }
Chris@0 439 }
Chris@0 440 // Unset static flag.
Chris@0 441 static::$inDeletion = FALSE;
Chris@0 442 }
Chris@0 443
Chris@0 444 /**
Chris@0 445 * {@inheritdoc}
Chris@0 446 */
Chris@0 447 public function getSchema() {
Chris@0 448 if (!isset($this->schema)) {
Chris@0 449 // Get the schema from the field item class.
Chris@0 450 $class = $this->getFieldItemClass();
Chris@0 451 $schema = $class::schema($this);
Chris@0 452 // Fill in default values for optional entries.
Chris@0 453 $schema += [
Chris@0 454 'columns' => [],
Chris@0 455 'unique keys' => [],
Chris@0 456 'indexes' => [],
Chris@0 457 'foreign keys' => [],
Chris@0 458 ];
Chris@0 459
Chris@0 460 // Merge custom indexes with those specified by the field type. Custom
Chris@0 461 // indexes prevail.
Chris@0 462 $schema['indexes'] = $this->indexes + $schema['indexes'];
Chris@0 463
Chris@0 464 $this->schema = $schema;
Chris@0 465 }
Chris@0 466
Chris@0 467 return $this->schema;
Chris@0 468 }
Chris@0 469
Chris@0 470 /**
Chris@0 471 * {@inheritdoc}
Chris@0 472 */
Chris@0 473 public function hasCustomStorage() {
Chris@0 474 return $this->custom_storage;
Chris@0 475 }
Chris@0 476
Chris@0 477 /**
Chris@0 478 * {@inheritdoc}
Chris@0 479 */
Chris@0 480 public function isBaseField() {
Chris@0 481 return FALSE;
Chris@0 482 }
Chris@0 483
Chris@0 484 /**
Chris@0 485 * {@inheritdoc}
Chris@0 486 */
Chris@0 487 public function getColumns() {
Chris@0 488 $schema = $this->getSchema();
Chris@0 489 // A typical use case for the method is to iterate on the columns, while
Chris@0 490 // some other use cases rely on identifying the first column with the key()
Chris@0 491 // function. Since the schema is persisted in the Field object, we take care
Chris@0 492 // of resetting the array pointer so that the former does not interfere with
Chris@0 493 // the latter.
Chris@0 494 reset($schema['columns']);
Chris@0 495 return $schema['columns'];
Chris@0 496 }
Chris@0 497
Chris@0 498 /**
Chris@0 499 * {@inheritdoc}
Chris@0 500 */
Chris@0 501 public function getBundles() {
Chris@0 502 if (!$this->isDeleted()) {
Chris@0 503 $map = \Drupal::entityManager()->getFieldMap();
Chris@0 504 if (isset($map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'])) {
Chris@0 505 return $map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'];
Chris@0 506 }
Chris@0 507 }
Chris@0 508 return [];
Chris@0 509 }
Chris@0 510
Chris@0 511 /**
Chris@0 512 * {@inheritdoc}
Chris@0 513 */
Chris@0 514 public function getName() {
Chris@0 515 return $this->field_name;
Chris@0 516 }
Chris@0 517
Chris@0 518 /**
Chris@0 519 * {@inheritdoc}
Chris@0 520 */
Chris@0 521 public function isDeleted() {
Chris@0 522 return $this->deleted;
Chris@0 523 }
Chris@0 524
Chris@0 525 /**
Chris@0 526 * {@inheritdoc}
Chris@0 527 */
Chris@0 528 public function getTypeProvider() {
Chris@0 529 return $this->module;
Chris@0 530 }
Chris@0 531
Chris@0 532 /**
Chris@0 533 * {@inheritdoc}
Chris@0 534 */
Chris@0 535 public function getType() {
Chris@0 536 return $this->type;
Chris@0 537 }
Chris@0 538
Chris@0 539 /**
Chris@0 540 * {@inheritdoc}
Chris@0 541 */
Chris@0 542 public function getSettings() {
Chris@0 543 // @todo FieldTypePluginManager maintains its own static cache. However, do
Chris@0 544 // some CPU and memory profiling to see if it's worth statically caching
Chris@0 545 // $field_type_info, or the default field storage and field settings,
Chris@0 546 // within $this.
Chris@0 547 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 548
Chris@0 549 $settings = $field_type_manager->getDefaultStorageSettings($this->getType());
Chris@0 550 return $this->settings + $settings;
Chris@0 551 }
Chris@0 552
Chris@0 553 /**
Chris@0 554 * {@inheritdoc}
Chris@0 555 */
Chris@0 556 public function getSetting($setting_name) {
Chris@0 557 // @todo See getSettings() about potentially statically caching this.
Chris@0 558 // We assume here that one call to array_key_exists() is more efficient
Chris@0 559 // than calling getSettings() when all we need is a single setting.
Chris@0 560 if (array_key_exists($setting_name, $this->settings)) {
Chris@0 561 return $this->settings[$setting_name];
Chris@0 562 }
Chris@0 563 $settings = $this->getSettings();
Chris@0 564 if (array_key_exists($setting_name, $settings)) {
Chris@0 565 return $settings[$setting_name];
Chris@0 566 }
Chris@0 567 else {
Chris@0 568 return NULL;
Chris@0 569 }
Chris@0 570 }
Chris@0 571
Chris@0 572 /**
Chris@0 573 * {@inheritdoc}
Chris@0 574 */
Chris@0 575 public function setSetting($setting_name, $value) {
Chris@0 576 $this->settings[$setting_name] = $value;
Chris@0 577 return $this;
Chris@0 578 }
Chris@0 579
Chris@0 580 /**
Chris@0 581 * {@inheritdoc}
Chris@0 582 */
Chris@0 583 public function setSettings(array $settings) {
Chris@0 584 $this->settings = $settings + $this->settings;
Chris@0 585 return $this;
Chris@0 586 }
Chris@0 587
Chris@0 588 /**
Chris@0 589 * {@inheritdoc}
Chris@0 590 */
Chris@0 591 public function isTranslatable() {
Chris@0 592 return $this->translatable;
Chris@0 593 }
Chris@0 594
Chris@0 595 /**
Chris@0 596 * {@inheritdoc}
Chris@0 597 */
Chris@0 598 public function isRevisionable() {
Chris@0 599 // All configurable fields are revisionable.
Chris@0 600 return TRUE;
Chris@0 601 }
Chris@0 602
Chris@0 603 /**
Chris@0 604 * {@inheritdoc}
Chris@0 605 */
Chris@0 606 public function setTranslatable($translatable) {
Chris@0 607 $this->translatable = $translatable;
Chris@0 608 return $this;
Chris@0 609 }
Chris@0 610
Chris@0 611 /**
Chris@0 612 * {@inheritdoc}
Chris@0 613 */
Chris@0 614 public function getProvider() {
Chris@0 615 return 'field';
Chris@0 616 }
Chris@0 617
Chris@0 618 /**
Chris@0 619 * {@inheritdoc}
Chris@0 620 */
Chris@0 621 public function getLabel() {
Chris@0 622 return $this->label();
Chris@0 623 }
Chris@0 624
Chris@0 625 /**
Chris@0 626 * {@inheritdoc}
Chris@0 627 */
Chris@0 628 public function getDescription() {
Chris@0 629 return NULL;
Chris@0 630 }
Chris@0 631
Chris@0 632 /**
Chris@0 633 * {@inheritdoc}
Chris@0 634 */
Chris@0 635 public function getCardinality() {
Chris@0 636 /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
Chris@0 637 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
Chris@0 638 $definition = $field_type_manager->getDefinition($this->getType());
Chris@0 639 $enforced_cardinality = isset($definition['cardinality']) ? $definition['cardinality'] : NULL;
Chris@0 640
Chris@0 641 // Enforced cardinality is a positive integer or -1.
Chris@0 642 if ($enforced_cardinality !== NULL && $enforced_cardinality < 1 && $enforced_cardinality !== FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
Chris@0 643 throw new FieldException("Invalid enforced cardinality '$enforced_cardinality'. Allowed values: a positive integer or -1.");
Chris@0 644 }
Chris@0 645
Chris@0 646 return $enforced_cardinality ?: $this->cardinality;
Chris@0 647 }
Chris@0 648
Chris@0 649 /**
Chris@0 650 * {@inheritdoc}
Chris@0 651 */
Chris@0 652 public function setCardinality($cardinality) {
Chris@0 653 $this->cardinality = $cardinality;
Chris@0 654 return $this;
Chris@0 655 }
Chris@0 656
Chris@0 657 /**
Chris@0 658 * {@inheritdoc}
Chris@0 659 */
Chris@0 660 public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
Chris@0 661 // If the field item class implements the interface, create an orphaned
Chris@0 662 // runtime item object, so that it can be used as the options provider
Chris@0 663 // without modifying the entity being worked on.
Chris@0 664 if (is_subclass_of($this->getFieldItemClass(), OptionsProviderInterface::class)) {
Chris@0 665 $items = $entity->get($this->getName());
Chris@0 666 return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
Chris@0 667 }
Chris@0 668 // @todo: Allow setting custom options provider, see
Chris@0 669 // https://www.drupal.org/node/2002138.
Chris@0 670 }
Chris@0 671
Chris@0 672 /**
Chris@0 673 * {@inheritdoc}
Chris@0 674 */
Chris@0 675 public function isMultiple() {
Chris@0 676 $cardinality = $this->getCardinality();
Chris@0 677 return ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1);
Chris@0 678 }
Chris@0 679
Chris@0 680 /**
Chris@0 681 * {@inheritdoc}
Chris@0 682 */
Chris@0 683 public function isLocked() {
Chris@0 684 return $this->locked;
Chris@0 685 }
Chris@0 686
Chris@0 687 /**
Chris@0 688 * {@inheritdoc}
Chris@0 689 */
Chris@0 690 public function setLocked($locked) {
Chris@0 691 $this->locked = $locked;
Chris@0 692 return $this;
Chris@0 693 }
Chris@0 694
Chris@0 695 /**
Chris@0 696 * {@inheritdoc}
Chris@0 697 */
Chris@0 698 public function getTargetEntityTypeId() {
Chris@0 699 return $this->entity_type;
Chris@0 700 }
Chris@0 701
Chris@0 702 /**
Chris@0 703 * {@inheritdoc}
Chris@0 704 */
Chris@0 705 public function isQueryable() {
Chris@0 706 return TRUE;
Chris@0 707 }
Chris@0 708
Chris@0 709 /**
Chris@0 710 * Determines whether a field has any data.
Chris@0 711 *
Chris@0 712 * @return bool
Chris@0 713 * TRUE if the field has data for any entity; FALSE otherwise.
Chris@0 714 */
Chris@0 715 public function hasData() {
Chris@0 716 return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE);
Chris@0 717 }
Chris@0 718
Chris@0 719 /**
Chris@0 720 * Implements the magic __sleep() method.
Chris@0 721 *
Chris@0 722 * Using the Serialize interface and serialize() / unserialize() methods
Chris@0 723 * breaks entity forms in PHP 5.4.
Chris@0 724 * @todo Investigate in https://www.drupal.org/node/2074253.
Chris@0 725 */
Chris@0 726 public function __sleep() {
Chris@0 727 // Only serialize necessary properties, excluding those that can be
Chris@0 728 // recalculated.
Chris@0 729 $properties = get_object_vars($this);
Chris@0 730 unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
Chris@0 731 return array_keys($properties);
Chris@0 732 }
Chris@0 733
Chris@0 734 /**
Chris@0 735 * {@inheritdoc}
Chris@0 736 */
Chris@0 737 public function getConstraints() {
Chris@0 738 return [];
Chris@0 739 }
Chris@0 740
Chris@0 741 /**
Chris@0 742 * {@inheritdoc}
Chris@0 743 */
Chris@0 744 public function getConstraint($constraint_name) {
Chris@0 745 return NULL;
Chris@0 746 }
Chris@0 747
Chris@0 748 /**
Chris@0 749 * {@inheritdoc}
Chris@0 750 */
Chris@0 751 public function getPropertyDefinition($name) {
Chris@0 752 if (!isset($this->propertyDefinitions)) {
Chris@0 753 $this->getPropertyDefinitions();
Chris@0 754 }
Chris@0 755 if (isset($this->propertyDefinitions[$name])) {
Chris@0 756 return $this->propertyDefinitions[$name];
Chris@0 757 }
Chris@0 758 }
Chris@0 759
Chris@0 760 /**
Chris@0 761 * {@inheritdoc}
Chris@0 762 */
Chris@0 763 public function getPropertyDefinitions() {
Chris@0 764 if (!isset($this->propertyDefinitions)) {
Chris@0 765 $class = $this->getFieldItemClass();
Chris@0 766 $this->propertyDefinitions = $class::propertyDefinitions($this);
Chris@0 767 }
Chris@0 768 return $this->propertyDefinitions;
Chris@0 769 }
Chris@0 770
Chris@0 771 /**
Chris@0 772 * {@inheritdoc}
Chris@0 773 */
Chris@0 774 public function getPropertyNames() {
Chris@0 775 return array_keys($this->getPropertyDefinitions());
Chris@0 776 }
Chris@0 777
Chris@0 778 /**
Chris@0 779 * {@inheritdoc}
Chris@0 780 */
Chris@0 781 public function getMainPropertyName() {
Chris@0 782 $class = $this->getFieldItemClass();
Chris@0 783 return $class::mainPropertyName();
Chris@0 784 }
Chris@0 785
Chris@0 786 /**
Chris@0 787 * {@inheritdoc}
Chris@0 788 */
Chris@0 789 public function getUniqueStorageIdentifier() {
Chris@0 790 return $this->uuid();
Chris@0 791 }
Chris@0 792
Chris@0 793 /**
Chris@0 794 * Helper to retrieve the field item class.
Chris@0 795 */
Chris@0 796 protected function getFieldItemClass() {
Chris@0 797 $type_definition = \Drupal::typedDataManager()
Chris@0 798 ->getDefinition('field_item:' . $this->getType());
Chris@0 799 return $type_definition['class'];
Chris@0 800 }
Chris@0 801
Chris@0 802 /**
Chris@0 803 * Loads a field config entity based on the entity type and field name.
Chris@0 804 *
Chris@0 805 * @param string $entity_type_id
Chris@0 806 * ID of the entity type.
Chris@0 807 * @param string $field_name
Chris@0 808 * Name of the field.
Chris@0 809 *
Chris@0 810 * @return static
Chris@0 811 * The field config entity if one exists for the provided field name,
Chris@0 812 * otherwise NULL.
Chris@0 813 */
Chris@0 814 public static function loadByName($entity_type_id, $field_name) {
Chris@0 815 return \Drupal::entityManager()->getStorage('field_storage_config')->load($entity_type_id . '.' . $field_name);
Chris@0 816 }
Chris@0 817
Chris@0 818 /**
Chris@0 819 * {@inheritdoc}
Chris@0 820 */
Chris@0 821 public function isDeletable() {
Chris@0 822 // The field storage is not deleted, is configured to be removed when there
Chris@0 823 // are no fields, the field storage has no bundles, and field storages are
Chris@0 824 // not in the process of being deleted.
Chris@0 825 return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion;
Chris@0 826 }
Chris@0 827
Chris@0 828 /**
Chris@0 829 * {@inheritdoc}
Chris@0 830 */
Chris@0 831 public function getIndexes() {
Chris@0 832 return $this->indexes;
Chris@0 833 }
Chris@0 834
Chris@0 835 /**
Chris@0 836 * {@inheritdoc}
Chris@0 837 */
Chris@0 838 public function setIndexes(array $indexes) {
Chris@0 839 $this->indexes = $indexes;
Chris@0 840 return $this;
Chris@0 841 }
Chris@0 842
Chris@0 843 }