FieldStorageConfig.php 22.2 KB
Newer Older
1
2
3
4
<?php

/**
 * @file
5
 * Contains \Drupal\field\Entity\FieldStorageConfig.
6
7
 */

8
namespace Drupal\field\Entity;
9

10
use Drupal\Component\Utility\Unicode;
11
use Drupal\Core\Config\Entity\ConfigEntityBase;
12
use Drupal\Core\Entity\EntityStorageInterface;
13
use Drupal\Core\Entity\FieldableEntityInterface;
14
use Drupal\Core\Field\FieldException;
15
use Drupal\Core\Field\FieldStorageDefinitionInterface;
16
use Drupal\Core\TypedData\OptionsProviderInterface;
17
use Drupal\field\FieldStorageConfigInterface;
18
19

/**
20
 * Defines the Field storage configuration entity.
21
 *
22
 * @ConfigEntityType(
23
 *   id = "field_storage_config",
24
 *   label = @Translation("Field storage"),
25
 *   handlers = {
26
 *     "storage" = "Drupal\field\FieldStorageConfigStorage"
27
 *   },
28
 *   config_prefix = "storage",
29
30
 *   entity_keys = {
 *     "id" = "id",
31
 *     "label" = "id"
32
33
34
35
36
37
38
39
40
41
42
43
44
 *   },
 *   config_export = {
 *     "id",
 *     "field_name",
 *     "entity_type",
 *     "type",
 *     "settings",
 *     "module",
 *     "locked",
 *     "cardinality",
 *     "translatable",
 *     "indexes",
 *     "persist_with_no_fields",
45
46
47
 *   }
 * )
 */
48
class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigInterface {
49
50

  /**
51
   * The maximum length of the field name, in characters.
52
53
54
   *
   * For fields created through Field UI, this includes the 'field_' prefix.
   */
55
  const NAME_MAX_LENGTH = 32;
56
57

  /**
58
59
60
61
62
63
64
65
   * The field ID.
   *
   * The ID consists of 2 parts: the entity type and the field name.
   *
   * Example: node.body, user.field_main_image.
   *
   * @var string
   */
66
  protected $id;
67
68
69

  /**
   * The field name.
70
71
   *
   * This is the name of the property under which the field values are placed in
72
73
   * an entity: $entity->{$field_name}. The maximum length is
   * Field:NAME_MAX_LENGTH.
74
75
76
77
78
   *
   * Example: body, field_main_image.
   *
   * @var string
   */
79
  protected $field_name;
80

81
82
83
84
85
  /**
   * The name of the entity type the field can be attached to.
   *
   * @var string
   */
86
  protected $entity_type;
87

88
89
90
  /**
   * The field type.
   *
91
   * Example: text, integer.
92
93
94
   *
   * @var string
   */
95
  protected $type;
96
97
98
99
100
101

  /**
   * The name of the module that provides the field type.
   *
   * @var string
   */
102
  protected $module;
103
104
105
106
107

  /**
   * Field-type specific settings.
   *
   * An array of key/value pairs, The keys and default values are defined by the
108
   * field type.
109
110
111
   *
   * @var array
   */
112
  protected $settings = [];
113
114
115
116
117

  /**
   * The field cardinality.
   *
   * The maximum number of values the field can hold. Possible values are
118
119
   * positive integers or
   * FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED. Defaults to 1.
120
   *
121
   * @var int
122
   */
123
  protected $cardinality = 1;
124
125
126
127

  /**
   * Flag indicating whether the field is translatable.
   *
128
   * Defaults to TRUE.
129
130
131
   *
   * @var bool
   */
132
  protected $translatable = TRUE;
133
134
135
136
137
138
139

