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