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