  /**
   * Flag indicating whether the field is available for editing.
   *
   * If TRUE, some actions not available though the UI (but are still possible
   * through direct API manipulation):
   * - field settings cannot be changed,
140
141
   * - new fields cannot be created
   * - existing fields cannot be deleted.
142
143
144
145
   * Defaults to FALSE.
   *
   * @var bool
   */
146
  protected $locked = FALSE;
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  /**
   * Flag indicating whether the field storage should be deleted when orphaned.
   *
   * By default field storages for configurable fields are removed when there
   * are no remaining fields using them. If multiple modules provide bundles
   * which need to use the same field storage then setting this to TRUE will
   * preserve the field storage regardless of what happens to the bundles. The
   * classic use case for this is node body field storage since Book, Forum, the
   * Standard profile and bundle (node type) creation through the UI all use
   * same field storage.
   *
   * @var bool
   */
  protected $persist_with_no_fields = FALSE;

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  /**
   * The custom storage indexes for the field data storage.
   *
   * This set of indexes is merged with the "default" indexes specified by the
   * field type in hook_field_schema() to determine the actual set of indexes
   * that get created.
   *
   * The indexes are defined using the same definition format as Schema API
   * index specifications. Only columns that are part of the field schema, as
   * defined by the field type in hook_field_schema(), are allowed.
   *
   * Some storage backends might not support indexes, and discard that
   * information.
   *
   * @var array
   */
179
  protected $indexes = [];
180
181
182
183
184
185
186
187
188
189
190
191
192
193

  /**
   * Flag indicating whether the field is deleted.
   *
   * The delete() method marks the field as "deleted" and removes the
   * corresponding entry from the config storage, but keeps its definition in
   * the state storage while field data is purged by a separate
   * garbage-collection process.
   *
   * Deleted fields stay out of the regular entity lifecycle (notably, their
   * values are not populated in loaded entities, and are not saved back).
   *
   * @var bool
   */
194
  protected $deleted = FALSE;
195
196
197
198
199
200
201
202

  /**
   * The field schema.
   *
   * @var array
   */
  protected $schema;

203
204
205
206
207
208
209
210
211
  /**
   * An array of field property definitions.
   *
   * @var \Drupal\Core\TypedData\DataDefinitionInterface[]
   *
   * @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
   */
  protected $propertyDefinitions;

212
213
214
215
216
217
218
  /**
   * Static flag set to prevent recursion during field deletes.
   *
   * @var bool
   */
  protected static $inDeletion = FALSE;

219
  /**
220
   * Constructs a FieldStorageConfig object.
221
222
223
224
225
226
   *
   * @param array $values
   *   An array of field properties, keyed by property name. Most array
   *   elements will be used to set the corresponding properties on the class;
   *   see the class property documentation for details. Some array elements
   *   have special meanings and a few are required. Special elements are:
227
   *   - name: required. As a temporary Backwards Compatibility layer right now,
228
   *     a 'field_name' property can be accepted in place of 'id'.
229
   *   - entity_type: required.
230
231
232
   *   - type: required.
   *
   * In most cases, Field entities are created via
233
   * entity_create('field_storage_config', $values)), where $values is the same
234
235
236
   * parameter as in this constructor.
   *
   * @see entity_create()
237
   */
238
  public function __construct(array $values, $entity_type = 'field_storage_config') {
239
    // Check required properties.
240
241
    if (empty($values['field_name'])) {
      throw new FieldException('Attempt to create a field storage without a field name.');
242
    }
243
    if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) {
244
      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");
245
    }
246
    if (empty($values['type'])) {
247
      throw new FieldException("Attempt to create a field storage {$values['field_name']} with no type.");
248
249
    }
    if (empty($values['entity_type'])) {
250
      throw new FieldException("Attempt to create a field storage {$values['field_name']} with no entity_type.");
251
    }
252
253
254
255

    parent::__construct($values, $entity_type);
  }

256
257
258
259
  /**
   * {@inheritdoc}
   */
  public function id() {
260
    return $this->getTargetEntityTypeId() . '.' . $this->getName();
261
262
  }

