Merge pull request #18576 from eileenmcnaughton/iso
[civicrm-core.git] / Civi / Api4 / Service / Schema / Joiner.php
CommitLineData
19b53e5b
C
1<?php
2
380f3545
TO
3/*
4 +--------------------------------------------------------------------+
41498ac5 5 | Copyright CiviCRM LLC. All rights reserved. |
380f3545 6 | |
41498ac5
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 |
380f3545
TO
10 +--------------------------------------------------------------------+
11 */
12
13/**
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
380f3545
TO
17 */
18
19
19b53e5b
C
20namespace Civi\Api4\Service\Schema;
21
22use Civi\Api4\Query\Api4SelectQuery;
23
24class Joiner {
25 /**
26 * @var SchemaMap
27 */
28 protected $schemaMap;
29
30 /**
31 * @var \Civi\Api4\Service\Schema\Joinable\Joinable[][]
32 */
33 protected $cache = [];
34
35 /**
36 * @param SchemaMap $schemaMap
37 */
38 public function __construct(SchemaMap $schemaMap) {
39 $this->schemaMap = $schemaMap;
40 }
41
42 /**
43 * @param \Civi\Api4\Query\Api4SelectQuery $query
44 * The query object to do the joins on
45 * @param string $joinPath
46 * A path of aliases in dot notation, e.g. contact.phone
47 * @param string $side
48 * Can be LEFT or INNER
49 *
50 * @throws \Exception
51 * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
52 * The path used to make the join
53 */
54 public function join(Api4SelectQuery $query, $joinPath, $side = 'LEFT') {
55 $fullPath = $this->getPath($query->getFrom(), $joinPath);
56 $baseTable = $query::MAIN_TABLE_ALIAS;
57
58 foreach ($fullPath as $link) {
59 $target = $link->getTargetTable();
60 $alias = $link->getAlias();
77fdabbf 61 $bao = \CRM_Core_DAO_AllCoreTables::getBAOClassName(\CRM_Core_DAO_AllCoreTables::getClassForTable($target));
e3d9e6ac
CW
62 $conditions = $link->getConditionsForJoin($baseTable);
63 // Custom fields do not have a bao, and currently do not have field-specific ACLs
64 if ($bao) {
65 $conditions = array_merge($conditions, $query->getAclClause($alias, $bao, explode('.', $joinPath)));
66 }
19b53e5b
C
67
68 $query->join($side, $target, $alias, $conditions);
19b53e5b
C
69
70 $baseTable = $link->getAlias();
71 }
72
73 return $fullPath;
74 }
75
76 /**
334bebdd
CW
77 * Determines if path string points to a simple n-1 join that can be automatically added
78 *
79 * @param string $baseTable
19b53e5b
C
80 * @param $joinPath
81 *
82 * @return bool
83 */
334bebdd
CW
84 public function canAutoJoin($baseTable, $joinPath) {
85 try {
86 $path = $this->getPath($baseTable, $joinPath);
87 foreach ($path as $joinable) {
88 if ($joinable->getJoinType() === $joinable::JOIN_TYPE_ONE_TO_MANY) {
89 return FALSE;
90 }
91 }
92 return TRUE;
93 }
94 catch (\Exception $e) {
95 return FALSE;
96 }
19b53e5b
C
97 }
98
99 /**
100 * @param string $baseTable
101 * @param string $joinPath
102 *
334bebdd 103 * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
19b53e5b
C
104 * @throws \Exception
105 */
106 protected function getPath($baseTable, $joinPath) {
107 $cacheKey = sprintf('%s.%s', $baseTable, $joinPath);
108 if (!isset($this->cache[$cacheKey])) {
109 $stack = explode('.', $joinPath);
110 $fullPath = [];
111
112 foreach ($stack as $key => $targetAlias) {
113 $links = $this->schemaMap->getPath($baseTable, $targetAlias);
114
115 if (empty($links)) {
116 throw new \Exception(sprintf('Cannot join %s to %s', $baseTable, $targetAlias));
117 }
118 else {
119 $fullPath = array_merge($fullPath, $links);
120 $lastLink = end($links);
121 $baseTable = $lastLink->getTargetTable();
122 }
123 }
124
125 $this->cache[$cacheKey] = $fullPath;
126 }
127
128 return $this->cache[$cacheKey];
129 }
130
131}