comparison src/Helpers/RelationshipNavigator.php @ 23:827efbf4d73c main-dev

Huge changes to the relationships for models and more complex
author Luka Sitas <sitas.luka.97@gmail.com>
date Fri, 11 Apr 2025 20:50:20 -0400
parents
children 31109c61ce02
comparison
equal deleted inserted replaced
22:ee8ef14e158d 23:827efbf4d73c
1 <?php
2
3 namespace Wizard\MagicForger\Helpers;
4
5 use Illuminate\Support\Facades\DB;
6
7 class RelationshipNavigator
8 {
9 public static function handle()
10 {
11 $tables = DB::select('SHOW TABLES');
12 $tableNames = array_map(function ($table) {
13 return current((array) $table);
14 }, $tables);
15
16 foreach ($tableNames as $table) {
17 echo "Table: $table \n";
18
19 $relations = self::getRelations($table);
20 echo "Relationships: \n";
21 foreach ($relations as $relation => $related_tables) {
22 echo $relation.": \n";
23 foreach ($related_tables as $related_table) {
24 echo "\t";
25 foreach ($related_table as $key => $value) {
26 echo "$key : ";
27 if (is_array($value)) {
28 foreach ($related_table as $key => $value) {
29 echo "\n\t\t";
30 echo "$key: $value";
31 }
32 } else {
33 echo $value.' ';
34 }
35 }
36 echo "\n";
37 }
38 }
39 echo "\n --- \n";
40 }
41 }
42
43 public static function getRelations($table)
44 {
45 $relations = [
46 'belongsTo' => [],
47 'hasMany' => [],
48 'hasManyThrough' => [],
49 ];
50
51 $foreignKeys = DB::select("SHOW KEYS FROM $table WHERE Key_name != 'PRIMARY'");
52 $referencedTables = self::getAllReferencedTables($table);
53
54 // BelongsTo Relationships
55 foreach ($foreignKeys as $fk) {
56 $column = $fk->Column_name;
57
58 // skip created and updated by
59 if (in_array($column, ['created_by', 'updated_by'])) {
60 continue;
61 }
62
63 $referencedTable = $referencedTables[$column] ?? null;
64
65 if ($referencedTable) {
66 $relations['belongsTo'][] = ['column' => $column, 'table' => $referencedTable->REFERENCED_TABLE_NAME];
67 }
68
69 }
70
71 // HasMany Relationships
72 if ($reverseRelation = self::findReverseRelation($table)) {
73 foreach ($reverseRelation as $relatedTable) {
74 $relations['hasMany'][] = $relatedTable;
75 }
76 }
77
78 // HasManyThrough Relationships
79 if ($hasManyThroughRelations = self::findHasManyThroughRelations($table)) {
80 foreach ($hasManyThroughRelations as $relatedTable) {
81 $relations['hasManyThrough'][] = $relatedTable;
82 }
83 }
84
85 return $relations;
86 }
87
88 public static function getAllReferencedTables($table)
89 {
90 $results = DB::select("
91 SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
92 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
93 WHERE TABLE_SCHEMA = DATABASE()
94 AND TABLE_NAME = '$table'
95 ");
96
97 $tables = self::re_key_array($results, 'COLUMN_NAME');
98
99 return (count($tables) > 0) ? $tables : null;
100 }
101
102 public static function findReverseRelation($table)
103 {
104 $relations = DB::select("
105 SELECT TABLE_NAME, COLUMN_NAME
106 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
107 WHERE TABLE_SCHEMA = DATABASE()
108 AND REFERENCED_TABLE_NAME = '$table'
109 AND REFERENCED_COLUMN_NAME = 'id'
110 AND COLUMN_NAME != 'created_by'
111 AND COLUMN_NAME != 'updated_by'
112 ");
113
114 $relatedTables = array_map(function ($rel) {
115 return ['table' => $rel->TABLE_NAME, 'column' => $rel->COLUMN_NAME];
116 }, $relations);
117
118 return $relatedTables ? $relatedTables : null;
119 }
120
121 public static function findHasManyThroughRelations($table)
122 {
123 $relations = [];
124
125 // Find potential intermediary tables
126 $intermediaryTables = self::findReverseRelation($table);
127
128 if (! is_null($intermediaryTables)) {
129
130 foreach ($intermediaryTables as $intermediary) {
131
132 if ($is_pivot = self::isPivot($intermediary['table'])) {
133 $is_pivot = current($is_pivot);
134
135 // reformat the table based on the current and external
136 $potential_tables = array_keys($is_pivot['tables']);
137 $external_table = $potential_tables[0] == $table ? $is_pivot['tables'][$potential_tables[1]] : $is_pivot['tables'][$potential_tables[0]];
138 $internal_table = $potential_tables[0] == $table ? $is_pivot['tables'][$potential_tables[0]] : $is_pivot['tables'][$potential_tables[1]];
139
140 $hasManyThrough = [
141 'table' => $external_table['table_name'],
142 'through' => [
143 'table' => $is_pivot['table'],
144 'external_column' => $external_table['column'],
145 'internal_column' => $internal_table['column']
146 ]
147 ];
148 $relations[] = $hasManyThrough;
149 }
150 }
151
152 }
153
154 return $relations;
155 }
156
157 public static function isPivot($table)
158 {
159
160 $relations = [];
161 // TODO: alsot get the columns that are relevant
162 $pivotTables = DB::select("
163 SELECT TABLE_NAME, TABLE_COMMENT
164 FROM INFORMATION_SCHEMA.TABLES
165 WHERE TABLE_SCHEMA = DATABASE()
166 AND TABLE_NAME = '{$table}'
167 AND TABLE_COMMENT != ''
168 ");
169
170 if(!is_null($pivotTables) && count($pivotTables) > 0) {
171 $ref = current($pivotTables);
172
173 $pivots = json_decode(str_replace('PIVOT:', '', $ref->TABLE_COMMENT), true);
174
175 $tables = [];
176 if (count($pivots) > 0) {
177 //re-key array
178 $references = self::getAllReferencedTables($table);
179 $references = self::re_key_array($references, 'REFERENCED_TABLE_NAME');
180
181 foreach($pivots as $key => $value) {
182 if($ref_data = ($references[$value] ?? null)) {
183 $tables[$value] = [
184 'table_name' => $value,
185 'column' => $ref_data->COLUMN_NAME
186 ];
187 }
188 }
189 }
190 $relations[] = ['table' => $ref->TABLE_NAME, 'tables' => $tables];
191 }
192
193 return $relations != [] ? $relations : null;
194 }
195
196 public static function re_key_array($old_array, $key) {
197 $new_array = [];
198 if (count($old_array) > 0) {
199 foreach ($old_array as $array) {
200 $new_array[$array->$key] = $array;
201 }
202 }
203 return $new_array;
204 }
205 }