/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php

https://gitlab.com/geeta7/drupal · PHP · 263 lines · 122 code · 21 blank · 120 comment · 12 complexity · d6c6b169fed4a5e086f60bfda70d9a42 MD5 · raw file

  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Core\Config\Entity\Query\QueryFactory.
  5. */
  6. namespace Drupal\Core\Config\Entity\Query;
  7. use Drupal\Core\Config\Config;
  8. use Drupal\Core\Config\ConfigCrudEvent;
  9. use Drupal\Core\Config\ConfigEvents;
  10. use Drupal\Core\Config\ConfigFactoryInterface;
  11. use Drupal\Core\Config\ConfigManagerInterface;
  12. use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
  13. use Drupal\Core\Entity\EntityTypeInterface;
  14. use Drupal\Core\Entity\Query\QueryBase;
  15. use Drupal\Core\Entity\Query\QueryException;
  16. use Drupal\Core\Entity\Query\QueryFactoryInterface;
  17. use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
  18. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  19. /**
  20. * Provides a factory for creating entity query objects for the config backend.
  21. */
  22. class QueryFactory implements QueryFactoryInterface, EventSubscriberInterface {
  23. /**
  24. * The prefix for the key value collection for fast lookups.
  25. */
  26. const CONFIG_LOOKUP_PREFIX = 'config.entity.key_store.';
  27. /**
  28. * The config factory used by the config entity query.
  29. *
  30. * @var \Drupal\Core\Config\ConfigFactoryInterface;
  31. */
  32. protected $configFactory;
  33. /**
  34. * The namespace of this class, the parent class etc.
  35. *
  36. * @var array
  37. */
  38. protected $namespaces;
  39. /**
  40. * Constructs a QueryFactory object.
  41. *
  42. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  43. * The config storage used by the config entity query.
  44. * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
  45. * The key value factory.
  46. * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
  47. * The configuration manager.
  48. */
  49. public function __construct(ConfigFactoryInterface $config_factory, KeyValueFactoryInterface $key_value, ConfigManagerInterface $config_manager) {
  50. $this->configFactory = $config_factory;
  51. $this->keyValueFactory = $key_value;
  52. $this->configManager = $config_manager;
  53. $this->namespaces = QueryBase::getNamespaces($this);
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function get(EntityTypeInterface $entity_type, $conjunction) {
  59. return new Query($entity_type, $conjunction, $this->configFactory, $this->keyValueFactory, $this->namespaces);
  60. }
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function getAggregate(EntityTypeInterface $entity_type, $conjunction) {
  65. throw new QueryException('Aggregation over configuration entities is not supported');
  66. }
  67. /**
  68. * Gets the key value store used to store fast lookups.
  69. *
  70. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  71. * The entity type.
  72. *
  73. * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface
  74. * The key value store used to store fast lookups.
  75. */
  76. protected function getConfigKeyStore(EntityTypeInterface $entity_type) {
  77. return $this->keyValueFactory->get(static::CONFIG_LOOKUP_PREFIX . $entity_type->id());
  78. }
  79. /**
  80. * Updates or adds lookup data.
  81. *
  82. * @param \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type
  83. * The entity type.
  84. * @param \Drupal\Core\Config\Config $config
  85. * The configuration object that is being saved.
  86. */
  87. protected function updateConfigKeyStore(ConfigEntityTypeInterface $entity_type, Config $config) {
  88. $config_key_store = $this->getConfigKeyStore($entity_type);
  89. foreach ($entity_type->getLookupKeys() as $lookup_key) {
  90. foreach ($this->getKeys($config, $lookup_key, 'get', $entity_type) as $key) {
  91. $values = $config_key_store->get($key, []);
  92. if (!in_array($config->getName(), $values, TRUE)) {
  93. $values[] = $config->getName();
  94. $config_key_store->set($key, $values);
  95. }
  96. }
  97. }
  98. }
  99. /**
  100. * Deletes lookup data.
  101. *
  102. * @param \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type
  103. * The entity type.
  104. * @param \Drupal\Core\Config\Config $config
  105. * The configuration object that is being deleted.
  106. */
  107. protected function deleteConfigKeyStore(ConfigEntityTypeInterface $entity_type, Config $config) {
  108. $config_key_store = $this->getConfigKeyStore($entity_type);
  109. foreach ($entity_type->getLookupKeys() as $lookup_key) {
  110. foreach ($this->getKeys($config, $lookup_key, 'getOriginal', $entity_type) as $key) {
  111. $values = $config_key_store->get($key, []);
  112. $pos = array_search($config->getName(), $values, TRUE);
  113. if ($pos !== FALSE) {
  114. unset($values[$pos]);
  115. }
  116. if (empty($values)) {
  117. $config_key_store->delete($key);
  118. }
  119. else {
  120. $config_key_store->set($key, $values);
  121. }
  122. }
  123. }
  124. }
  125. /**
  126. * Creates lookup keys for configuration data.
  127. *
  128. * @param \Drupal\Core\Config\Config $config
  129. * The configuration object.
  130. * @param string $key
  131. * The configuration key to look for.
  132. * @param string $get_method
  133. * Which method on the config object to call to get the value. Either 'get'
  134. * or 'getOriginal'.
  135. * @param \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type
  136. * The configuration entity type.
  137. *
  138. * @return array
  139. * An array of lookup keys concatenated to the configuration values.
  140. *
  141. * @throws \Drupal\Core\Config\Entity\Query\InvalidLookupKeyException
  142. * The provided $key cannot end with a wildcard. This makes no sense since
  143. * you cannot do fast lookups against this.
  144. */
  145. protected function getKeys(Config $config, $key, $get_method, ConfigEntityTypeInterface $entity_type) {
  146. if (substr($key, -1) == '*') {
  147. throw new InvalidLookupKeyException(strtr('%entity_type lookup key %key ends with a wildcard this can not be used as a lookup', ['%entity_type' => $entity_type->id(), '%key' => $key]));
  148. }
  149. $parts = explode('.*', $key);
  150. // Remove leading dots.
  151. array_walk($parts, function (&$value) {
  152. $value = trim($value, '.');
  153. });
  154. $values = (array) $this->getValues($config, $parts[0], $get_method, $parts);
  155. $output = array();
  156. // Flatten the array to a single dimension and add the key to all the
  157. // values.
  158. array_walk_recursive($values, function ($current) use (&$output, $key) {
  159. if (is_scalar($current)) {
  160. $current = $key . ':' . $current;
  161. }
  162. $output[] = $current;
  163. });
  164. return $output;
  165. }
  166. /**
  167. * Finds all the values for a configuration key in a configuration object.
  168. *
  169. * @param \Drupal\Core\Config\Config $config
  170. * The configuration object.
  171. * @param string $key
  172. * The current key being checked.
  173. * @param string $get_method
  174. * Which method on the config object to call to get the value.
  175. * @param array $parts
  176. * All the parts of a configuration key we are checking.
  177. * @param int $start
  178. * Which position of $parts we are processing. Defaults to 0.
  179. *
  180. * @return array|NULL
  181. * The array of configuration values the match the provided key. NULL if
  182. * the configuration object does not have a value that corresponds to the
  183. * key.
  184. */
  185. protected function getValues(Config $config, $key, $get_method, array $parts, $start = 0) {
  186. $value = $config->$get_method($key);
  187. if (is_array($value)) {
  188. $new_value = [];
  189. $start++;
  190. if (!isset($parts[$start])) {
  191. // The configuration object does not have a value that corresponds to
  192. // the key.
  193. return NULL;
  194. }
  195. foreach (array_keys($value) as $key_bit) {
  196. $new_key = $key . '.' . $key_bit;
  197. if (!empty($parts[$start])) {
  198. $new_key .= '.' . $parts[$start];
  199. }
  200. $new_value[] = $this->getValues($config, $new_key, $get_method, $parts, $start);
  201. }
  202. $value = $new_value;
  203. }
  204. return $value;
  205. }
  206. /**
  207. * Updates configuration entity in the key store.
  208. *
  209. * @param ConfigCrudEvent $event
  210. * The configuration event.
  211. */
  212. public function onConfigSave(ConfigCrudEvent $event) {
  213. $saved_config = $event->getConfig();
  214. $entity_type_id = $this->configManager->getEntityTypeIdByName($saved_config->getName());
  215. if ($entity_type_id) {
  216. $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id);
  217. $this->updateConfigKeyStore($entity_type, $saved_config);
  218. }
  219. }
  220. /**
  221. * Removes configuration entity from key store.
  222. *
  223. * @param \Drupal\Core\Config\ConfigCrudEvent $event
  224. * The configuration event.
  225. */
  226. public function onConfigDelete(ConfigCrudEvent $event) {
  227. $saved_config = $event->getConfig();
  228. $entity_type_id = $this->configManager->getEntityTypeIdByName($saved_config->getName());
  229. if ($entity_type_id) {
  230. $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id);
  231. $this->deleteConfigKeyStore($entity_type, $saved_config);
  232. }
  233. }
  234. /**
  235. * {@inheritdoc}
  236. */
  237. static function getSubscribedEvents() {
  238. $events[ConfigEvents::SAVE][] = array('onConfigSave', 128);
  239. $events[ConfigEvents::DELETE][] = array('onConfigDelete', 128);
  240. return $events;
  241. }
  242. }