@@ -58,14 +58,75 @@ From this shortest path, it can build the correct JOINs.
58
58
59
59
Now that you understand the concept, you should understand the power and the limits of MagicJoin.
60
60
61
- - MagicJoin ** cannot generate queries with recursive relationships** (like a parent/child relationship)
61
+ - MagicJoin ** cannot generate queries with recursive relationships** (like a parent/child relationship). This is
62
+ because parent/child relationships are represented by loops in the dependency graph, and a loop is never the
63
+ shortest path.
62
64
- MagicJoin assumes you are looking for the shortest path between 2 tables
63
65
- This is 80% of the case true
64
66
- If you are in the remaining 20%, do not use MagicJoin
65
67
66
68
<div class =" alert alert-warning " >MagicJoin is meant to be used on the 80% of the cases where writing joins is trivial
67
69
and boring. If you have complex joins, do not try to use MagicJoin. Go back to pure SQL instead.</div >
68
70
71
+ If you are looking for details on how this shortest path is computed, have a look at
72
+ [ mouf/schema-analyzer] ( http://mouf-php.com/packages/mouf/schema-analyzer/README.md ) which is the package in charge
73
+ of computing that shortest path.
74
+
75
+ ###Ambiguity exceptions and fine-tuning
76
+
77
+ Sometimes, there can be 2 possible paths that link 2 tables and that are equally short.
78
+ In this case, rather than choosing a path at random, MagicQuery will let you know there is a problem by issuing
79
+ a ` ShortestPathAmbiguityException ` .
80
+
81
+ ![ Ambiguity in paths] ( images/shortest_path.png )
82
+
83
+ Let's have a look at the example above. We have * products* that are part of a * store* . Stores are part of a * district* .
84
+ Both * districts* and * products* have a * status* .
85
+
86
+ Now, let's have a look at this query:
87
+
88
+ ``` sql
89
+ SELECT products.* FROM MAGICJOIN(products) WHERE district .name = ' NY' ;
90
+ ```
91
+
92
+ Very obviously, we want all the products from the district. Bu for MagicJoin, this is far from obvious.
93
+ There are really 2 paths from products to district. One is going through the "store" table and one through the "status"
94
+ table. Those paths are equally short.
95
+
96
+ We need to * hint* MagicJoin into understanding that the "status" table is irrelevant.
97
+
98
+ To do this, we must first provide to MagicJoin an instance of ` SchemaAnalyzer ` . [ ` SchemaAnalyzer ` is the underlying
99
+ package that is in charge of computing the shortest path.] ( http://mouf-php.com/packages/mouf/schema-analyzer/README.md )
100
+
101
+ ``` php
102
+ use Mouf\Database\MagicQuery;
103
+ use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
104
+
105
+ // $conn is a Doctrine DBAL connection.
106
+ // $cache is a Doctrine Cache
107
+
108
+ // Get a SchemaAnalyzer object.
109
+ $schemaAnalyzer = new SchemaAnalyzer($conn->getSchemaManager(), $cache, "some_unique_key");
110
+
111
+ // On SchemaAnalyzer, let's modify the cost of the "status" table to make it unlikely to be used:
112
+ $schemaAnalyzer->setTableCostModifier("status", SchemaAnalyzer::WEIGHT_IRRELEVANT);
113
+
114
+ // Get a MagicQuery object.
115
+ $magicQuery = new MagicQuery($conn, $cache, $schemaAnalyzer);
116
+
117
+ $completeSql = $magicQuery->build("SELECT products.* FROM MAGICJOIN(products) WHERE district.name = 'NY'");
118
+ ```
119
+
120
+ The call to ` setTableCostModifier ` above will explain to MagicJoin that the * status* table is mostly irrelevant,
121
+ and that it should most of the time be avoided.
122
+
123
+ You can also set a * cost* on a single foreign key rather than on a table. For instance:
124
+
125
+ ``` php
126
+ // Avoid using the "status_id" foreign_key from the "products" table.
127
+ $schemaAnalyzer->setForeignKeyCost("products", "status_id", SchemaAnalyzer::WEIGHT_IRRELEVANT);
128
+ ```
129
+
69
130
###With great power comes great responsibility
70
131
71
132
MagicJoin is a powerful feature because it can ** guess** what you want to do. Please be wise while using it.
0 commit comments