44
55use Closure ;
66use Illuminate \Database \Eloquent \Model ;
7+ use Illuminate \Database \Eloquent \Relations \BelongsToMany ;
8+ use Illuminate \Database \Eloquent \Relations \HasManyThrough ;
79use Illuminate \Database \Query \Builder as QueryBuilder ;
810use Illuminate \Support \Str ;
911use KirschbaumDevelopment \EloquentJoins \PowerJoinClause ;
@@ -17,6 +19,13 @@ class JoinRelationship
1719 */
1820 public static $ joinRelationshipCache = [];
1921
22+ /**
23+ * Cache to not join the same relationship twice.
24+ *
25+ * @var array
26+ */
27+ public static $ powerJoinAliasesCache = [];
28+
2029 /**
2130 * Join method map.
2231 */
@@ -31,30 +40,62 @@ class JoinRelationship
3140 */
3241 public function joinRelationship ()
3342 {
34- return function ($ relationName , $ callback = null , $ joinType = 'join ' ) {
43+ return function ($ relationName , $ callback = null , $ joinType = 'join ' , $ useAlias = false ) {
3544 $ joinType = JoinRelationship::$ joinMethodsMap [$ joinType ] ?? $ joinType ;
3645
3746 if (is_null ($ this ->getSelect ())) {
3847 $ this ->select (sprintf ('%s.* ' , $ this ->getModel ()->getTable ()));
3948 }
4049
4150 if (Str::contains ($ relationName , '. ' )) {
42- return $ this ->joinNestedRelationship ($ relationName , $ callback , $ joinType );
51+ return $ this ->joinNestedRelationship ($ relationName , $ callback , $ joinType, $ useAlias );
4352 }
4453
4554 if ($ this ->relationshipAlreadyJoined ($ relationName )) {
4655 return $ this ;
4756 }
4857
4958 $ relation = $ this ->getModel ()->{$ relationName }();
50- $ relation ->performJoinForEloquentPowerJoins ($ this , $ joinType , $ callback );
59+ $ alias = $ useAlias ? $ this ->generateAliasForRelationship ($ relation , $ relationName ) : null ;
60+ $ relation ->performJoinForEloquentPowerJoins ($ this , $ joinType , $ callback , $ alias );
5161
5262 $ this ->markRelationshipAsAlreadyJoined ($ relationName );
63+ $ this ->clearPowerJoinCaches ();
5364
5465 return $ this ;
5566 };
5667 }
5768
69+ /**
70+ * Join the relationship(s) using table aliases.
71+ */
72+ public function joinRelationshipUsingAlias ()
73+ {
74+ return function ($ relationName , $ callback = null ) {
75+ return $ this ->joinRelationship ($ relationName , $ callback , 'join ' , true );
76+ };
77+ }
78+
79+ /**
80+ * Left join the relationship(s) using table aliases.
81+ */
82+ public function leftJoinRelationshipUsingAlias ()
83+ {
84+ return function ($ relationName , $ callback = null ) {
85+ return $ this ->joinRelationship ($ relationName , $ callback , 'leftJoin ' , true );
86+ };
87+ }
88+
89+ /**
90+ * Right join the relationship(s) using table aliases.
91+ */
92+ public function rightJoinRelationshipUsingAlias ()
93+ {
94+ return function ($ relationName , $ callback = null ) {
95+ return $ this ->joinRelationship ($ relationName , $ callback , 'rightJoin ' , true );
96+ };
97+ }
98+
5899 public function joinRelation ()
59100 {
60101 return function ($ relationName , $ callback = null , $ joinType = 'join ' ) {
@@ -64,29 +105,29 @@ public function joinRelation()
64105
65106 public function leftJoinRelationship ()
66107 {
67- return function ($ relation , $ callback = null ) {
68- return $ this ->joinRelationship ($ relation , $ callback , 'leftJoin ' );
108+ return function ($ relation , $ callback = null , $ useAlias = false ) {
109+ return $ this ->joinRelationship ($ relation , $ callback , 'leftJoin ' , $ useAlias );
69110 };
70111 }
71112
72113 public function leftJoinRelation ()
73114 {
74- return function ($ relation , $ callback = null ) {
75- return $ this ->joinRelationship ($ relation , $ callback , 'leftJoin ' );
115+ return function ($ relation , $ callback = null , $ useAlias = false ) {
116+ return $ this ->joinRelationship ($ relation , $ callback , 'leftJoin ' , $ useAlias );
76117 };
77118 }
78119
79120 public function rightJoinRelationship ()
80121 {
81- return function ($ relation , $ callback = null ) {
82- return $ this ->joinRelationship ($ relation , $ callback , 'rightJoin ' );
122+ return function ($ relation , $ callback = null , $ useAlias = false ) {
123+ return $ this ->joinRelationship ($ relation , $ callback , 'rightJoin ' , $ useAlias );
83124 };
84125 }
85126
86127 public function rightJoinRelation ()
87128 {
88- return function ($ relation , $ callback = null ) {
89- return $ this ->joinRelationship ($ relation , $ callback , 'rightJoin ' );
129+ return function ($ relation , $ callback = null , $ useAlias = false ) {
130+ return $ this ->joinRelationship ($ relation , $ callback , 'rightJoin ' , $ useAlias );
90131 };
91132 }
92133
@@ -95,15 +136,20 @@ public function rightJoinRelation()
95136 */
96137 public function joinNestedRelationship ()
97138 {
98- return function ($ relations , $ callback = null , $ joinType = 'join ' ) {
139+ return function ($ relations , $ callback = null , $ joinType = 'join ' , $ useAlias = false ) {
99140 $ relations = explode ('. ' , $ relations );
100141 $ latestRelation = null ;
101142
102143 foreach ($ relations as $ index => $ relationName ) {
103144 $ currentModel = $ latestRelation ? $ latestRelation ->getModel () : $ this ->getModel ();
104145 $ relation = $ currentModel ->{$ relationName }();
146+ $ alias = $ useAlias ? $ this ->generateAliasForRelationship ($ relation , $ relationName ) : null ;
105147 $ relationCallback = null ;
106148
149+ if ($ useAlias ) {
150+ $ this ->cachePowerJoinAlias ($ relation ->getModel (), $ alias );
151+ }
152+
107153 if ($ callback && is_array ($ callback ) && isset ($ callback [$ relationName ])) {
108154 $ relationCallback = $ callback [$ relationName ];
109155 }
@@ -116,13 +162,16 @@ public function joinNestedRelationship()
116162 $ relation ->performJoinForEloquentPowerJoins (
117163 $ this ,
118164 $ joinType ,
119- $ relationCallback
165+ $ relationCallback ,
166+ $ alias
120167 );
121168
122169 $ latestRelation = $ relation ;
123170 $ this ->markRelationshipAsAlreadyJoined ($ relationName );
124171 }
125172
173+ $ this ->clearPowerJoinCaches ();
174+
126175 return $ this ;
127176 };
128177 }
@@ -217,7 +266,7 @@ public function orderByMaxUsingJoins()
217266 public function relationshipAlreadyJoined ()
218267 {
219268 return function ($ relation ) {
220- return isset (JoinRelationship::$ joinRelationshipCache [spl_object_hash ($ this )][$ relation ]);
269+ return isset (JoinRelationship::$ joinRelationshipCache [spl_object_id ($ this )][$ relation ]);
221270 };
222271 }
223272
@@ -227,7 +276,7 @@ public function relationshipAlreadyJoined()
227276 public function markRelationshipAsAlreadyJoined ()
228277 {
229278 return function ($ relation ) {
230- JoinRelationship::$ joinRelationshipCache [spl_object_hash ($ this )][$ relation ] = true ;
279+ JoinRelationship::$ joinRelationshipCache [spl_object_id ($ this )][$ relation ] = true ;
231280 };
232281 }
233282
@@ -292,4 +341,40 @@ public function newPowerJoinClause()
292341 return new PowerJoinClause ($ parentQuery , $ type , $ table , $ model );
293342 };
294343 }
344+
345+ public function generateAliasForRelationship ()
346+ {
347+ return function ($ relation , $ relationName ) {
348+ if ($ relation instanceof BelongsToMany || $ relation instanceof HasManyThrough) {
349+ return [
350+ md5 ($ relationName .'table1 ' .time ()),
351+ md5 ($ relationName .'table2 ' .time ()),
352+ ];
353+ }
354+
355+ return md5 ($ relationName .time ());
356+ };
357+ }
358+
359+ /**
360+ * Cache the power join table alias used for the power join.
361+ */
362+ public function cachePowerJoinAlias ()
363+ {
364+ return function ($ model , $ alias ) {
365+ JoinRelationship::$ powerJoinAliasesCache [spl_object_id ($ model )] = $ alias ;
366+ };
367+ }
368+
369+ /**
370+ * Clear the power join caches.
371+ */
372+ public function clearPowerJoinCaches ()
373+ {
374+ return function () {
375+ JoinRelationship::$ powerJoinAliasesCache = [];
376+
377+ return $ this ;
378+ };
379+ }
295380}
0 commit comments