ConfigEntityBase.php 10.2 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Definition of Drupal\Core\Config\Entity\ConfigEntityBase.
6 7
 */

8
namespace Drupal\Core\Config\Entity;
9

10
use Drupal\Component\Plugin\ConfigurablePluginInterface;
11
use Drupal\Component\Utility\String;
12
use Drupal\Core\Cache\Cache;
13
use Drupal\Core\Config\Schema\SchemaIncompleteException;
14
use Drupal\Core\Entity\Entity;
15
use Drupal\Core\Config\ConfigDuplicateUUIDException;
16
use Drupal\Core\Entity\EntityStorageInterface;
17
use Drupal\Core\Entity\EntityWithPluginBagsInterface;
18
use Drupal\Core\Language\LanguageInterface;
19
use Drupal\Core\Plugin\PluginDependencyTrait;
20 21

/**
22
 * Defines a base configuration entity class.
23 24
 *
 * @ingroup entity_api
25
 */
26
abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface {
27

28 29 30 31
  use PluginDependencyTrait {
    addDependency as addDependencyTrait;
  }

32
  /**
33
   * The original ID of the configuration entity.
34
   *
35 36
   * The ID of a configuration entity is a unique string (machine name). When a
   * configuration entity is updated and its machine name is renamed, the
37 38 39 40
   * original ID needs to be known.
   *
   * @var string
   */
41
  protected $originalId;
42

43 44 45 46 47 48 49 50 51 52
  /**
   * The name of the property that is used to store plugin configuration.
   *
   * This is needed when the entity utilizes a PluginBag, to dictate where the
   * plugin configuration should be stored.
   *
   * @var string
   */
  protected $pluginConfigKey;

53 54 55 56 57
  /**
   * The enabled/disabled status of the configuration entity.
   *
   * @var bool
   */
58
  protected $status = TRUE;
59

60 61 62 63 64
  /**
   * The UUID for this entity.
   *
   * @var string
   */
65
  protected $uuid;
66

67 68 69 70 71 72 73 74
  /**
   * Whether the config is being created, updated or deleted through the
   * import process.
   *
   * @var bool
   */
  private $isSyncing = FALSE;

75 76 77 78 79 80 81
  /**
   * Whether the config is being deleted by the uninstall process.
   *
   * @var bool
   */
  private $isUninstalling = FALSE;

82 83 84 85 86
  /**
   * The language code of the entity's default language.
   *
   * @var string
   */
87
  protected $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED;
88

89 90 91
  /**
   * Overrides Entity::__construct().
   */
92
  public function __construct(array $values, $entity_type) {
93 94 95
    parent::__construct($values, $entity_type);

    // Backup the original ID, if any.
96 97 98
    // Configuration entity IDs are strings, and '0' is a valid ID.
    $original_id = $this->id();
    if ($original_id !== NULL && $original_id !== '') {
99
      $this->setOriginalId($original_id);
100 101 102 103
    }
  }

  /**
104
   * {@inheritdoc}
105
   */
106 107
  public function getOriginalId() {
    return $this->originalId;
108 109 110
  }

  /**
111
   * {@inheritdoc}
112
   */
113 114 115
  public function setOriginalId($id) {
    $this->originalId = $id;

116
    return parent::setOriginalId($id);
117 118 119
  }

  /**
120
   * Overrides Entity::isNew().
121
   *
122 123 124
   * EntityInterface::enforceIsNew() is only supported for newly created
   * configuration entities but has no effect after saving, since each
   * configuration entity is unique.
125
   */
126
  public function isNew() {
127
    return !empty($this->enforceIsNew);
128 129 130
  }

  /**
131
   * {@inheritdoc}
132
   */
133
  public function get($property_name) {
134 135 136 137
    return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
  }

  /**
138
   * {@inheritdoc}
139
   */
140
  public function set($property_name, $value) {
141 142 143
    if ($this instanceof EntityWithPluginBagsInterface) {
      $plugin_bags = $this->getPluginBags();
      if (isset($plugin_bags[$property_name])) {
144
        // If external code updates the settings, pass it along to the plugin.
145
        $plugin_bags[$property_name]->setConfiguration($value);
146 147 148
      }
    }

149
    $this->{$property_name} = $value;
150 151

    return $this;
152 153
  }

154
  /**
155
   * {@inheritdoc}
156 157
   */
  public function enable() {
158
    return $this->setStatus(TRUE);
159 160 161
  }

  /**
162
   * {@inheritdoc}
163 164
   */
  public function disable() {
165 166
    // An entity was disabled, invalidate its own cache tag.
    Cache::invalidateTags(array($this->entityTypeId => array($this->id())));
167 168 169 170
    return $this->setStatus(FALSE);
  }

  /**
171
   * {@inheritdoc}
172
   */
173 174
  public function setStatus($status) {
    $this->status = (bool) $status;
175 176 177 178
    return $this;
  }

  /**
179
   * {@inheritdoc}
180 181 182 183 184
   */
  public function status() {
    return !empty($this->status);
  }

185
  /**
186 187 188 189
   * {@inheritdoc}
   */
  public function setSyncing($syncing) {
    $this->isSyncing = $syncing;
190 191

    return $this;
192 193 194 195 196 197 198 199 200
  }

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

201 202 203 204 205 206 207 208 209 210 211 212 213 214
  /**
   * {@inheritdoc}
   */
  public function setUninstalling($uninstalling) {
    $this->isUninstalling = $uninstalling;
  }

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

215 216
  /**
   * {@inheritdoc}
217 218
   */
  public function createDuplicate() {
219
    $duplicate = parent::createDuplicate();
220

221
    // Prevent the new duplicate from being misinterpreted as a rename.
222
    $duplicate->setOriginalId(NULL);
223 224 225
    return $duplicate;
  }

226
  /**
227
   * Helper callback for uasort() to sort configuration entities by weight and label.
228
   */
229
  public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
230 231 232 233 234 235 236 237 238
    $a_weight = isset($a->weight) ? $a->weight : 0;
    $b_weight = isset($b->weight) ? $b->weight : 0;
    if ($a_weight == $b_weight) {
      $a_label = $a->label();
      $b_label = $b->label();
      return strnatcasecmp($a_label, $b_label);
    }
    return ($a_weight < $b_weight) ? -1 : 1;
  }
