api de gestion de ticket, basé sur php-crud-api. Le but est de décorrélé les outils de gestion des données, afin
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RelationJoiner.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. namespace Tqdev\PhpCrudApi\Record;
  3. use Tqdev\PhpCrudApi\Column\Reflection\ReflectedTable;
  4. use Tqdev\PhpCrudApi\Column\ReflectionService;
  5. use Tqdev\PhpCrudApi\Database\GenericDB;
  6. use Tqdev\PhpCrudApi\Middleware\Communication\VariableStore;
  7. use Tqdev\PhpCrudApi\Record\Condition\ColumnCondition;
  8. use Tqdev\PhpCrudApi\Record\Condition\OrCondition;
  9. class RelationJoiner
  10. {
  11. private $reflection;
  12. private $ordering;
  13. private $columns;
  14. public function __construct(ReflectionService $reflection, ColumnIncluder $columns)
  15. {
  16. $this->reflection = $reflection;
  17. $this->ordering = new OrderingInfo();
  18. $this->columns = $columns;
  19. }
  20. public function addMandatoryColumns(ReflectedTable $table, array &$params) /*: void*/
  21. {
  22. if (!isset($params['join']) || !isset($params['include'])) {
  23. return;
  24. }
  25. $params['mandatory'] = array();
  26. foreach ($params['join'] as $tableNames) {
  27. $t1 = $table;
  28. foreach (explode(',', $tableNames) as $tableName) {
  29. if (!$this->reflection->hasTable($tableName)) {
  30. continue;
  31. }
  32. $t2 = $this->reflection->getTable($tableName);
  33. $fks1 = $t1->getFksTo($t2->getName());
  34. $t3 = $this->hasAndBelongsToMany($t1, $t2);
  35. if ($t3 != null || count($fks1) > 0) {
  36. $params['mandatory'][] = $t2->getName() . '.' . $t2->getPk()->getName();
  37. }
  38. foreach ($fks1 as $fk) {
  39. $params['mandatory'][] = $t1->getName() . '.' . $fk->getName();
  40. }
  41. $fks2 = $t2->getFksTo($t1->getName());
  42. if ($t3 != null || count($fks2) > 0) {
  43. $params['mandatory'][] = $t1->getName() . '.' . $t1->getPk()->getName();
  44. }
  45. foreach ($fks2 as $fk) {
  46. $params['mandatory'][] = $t2->getName() . '.' . $fk->getName();
  47. }
  48. $t1 = $t2;
  49. }
  50. }
  51. }
  52. private function getJoinsAsPathTree(array $params): PathTree
  53. {
  54. $joins = new PathTree();
  55. if (isset($params['join'])) {
  56. foreach ($params['join'] as $tableNames) {
  57. $path = array();
  58. foreach (explode(',', $tableNames) as $tableName) {
  59. $t = $this->reflection->getTable($tableName);
  60. if ($t != null) {
  61. $path[] = $t->getName();
  62. }
  63. }
  64. $joins->put($path, true);
  65. }
  66. }
  67. return $joins;
  68. }
  69. public function addJoins(ReflectedTable $table, array &$records, array $params, GenericDB $db) /*: void*/
  70. {
  71. $joins = $this->getJoinsAsPathTree($params);
  72. $this->addJoinsForTables($table, $joins, $records, $params, $db);
  73. }
  74. private function hasAndBelongsToMany(ReflectedTable $t1, ReflectedTable $t2) /*: ?ReflectedTable*/
  75. {
  76. foreach ($this->reflection->getTableNames() as $tableName) {
  77. $t3 = $this->reflection->getTable($tableName);
  78. if (count($t3->getFksTo($t1->getName())) > 0 && count($t3->getFksTo($t2->getName())) > 0) {
  79. return $t3;
  80. }
  81. }
  82. return null;
  83. }
  84. private function addJoinsForTables(ReflectedTable $t1, PathTree $joins, array &$records, array $params, GenericDB $db)
  85. {
  86. foreach ($joins->getKeys() as $t2Name) {
  87. $t2 = $this->reflection->getTable($t2Name);
  88. $belongsTo = count($t1->getFksTo($t2->getName())) > 0;
  89. $hasMany = count($t2->getFksTo($t1->getName())) > 0;
  90. if (!$belongsTo && !$hasMany) {
  91. $t3 = $this->hasAndBelongsToMany($t1, $t2);
  92. } else {
  93. $t3 = null;
  94. }
  95. $hasAndBelongsToMany = ($t3 != null);
  96. $newRecords = array();
  97. $fkValues = null;
  98. $pkValues = null;
  99. $habtmValues = null;
  100. if ($belongsTo) {
  101. $fkValues = $this->getFkEmptyValues($t1, $t2, $records);
  102. $this->addFkRecords($t2, $fkValues, $params, $db, $newRecords);
  103. }
  104. if ($hasMany) {
  105. $pkValues = $this->getPkEmptyValues($t1, $records);
  106. $this->addPkRecords($t1, $t2, $pkValues, $params, $db, $newRecords);
  107. }
  108. if ($hasAndBelongsToMany) {
  109. $habtmValues = $this->getHabtmEmptyValues($t1, $t2, $t3, $db, $records);
  110. $this->addFkRecords($t2, $habtmValues->fkValues, $params, $db, $newRecords);
  111. }
  112. $this->addJoinsForTables($t2, $joins->get($t2Name), $newRecords, $params, $db);
  113. if ($fkValues != null) {
  114. $this->fillFkValues($t2, $newRecords, $fkValues);
  115. $this->setFkValues($t1, $t2, $records, $fkValues);
  116. }
  117. if ($pkValues != null) {
  118. $this->fillPkValues($t1, $t2, $newRecords, $pkValues);
  119. $this->setPkValues($t1, $t2, $records, $pkValues);
  120. }
  121. if ($habtmValues != null) {
  122. $this->fillFkValues($t2, $newRecords, $habtmValues->fkValues);
  123. $this->setHabtmValues($t1, $t2, $records, $habtmValues);
  124. }
  125. }
  126. }
  127. private function getFkEmptyValues(ReflectedTable $t1, ReflectedTable $t2, array $records): array
  128. {
  129. $fkValues = array();
  130. $fks = $t1->getFksTo($t2->getName());
  131. foreach ($fks as $fk) {
  132. $fkName = $fk->getName();
  133. foreach ($records as $record) {
  134. if (isset($record[$fkName])) {
  135. $fkValue = $record[$fkName];
  136. $fkValues[$fkValue] = null;
  137. }
  138. }
  139. }
  140. return $fkValues;
  141. }
  142. private function addFkRecords(ReflectedTable $t2, array $fkValues, array $params, GenericDB $db, array &$records) /*: void*/
  143. {
  144. $columnNames = $this->columns->getNames($t2, false, $params);
  145. $fkIds = array_keys($fkValues);
  146. foreach ($db->selectMultiple($t2, $columnNames, $fkIds) as $record) {
  147. $records[] = $record;
  148. }
  149. }
  150. private function fillFkValues(ReflectedTable $t2, array $fkRecords, array &$fkValues) /*: void*/
  151. {
  152. $pkName = $t2->getPk()->getName();
  153. foreach ($fkRecords as $fkRecord) {
  154. $pkValue = $fkRecord[$pkName];
  155. $fkValues[$pkValue] = $fkRecord;
  156. }
  157. }
  158. private function setFkValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, array $fkValues) /*: void*/
  159. {
  160. $fks = $t1->getFksTo($t2->getName());
  161. foreach ($fks as $fk) {
  162. $fkName = $fk->getName();
  163. foreach ($records as $i => $record) {
  164. if (isset($record[$fkName])) {
  165. $key = $record[$fkName];
  166. $records[$i][$fkName] = $fkValues[$key];
  167. }
  168. }
  169. }
  170. }
  171. private function getPkEmptyValues(ReflectedTable $t1, array $records): array
  172. {
  173. $pkValues = array();
  174. $pkName = $t1->getPk()->getName();
  175. foreach ($records as $record) {
  176. $key = $record[$pkName];
  177. $pkValues[$key] = array();
  178. }
  179. return $pkValues;
  180. }
  181. private function addPkRecords(ReflectedTable $t1, ReflectedTable $t2, array $pkValues, array $params, GenericDB $db, array &$records) /*: void*/
  182. {
  183. $fks = $t2->getFksTo($t1->getName());
  184. $columnNames = $this->columns->getNames($t2, false, $params);
  185. $pkValueKeys = implode(',', array_keys($pkValues));
  186. $conditions = array();
  187. foreach ($fks as $fk) {
  188. $conditions[] = new ColumnCondition($fk, 'in', $pkValueKeys);
  189. }
  190. $condition = OrCondition::fromArray($conditions);
  191. $columnOrdering = array();
  192. $limit = VariableStore::get("joinLimits.maxRecords") ?: -1;
  193. if ($limit != -1) {
  194. $columnOrdering = $this->ordering->getDefaultColumnOrdering($t2);
  195. }
  196. foreach ($db->selectAll($t2, $columnNames, $condition, $columnOrdering, 0, $limit) as $record) {
  197. $records[] = $record;
  198. }
  199. }
  200. private function fillPkValues(ReflectedTable $t1, ReflectedTable $t2, array $pkRecords, array &$pkValues) /*: void*/
  201. {
  202. $fks = $t2->getFksTo($t1->getName());
  203. foreach ($fks as $fk) {
  204. $fkName = $fk->getName();
  205. foreach ($pkRecords as $pkRecord) {
  206. $key = $pkRecord[$fkName];
  207. if (isset($pkValues[$key])) {
  208. $pkValues[$key][] = $pkRecord;
  209. }
  210. }
  211. }
  212. }
  213. private function setPkValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, array $pkValues) /*: void*/
  214. {
  215. $pkName = $t1->getPk()->getName();
  216. $t2Name = $t2->getName();
  217. foreach ($records as $i => $record) {
  218. $key = $record[$pkName];
  219. $records[$i][$t2Name] = $pkValues[$key];
  220. }
  221. }
  222. private function getHabtmEmptyValues(ReflectedTable $t1, ReflectedTable $t2, ReflectedTable $t3, GenericDB $db, array $records): HabtmValues
  223. {
  224. $pkValues = $this->getPkEmptyValues($t1, $records);
  225. $fkValues = array();
  226. $fk1 = $t3->getFksTo($t1->getName())[0];
  227. $fk2 = $t3->getFksTo($t2->getName())[0];
  228. $fk1Name = $fk1->getName();
  229. $fk2Name = $fk2->getName();
  230. $columnNames = array($fk1Name, $fk2Name);
  231. $pkIds = implode(',', array_keys($pkValues));
  232. $condition = new ColumnCondition($t3->getColumn($fk1Name), 'in', $pkIds);
  233. $columnOrdering = array();
  234. $limit = VariableStore::get("joinLimits.maxRecords") ?: -1;
  235. if ($limit != -1) {
  236. $columnOrdering = $this->ordering->getDefaultColumnOrdering($t3);
  237. }
  238. $records = $db->selectAll($t3, $columnNames, $condition, $columnOrdering, 0, $limit);
  239. foreach ($records as $record) {
  240. $val1 = $record[$fk1Name];
  241. $val2 = $record[$fk2Name];
  242. $pkValues[$val1][] = $val2;
  243. $fkValues[$val2] = null;
  244. }
  245. return new HabtmValues($pkValues, $fkValues);
  246. }
  247. private function setHabtmValues(ReflectedTable $t1, ReflectedTable $t2, array &$records, HabtmValues $habtmValues) /*: void*/
  248. {
  249. $pkName = $t1->getPk()->getName();
  250. $t2Name = $t2->getName();
  251. foreach ($records as $i => $record) {
  252. $key = $record[$pkName];
  253. $val = array();
  254. $fks = $habtmValues->pkValues[$key];
  255. foreach ($fks as $fk) {
  256. $val[] = $habtmValues->fkValues[$fk];
  257. }
  258. $records[$i][$t2Name] = $val;
  259. }
  260. }
  261. }