55use Closure ;
66use Illuminate \Database \Eloquent \Relations \BelongsTo ;
77use Illuminate \Database \Eloquent \Relations \HasOneOrMany ;
8- use Illuminate \Database \Eloquent \Relations \Relation ;
9- use Illuminate \Database \Eloquent \Builder as EloquentBuilder ;
8+ use Jenssegers \Mongodb \Eloquent \Model ;
109
1110trait QueriesRelationships
1211{
@@ -28,9 +27,9 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
2827
2928 $ relation = $ this ->getRelationWithoutConstraints ($ relation );
3029
31- // If this is a hybrid relation then we can not use an existence query
30+ // If this is a hybrid relation then we can not use a normal whereExists() query that relies on a subquery
3231 // We need to use a `whereIn` query
33- if ($ relation -> getParent ()-> getConnectionName () !== $ relation -> getRelated ()-> getConnectionName ( )) {
32+ if ($ this -> getModel () instanceof Model || $ this -> isAcrossConnections ( $ relation )) {
3433 return $ this ->addHybridHas ($ relation , $ operator , $ count , $ boolean , $ callback );
3534 }
3635
@@ -57,6 +56,15 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
5756 );
5857 }
5958
59+ /**
60+ * @param $relation
61+ * @return bool
62+ */
63+ protected function isAcrossConnections ($ relation )
64+ {
65+ return $ relation ->getParent ()->getConnectionName () !== $ relation ->getRelated ()->getConnectionName ();
66+ }
67+
6068 /**
6169 * Compare across databases
6270 * @param $relation
@@ -65,6 +73,7 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', C
6573 * @param string $boolean
6674 * @param Closure|null $callback
6775 * @return mixed
76+ * @throws \Exception
6877 */
6978 public function addHybridHas ($ relation , $ operator = '>= ' , $ count = 1 , $ boolean = 'and ' , Closure $ callback = null )
7079 {
@@ -73,23 +82,31 @@ public function addHybridHas($relation, $operator = '>=', $count = 1, $boolean =
7382 $ hasQuery ->callScope ($ callback );
7483 }
7584
85+ // If the operator is <, <= or !=, we will use whereNotIn.
86+ $ not = in_array ($ operator , ['< ' , '<= ' , '!= ' ]);
87+ // If we are comparing to 0, we need an additional $not flip.
88+ if ($ count == 0 ) {
89+ $ not = ! $ not ;
90+ }
91+
7692 $ relations = $ hasQuery ->pluck ($ this ->getHasCompareKey ($ relation ));
77- $ constraintKey = $ this ->getRelatedConstraintKey ($ relation );
7893
79- return $ this ->addRelatedCountConstraint ($ constraintKey , $ relations , $ operator , $ count , $ boolean );
94+ $ relatedIds = $ this ->getConstrainedRelatedIds ($ relations , $ operator , $ count );
95+
96+ return $ this ->whereIn ($ this ->getRelatedConstraintKey ($ relation ), $ relatedIds , $ boolean , $ not );
8097 }
8198
8299
83100 /**
84- * Returns key we are constraining this parent model's query witth
101+ * Returns key we are constraining this parent model's query with
85102 * @param $relation
86103 * @return string
87104 * @throws \Exception
88105 */
89106 protected function getRelatedConstraintKey ($ relation )
90107 {
91108 if ($ relation instanceof HasOneOrMany) {
92- return $ relation -> getQualifiedParentKeyName ();
109+ return $ this -> model -> getKeyName ();
93110 }
94111
95112 if ($ relation instanceof BelongsTo) {
@@ -105,47 +122,20 @@ protected function getRelatedConstraintKey($relation)
105122 */
106123 protected function getHasCompareKey ($ relation )
107124 {
108- if ($ relation instanceof HasOneOrMany ) {
109- return $ relation ->getForeignKeyName ();
125+ if (method_exists ( $ relation, ' getHasCompareKey ' ) ) {
126+ return $ relation ->getHasCompareKey ();
110127 }
111128
112- $ keyMethods = ['getOwnerKey ' , 'getHasCompareKey ' ];
113- foreach ($ keyMethods as $ method ) {
114- if (method_exists ($ relation , $ method )) {
115- return $ relation ->$ method ();
116- }
117- }
129+ return $ relation instanceof HasOneOrMany ? $ relation ->getForeignKeyName () : $ relation ->getOwnerKey ();
118130 }
119131
120132 /**
121- * Add the "has" condition where clause to the query.
122- *
123- * @param \Illuminate\Database\Eloquent\Builder $hasQuery
124- * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
125- * @param string $operator
126- * @param int $count
127- * @param string $boolean
128- * @return \Illuminate\Database\Eloquent\Builder|static
129- */
130- protected function addHasWhere (EloquentBuilder $ hasQuery , Relation $ relation , $ operator , $ count , $ boolean )
131- {
132- $ query = $ hasQuery ->getQuery ();
133- // Get the number of related objects for each possible parent.
134- $ relations = $ query ->pluck ($ relation ->getHasCompareKey ());
135-
136- return $ this ->addRelatedCountConstraint ($ this ->model ->getKeyName (), $ relations , $ operator , $ count , $ boolean );
137- }
138-
139- /**
140- * Consta
141- * @param $key
142133 * @param $relations
143134 * @param $operator
144135 * @param $count
145- * @param $boolean
146- * @return mixed
136+ * @return array
147137 */
148- protected function addRelatedCountConstraint ( $ key , $ relations , $ operator , $ count, $ boolean )
138+ protected function getConstrainedRelatedIds ( $ relations , $ operator , $ count )
149139 {
150140 $ relationCount = array_count_values (array_map (function ($ id ) {
151141 return (string )$ id ; // Convert Back ObjectIds to Strings
@@ -169,16 +159,7 @@ protected function addRelatedCountConstraint($key, $relations, $operator, $count
169159 }
170160 });
171161
172- // If the operator is <, <= or !=, we will use whereNotIn.
173- $ not = in_array ($ operator , ['< ' , '<= ' , '!= ' ]);
174- // If we are comparing to 0, we need an additional $not flip.
175- if ($ count == 0 ) {
176- $ not = ! $ not ;
177- }
178162 // All related ids.
179- $ relatedIds = array_keys ($ relationCount );
180-
181- // Add whereIn to the query.
182- return $ this ->whereIn ($ key , $ relatedIds , $ boolean , $ not );
163+ return array_keys ($ relationCount );
183164 }
184165}
0 commit comments