@@ -58,14 +58,75 @@ From this shortest path, it can build the correct JOINs.
5858
5959Now that you understand the concept, you should understand the power and the limits of MagicJoin.
6060
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.
6264- MagicJoin assumes you are looking for the shortest path between 2 tables
6365 - This is 80% of the case true
6466 - If you are in the remaining 20%, do not use MagicJoin
6567
6668<div class =" alert alert-warning " >MagicJoin is meant to be used on the 80% of the cases where writing joins is trivial
6769and boring. If you have complex joins, do not try to use MagicJoin. Go back to pure SQL instead.</div >
6870
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+
69130###With great power comes great responsibility
70131
71132MagicJoin is a powerful feature because it can ** guess** what you want to do. Please be wise while using it.
0 commit comments