239 240

  /**
241
   * {@inheritdoc}
242
   */
243
  public function toArray() {
244
    $properties = array();
245 246 247 248 249 250 251 252
    $config_name = $this->getEntityType()->getConfigPrefix() . '.' . $this->id();
    $definition = $this->getTypedConfig()->getDefinition($config_name);
    if (!isset($definition['mapping'])) {
      throw new SchemaIncompleteException(String::format('Incomplete or missing schema for @config_name', array('@config_name' => $config_name)));
    }
    $id_key = $this->getEntityType()->getKey('id');
    foreach (array_keys($definition['mapping']) as $name) {
      // Special handling for IDs so that computed compound IDs work.
253
      // @see \Drupal\Core\Entity\EntityDisplayBase::id()
254 255 256 257 258 259
      if ($name == $id_key) {
        $properties[$name] = $this->id();
      }
      else {
        $properties[$name] = $this->get($name);
      }
260 261 262
    }
    return $properties;
  }
263

264 265 266 267 268 269 270 271 272
  /**
   * Gets the typed config manager.
   *
   * @return \Drupal\Core\Config\TypedConfigManagerInterface
   */
  protected function getTypedConfig() {
    return \Drupal::service('config.typed');
  }

273 274 275
  /**
   * {@inheritdoc}
   */
276 277
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);
278

279
    if ($this instanceof EntityWithPluginBagsInterface) {
280 281
      // Any changes to the plugin configuration must be saved to the entity's
      // copy as well.
282 283 284
      foreach ($this->getPluginBags() as $plugin_config_key => $plugin_bag) {
        $this->set($plugin_config_key, $plugin_bag->getConfiguration());
      }
285 286
    }

287 288
    // Ensure this entity's UUID does not exist with a different ID, regardless
    // of whether it's new or updated.
289
    $matching_entities = $storage->getQuery()
290 291 292
      ->condition('uuid', $this->uuid())
      ->execute();
    $matched_entity = reset($matching_entities);
293
    if (!empty($matched_entity) && ($matched_entity != $this->id()) && $matched_entity != $this->getOriginalId()) {
294
      throw new ConfigDuplicateUUIDException(String::format('Attempt to save a configuration entity %id with UUID %uuid when this UUID is already used for %matched', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%matched' => $matched_entity)));
295 296
    }

297
    // If this entity is not new, load the original entity for comparison.
298
    if (!$this->isNew()) {
299
      $original = $storage->loadUnchanged($this->getOriginalId());
300 301
      // Ensure that the UUID cannot be changed for an existing entity.
      if ($original && ($original->uuid() != $this->uuid())) {
302
        throw new ConfigDuplicateUUIDException(String::format('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid())));
303 304
      }
    }
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    if (!$this->isSyncing()) {
      // Ensure the correct dependencies are present. If the configuration is
      // being written during a configuration synchronisation then there is no
      // need to recalculate the dependencies.
      $this->calculateDependencies();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    // Dependencies should be recalculated on every save. This ensures stale
    // dependencies are never saved.
    $this->dependencies = array();
320
    if ($this instanceof EntityWithPluginBagsInterface) {
321 322
      // Configuration entities need to depend on the providers of any plugins
      // that they store the configuration for.
323 324 325 326
      foreach ($this->getPluginBags() as $plugin_bag) {
        foreach ($plugin_bag as $instance) {
          $this->calculatePluginDependencies($instance);
        }
327 328
      }
    }
329 330 331 332 333 334 335
    if ($this instanceof ThirdPartySettingsInterface) {
      // Configuration entities need to depend on the providers of any third
      // parties that they store the configuration for.
      foreach ($this->getThirdPartyProviders() as $provider) {
        $this->addDependency('module', $provider);
      }
    }
336
    return $this->dependencies;
337 338
  }

339 340 341
  /**
   * {@inheritdoc}
   */
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
  public function urlInfo($rel = 'edit-form') {
    return parent::urlInfo($rel);
  }

  /**
   * {@inheritdoc}
   */
  public function getSystemPath($rel = 'edit-form') {
    return parent::getSystemPath($rel);
  }

  /**
   * {@inheritdoc}
   */
  public function url($rel = 'edit-form', $options = array()) {
    return parent::url($rel, $options);
358 359
  }

360
  /**
361
   * {@inheritdoc}
362 363 364 365 366 367
   */
  protected function addDependency($type, $name) {
    // A config entity is always dependent on its provider. There is no need to
    // explicitly declare the dependency. An explicit dependency on Core, which
    // provides some plugins, is also not needed.
    // @see \Drupal\Core\Config\Entity\ConfigEntityDependency::hasDependency()
368
    if ($type == 'module' && ($name == $this->getEntityType()->getProvider() || $name == 'core')) {
369 370 371
      return $this;
    }

372
    return $this->addDependencyTrait($type, $name);
373 374
  }

375 376 377 378 379 380 381
  /**
   * {@inheritdoc}
   */
  public function getConfigDependencyName() {
    return $this->getEntityType()->getConfigPrefix() . '.' . $this->id();
  }

382
}