PathautoState.php 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. namespace Drupal\pathauto;
  3. use Drupal\Component\Utility\Crypt;
  4. use Drupal\Core\TypedData\TypedData;
  5. /**
  6. * A property that stores in keyvalue whether an entity should receive an alias.
  7. */
  8. class PathautoState extends TypedData {
  9. /**
  10. * An automatic alias should not be created.
  11. */
  12. const SKIP = 0;
  13. /**
  14. * An automatic alias should be created.
  15. */
  16. const CREATE = 1;
  17. /**
  18. * Pathauto state.
  19. *
  20. * @var int
  21. */
  22. protected $value;
  23. /**
  24. * @var \Drupal\Core\Field\FieldItemInterface
  25. */
  26. protected $parent;
  27. /**
  28. * {@inheritdoc}
  29. */
  30. public function getValue() {
  31. if ($this->value === NULL) {
  32. // If no value has been set or loaded yet, try to load a value if this
  33. // entity has already been saved.
  34. $this->value = \Drupal::keyValue($this->getCollection())
  35. ->get(static::getPathautoStateKey($this->parent->getEntity()->id()));
  36. // If it was not yet saved or no value was found, then set the flag to
  37. // create the alias if there is a matching pattern.
  38. if ($this->value === NULL) {
  39. $entity = $this->parent->getEntity();
  40. $pattern = \Drupal::service('pathauto.generator')->getPatternByEntity($entity);
  41. $this->value = !empty($pattern) ? static::CREATE : static::SKIP;
  42. }
  43. }
  44. return $this->value;
  45. }
  46. /**
  47. * {@inheritdoc}
  48. */
  49. public function setValue($value, $notify = TRUE) {
  50. $this->value = $value;
  51. // Notify the parent of any changes.
  52. if ($notify && isset($this->parent)) {
  53. $this->parent->onChange($this->name);
  54. }
  55. }
  56. /**
  57. * Returns TRUE if a value was set.
  58. */
  59. public function hasValue() {
  60. return $this->value !== NULL;
  61. }
  62. /**
  63. * Persists the state.
  64. */
  65. public function persist() {
  66. \Drupal::keyValue($this->getCollection())
  67. ->set(static::getPathautoStateKey($this->parent->getEntity()->id()), $this->getValue());
  68. }
  69. /**
  70. * Deletes the stored state.
  71. */
  72. public function purge() {
  73. \Drupal::keyValue($this->getCollection())
  74. ->delete(static::getPathautoStateKey($this->parent->getEntity()->id()));
  75. }
  76. /**
  77. * Returns the key value collection that should be used for the given entity.
  78. * @return string
  79. */
  80. protected function getCollection() {
  81. return 'pathauto_state.' . $this->parent->getEntity()->getEntityTypeId();
  82. }
  83. /**
  84. * Deletes the URL aliases for multiple entities of the same type.
  85. *
  86. * @param string $entity_type_id
  87. * The entity type ID of entities being deleted.
  88. * @param int[] $pids_by_id
  89. * A list of path IDs keyed by entity ID.
  90. */
  91. public static function bulkDelete($entity_type_id, array $pids_by_id) {
  92. foreach ($pids_by_id as $id => $pid) {
  93. // Some key-values store entries have computed keys.
  94. $key = static::getPathautoStateKey($id);
  95. if ($key !== $id) {
  96. $pids_by_id[$key] = $pid;
  97. unset($pids_by_id[$id]);
  98. }
  99. }
  100. $states = \Drupal::keyValue("pathauto_state.$entity_type_id")
  101. ->getMultiple(array_keys($pids_by_id));
  102. $pids = [];
  103. foreach ($pids_by_id as $id => $pid) {
  104. // Only delete aliases that were created by this module.
  105. if (isset($states[$id]) && $states[$id] == PathautoState::CREATE) {
  106. $pids[] = $pid;
  107. }
  108. }
  109. \Drupal::service('pathauto.alias_storage_helper')->deleteMultiple($pids);
  110. }
  111. /**
  112. * Gets the key-value store entry key for 'pathauto_state.*' collections.
  113. *
  114. * Normally we want to use the entity ID as key for 'pathauto_state.*'
  115. * collection entries. But some entity types may use string IDs. When such IDs
  116. * are exceeding 128 characters, which is the limit for the 'name' column in
  117. * the {key_value} table, the insertion of the ID in {key_value} will fail.
  118. * Thus we test if we can use the plain ID or we need to store a hashed
  119. * version of the entity ID. Also, it is not possible to rely on the UUID as
  120. * entity types might not have one or might use a non-standard format.
  121. *
  122. * The code is inspired by
  123. * \Drupal\Core\Cache\DatabaseBackend::normalizeCid().
  124. *
  125. * @param int|string $entity_id
  126. * The entity id for which to compute the key.
  127. *
  128. * @return int|string
  129. * The key used to store the value in the key-value store.
  130. *
  131. * @see \Drupal\Core\Cache\DatabaseBackend::normalizeCid()
  132. */
  133. public static function getPathautoStateKey($entity_id) {
  134. $entity_id_is_ascii = mb_check_encoding($entity_id, 'ASCII');
  135. if ($entity_id_is_ascii && strlen($entity_id) <= 128) {
  136. // The original entity ID, if it's an ASCII of 128 characters or less.
  137. return $entity_id;
  138. }
  139. return Crypt::hashBase64($entity_id);
  140. }
  141. }