annotate core/modules/field/src/Entity/FieldStorageConfig.php @ 19:fa3358dc1485 tip

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