4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
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 +--------------------------------------------------------------------+
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 namespace Civi\Api4\Service\Schema
;
22 use Civi\Api4\Service\Schema\Joinable\BridgeJoinable
;
26 const MAX_JOIN_DEPTH
= 3;
31 protected $tables = [];
34 * @param $baseTableName
35 * @param $targetTableAlias
37 * @return \Civi\Api4\Service\Schema\Joinable\Joinable[]
38 * Array of links to the target table, empty if no path found
40 public function getPath($baseTableName, $targetTableAlias) {
41 $table = $this->getTableByName($baseTableName);
48 $this->findPaths($table, $targetTableAlias, 1, $path);
50 foreach ($path as $index => $pathLink) {
51 if ($pathLink instanceof BridgeJoinable
) {
52 $start = array_slice($path, 0, $index);
53 $middle = [$pathLink->getMiddleLink()];
54 $end = array_slice($path, $index, count($path) - $index);
55 $path = array_merge($start, $middle, $end);
65 public function getTables() {
74 public function getTableByName($name) {
75 foreach ($this->tables
as $table) {
76 if ($table->getName() === $name) {
85 * Adds a table to the schema map if it has not already been added
91 public function addTable(Table
$table) {
92 if (!$this->getTableByName($table->getName())) {
93 $this->tables
[] = $table;
100 * @param array $tables
102 public function addTables(array $tables) {
103 foreach ($tables as $table) {
104 $this->addTable($table);
109 * Recursive function to traverse the schema looking for a path
111 * @param Table $table
112 * The current table to base fromm
113 * @param string $target
114 * The target joinable table alias
116 * The current level of recursion which reflects the number of joins needed
117 * @param \Civi\Api4\Service\Schema\Joinable\Joinable[] $path
118 * (By-reference) The possible paths to the target table
119 * @param \Civi\Api4\Service\Schema\Joinable\Joinable[] $currentPath
120 * For internal use only to track the path to reach the target table
122 private function findPaths(Table
$table, $target, $depth, &$path, $currentPath = []
124 static $visited = [];
131 $canBeShorter = empty($path) ||
count($currentPath) +
1 < count($path);
132 $tooFar = $depth > self
::MAX_JOIN_DEPTH
;
133 $beenHere = in_array($table->getName(), $visited);
135 if ($tooFar ||
$beenHere ||
!$canBeShorter) {
139 // prevent circular reference
140 $visited[] = $table->getName();
142 foreach ($table->getExternalLinks() as $link) {
143 if ($link->getAlias() === $target) {
144 $path = array_merge($currentPath, [$link]);
147 $linkTable = $this->getTableByName($link->getTargetTable());
149 $nextStep = array_merge($currentPath, [$link]);
150 $this->findPaths($linkTable, $target, $depth +
1, $path, $nextStep);