123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- <?php
-
- namespace Tqdev\PhpCrudApi\Record;
-
- use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
- use Tqdev\PhpCrudApi\Column\ReflectionService;
- use Tqdev\PhpCrudApi\Database\GenericDB;
- use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
- use Tqdev\PhpCrudApi\Record\Condition\ColumnCondition;
- use Tqdev\PhpCrudApi\Record\Condition\OrCondition;
-
- class RelationJoiner
- {
- private $reflection;
- private $ordering;
- private $columns;
-
- public function __construct(ReflectionService $reflection, ColumnIncluder $columns)
- {
- $this->reflection = $reflection;
- $this->ordering = new OrderingInfo();
- $this->columns = $columns;
- }
-
- public function addMandatoryColumns(ReflectedTable $table, array &$params) /*: void*/
- {
- if (!isset($params['join']) || !isset($params['include'])) {
- return;
- }
- $params['mandatory'] = array();
- foreach ($params['join'] as $tableNames) {
- $t1 = $table;
- foreach (explode(',', $tableNames) as $tableName) {
- if (!$this->reflection->hasTable($tableName)) {
- continue;
- }
- $t2 = $this->reflection->getTable($tableName);
- $fks1 = $t1->getFksTo($t2->getName());
- $t3 = $this->hasAndBelongsToMany($t1, $t2);
- if ($t3 != null || count($fks1) > 0) {
- $params['mandatory'][] = $t2->getName() . '.' . $t2->getPk()->getName();
- }
- foreach ($fks1 as $fk) {
- $params['mandatory'][] = $t1->getName() . '.' . $fk->getName();
- }
- $fks2 = $t2->getFksTo($t1->getName());
- if ($t3 != null || count($fks2) > 0) {
- $params['mandatory'][] = $t1->getName() . '.' . $t1->getPk()->getName();
- }
- foreach ($fks2 as $fk) {
- $params['mandatory'][] = $t2->getName() . '.' . $fk->getName();
- }
- $t1 = $t2;
- }
- }
- }
-
- private function getJoinsAsPathTree(array $params): PathTree
- {
- $joins = new PathTree();
- if (isset($params['join'])) {
- foreach ($params['join'] as $tableNames) {
- $path = array();
- foreach (explode(',', $tableNames) as $tableName) {
- $t = $this->reflection->getTable($tableName);
- if ($t != null) {
- $path[] = $t->getName();
- }
- }
- $joins->put($path, true);
- }
- }
- return $joins;
- }
-
- public function addJoins(ReflectedTable $table, array &$records, array $params, GenericDB $db) /*: void*/
- {
- $joins = $this->getJoinsAsPathTree($params);
- $this->addJoinsForTables($table, $joins, $records, $params, $db);
- }
-
- private function hasAndBelongsToMany(ReflectedTable $t1, ReflectedTable $t2) /*: ?ReflectedTable*/
- {
- foreach ($this->reflection->getTableNames() as $tableName) {
- $t3 = $this->reflection->getTable($tableName);
- if (count($t3->getFksTo($t1->getName())) > 0 && count($t3->getFksTo($t2->getName())) > 0) {
- return $t3;
- }
- }
- return null;
- }
-
- private function addJoinsForTables(ReflectedTable $t1, PathTree $joins, array &$records, array $params, GenericDB $db)
- {
- foreach ($joins->getKeys() as $t2Name) {
- $t2 = $this->reflection->getTable($t2Name);
-
- $belongsTo = count($t1->getFksTo($t2->getName())) > 0;
- $hasMany = count($t2->getFksTo($t1->getName())) > 0;
- if (!$belongsTo && !$hasMany) {
- $t3 = $this->hasAndBelongsToMany($t1, $t2);
- } else {
- $t3 = null;
- }
- $hasAndBelongsToMany = ($t3 != null);
-
- $newRecords = array();
- $fkValues = null;
- $pkValues = null;
- $habtmValues = null;
-
- if ($belongsTo) {
- $fkValues = $this->getFkEmptyValues($t1, $t2, $records);
- $this->addFkRecords($t2, $fkValues, $params, $db, $newRecords);
- }
- if ($hasMany) {
- $pkValues = $this->getPkEmptyValues($t1, $records);
- $this->addPkRecords($t1, $t2, $pkValues, $params, $db, $newRecords);
- }
- if ($hasAndBelongsToMany) {
- $habtmValues = $this->getHabtmEmptyValues($t1, $t2, $t3, $db, $records);
- $this->addFkRecords($t2, $habtmValues->fkValues, $params, $db, $newRecords);
- }
-
- $this->addJoinsForTables($t2, $joins->get($t2Name), $newRecords, $params, $db);
-
- if ($fkValues != null) {
- $this->fillFkValues($t2, $newRecords, $fkValues);
- $this->setFkValues($t1, $t2, $records, $fkValues);
- }
- if ($pkValues != null) {
- $this->fillPkValues($t1, $t2, $newRecords, $pkValues);
- $this->setPkValues($t1, $t2, $records, $pkValues);
- }
- if ($habtmValues != null) {
- $this->fillFkValues($t2, $newRecords, $habtmValues->fkValues);
- $this->setHabtmValues($t1, $t2, $records, $habtmValues);
- }
- }
- }
-
- private function getFkEmptyValues(ReflectedTable $t1, ReflectedTable $t2, array $records): array
- {
- $fkValues = array();
- $fks = $t1->getFksTo($t2->getName());
- foreach ($fks as $fk) {
- $fkName = $fk->getName();
- foreach ($records as $record) {
- if (isset($record[$fkName])) {
- $fkValue = $record[$fkName];
- $fkValues[$fkValue] = null;
- }
- }
- }
- return $fkValues;
- }
-
- private function addFkRecords(ReflectedTable $t2, array $fkValues, array $params, GenericDB $db, array &$records) /*: void*/
- {
- $columnNames = $this->columns->getNames($t2, false, $params);
- $fkIds = array_keys($fkValues);
-
- foreach ($db->selectMultiple($t2, $columnNames, $fkIds) as $record) {
- $records[] = $record;
- }
- }
-
- private function fillFkValues(ReflectedTable $t2, array $fkRecords, array &$fkValues) /*: void*/
- {
- $pkName = $t2->getPk()->getName();
- foreach ($fkRecords as $fkRecord) {
- $pkValue = $fkRecord[$pkName];
- $fkValues[$pkValue] = $fkRecord;
- }
- }
-
- private function setFkValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, array $fkValues) /*: void*/
- {
- $fks = $t1->getFksTo($t2->getName());
- foreach ($fks as $fk) {
- $fkName = $fk->getName();
- foreach ($records as $i => $record) {
- if (isset($record[$fkName])) {
- $key = $record[$fkName];
- $records[$i][$fkName] = $fkValues[$key];
- }
- }
- }
- }
-
- private function getPkEmptyValues(ReflectedTable $t1, array $records): array
- {
- $pkValues = array();
- $pkName = $t1->getPk()->getName();
- foreach ($records as $record) {
- $key = $record[$pkName];
- $pkValues[$key] = array();
- }
- return $pkValues;
- }
-
- private function addPkRecords(ReflectedTable $t1, ReflectedTable $t2, array $pkValues, array $params, GenericDB $db, array &$records) /*: void*/
- {
- $fks = $t2->getFksTo($t1->getName());
- $columnNames = $this->columns->getNames($t2, false, $params);
- $pkValueKeys = implode(',', array_keys($pkValues));
- $conditions = array();
- foreach ($fks as $fk) {
- $conditions[] = new ColumnCondition($fk, 'in', $pkValueKeys);
- }
- $condition = OrCondition::fromArray($conditions);
- $columnOrdering = array();
- $limit = VariableStore::get("joinLimits.maxRecords") ?: -1;
- if ($limit != -1) {
- $columnOrdering = $this->ordering->getDefaultColumnOrdering($t2);
- }
- foreach ($db->selectAll($t2, $columnNames, $condition, $columnOrdering, 0, $limit) as $record) {
- $records[] = $record;
- }
- }
-
- private function fillPkValues(ReflectedTable $t1, ReflectedTable $t2, array $pkRecords, array &$pkValues) /*: void*/
- {
- $fks = $t2->getFksTo($t1->getName());
- foreach ($fks as $fk) {
- $fkName = $fk->getName();
- foreach ($pkRecords as $pkRecord) {
- $key = $pkRecord[$fkName];
- if (isset($pkValues[$key])) {
- $pkValues[$key][] = $pkRecord;
- }
- }
- }
- }
-
- private function setPkValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, array $pkValues) /*: void*/
- {
- $pkName = $t1->getPk()->getName();
- $t2Name = $t2->getName();
-
- foreach ($records as $i => $record) {
- $key = $record[$pkName];
- $records[$i][$t2Name] = $pkValues[$key];
- }
- }
-
- private function getHabtmEmptyValues(ReflectedTable $t1, ReflectedTable $t2, ReflectedTable $t3, GenericDB $db, array $records): HabtmValues
- {
- $pkValues = $this->getPkEmptyValues($t1, $records);
- $fkValues = array();
-
- $fk1 = $t3->getFksTo($t1->getName())[0];
- $fk2 = $t3->getFksTo($t2->getName())[0];
-
- $fk1Name = $fk1->getName();
- $fk2Name = $fk2->getName();
-
- $columnNames = array($fk1Name, $fk2Name);
-
- $pkIds = implode(',', array_keys($pkValues));
- $condition = new ColumnCondition($t3->getColumn($fk1Name), 'in', $pkIds);
- $columnOrdering = array();
-
- $limit = VariableStore::get("joinLimits.maxRecords") ?: -1;
- if ($limit != -1) {
- $columnOrdering = $this->ordering->getDefaultColumnOrdering($t3);
- }
- $records = $db->selectAll($t3, $columnNames, $condition, $columnOrdering, 0, $limit);
- foreach ($records as $record) {
- $val1 = $record[$fk1Name];
- $val2 = $record[$fk2Name];
- $pkValues[$val1][] = $val2;
- $fkValues[$val2] = null;
- }
-
- return new HabtmValues($pkValues, $fkValues);
- }
-
- private function setHabtmValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, HabtmValues $habtmValues) /*: void*/
- {
- $pkName = $t1->getPk()->getName();
- $t2Name = $t2->getName();
- foreach ($records as $i => $record) {
- $key = $record[$pkName];
- $val = array();
- $fks = $habtmValues->pkValues[$key];
- foreach ($fks as $fk) {
- $val[] = $habtmValues->fkValues[$fk];
- }
- $records[$i][$t2Name] = $val;
- }
- }
- }
|