Commit | Line | Data |
---|---|---|
4ef04170 TO |
1 | <?php |
2 | ||
3 | /* | |
4 | +--------------------------------------------------------------------+ | |
bc77d7c0 | 5 | | Copyright CiviCRM LLC. All rights reserved. | |
4ef04170 | 6 | | | |
bc77d7c0 TO |
7 | | This work is published under the GNU AGPLv3 license with some | |
8 | | permitted exceptions and without any warranty. For full license | | |
9 | | and copyright information, see https://civicrm.org/licensing | | |
4ef04170 TO |
10 | +--------------------------------------------------------------------+ |
11 | */ | |
12 | ||
13 | /** | |
14 | * | |
15 | * @package CRM | |
ca5cec67 | 16 | * @copyright CiviCRM LLC https://civicrm.org/licensing |
4ef04170 TO |
17 | */ |
18 | class CRM_Core_DAO_AllCoreTables { | |
19 | ||
95b9a42e TO |
20 | private static $tables = NULL; |
21 | private static $daoToClass = NULL; | |
22 | private static $entityTypes = NULL; | |
4ef04170 | 23 | |
8246bca4 | 24 | /** |
25 | * Initialise. | |
26 | * | |
27 | * @param bool $fresh | |
28 | */ | |
95b9a42e | 29 | public static function init($fresh = FALSE) { |
4ef04170 | 30 | static $init = FALSE; |
95b9a42e TO |
31 | if ($init && !$fresh) { |
32 | return; | |
4ef04170 | 33 | } |
be2fb01f | 34 | Civi::$statics[__CLASS__] = []; |
4ef04170 TO |
35 | |
36 | $file = preg_replace('/\.php$/', '.data.php', __FILE__); | |
37 | $entityTypes = require $file; | |
38 | CRM_Utils_Hook::entityTypes($entityTypes); | |
39 | ||
be2fb01f CW |
40 | self::$entityTypes = []; |
41 | self::$tables = []; | |
42 | self::$daoToClass = []; | |
4ef04170 | 43 | foreach ($entityTypes as $entityType) { |
740dd877 TO |
44 | self::registerEntityType( |
45 | $entityType['name'], | |
46 | $entityType['class'], | |
47 | $entityType['table'], | |
2e1f50d6 CW |
48 | $entityType['fields_callback'] ?? NULL, |
49 | $entityType['links_callback'] ?? NULL | |
740dd877 | 50 | ); |
4ef04170 TO |
51 | } |
52 | ||
53 | $init = TRUE; | |
54 | } | |
55 | ||
56 | /** | |
57 | * (Quasi-Private) Do not call externally (except for unit-testing) | |
8246bca4 | 58 | * |
40ac12d3 | 59 | * @param string $briefName |
8246bca4 | 60 | * @param string $className |
61 | * @param string $tableName | |
62 | * @param string $fields_callback | |
63 | * @param string $links_callback | |
4ef04170 | 64 | */ |
40ac12d3 CW |
65 | public static function registerEntityType($briefName, $className, $tableName, $fields_callback = NULL, $links_callback = NULL) { |
66 | self::$daoToClass[$briefName] = $className; | |
4ef04170 | 67 | self::$tables[$tableName] = $className; |
40ac12d3 CW |
68 | self::$entityTypes[$briefName] = [ |
69 | 'name' => $briefName, | |
4ef04170 TO |
70 | 'class' => $className, |
71 | 'table' => $tableName, | |
740dd877 TO |
72 | 'fields_callback' => $fields_callback, |
73 | 'links_callback' => $links_callback, | |
be2fb01f | 74 | ]; |
4ef04170 TO |
75 | } |
76 | ||
95b9a42e TO |
77 | /** |
78 | * @return array | |
40ac12d3 | 79 | * Ex: $result['Contact']['table'] == 'civicrm_contact'; |
95b9a42e TO |
80 | */ |
81 | public static function get() { | |
4ef04170 TO |
82 | self::init(); |
83 | return self::$entityTypes; | |
84 | } | |
85 | ||
95b9a42e TO |
86 | /** |
87 | * @return array | |
88 | * List of SQL table names. | |
89 | */ | |
90 | public static function tables() { | |
4ef04170 TO |
91 | self::init(); |
92 | return self::$tables; | |
93 | } | |
94 | ||
6b86d84f AS |
95 | /** |
96 | * @return array | |
97 | * List of indices. | |
98 | */ | |
99 | public static function indices($localize = TRUE) { | |
be2fb01f | 100 | $indices = []; |
6b86d84f AS |
101 | self::init(); |
102 | foreach (self::$daoToClass as $class) { | |
be2fb01f | 103 | if (is_callable([$class, 'indices'])) { |
6b86d84f AS |
104 | $indices[$class::getTableName()] = $class::indices($localize); |
105 | } | |
106 | } | |
107 | return $indices; | |
108 | } | |
109 | ||
110 | /** | |
111 | * Modify indices to account for localization options. | |
112 | * | |
8a4fede3 | 113 | * @param string $class DAO class |
6b86d84f AS |
114 | * @param array $originalIndices index definitions before localization |
115 | * | |
116 | * @return array | |
117 | * index definitions after localization | |
118 | */ | |
119 | public static function multilingualize($class, $originalIndices) { | |
394d18d3 CW |
120 | $locales = CRM_Core_I18n::getMultilingual(); |
121 | if (!$locales) { | |
6b86d84f AS |
122 | return $originalIndices; |
123 | } | |
124 | $classFields = $class::fields(); | |
125 | ||
be2fb01f | 126 | $finalIndices = []; |
6b86d84f AS |
127 | foreach ($originalIndices as $index) { |
128 | if ($index['localizable']) { | |
129 | foreach ($locales as $locale) { | |
130 | $localIndex = $index; | |
131 | $localIndex['name'] .= "_" . $locale; | |
be2fb01f | 132 | $fields = []; |
6b86d84f AS |
133 | foreach ($localIndex['field'] as $field) { |
134 | $baseField = explode('(', $field); | |
135 | if ($classFields[$baseField[0]]['localizable']) { | |
136 | // field name may have eg (3) at end for prefix length | |
137 | // last_name => last_name_fr_FR | |
138 | // last_name(3) => last_name_fr_FR(3) | |
139 | $fields[] = preg_replace('/^([^(]+)(\(\d+\)|)$/', '${1}_' . $locale . '${2}', $field); | |
140 | } | |
141 | else { | |
142 | $fields[] = $field; | |
143 | } | |
144 | } | |
145 | $localIndex['field'] = $fields; | |
146 | $finalIndices[$localIndex['name']] = $localIndex; | |
147 | } | |
148 | } | |
149 | else { | |
150 | $finalIndices[$index['name']] = $index; | |
151 | } | |
152 | } | |
153 | CRM_Core_BAO_SchemaHandler::addIndexSignature(self::getTableForClass($class), $finalIndices); | |
154 | return $finalIndices; | |
155 | } | |
156 | ||
95b9a42e TO |
157 | /** |
158 | * @return array | |
159 | * Mapping from brief-names to class-names. | |
160 | * Ex: $result['Contact'] == 'CRM_Contact_DAO_Contact'. | |
161 | */ | |
162 | public static function daoToClass() { | |
4ef04170 TO |
163 | self::init(); |
164 | return self::$daoToClass; | |
165 | } | |
166 | ||
95b9a42e TO |
167 | /** |
168 | * @return array | |
169 | * Mapping from table-names to class-names. | |
170 | * Ex: $result['civicrm_contact'] == 'CRM_Contact_DAO_Contact'. | |
171 | */ | |
172 | public static function getCoreTables() { | |
4ef04170 TO |
173 | return self::tables(); |
174 | } | |
175 | ||
95b9a42e TO |
176 | /** |
177 | * Determine whether $tableName is a core table. | |
178 | * | |
179 | * @param string $tableName | |
180 | * @return bool | |
181 | */ | |
182 | public static function isCoreTable($tableName) { | |
4c1bd923 | 183 | return array_key_exists($tableName, self::tables()); |
4ef04170 TO |
184 | } |
185 | ||
8246bca4 | 186 | /** |
3ab49d54 | 187 | * Get the DAO for a BAO class. |
8246bca4 | 188 | * |
3ab49d54 | 189 | * @param string $baoName |
8246bca4 | 190 | * |
3ab49d54 CW |
191 | * @return string|CRM_Core_DAO |
192 | */ | |
193 | public static function getCanonicalClassName($baoName) { | |
194 | return str_replace('_BAO_', '_DAO_', $baoName); | |
195 | } | |
196 | ||
197 | /** | |
198 | * Get the BAO for a DAO class. | |
199 | * | |
200 | * @param string $daoName | |
201 | * | |
202 | * @return string|CRM_Core_DAO | |
8246bca4 | 203 | */ |
3ab49d54 CW |
204 | public static function getBAOClassName($daoName) { |
205 | $baoName = str_replace('_DAO_', '_BAO_', $daoName); | |
9cef6066 | 206 | return $daoName === $baoName || class_exists($baoName) ? $baoName : $daoName; |
4ef04170 TO |
207 | } |
208 | ||
74c303ca | 209 | /** |
210 | * Convert possibly underscore separated words to camel case with special handling for 'UF' | |
211 | * e.g membership_payment returns MembershipPayment | |
212 | * | |
213 | * @param string $name | |
214 | * @param bool $legacyV3 | |
215 | * @return string | |
216 | */ | |
217 | public static function convertEntityNameToCamel(string $name, $legacyV3 = FALSE): string { | |
218 | // This map only applies to APIv3 | |
219 | $map = [ | |
220 | 'acl' => 'Acl', | |
221 | 'ACL' => 'Acl', | |
222 | 'im' => 'Im', | |
223 | 'IM' => 'Im', | |
224 | ]; | |
225 | if ($legacyV3 && isset($map[$name])) { | |
226 | return $map[$name]; | |
227 | } | |
228 | ||
229 | $fragments = explode('_', $name); | |
230 | foreach ($fragments as & $fragment) { | |
231 | $fragment = ucfirst($fragment); | |
232 | // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores) | |
233 | if (strpos($fragment, 'Uf') === 0 && strlen($name) > 2) { | |
234 | $fragment = 'UF' . ucfirst(substr($fragment, 2)); | |
235 | } | |
236 | } | |
237 | // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in underscore-separated) | |
238 | if ($fragments[0] === 'Uf') { | |
239 | $fragments[0] = 'UF'; | |
240 | } | |
241 | return implode('', $fragments); | |
242 | } | |
243 | ||
244 | /** | |
245 | * Convert CamelCase to snake_case, with special handling for some entity names. | |
246 | * | |
247 | * Eg. Activity returns activity | |
248 | * UFGroup returns uf_group | |
249 | * OptionValue returns option_value | |
250 | * | |
251 | * @param string $name | |
252 | * | |
253 | * @return string | |
254 | */ | |
255 | public static function convertEntityNameToLower(string $name): string { | |
256 | if ($name === strtolower($name)) { | |
257 | return $name; | |
258 | } | |
259 | if ($name === 'PCP' || $name === 'IM' || $name === 'ACL') { | |
260 | return strtolower($name); | |
261 | } | |
262 | return strtolower(ltrim(str_replace('U_F', | |
263 | 'uf', | |
264 | // That's CamelCase, beside an odd UFCamel that is expected as uf_camel | |
265 | preg_replace('/(?=[A-Z])/', '_$0', $name) | |
266 | ), '_')); | |
267 | } | |
268 | ||
95b9a42e | 269 | /** |
8246bca4 | 270 | * Get a list of all DAO classes. |
271 | * | |
95b9a42e TO |
272 | * @return array |
273 | * List of class names. | |
274 | */ | |
275 | public static function getClasses() { | |
4ef04170 TO |
276 | return array_values(self::daoToClass()); |
277 | } | |
278 | ||
c8d84653 TO |
279 | /** |
280 | * Get a list of all extant BAO classes. | |
281 | * | |
282 | * @return array | |
283 | * Ex: ['Contact' => 'CRM_Contact_BAO_Contact'] | |
284 | */ | |
285 | public static function getBaoClasses() { | |
286 | $r = []; | |
287 | foreach (\CRM_Core_DAO_AllCoreTables::daoToClass() as $entity => $daoClass) { | |
288 | $baoClass = str_replace('_DAO_', '_BAO_', $daoClass); | |
289 | if (class_exists($baoClass)) { | |
290 | $r[$entity] = $baoClass; | |
291 | } | |
292 | } | |
293 | return $r; | |
294 | } | |
295 | ||
8246bca4 | 296 | /** |
297 | * Get the classname for the table. | |
298 | * | |
299 | * @param string $tableName | |
721c9da1 | 300 | * @return string|CRM_Core_DAO|NULL |
8246bca4 | 301 | */ |
95b9a42e | 302 | public static function getClassForTable($tableName) { |
3a575348 | 303 | //CRM-19677: on multilingual setup, trim locale from $tableName to fetch class name |
304 | if (CRM_Core_I18n::isMultilingual()) { | |
305 | global $dbLocale; | |
306 | $tableName = str_replace($dbLocale, '', $tableName); | |
307 | } | |
4ef04170 TO |
308 | return CRM_Utils_Array::value($tableName, self::tables()); |
309 | } | |
310 | ||
95b9a42e TO |
311 | /** |
312 | * Given a brief-name, determine the full class-name. | |
313 | * | |
e6766df9 | 314 | * @param string $briefName |
95b9a42e | 315 | * Ex: 'Contact'. |
721c9da1 | 316 | * @return string|CRM_Core_DAO|NULL |
95b9a42e TO |
317 | * Ex: 'CRM_Contact_DAO_Contact'. |
318 | */ | |
e6766df9 | 319 | public static function getFullName($briefName) { |
40ac12d3 CW |
320 | self::init(); |
321 | return self::$entityTypes[$briefName]['class'] ?? NULL; | |
4ef04170 TO |
322 | } |
323 | ||
95b9a42e TO |
324 | /** |
325 | * Given a full class-name, determine the brief-name. | |
326 | * | |
327 | * @param string $className | |
328 | * Ex: 'CRM_Contact_DAO_Contact'. | |
329 | * @return string|NULL | |
330 | * Ex: 'Contact'. | |
331 | */ | |
332 | public static function getBriefName($className) { | |
3d182a04 CW |
333 | $className = self::getCanonicalClassName($className); |
334 | return array_search($className, self::daoToClass(), TRUE) ?: NULL; | |
4ef04170 TO |
335 | } |
336 | ||
337 | /** | |
338 | * @param string $className DAO or BAO name | |
339 | * @return string|FALSE SQL table name | |
340 | */ | |
95b9a42e TO |
341 | public static function getTableForClass($className) { |
342 | return array_search(self::getCanonicalClassName($className), | |
343 | self::tables()); | |
4ef04170 TO |
344 | } |
345 | ||
9d4c4ffd | 346 | /** |
347 | * Convert the entity name into a table name. | |
348 | * | |
40ac12d3 | 349 | * @param string $briefName |
9d4c4ffd | 350 | * |
351 | * @return FALSE|string | |
352 | */ | |
40ac12d3 CW |
353 | public static function getTableForEntityName($briefName) { |
354 | self::init(); | |
355 | return self::$entityTypes[$briefName]['table']; | |
9d4c4ffd | 356 | } |
357 | ||
17019d49 CW |
358 | /** |
359 | * Convert table name to brief entity name. | |
360 | * | |
361 | * @param string $tableName | |
362 | * | |
363 | * @return FALSE|string | |
364 | */ | |
365 | public static function getEntityNameForTable(string $tableName) { | |
40ac12d3 | 366 | self::init(); |
99d9ce89 CW |
367 | // CRM-19677: on multilingual setup, trim locale from $tableName to fetch class name |
368 | if (CRM_Core_I18n::isMultilingual()) { | |
369 | global $dbLocale; | |
370 | $tableName = str_replace($dbLocale, '', $tableName); | |
371 | } | |
40ac12d3 CW |
372 | $matches = CRM_Utils_Array::findAll(self::$entityTypes, ['table' => $tableName]); |
373 | return $matches ? $matches[0]['name'] : NULL; | |
17019d49 CW |
374 | } |
375 | ||
8246bca4 | 376 | /** |
377 | * Reinitialise cache. | |
8246bca4 | 378 | */ |
ec114c21 EM |
379 | public static function reinitializeCache() { |
380 | self::init(TRUE); | |
4ef04170 TO |
381 | } |
382 | ||
84a0493c TO |
383 | /** |
384 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
385 | * | |
386 | * @param string $dao | |
387 | * Ex: 'CRM_Core_DAO_Address'. | |
388 | * @param string $labelName | |
389 | * Ex: 'address'. | |
390 | * @param bool $prefix | |
391 | * @param array $foreignDAOs | |
392 | * @return array | |
393 | */ | |
394 | public static function getExports($dao, $labelName, $prefix, $foreignDAOs) { | |
395 | // Bug-level compatibility -- or sane behavior? | |
396 | $cacheKey = $dao . ':export'; | |
397 | // $cacheKey = $dao . ':' . ($prefix ? 'export-prefix' : 'export'); | |
398 | ||
399 | if (!isset(Civi::$statics[__CLASS__][$cacheKey])) { | |
be2fb01f | 400 | $exports = []; |
84a0493c TO |
401 | $fields = $dao::fields(); |
402 | ||
8246bca4 | 403 | foreach ($fields as $name => $field) { |
de6c59ca | 404 | if (!empty($field['export'])) { |
84a0493c TO |
405 | if ($prefix) { |
406 | $exports[$labelName] = & $fields[$name]; | |
8246bca4 | 407 | } |
408 | else { | |
84a0493c TO |
409 | $exports[$name] = & $fields[$name]; |
410 | } | |
411 | } | |
412 | } | |
413 | ||
414 | foreach ($foreignDAOs as $foreignDAO) { | |
415 | $exports = array_merge($exports, $foreignDAO::export(TRUE)); | |
416 | } | |
417 | ||
418 | Civi::$statics[__CLASS__][$cacheKey] = $exports; | |
419 | } | |
420 | return Civi::$statics[__CLASS__][$cacheKey]; | |
421 | } | |
422 | ||
423 | /** | |
424 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
425 | * | |
426 | * @param string $dao | |
427 | * Ex: 'CRM_Core_DAO_Address'. | |
428 | * @param string $labelName | |
429 | * Ex: 'address'. | |
430 | * @param bool $prefix | |
431 | * @param array $foreignDAOs | |
432 | * @return array | |
433 | */ | |
434 | public static function getImports($dao, $labelName, $prefix, $foreignDAOs) { | |
435 | // Bug-level compatibility -- or sane behavior? | |
436 | $cacheKey = $dao . ':import'; | |
437 | // $cacheKey = $dao . ':' . ($prefix ? 'import-prefix' : 'import'); | |
438 | ||
439 | if (!isset(Civi::$statics[__CLASS__][$cacheKey])) { | |
be2fb01f | 440 | $imports = []; |
84a0493c TO |
441 | $fields = $dao::fields(); |
442 | ||
8246bca4 | 443 | foreach ($fields as $name => $field) { |
de6c59ca | 444 | if (!empty($field['import'])) { |
84a0493c TO |
445 | if ($prefix) { |
446 | $imports[$labelName] = & $fields[$name]; | |
8246bca4 | 447 | } |
448 | else { | |
84a0493c TO |
449 | $imports[$name] = & $fields[$name]; |
450 | } | |
451 | } | |
452 | } | |
453 | ||
454 | foreach ($foreignDAOs as $foreignDAO) { | |
455 | $imports = array_merge($imports, $foreignDAO::import(TRUE)); | |
456 | } | |
457 | ||
458 | Civi::$statics[__CLASS__][$cacheKey] = $imports; | |
459 | } | |
460 | return Civi::$statics[__CLASS__][$cacheKey]; | |
461 | } | |
462 | ||
740dd877 TO |
463 | /** |
464 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
465 | * | |
466 | * Apply any third-party alterations to the `fields()`. | |
467 | * | |
40ac12d3 CW |
468 | * TODO: This function should probably take briefName as the key instead of className |
469 | * because the latter is not always unique (e.g. virtual entities) | |
470 | * | |
740dd877 TO |
471 | * @param string $className |
472 | * @param string $event | |
473 | * @param mixed $values | |
474 | */ | |
475 | public static function invoke($className, $event, &$values) { | |
476 | self::init(); | |
40ac12d3 CW |
477 | $briefName = self::getBriefName($className); |
478 | if (isset(self::$entityTypes[$briefName][$event])) { | |
479 | foreach (self::$entityTypes[$briefName][$event] as $filter) { | |
be2fb01f | 480 | $args = [$className, &$values]; |
740dd877 TO |
481 | \Civi\Core\Resolver::singleton()->call($filter, $args); |
482 | } | |
483 | } | |
484 | } | |
485 | ||
4ef04170 | 486 | } |