Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/views/src/ViewsData.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\views; | |
4 | |
5 use Drupal\Component\Utility\NestedArray; | |
6 use Drupal\Core\Cache\Cache; | |
7 use Drupal\Core\Cache\CacheBackendInterface; | |
8 use Drupal\Core\Config\ConfigFactoryInterface; | |
9 use Drupal\Core\Extension\ModuleHandlerInterface; | |
10 use Drupal\Core\Language\LanguageManagerInterface; | |
11 | |
12 /** | |
13 * Class to manage and lazy load cached views data. | |
14 * | |
15 * If a table is requested and cannot be loaded from cache, all data is then | |
16 * requested from cache. A table-specific cache entry will then be created for | |
17 * the requested table based on this cached data. Table data is only rebuilt | |
18 * when no cache entry for all table data can be retrieved. | |
19 */ | |
20 class ViewsData { | |
21 | |
22 /** | |
23 * The base cache ID to use. | |
24 * | |
25 * @var string | |
26 */ | |
27 protected $baseCid = 'views_data'; | |
28 | |
29 /** | |
30 * The cache backend to use. | |
31 * | |
32 * @var \Drupal\Core\Cache\CacheBackendInterface | |
33 */ | |
34 protected $cacheBackend; | |
35 | |
36 /** | |
37 * Table data storage. | |
38 * | |
39 * This is used for explicitly requested tables. | |
40 * | |
41 * @var array | |
42 */ | |
43 protected $storage = []; | |
44 | |
45 /** | |
46 * All table storage data loaded from cache. | |
47 * | |
48 * This is used when all data has been loaded from the cache to prevent | |
49 * further cache get calls when rebuilding all data or for single tables. | |
50 * | |
51 * @var array | |
52 */ | |
53 protected $allStorage = []; | |
54 | |
55 /** | |
56 * Whether the data has been fully loaded in this request. | |
57 * | |
58 * @var bool | |
59 */ | |
60 protected $fullyLoaded = FALSE; | |
61 | |
62 /** | |
63 * Whether or not to skip data caching and rebuild data each time. | |
64 * | |
65 * @var bool | |
66 */ | |
67 protected $skipCache = FALSE; | |
68 | |
69 /** | |
70 * The current language code. | |
71 * | |
72 * @var string | |
73 */ | |
74 protected $langcode; | |
75 | |
76 /** | |
77 * Stores a module manager to invoke hooks. | |
78 * | |
79 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
80 */ | |
81 protected $moduleHandler; | |
82 | |
83 /** | |
84 * The language manager. | |
85 * | |
86 * @var \Drupal\Core\Language\LanguageManagerInterface | |
87 */ | |
88 protected $languageManager; | |
89 | |
90 /** | |
91 * Constructs this ViewsData object. | |
92 * | |
93 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend | |
94 * The cache backend to use. | |
95 * @param \Drupal\Core\Config\ConfigFactoryInterface $config | |
96 * The configuration factory object to use. | |
97 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
98 * The module handler class to use for invoking hooks. | |
99 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
100 * The language manager. | |
101 */ | |
102 public function __construct(CacheBackendInterface $cache_backend, ConfigFactoryInterface $config, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) { | |
103 $this->cacheBackend = $cache_backend; | |
104 $this->moduleHandler = $module_handler; | |
105 $this->languageManager = $language_manager; | |
106 | |
107 $this->langcode = $this->languageManager->getCurrentLanguage()->getId(); | |
108 $this->skipCache = $config->get('views.settings')->get('skip_cache'); | |
109 } | |
110 | |
111 /** | |
112 * Gets all table data. | |
113 * | |
114 * @see https://www.drupal.org/node/2723553 | |
115 * | |
116 * @return array | |
117 * An array of table data. | |
118 */ | |
119 public function getAll() { | |
120 if (!$this->fullyLoaded) { | |
121 $this->allStorage = $this->getData(); | |
122 } | |
123 | |
124 // Set storage from allStorage outside of the fullyLoaded check to prevent | |
125 // cache calls on requests that have requested all data to get a single | |
126 // tables data. Make sure $this->storage is populated in this case. | |
127 $this->storage = $this->allStorage; | |
128 return $this->allStorage; | |
129 } | |
130 | |
131 /** | |
132 * Gets data for a particular table, or all tables. | |
133 * | |
134 * @param string|null $key | |
135 * The key of the cache entry to retrieve. Defaults to NULL, this will | |
136 * return all table data. | |
137 * | |
138 * @deprecated NULL $key deprecated in Drupal 8.2.x and will be removed in | |
139 * 9.0.0. Use getAll() instead. | |
140 * | |
141 * @see https://www.drupal.org/node/2723553 | |
142 * | |
143 * @return array | |
144 * An array of table data. | |
145 */ | |
146 public function get($key = NULL) { | |
147 if (!$key) { | |
148 return $this->getAll(); | |
149 } | |
150 if (!isset($this->storage[$key])) { | |
151 // Prepare a cache ID for get and set. | |
152 $cid = $this->baseCid . ':' . $key; | |
153 $from_cache = FALSE; | |
154 | |
155 if ($data = $this->cacheGet($cid)) { | |
156 $this->storage[$key] = $data->data; | |
157 $from_cache = TRUE; | |
158 } | |
159 // If there is no cached entry and data is not already fully loaded, | |
160 // rebuild. This will stop requests for invalid tables calling getData. | |
161 elseif (!$this->fullyLoaded) { | |
162 $this->allStorage = $this->getData(); | |
163 } | |
164 | |
165 if (!$from_cache) { | |
166 if (!isset($this->allStorage[$key])) { | |
167 // Write an empty cache entry if no information for that table | |
168 // exists to avoid repeated cache get calls for this table and | |
169 // prevent loading all tables unnecessarily. | |
170 $this->storage[$key] = []; | |
171 $this->allStorage[$key] = []; | |
172 } | |
173 else { | |
174 $this->storage[$key] = $this->allStorage[$key]; | |
175 } | |
176 | |
177 // Create a cache entry for the requested table. | |
178 $this->cacheSet($cid, $this->allStorage[$key]); | |
179 } | |
180 } | |
181 return $this->storage[$key]; | |
182 } | |
183 | |
184 /** | |
185 * Gets data from the cache backend. | |
186 * | |
187 * @param string $cid | |
188 * The cache ID to return. | |
189 * | |
190 * @return mixed | |
191 * The cached data, if any. This will immediately return FALSE if the | |
192 * $skipCache property is TRUE. | |
193 */ | |
194 protected function cacheGet($cid) { | |
195 if ($this->skipCache) { | |
196 return FALSE; | |
197 } | |
198 | |
199 return $this->cacheBackend->get($this->prepareCid($cid)); | |
200 } | |
201 | |
202 /** | |
203 * Sets data to the cache backend. | |
204 * | |
205 * @param string $cid | |
206 * The cache ID to set. | |
207 * @param mixed $data | |
208 * The data that will be cached. | |
209 */ | |
210 protected function cacheSet($cid, $data) { | |
211 return $this->cacheBackend->set($this->prepareCid($cid), $data, Cache::PERMANENT, ['views_data', 'config:core.extension']); | |
212 } | |
213 | |
214 /** | |
215 * Prepares the cache ID by appending a language code. | |
216 * | |
217 * @param string $cid | |
218 * The cache ID to prepare. | |
219 * | |
220 * @return string | |
221 * The prepared cache ID. | |
222 */ | |
223 protected function prepareCid($cid) { | |
224 return $cid . ':' . $this->langcode; | |
225 } | |
226 | |
227 /** | |
228 * Gets all data invoked by hook_views_data(). | |
229 * | |
230 * This is requested from the cache before being rebuilt. | |
231 * | |
232 * @return array | |
233 * An array of all data. | |
234 */ | |
235 protected function getData() { | |
236 $this->fullyLoaded = TRUE; | |
237 | |
238 if ($data = $this->cacheGet($this->baseCid)) { | |
239 return $data->data; | |
240 } | |
241 else { | |
242 $modules = $this->moduleHandler->getImplementations('views_data'); | |
243 $data = []; | |
244 foreach ($modules as $module) { | |
245 $views_data = $this->moduleHandler->invoke($module, 'views_data'); | |
246 // Set the provider key for each base table. | |
247 foreach ($views_data as &$table) { | |
248 if (isset($table['table']) && !isset($table['table']['provider'])) { | |
249 $table['table']['provider'] = $module; | |
250 } | |
251 } | |
252 $data = NestedArray::mergeDeep($data, $views_data); | |
253 } | |
254 $this->moduleHandler->alter('views_data', $data); | |
255 | |
256 $this->processEntityTypes($data); | |
257 | |
258 // Keep a record with all data. | |
259 $this->cacheSet($this->baseCid, $data); | |
260 | |
261 return $data; | |
262 } | |
263 } | |
264 | |
265 /** | |
266 * Links tables with 'entity type' to respective generic entity-type tables. | |
267 * | |
268 * @param array $data | |
269 * The array of data to alter entity data for, passed by reference. | |
270 */ | |
271 protected function processEntityTypes(array &$data) { | |
272 foreach ($data as $table_name => $table_info) { | |
273 // Add in a join from the entity-table if an entity-type is given. | |
274 if (!empty($table_info['table']['entity type'])) { | |
275 $entity_table = 'views_entity_' . $table_info['table']['entity type']; | |
276 | |
277 $data[$entity_table]['table']['join'][$table_name] = [ | |
278 'left_table' => $table_name, | |
279 ]; | |
280 $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type']; | |
281 // Copy over the default table group if we have none yet. | |
282 if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) { | |
283 $data[$entity_table]['table']['group'] = $table_info['table']['group']; | |
284 } | |
285 } | |
286 } | |
287 } | |
288 | |
289 /** | |
290 * Fetches a list of all base tables available. | |
291 * | |
292 * @return array | |
293 * An array of base table data keyed by table name. Each item contains the | |
294 * following keys: | |
295 * - title: The title label for the base table. | |
296 * - help: The help text for the base table. | |
297 * - weight: The weight of the base table. | |
298 */ | |
299 public function fetchBaseTables() { | |
300 $tables = []; | |
301 | |
302 foreach ($this->get() as $table => $info) { | |
303 if (!empty($info['table']['base'])) { | |
304 $tables[$table] = [ | |
305 'title' => $info['table']['base']['title'], | |
306 'help' => !empty($info['table']['base']['help']) ? $info['table']['base']['help'] : '', | |
307 'weight' => !empty($info['table']['base']['weight']) ? $info['table']['base']['weight'] : 0, | |
308 ]; | |
309 } | |
310 } | |
311 | |
312 // Sorts by the 'weight' and then by 'title' element. | |
313 uasort($tables, function ($a, $b) { | |
314 if ($a['weight'] != $b['weight']) { | |
315 return $a['weight'] < $b['weight'] ? -1 : 1; | |
316 } | |
317 if ($a['title'] != $b['title']) { | |
318 return $a['title'] < $b['title'] ? -1 : 1; | |
319 } | |
320 return 0; | |
321 }); | |
322 | |
323 return $tables; | |
324 } | |
325 | |
326 /** | |
327 * Clears the class storage and cache. | |
328 */ | |
329 public function clear() { | |
330 $this->storage = []; | |
331 $this->allStorage = []; | |
332 $this->fullyLoaded = FALSE; | |
333 Cache::invalidateTags(['views_data']); | |
334 } | |
335 | |
336 } |