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 CW |
366 | self::init(); |
367 | $matches = CRM_Utils_Array::findAll(self::$entityTypes, ['table' => $tableName]); | |
368 | return $matches ? $matches[0]['name'] : NULL; | |
17019d49 CW |
369 | } |
370 | ||
8246bca4 | 371 | /** |
372 | * Reinitialise cache. | |
8246bca4 | 373 | */ |
ec114c21 EM |
374 | public static function reinitializeCache() { |
375 | self::init(TRUE); | |
4ef04170 TO |
376 | } |
377 | ||
84a0493c TO |
378 | /** |
379 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
380 | * | |
381 | * @param string $dao | |
382 | * Ex: 'CRM_Core_DAO_Address'. | |
383 | * @param string $labelName | |
384 | * Ex: 'address'. | |
385 | * @param bool $prefix | |
386 | * @param array $foreignDAOs | |
387 | * @return array | |
388 | */ | |
389 | public static function getExports($dao, $labelName, $prefix, $foreignDAOs) { | |
390 | // Bug-level compatibility -- or sane behavior? | |
391 | $cacheKey = $dao . ':export'; | |
392 | // $cacheKey = $dao . ':' . ($prefix ? 'export-prefix' : 'export'); | |
393 | ||
394 | if (!isset(Civi::$statics[__CLASS__][$cacheKey])) { | |
be2fb01f | 395 | $exports = []; |
84a0493c TO |
396 | $fields = $dao::fields(); |
397 | ||
8246bca4 | 398 | foreach ($fields as $name => $field) { |
de6c59ca | 399 | if (!empty($field['export'])) { |
84a0493c TO |
400 | if ($prefix) { |
401 | $exports[$labelName] = & $fields[$name]; | |
8246bca4 | 402 | } |
403 | else { | |
84a0493c TO |
404 | $exports[$name] = & $fields[$name]; |
405 | } | |
406 | } | |
407 | } | |
408 | ||
409 | foreach ($foreignDAOs as $foreignDAO) { | |
410 | $exports = array_merge($exports, $foreignDAO::export(TRUE)); | |
411 | } | |
412 | ||
413 | Civi::$statics[__CLASS__][$cacheKey] = $exports; | |
414 | } | |
415 | return Civi::$statics[__CLASS__][$cacheKey]; | |
416 | } | |
417 | ||
418 | /** | |
419 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
420 | * | |
421 | * @param string $dao | |
422 | * Ex: 'CRM_Core_DAO_Address'. | |
423 | * @param string $labelName | |
424 | * Ex: 'address'. | |
425 | * @param bool $prefix | |
426 | * @param array $foreignDAOs | |
427 | * @return array | |
428 | */ | |
429 | public static function getImports($dao, $labelName, $prefix, $foreignDAOs) { | |
430 | // Bug-level compatibility -- or sane behavior? | |
431 | $cacheKey = $dao . ':import'; | |
432 | // $cacheKey = $dao . ':' . ($prefix ? 'import-prefix' : 'import'); | |
433 | ||
434 | if (!isset(Civi::$statics[__CLASS__][$cacheKey])) { | |
be2fb01f | 435 | $imports = []; |
84a0493c TO |
436 | $fields = $dao::fields(); |
437 | ||
8246bca4 | 438 | foreach ($fields as $name => $field) { |
de6c59ca | 439 | if (!empty($field['import'])) { |
84a0493c TO |
440 | if ($prefix) { |
441 | $imports[$labelName] = & $fields[$name]; | |
8246bca4 | 442 | } |
443 | else { | |
84a0493c TO |
444 | $imports[$name] = & $fields[$name]; |
445 | } | |
446 | } | |
447 | } | |
448 | ||
449 | foreach ($foreignDAOs as $foreignDAO) { | |
450 | $imports = array_merge($imports, $foreignDAO::import(TRUE)); | |
451 | } | |
452 | ||
453 | Civi::$statics[__CLASS__][$cacheKey] = $imports; | |
454 | } | |
455 | return Civi::$statics[__CLASS__][$cacheKey]; | |
456 | } | |
457 | ||
740dd877 TO |
458 | /** |
459 | * (Quasi-Private) Do not call externally. For use by DAOs. | |
460 | * | |
461 | * Apply any third-party alterations to the `fields()`. | |
462 | * | |
40ac12d3 CW |
463 | * TODO: This function should probably take briefName as the key instead of className |
464 | * because the latter is not always unique (e.g. virtual entities) | |
465 | * | |
740dd877 TO |
466 | * @param string $className |
467 | * @param string $event | |
468 | * @param mixed $values | |
469 | */ | |
470 | public static function invoke($className, $event, &$values) { | |
471 | self::init(); | |
40ac12d3 CW |
472 | $briefName = self::getBriefName($className); |
473 | if (isset(self::$entityTypes[$briefName][$event])) { | |
474 | foreach (self::$entityTypes[$briefName][$event] as $filter) { | |
be2fb01f | 475 | $args = [$className, &$values]; |
740dd877 TO |
476 | \Civi\Core\Resolver::singleton()->call($filter, $args); |
477 | } | |
478 | } | |
479 | } | |
480 | ||
4ef04170 | 481 | } |