1
1
<?php
2
+
2
3
namespace Mouf \Database \SchemaAnalyzer ;
4
+
5
+ use Doctrine \DBAL \Schema \ForeignKeyConstraint ;
3
6
use Doctrine \DBAL \Schema \Schema ;
4
7
use Doctrine \DBAL \Schema \Table ;
8
+ use Fhaculty \Graph \Edge \Base ;
5
9
use Fhaculty \Graph \Graph ;
10
+ use Graphp \Algorithms \ShortestPath \Dijkstra ;
6
11
7
12
/**
8
13
* This class can analyze a database model.
@@ -33,25 +38,28 @@ public function __construct(Schema $schema)
33
38
* Detect all junctions tables in the schema.
34
39
* A table is a junction table if:
35
40
* - 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).
37
42
*
38
43
*
39
44
* @return Table[]
40
45
*/
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 ' ]);
43
49
}
44
50
45
51
/**
46
52
* Returns true if $table is a junction table.
47
53
* I.e:
48
54
* - 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).
50
56
*
51
57
* @param Table $table
58
+ *
52
59
* @return bool
53
60
*/
54
- private function isJunctionTable (Table $ table ) {
61
+ private function isJunctionTable (Table $ table )
62
+ {
55
63
$ foreignKeys = $ table ->getForeignKeys ();
56
64
if (count ($ foreignKeys ) != 2 ) {
57
65
return false ;
@@ -99,15 +107,57 @@ private function isJunctionTable(Table $table) {
99
107
/**
100
108
* Get the shortest path between 2 tables.
101
109
*
102
- * @param $fromTable
103
- * @param $toTable
110
+ * @param string $fromTable
111
+ * @param string $toTable
112
+ *
113
+ * @return ForeignKeyConstraint[]
104
114
*/
105
- public function getShortestPath ($ fromTable , $ toTable ) {
115
+ public function getShortestPath ($ fromTable , $ toTable )
116
+ {
106
117
$ 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 ;
108
157
}
109
158
110
- public function buildSchemaGraph () {
159
+ private function buildSchemaGraph ()
160
+ {
111
161
$ graph = new Graph ();
112
162
113
163
// First, let's create all the vertex
@@ -121,7 +171,7 @@ public function buildSchemaGraph() {
121
171
// Create an undirected edge, with weight = 1
122
172
$ edge = $ graph ->getVertex ($ table ->getName ())->createEdge ($ graph ->getVertex ($ fk ->getForeignTableName ()));
123
173
$ edge ->setWeight (self ::$ WEIGHT_FK );
124
- $ edge ->getAttributeBag ()->setAttribute (" fk " , $ fk );
174
+ $ edge ->getAttributeBag ()->setAttribute (' fk ' , $ fk );
125
175
}
126
176
}
127
177
@@ -132,9 +182,9 @@ public function buildSchemaGraph() {
132
182
$ tables [] = $ fk ->getForeignTableName ();
133
183
}
134
184
135
- $ edge = $ graph ->getVertex ($ tables [0 ]-> getName ()) ->createEdge ($ graph ->getVertex ($ tables [1 ]-> getName () ));
185
+ $ edge = $ graph ->getVertex ($ tables [0 ]) ->createEdge ($ graph ->getVertex ($ tables [1 ]));
136
186
$ edge ->setWeight (self ::$ WEIGHT_JOINTURE_TABLE );
137
- $ edge ->getAttributeBag ()->setAttribute (" junction " , $ junctionTable );
187
+ $ edge ->getAttributeBag ()->setAttribute (' junction ' , $ junctionTable );
138
188
}
139
189
140
190
return $ graph ;
0 commit comments