Skip to content

Commit 299d5ef

Browse files
committed
Add exception for non-supported withAggregate for hybrid relationships
1 parent 303ceb7 commit 299d5ef

File tree

4 files changed

+47
-33
lines changed

4 files changed

+47
-33
lines changed

Diff for: src/Eloquent/Builder.php

+12-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use MongoDB\Driver\CursorInterface;
1717
use MongoDB\Driver\Exception\WriteException;
1818
use MongoDB\Laravel\Connection;
19+
use MongoDB\Laravel\Eloquent\Model as DocumentModel;
1920
use MongoDB\Laravel\Helpers\QueriesRelationships;
2021
use MongoDB\Laravel\Query\AggregationBuilder;
2122
use MongoDB\Laravel\Relations\EmbedsOneOrMany;
@@ -314,16 +315,25 @@ public function withAggregate($relations, $column, $function = null)
314315
$relations = is_array($relations) ? $relations : [$relations];
315316

316317
foreach ($this->parseWithRelations($relations) as $name => $constraints) {
317-
// For "count" and "exist" we can use the embedded list of ids
318-
// for embedded relations, everything can be computed directly using a projection.
319318
$segments = explode(' ', $name);
320319

321320
$name = $segments[0];
322321
$alias = (count($segments) === 3 && Str::lower($segments[1]) === 'as' ? $segments[2] : Str::snake($name) . '_' . $function);
323322

324323
$relation = $this->getRelationWithoutConstraints($name);
325324

325+
if (! DocumentModel::isDocumentModel($relation->getRelated())) {
326+
throw new InvalidArgumentException('WithAggregate does not support hybrid relations');
327+
}
328+
326329
if ($relation instanceof EmbedsOneOrMany) {
330+
$subQuery = $this->newQuery();
331+
$constraints($subQuery);
332+
if ($subQuery->getQuery()->wheres) {
333+
// @see https://jira.mongodb.org/browse/PHPORM-292
334+
throw new InvalidArgumentException('Constraints are not supported for embedded relations');
335+
}
336+
327337
switch ($function) {
328338
case 'count':
329339
$this->getQuery()->project([$alias => ['$size' => ['$ifNull' => ['$' . $name, []]]]]);
@@ -337,7 +347,6 @@ public function withAggregate($relations, $column, $function = null)
337347
throw new InvalidArgumentException(sprintf('Invalid aggregate function "%s"', $function));
338348
}
339349
} else {
340-
// @todo support "exists"
341350
$this->withAggregate[$alias] = [
342351
'relation' => $relation,
343352
'function' => $function,
@@ -346,11 +355,6 @@ public function withAggregate($relations, $column, $function = null)
346355
'alias' => $alias,
347356
];
348357
}
349-
350-
// @todo HasMany ?
351-
352-
// Otherwise, we need to store the aggregate request to run during "eagerLoadRelation"
353-
// after the root results are retrieved.
354358
}
355359

356360
return $this;

Diff for: tests/Eloquent/EloquentWithAggregateTest.php

+17-25
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Database\Eloquent\Builder;
66
use Illuminate\Support\Collection;
77
use Illuminate\Support\Facades\DB;
8+
use InvalidArgumentException;
89
use MongoDB\Laravel\Tests\Eloquent\Models\EloquentWithAggregateModel1;
910
use MongoDB\Laravel\Tests\Eloquent\Models\EloquentWithAggregateModel2;
1011
use MongoDB\Laravel\Tests\Eloquent\Models\EloquentWithAggregateModel3;
@@ -116,36 +117,16 @@ public function testWithAggregateFiltered()
116117

117118
public function testWithAggregateEmbedFiltered()
118119
{
119-
self::markTestSkipped('EmbedsMany does not support filtering. $filter requires an expression but the Query Builder generates query predicates.');
120-
121-
EloquentWithAggregateModel1::create(['id' => 1]);
122-
$one = EloquentWithAggregateModel1::create(['id' => 2]);
123-
$one->embeddeds()->create(['value' => 4]);
124-
$one->embeddeds()->create(['value' => 6]);
125-
$one->embeddeds()->create(['value' => 8]);
120+
EloquentWithAggregateModel1::create(['id' => 2]);
126121
$filter = static function (Builder $query) {
127122
$query->where('value', '<=', 6);
128123
};
129124

130-
$results = EloquentWithAggregateModel1::withCount(['embeddeds' => $filter])->where('id', 2);
131-
self::assertSameResults([
132-
['id' => 2, 'embeddeds_count' => 2],
133-
], $results->get());
134-
135-
$results = EloquentWithAggregateModel1::withMax(['embeddeds' => $filter], 'value')->where('id', 2);
136-
self::assertSameResults([
137-
['id' => 2, 'embeddeds_max' => 6],
138-
], $results->get());
139-
140-
$results = EloquentWithAggregateModel1::withMin(['embeddeds' => $filter], 'value')->where('id', 2);
141-
self::assertSameResults([
142-
['id' => 2, 'embeddeds_min' => 4],
143-
], $results->get());
125+
// @see https://jira.mongodb.org/browse/PHPORM-292
126+
self::expectException(InvalidArgumentException::class);
127+
self::expectExceptionMessage('Constraints are not supported for embedded relations');
144128

145-
$results = EloquentWithAggregateModel1::withAvg(['embeddeds' => $filter], 'value')->where('id', 2);
146-
self::assertSameResults([
147-
['id' => 2, 'embeddeds_avg' => 5.0],
148-
], $results->get());
129+
EloquentWithAggregateModel1::withCount(['embeddeds' => $filter])->where('id', 2)->get();
149130
}
150131

151132
public function testWithAggregateMultipleResults()
@@ -248,6 +229,17 @@ public function testGlobalScopes()
248229
self::assertSame(1, $result->all_fours_count);
249230
}
250231

232+
public function testHybridNotSupported()
233+
{
234+
EloquentWithAggregateModel1::create(['id' => 2]);
235+
236+
// @see https://jira.mongodb.org/browse/PHPORM-292
237+
self::expectException(InvalidArgumentException::class);
238+
self::expectExceptionMessage('WithAggregate does not support hybrid relations');
239+
240+
EloquentWithAggregateModel1::withCount('hybrids')->where('id', 2)->get();
241+
}
242+
251243
private static function assertSameResults(array $expected, Collection $collection)
252244
{
253245
$actual = $collection->toArray();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace MongoDB\Laravel\Tests\Eloquent\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class EloquentWithAggregateHybridModel extends Model
8+
{
9+
protected $connection = 'sqlite';
10+
public $table = 'hybrid';
11+
public $timestamps = false;
12+
protected $guarded = [];
13+
}

Diff for: tests/Eloquent/Models/EloquentWithAggregateModel1.php

+5
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,9 @@ public function embeddeds()
3030
{
3131
return $this->embedsMany(EloquentWithAggregateEmbeddedModel::class);
3232
}
33+
34+
public function hybrids()
35+
{
36+
return $this->hasMany(EloquentWithAggregateHybridModel::class, 'one_id');
37+
}
3338
}

0 commit comments

Comments
 (0)