263
  /**
264
   * Overrides \Drupal\Core\Entity\Entity::preSave().
265
   *
266
   * @throws \Drupal\Core\Field\FieldException
267
268
269
   *   If the field definition is invalid.
   * @throws \Drupal\Core\Entity\EntityStorageException
   *   In case of failures at the configuration storage level.
270
   */
271
  public function preSave(EntityStorageInterface $storage) {
272
    // Clear the derived data about the field.
273
    unset($this->schema);
274

275
276
277
278
279
280
281
    // Filter out unknown settings and make sure all settings are present, so
    // that a complete field definition is passed to the various hooks and
    // written to config.
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
    $default_settings = $field_type_manager->getDefaultStorageSettings($this->type);
    $this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;

282
    if ($this->isNew()) {
283
      $this->preSaveNew($storage);
284
285
    }
    else {
286
      $this->preSaveUpdated($storage);
287
    }
288
289

    parent::preSave($storage);
290
  }
291

292
  /**
293
   * Prepares saving a new field definition.
294
   *
295
296
   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
   *   The entity storage.
297
   *
298
   * @throws \Drupal\Core\Field\FieldException If the field definition is invalid.
299
   */
300
  protected function preSaveNew(EntityStorageInterface $storage) {
301
    $entity_manager = \Drupal::entityManager();
302
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
303

304
305
306
    // Assign the ID.
    $this->id = $this->id();

307
    // Field name cannot be longer than FieldStorageConfig::NAME_MAX_LENGTH characters.
308
    // We use Unicode::strlen() because the DB layer assumes that column widths
309
    // are given in characters rather than bytes.
310
    if (Unicode::strlen($this->getName()) > static::NAME_MAX_LENGTH) {
311
      throw new FieldException('Attempt to create a field storage with an name longer than ' . static::NAME_MAX_LENGTH . ' characters: ' . $this->getName());
312
    }
313

314
    // Disallow reserved field names.
315
316
    $disallowed_field_names = array_keys($entity_manager->getBaseFieldDefinitions($this->getTargetEntityTypeId()));
    if (in_array($this->getName(), $disallowed_field_names)) {
317
      throw new FieldException("Attempt to create field storage {$this->getName()} which is reserved by entity type {$this->getTargetEntityTypeId()}.");
318
319
320
    }

    // Check that the field type is known.
321
    $field_type = $field_type_manager->getDefinition($this->getType(), FALSE);
322
    if (!$field_type) {
323
      throw new FieldException("Attempt to create a field storage of unknown type {$this->getType()}.");
324
    }
325
    $this->module = $field_type['provider'];
326

327
328
    // Notify the entity manager.
    $entity_manager->onFieldStorageDefinitionCreate($this);
329
  }
330

331
332
333
334
335
336
  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    parent::calculateDependencies();
    // Ensure the field is dependent on the providing module.
337
    $this->addDependency('module', $this->getTypeProvider());
338
339
340
    // Ensure the field is dependent on the provider of the entity type.
    $entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
    $this->addDependency('module', $entity_type->getProvider());
341
342
343
    return $this->dependencies;
  }

344
  /**
345
   * Prepares saving an updated field definition.
346
   *
347
348
   * @param \Drupal\Core\Entity\EntityStorageInterface $storage
   *   The entity storage.
349
   */
350
  protected function preSaveUpdated(EntityStorageInterface $storage) {
351
    $module_handler = \Drupal::moduleHandler();
352
    $entity_manager = \Drupal::entityManager();
353

354
    // Some updates are always disallowed.
355
    if ($this->getType() != $this->original->getType()) {
356
      throw new FieldException("Cannot change the field type for an existing field storage.");
357
    }
358
    if ($this->getTargetEntityTypeId() != $this->original->getTargetEntityTypeId()) {
359
      throw new FieldException("Cannot change the entity type for an existing field storage.");
360
361
362
    }

    // See if any module forbids the update by throwing an exception. This
363
364
    // invokes hook_field_storage_config_update_forbid().
    $module_handler->invokeAll('field_storage_config_update_forbid', array($this, $this->original));
365

366
    // Notify the entity manager. A listener can reject the definition
367
368
    // update as invalid by raising an exception, which stops execution before
    // the definition is written to config.
369
    $entity_manager->onFieldStorageDefinitionUpdate($this, $this->original);
370
  }
