11<?php
2+
23namespace Mouf \Database \SchemaAnalyzer ;
4+
5+ use Doctrine \DBAL \Schema \ForeignKeyConstraint ;
36use Doctrine \DBAL \Schema \Schema ;
47use Doctrine \DBAL \Schema \Table ;
8+ use Fhaculty \Graph \Edge \Base ;
59use Fhaculty \Graph \Graph ;
10+ use Graphp \Algorithms \ShortestPath \Dijkstra ;
611
712/**
813 * This class can analyze a database model.
@@ -33,25 +38,28 @@ public function __construct(Schema $schema)
3338 * Detect all junctions tables in the schema.
3439 * A table is a junction table if:
3540 * - it has exactly 2 foreign keys
36- * - it has only 2 columns (or 3 columns if the third one is an autoincremented primary key)
41+ * - it has only 2 columns (or 3 columns if the third one is an autoincremented primary key).
3742 *
3843 *
3944 * @return Table[]
4045 */
41- public function detectJunctionTables () {
42- return array_filter ($ this ->schema ->getTables (), [$ this , "isJunctionTable " ]);
46+ public function detectJunctionTables ()
47+ {
48+ return array_filter ($ this ->schema ->getTables (), [$ this , 'isJunctionTable ' ]);
4349 }
4450
4551 /**
4652 * Returns true if $table is a junction table.
4753 * I.e:
4854 * - it must have exactly 2 foreign keys
49- * - it must have only 2 columns (or 3 columns if the third one is an autoincremented primary key)
55+ * - it must have only 2 columns (or 3 columns if the third one is an autoincremented primary key).
5056 *
5157 * @param Table $table
58+ *
5259 * @return bool
5360 */
54- private function isJunctionTable (Table $ table ) {
61+ private function isJunctionTable (Table $ table )
62+ {
5563 $ foreignKeys = $ table ->getForeignKeys ();
5664 if (count ($ foreignKeys ) != 2 ) {
5765 return false ;
@@ -99,15 +107,57 @@ private function isJunctionTable(Table $table) {
99107 /**
100108 * Get the shortest path between 2 tables.
101109 *
102- * @param $fromTable
103- * @param $toTable
110+ * @param string $fromTable
111+ * @param string $toTable
112+ *
113+ * @return ForeignKeyConstraint[]
104114 */
105- public function getShortestPath ($ fromTable , $ toTable ) {
115+ public function getShortestPath ($ fromTable , $ toTable )
116+ {
106117 $ graph = $ this ->buildSchemaGraph ();
107- // TODO
118+
119+ $ dijkstra = new Dijkstra ($ graph ->getVertex ($ fromTable ));
120+ $ walk = $ dijkstra ->getWalkTo ($ graph ->getVertex ($ toTable ));
121+
122+ $ foreignKeys = [];
123+
124+ $ currentTable = $ fromTable ;
125+
126+ foreach ($ walk ->getEdges () as $ edge ) {
127+ /* @var $edge Base */
128+
129+ if ($ fk = $ edge ->getAttribute ('fk ' )) {
130+ /* @var $fk ForeignKeyConstraint */
131+ $ foreignKeys [] = $ fk ;
132+ if ($ fk ->getForeignTableName () == $ currentTable ) {
133+ $ currentTable = $ fk ->getLocalTable ()->getName ();
134+ } else {
135+ $ currentTable = $ fk ->getForeignTableName ();
136+ }
137+ } elseif ($ junctionTable = $ edge ->getAttribute ('junction ' )) {
138+ /* @var $junctionTable Table */
139+ $ junctionFks = array_values ($ junctionTable ->getForeignKeys ());
140+ // We need to order the 2 FKs. The first one is the one that has a common point with the current table.
141+ $ fk = $ junctionFks [0 ];
142+ if ($ fk ->getForeignTableName () == $ currentTable ) {
143+ $ foreignKeys [] = $ fk ;
144+ $ foreignKeys [] = $ junctionFks [1 ];
145+ } else {
146+ $ foreignKeys [] = $ junctionFks [1 ];
147+ $ foreignKeys [] = $ fk ;
148+ }
149+ } else {
150+ // @codeCoverageIgnoreStart
151+ throw new SchemaAnalyzerException ('Unexpected edge. We should have a fk or a junction attribute. ' );
152+ // @codeCoverageIgnoreEnd
153+ }
154+ }
155+
156+ return $ foreignKeys ;
108157 }
109158
110- public function buildSchemaGraph () {
159+ private function buildSchemaGraph ()
160+ {
111161 $ graph = new Graph ();
112162
113163 // First, let's create all the vertex
@@ -121,7 +171,7 @@ public function buildSchemaGraph() {
121171 // Create an undirected edge, with weight = 1
122172 $ edge = $ graph ->getVertex ($ table ->getName ())->createEdge ($ graph ->getVertex ($ fk ->getForeignTableName ()));
123173 $ edge ->setWeight (self ::$ WEIGHT_FK );
124- $ edge ->getAttributeBag ()->setAttribute (" fk " , $ fk );
174+ $ edge ->getAttributeBag ()->setAttribute (' fk ' , $ fk );
125175 }
126176 }
127177
@@ -132,9 +182,9 @@ public function buildSchemaGraph() {
132182 $ tables [] = $ fk ->getForeignTableName ();
133183 }
134184
135- $ edge = $ graph ->getVertex ($ tables [0 ]-> getName ()) ->createEdge ($ graph ->getVertex ($ tables [1 ]-> getName () ));
185+ $ edge = $ graph ->getVertex ($ tables [0 ]) ->createEdge ($ graph ->getVertex ($ tables [1 ]));
136186 $ edge ->setWeight (self ::$ WEIGHT_JOINTURE_TABLE );
137- $ edge ->getAttributeBag ()->setAttribute (" junction " , $ junctionTable );
187+ $ edge ->getAttributeBag ()->setAttribute (' junction ' , $ junctionTable );
138188 }
139189
140190 return $ graph ;
0 commit comments