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