371

372
373
374
  /**
   * {@inheritdoc}
   */
375
  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
376
377
378
379
    if ($update) {
      // Invalidate the render cache for all affected entities.
      $entity_manager = \Drupal::entityManager();
      $entity_type = $this->getTargetEntityTypeId();
380
      if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
381
382
383
        $entity_manager->getViewBuilder($entity_type)->resetCache();
      }
    }
384
385
386
387
388
  }

  /**
   * {@inheritdoc}
   */
389
  public static function preDelete(EntityStorageInterface $storage, array $field_storages) {
390
    $state = \Drupal::state();
391

392
393
394
395
396
397
    // Set the static flag so that we don't delete field storages whilst
    // deleting fields.
    static::$inDeletion = TRUE;

    // Delete or fix any configuration that is dependent, for example, fields.
    parent::preDelete($storage, $field_storages);
398

399
400
    // Keep the field definitions in the state storage so we can use them later
    // during field_purge_batch().
401
    $deleted_storages = $state->get('field.storage.deleted') ?: array();
402
403
404
    foreach ($field_storages as $field_storage) {
      if (!$field_storage->deleted) {
        $config = $field_storage->toArray();
405
        $config['deleted'] = TRUE;
406
407
        $config['bundles'] = $field_storage->getBundles();
        $deleted_storages[$field_storage->uuid()] = $config;
408
409
      }
    }
410

411
    $state->set('field.storage.deleted', $deleted_storages);
412
  }
413

414
415
416
  /**
   * {@inheritdoc}
   */
417
  public static function postDelete(EntityStorageInterface $storage, array $fields) {
418
419
420
    // Notify the storage.
    foreach ($fields as $field) {
      if (!$field->deleted) {
421
        \Drupal::entityManager()->onFieldStorageDefinitionDelete($field);
422
        $field->deleted = TRUE;
423
      }
424
    }
425
426
    // Unset static flag.
    static::$inDeletion = FALSE;
427
428
429
  }

  /**
430
   * {@inheritdoc}
431
432
433
   */
  public function getSchema() {
    if (!isset($this->schema)) {
434
      // Get the schema from the field item class.
435
      $class = $this->getFieldItemClass();
436
437
      $schema = $class::schema($this);
      // Fill in default values for optional entries.
438
439
440
441
442
      $schema += array(
        'unique keys' => array(),
        'indexes' => array(),
        'foreign keys' => array(),
      );
443
444
445
446
447
448
449
450
451
452
453

      // Merge custom indexes with those specified by the field type. Custom
      // indexes prevail.
      $schema['indexes'] = $this->indexes + $schema['indexes'];

      $this->schema = $schema;
    }

    return $this->schema;
  }

454
455
456
457
458
459
460
  /**
   * {@inheritdoc}
   */
  public function hasCustomStorage() {
    return FALSE;
  }

461
462
463
464
465
466
467
  /**
   * {@inheritdoc}
   */
  public function isBaseField() {
    return FALSE;
  }

468
469
470
471
472
473
474
475
476
477
478
479
480
481
  /**
   * {@inheritdoc}
   */
  public function getColumns() {
    $schema = $this->getSchema();
    // A typical use case for the method is to iterate on the columns, while
    // some other use cases rely on identifying the first column with the key()
    // function. Since the schema is persisted in the Field object, we take care
    // of resetting the array pointer so that the former does not interfere with
    // the latter.
    reset($schema['columns']);
    return $schema['columns'];
  }

