Skip to content

Commit d768166

Browse files
committed
Improve error handling on unsupported hybrid queries
Hybrid belongs-to-many relationships are not supported for query constraints. However, the support check was done downstream of a bunch of Eloquent stuff, resulting in the user getting an exception that didn't tell them anything about the usage being unsupported. This moves that check further up the chain so that the user is alerted to the lack of support before we do anything else.
1 parent aad17bb commit d768166

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

src/Helpers/QueriesRelationships.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
1313
use Illuminate\Database\Eloquent\Relations\Relation;
1414
use Illuminate\Support\Collection;
15+
use LogicException;
1516
use MongoDB\Laravel\Eloquent\Model;
1617
use MongoDB\Laravel\Relations\MorphToMany;
1718

@@ -104,6 +105,8 @@ protected function isAcrossConnections(Relation $relation)
104105
*/
105106
public function addHybridHas(Relation $relation, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null)
106107
{
108+
$this->assertHybridRelationSupported($relation);
109+
107110
$hasQuery = $relation->getQuery();
108111
if ($callback) {
109112
$hasQuery->callScope($callback);
@@ -128,6 +131,26 @@ public function addHybridHas(Relation $relation, $operator = '>=', $count = 1, $
128131
return $this->whereIn($this->getRelatedConstraintKey($relation), $relatedIds, $boolean, $not);
129132
}
130133

134+
/**
135+
* @param Relation $relation
136+
*
137+
* @return void
138+
*
139+
* @throws Exception
140+
*/
141+
private function assertHybridRelationSupported(Relation $relation): void
142+
{
143+
if (
144+
$relation instanceof HasOneOrMany
145+
|| $relation instanceof BelongsTo
146+
|| ($relation instanceof BelongsToMany && ! $this->isAcrossConnections($relation))
147+
) {
148+
return;
149+
}
150+
151+
throw new LogicException(class_basename($relation) . ' is not supported for hybrid query constraints.');
152+
}
153+
131154
/**
132155
* @param Builder $hasQuery
133156
* @param Relation $relation
@@ -213,6 +236,8 @@ protected function getConstrainedRelatedIds($relations, $operator, $count)
213236
*/
214237
protected function getRelatedConstraintKey(Relation $relation)
215238
{
239+
$this->assertHybridRelationSupported($relation);
240+
216241
if ($relation instanceof HasOneOrMany) {
217242
return $relation->getLocalKeyName();
218243
}
@@ -221,7 +246,7 @@ protected function getRelatedConstraintKey(Relation $relation)
221246
return $relation->getForeignKeyName();
222247
}
223248

224-
if ($relation instanceof BelongsToMany && ! $this->isAcrossConnections($relation)) {
249+
if ($relation instanceof BelongsToMany) {
225250
return $this->model->getKeyName();
226251
}
227252

tests/HybridRelationsTest.php

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function testSqlRelations()
7878
$this->assertEquals('John Doe', $role->sqlUser->name);
7979

8080
// MongoDB User
81-
$user = new User();
81+
$user = new User();
8282
$user->name = 'John Doe';
8383
$user->save();
8484

@@ -105,7 +105,7 @@ public function testSqlRelations()
105105

106106
public function testHybridWhereHas()
107107
{
108-
$user = new SqlUser();
108+
$user = new SqlUser();
109109
$otherUser = new SqlUser();
110110
$this->assertInstanceOf(SqlUser::class, $user);
111111
$this->assertInstanceOf(SQLiteConnection::class, $user->getConnection());
@@ -114,11 +114,11 @@ public function testHybridWhereHas()
114114

115115
// SQL User
116116
$user->name = 'John Doe';
117-
$user->id = 2;
117+
$user->id = 2;
118118
$user->save();
119119
// Other user
120120
$otherUser->name = 'Other User';
121-
$otherUser->id = 3;
121+
$otherUser->id = 3;
122122
$otherUser->save();
123123
// Make sure they are created
124124
$this->assertIsInt($user->id);
@@ -159,7 +159,7 @@ public function testHybridWhereHas()
159159

160160
public function testHybridWith()
161161
{
162-
$user = new SqlUser();
162+
$user = new SqlUser();
163163
$otherUser = new SqlUser();
164164
$this->assertInstanceOf(SqlUser::class, $user);
165165
$this->assertInstanceOf(SQLiteConnection::class, $user->getConnection());
@@ -168,11 +168,11 @@ public function testHybridWith()
168168

169169
// SQL User
170170
$user->name = 'John Doe';
171-
$user->id = 2;
171+
$user->id = 2;
172172
$user->save();
173173
// Other user
174174
$otherUser->name = 'Other User';
175-
$otherUser->id = 3;
175+
$otherUser->id = 3;
176176
$otherUser->save();
177177
// Make sure they are created
178178
$this->assertIsInt($user->id);
@@ -268,6 +268,23 @@ public function testHybridBelongsToMany()
268268
$this->assertEquals(1, $check->skills->count());
269269
}
270270

271+
public function testQueryingHybridBelongsToManyRelationFails()
272+
{
273+
$user = new SqlUser();
274+
$this->assertInstanceOf(SQLiteConnection::class, $user->getConnection());
275+
276+
// Create Mysql Users
277+
$user->fill(['name' => 'John Doe'])->save();
278+
$skill = Skill::query()->create(['name' => 'MongoDB']);
279+
$user->skills()->save($skill);
280+
281+
$this->expectExceptionMessage('BelongsToMany is not supported for hybrid query constraints.');
282+
283+
SqlUser::whereHas('skills', function ($query) {
284+
return $query->where('name', 'LIKE', 'MongoDB');
285+
});
286+
}
287+
271288
public function testHybridMorphToManySqlModelToMongoModel()
272289
{
273290
// SqlModel -> MorphToMany -> MongoModel

0 commit comments

Comments
 (0)