dev/core#3719 fix inconistent handling of job_type:label
[civicrm-core.git] / CRM / Core / DAO / AllCoreTables.php
CommitLineData
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 */
18class 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 *
3fd42bb5 191 * @return string
3ab49d54
CW
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',
74c303ca 221 'im' => 'Im',
dab64f21 222 'pcp' => 'Pcp',
74c303ca 223 ];
dab64f21
CW
224 if ($legacyV3 && isset($map[strtolower($name)])) {
225 return $map[strtolower($name)];
74c303ca 226 }
227
228 $fragments = explode('_', $name);
229 foreach ($fragments as & $fragment) {
230 $fragment = ucfirst($fragment);
231 // Special case: UFGroup, UFJoin, UFMatch, UFField (if passed in without underscores)
232 if (strpos($fragment, 'Uf') === 0 && strlen($name) > 2) {
233 $fragment = 'UF' . ucfirst(substr($fragment, 2));
234 }
235 }
dab64f21
CW
236 // Exceptions to CamelCase: UFGroup, UFJoin, UFMatch, UFField, ACL, IM, PCP
237 $exceptions = ['Uf', 'Acl', 'Im', 'Pcp'];
238 if (in_array($fragments[0], $exceptions)) {
239 $fragments[0] = strtoupper($fragments[0]);
74c303ca 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}