diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Helpers/RelationshipNavigator.php	Fri Apr 11 20:50:20 2025 -0400
@@ -0,0 +1,205 @@
+<?php
+
+namespace Wizard\MagicForger\Helpers;
+
+use Illuminate\Support\Facades\DB;
+
+class RelationshipNavigator
+{
+    public static function handle()
+    {
+        $tables = DB::select('SHOW TABLES');
+        $tableNames = array_map(function ($table) {
+            return current((array) $table);
+        }, $tables);
+
+        foreach ($tableNames as $table) {
+            echo "Table: $table \n";
+
+            $relations = self::getRelations($table);
+            echo "Relationships: \n";
+            foreach ($relations as $relation => $related_tables) {
+                echo $relation.": \n";
+                foreach ($related_tables as $related_table) {
+                    echo "\t";
+                    foreach ($related_table as $key => $value) {
+                        echo "$key : ";
+                        if (is_array($value)) {
+                            foreach ($related_table as $key => $value) {
+                                echo "\n\t\t";
+                                echo "$key: $value";
+                            }
+                        } else {
+                            echo $value.' ';
+                        }
+                    }
+                    echo "\n";
+                }
+            }
+            echo "\n --- \n";
+        }
+    }
+
+    public static function getRelations($table)
+    {
+        $relations = [
+            'belongsTo' => [],
+            'hasMany' => [],
+            'hasManyThrough' => [],
+        ];
+
+        $foreignKeys = DB::select("SHOW KEYS FROM $table WHERE Key_name != 'PRIMARY'");
+        $referencedTables = self::getAllReferencedTables($table);
+
+        // BelongsTo Relationships
+        foreach ($foreignKeys as $fk) {
+            $column = $fk->Column_name;
+
+            // skip created and updated by
+            if (in_array($column, ['created_by', 'updated_by'])) {
+                continue;
+            }
+
+            $referencedTable = $referencedTables[$column] ?? null;
+
+            if ($referencedTable) {
+                $relations['belongsTo'][] = ['column' => $column, 'table' => $referencedTable->REFERENCED_TABLE_NAME];
+            }
+
+        }
+
+        // HasMany Relationships
+        if ($reverseRelation = self::findReverseRelation($table)) {
+            foreach ($reverseRelation as $relatedTable) {
+                $relations['hasMany'][] = $relatedTable;
+            }
+        }
+
+        // HasManyThrough Relationships
+        if ($hasManyThroughRelations = self::findHasManyThroughRelations($table)) {
+            foreach ($hasManyThroughRelations as $relatedTable) {
+                $relations['hasManyThrough'][] = $relatedTable;
+            }
+        }
+
+        return $relations;
+    }
+
+    public static function getAllReferencedTables($table)
+    {
+        $results = DB::select("
+            SELECT TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME 
+            FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
+            WHERE TABLE_SCHEMA = DATABASE() 
+            AND TABLE_NAME = '$table' 
+        ");
+
+        $tables = self::re_key_array($results, 'COLUMN_NAME');
+
+        return (count($tables) > 0) ? $tables : null;
+    }
+
+    public static function findReverseRelation($table)
+    {
+        $relations = DB::select("
+            SELECT TABLE_NAME, COLUMN_NAME
+            FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE 
+            WHERE TABLE_SCHEMA = DATABASE() 
+            AND REFERENCED_TABLE_NAME = '$table' 
+            AND REFERENCED_COLUMN_NAME = 'id'
+					  AND COLUMN_NAME != 'created_by'
+					  AND COLUMN_NAME != 'updated_by'
+        ");
+
+        $relatedTables = array_map(function ($rel) {
+            return ['table' => $rel->TABLE_NAME, 'column' => $rel->COLUMN_NAME];
+        }, $relations);
+
+        return $relatedTables ? $relatedTables : null;
+    }
+
+    public static function findHasManyThroughRelations($table)
+    {
+        $relations = [];
+
+        // Find potential intermediary tables
+        $intermediaryTables = self::findReverseRelation($table);
+
+        if (! is_null($intermediaryTables)) {
+
+            foreach ($intermediaryTables as $intermediary) {
+
+                if ($is_pivot = self::isPivot($intermediary['table'])) {
+                    $is_pivot = current($is_pivot);
+
+                    // reformat the table based on the current and external
+										$potential_tables = array_keys($is_pivot['tables']);
+										$external_table = $potential_tables[0] == $table ? $is_pivot['tables'][$potential_tables[1]] : $is_pivot['tables'][$potential_tables[0]];
+										$internal_table = $potential_tables[0] == $table ? $is_pivot['tables'][$potential_tables[0]] : $is_pivot['tables'][$potential_tables[1]];
+
+										$hasManyThrough = [
+											'table' => $external_table['table_name'],
+											'through' => [
+												'table' => $is_pivot['table'],
+												'external_column' => $external_table['column'],
+												'internal_column' => $internal_table['column']
+											]
+                    ];
+                    $relations[] = $hasManyThrough;
+                }
+            }
+
+        }
+
+        return $relations;
+    }
+
+    public static function isPivot($table)
+    {
+
+        $relations = [];
+        // TODO: alsot get the columns that are relevant
+        $pivotTables = DB::select("
+                SELECT TABLE_NAME, TABLE_COMMENT
+                FROM INFORMATION_SCHEMA.TABLES 
+                WHERE TABLE_SCHEMA = DATABASE() 
+                AND TABLE_NAME = '{$table}'
+								AND TABLE_COMMENT != ''
+            ");
+
+				if(!is_null($pivotTables) && count($pivotTables) > 0) {
+					$ref = current($pivotTables);
+
+					$pivots = json_decode(str_replace('PIVOT:', '', $ref->TABLE_COMMENT), true);
+
+					$tables = [];
+        	if (count($pivots) > 0) {
+							//re-key array
+        	    $references = self::getAllReferencedTables($table);
+							$references = self::re_key_array($references, 'REFERENCED_TABLE_NAME');
+
+							foreach($pivots as $key => $value) {
+								if($ref_data = ($references[$value] ?? null)) {
+									$tables[$value] = [
+										'table_name' => $value,
+										'column' => $ref_data->COLUMN_NAME
+									];
+								}
+							}
+        	}
+        	$relations[] = ['table' => $ref->TABLE_NAME, 'tables' => $tables];
+				}
+
+        return $relations != [] ? $relations : null;
+    }
+
+		public static function re_key_array($old_array, $key) {
+			$new_array = [];
+      if (count($old_array) > 0) {
+          foreach ($old_array as $array) {
+              $new_array[$array->$key] = $array;
+          }
+      }
+			return $new_array;
+		}
+}