482
  /**
483
   * {@inheritdoc}
484
485
   */
  public function getBundles() {
486
    if (!$this->isDeleted()) {
487
      $map = \Drupal::entityManager()->getFieldMap();
488
489
      if (isset($map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'])) {
        return $map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'];
490
491
492
493
494
      }
    }
    return array();
  }

495
496
497
  /**
   * {@inheritdoc}
   */
498
  public function getName() {
499
    return $this->field_name;
500
501
  }

502
503
504
505
506
507
508
509
510
511
512
513
514
515
  /**
   * {@inheritdoc}
   */
  public function isDeleted() {
    return $this->deleted;
  }

  /**
   * {@inheritdoc}
   */
  public function getTypeProvider() {
    return $this->module;
  }

516
517
518
  /**
   * {@inheritdoc}
   */
519
  public function getType() {
520
521
522
523
524
525
    return $this->type;
  }

  /**
   * {@inheritdoc}
   */
526
  public function getSettings() {
527
528
    // @todo FieldTypePluginManager maintains its own static cache. However, do
    //   some CPU and memory profiling to see if it's worth statically caching
529
530
    //   $field_type_info, or the default field storage and field settings,
    //   within $this.
531
    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
532

533
    $settings = $field_type_manager->getDefaultStorageSettings($this->getType());
534
    return $this->settings + $settings;
535
536
537
538
539
  }

  /**
   * {@inheritdoc}
   */
540
541
  public function getSetting($setting_name) {
    // @todo See getSettings() about potentially statically caching this.
542
543
    // We assume here that one call to array_key_exists() is more efficient
    // than calling getSettings() when all we need is a single setting.
544
    if (array_key_exists($setting_name, $this->settings)) {
545
546
      return $this->settings[$setting_name];
    }
547
548
549
    $settings = $this->getSettings();
    if (array_key_exists($setting_name, $settings)) {
      return $settings[$setting_name];
550
    }
551
552
553
    else {
      return NULL;
    }
554
555
  }

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
  /**
   * {@inheritdoc}
   */
  public function setSetting($setting_name, $value) {
    $this->settings[$setting_name] = $value;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setSettings(array $settings) {
    $this->settings = $settings;
    return $this;
  }

572
573
574
  /**
   * {@inheritdoc}
   */
575
  public function isTranslatable() {
576
577
578
    return $this->translatable;
  }

579
580
581
582
583
584
585
586
  /**
   * {@inheritdoc}
   */
  public function isRevisionable() {
    // All configurable fields are revisionable.
    return TRUE;
  }

587
  /**
588
   * {@inheritdoc}
589
590
591
592
593
594
   */
  public function setTranslatable($translatable) {
    $this->translatable = $translatable;
    return $this;
  }

595
596
597
598
599
600
601
  /**
   * {@inheritdoc}
   */
  public function getProvider() {
    return 'field';
  }

602
603
604
  /**
   * {@inheritdoc}
   */
605
  public function getLabel() {
606
607
608
609
610
611
    return $this->label();
  }

  /**
   * {@inheritdoc}
   */
612
  public function getDescription() {
613
    return NULL;
614
615
616
617
618
  }

  /**
   * {@inheritdoc}
   */
619
  public function getCardinality() {
620
621
622
    return $this->cardinality;
  }

623
624
625
626
627
628
629
630
  /**
   * {@inheritdoc}
   */
  public function setCardinality($cardinality) {
    $this->cardinality = $cardinality;
    return $this;
  }

631
632
633
  /**
   * {@inheritdoc}
   */
634
  public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
635
636
637
638
639
    // If the field item class implements the interface, create an orphaned
    // runtime item object, so that it can be used as the options provider
    // without modifying the entity being worked on.
    if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) {
      $items = $entity->get($this->getName());
640
      return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
641
642
643
644
645
    }
    // @todo: Allow setting custom options provider, see
    // https://www.drupal.org/node/2002138.
  }

646
647
648
  /**
   * {@inheritdoc}
   */
649
650
  public function isMultiple() {
    $cardinality = $this->getCardinality();
651
    return ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1);
652
653
  }

654
655
656
657
658
659
660
  /**
   * {@inheritdoc}
   */
  public function isLocked() {
    return $this->locked;
  }

661
662
663
664
665
666
667
668
  /**
   * {@inheritdoc}
   */
  public function setLocked($locked) {
    $this->locked = $locked;
    return $this;
  }

669
670
  /**
   * {@inheritdoc}
671
672
673
674
675
676
677
   */
  public function getTargetEntityTypeId() {
    return $this->entity_type;
  }

  /**
   * {@inheritdoc}
678
   */
679
  public function isQueryable() {
680
681
682
    return TRUE;
  }

683
684
685
  /**
   * Determines whether a field has any data.
   *
686
   * @return bool
687
688
689
   *   TRUE if the field has data for any entity; FALSE otherwise.
   */
  public function hasData() {
690
    return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE);
691
  }
692
693
694
695
696
697

  /**
   * Implements the magic __sleep() method.
   *
   * Using the Serialize interface and serialize() / unserialize() methods
   * breaks entity forms in PHP 5.4.
698
   * @todo Investigate in https://www.drupal.org/node/2074253.
699
700
   */
  public function __sleep() {
701
702
703
    // Only serialize necessary properties, excluding those that can be
    // recalculated.
    $properties = get_object_vars($this);
704
    unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
705
    return array_keys($properties);
706
707
  }

708
709
710
711
712
713
714
  /**
   * {@inheritdoc}
   */
  public function getConstraints() {
    return array();
  }

715
716
717
718
719
720
721
  /**
   * {@inheritdoc}
   */
  public function getConstraint($constraint_name) {
    return NULL;
  }

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
  /**
   * {@inheritdoc}
   */
  public function getPropertyDefinition($name) {
    if (!isset($this->propertyDefinitions)) {
      $this->getPropertyDefinitions();
    }
    if (isset($this->propertyDefinitions[$name])) {
      return $this->propertyDefinitions[$name];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getPropertyDefinitions() {
    if (!isset($this->propertyDefinitions)) {
      $class = $this->getFieldItemClass();
      $this->propertyDefinitions = $class::propertyDefinitions($this);
    }
    return $this->propertyDefinitions;
  }

  /**
   * {@inheritdoc}
   */
  public function getPropertyNames() {
    return array_keys($this->getPropertyDefinitions());
  }

  /**
   * {@inheritdoc}
   */
  public function getMainPropertyName() {
    $class = $this->getFieldItemClass();
    return $class::mainPropertyName();
  }

760
761
762
763
764
765
766
  /**
   * {@inheritdoc}
   */
  public function getUniqueStorageIdentifier() {
    return $this->uuid();
  }

767
768
769
770
  /**
   * Helper to retrieve the field item class.
   */
  protected function getFieldItemClass() {
771
772
773
    $type_definition = \Drupal::typedDataManager()
      ->getDefinition('field_item:' . $this->getType());
    return $type_definition['class'];
774
775
  }

776
777
778
779
780
781
782
783
784
785
786
787
788
  /**
   * Loads a field config entity based on the entity type and field name.
   *
   * @param string $entity_type_id
   *   ID of the entity type.
   * @param string $field_name
   *   Name of the field.
   *
   * @return static
   *   The field config entity if one exists for the provided field name,
   *   otherwise NULL.
   */
  public static function loadByName($entity_type_id, $field_name) {
789
    return \Drupal::entityManager()->getStorage('field_storage_config')->load($entity_type_id . '.' . $field_name);
790
791
  }

792
793
794
795
796
  /**
   * {@inheritdoc}
   */
  public function isDeletable() {
    // The field storage is not deleted, is configured to be removed when there
797
798
799
    // are no fields, the field storage has no bundles, and field storages are
    // not in the process of being deleted.
    return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion;
800
801
  }

802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
  /**
   * {@inheritdoc}
   */
  public function getIndexes() {
    return $this->indexes;
  }

  /**
   * {@inheritdoc}
   */
  public function setIndexes(array $indexes) {
    $this->indexes = $indexes;
    return $this;
  }

817
}