Skip to content

Commit 470d121

Browse files
committed
Adding has() functionality for hasMany and hasOne relations
1 parent 25a8bac commit 470d121

File tree

5 files changed

+176
-3
lines changed

5 files changed

+176
-3
lines changed

src/Jenssegers/Eloquent/Model.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<?php namespace Jenssegers\Eloquent;
22

3-
use Illuminate\Database\Eloquent\Relations\HasOne;
4-
use Illuminate\Database\Eloquent\Relations\HasMany;
53
use Illuminate\Database\Eloquent\Relations\MorphOne;
64
use Illuminate\Database\Eloquent\Relations\MorphMany;
75
use Illuminate\Database\Eloquent\Relations\Relation;
6+
use Jenssegers\Mongodb\Relations\HasOne;
7+
use Jenssegers\Mongodb\Relations\HasMany;
88
use Jenssegers\Mongodb\Relations\BelongsTo;
99
use Jenssegers\Mongodb\Relations\BelongsToMany;
1010
use Jenssegers\Mongodb\Relations\MorphTo;

src/Jenssegers/Mongodb/Eloquent/Builder.php

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php namespace Jenssegers\Mongodb\Eloquent;
22

33
use MongoCursor;
4+
use Illuminate\Database\Eloquent\Relations\Relation;
5+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
46

5-
class Builder extends \Illuminate\Database\Eloquent\Builder {
7+
class Builder extends EloquentBuilder {
68

79
/**
810
* The methods that should be returned from query builder.
@@ -14,6 +16,56 @@ class Builder extends \Illuminate\Database\Eloquent\Builder {
1416
'count', 'min', 'max', 'avg', 'sum', 'exists', 'push', 'pull'
1517
);
1618

19+
/**
20+
* Add the "has" condition where clause to the query.
21+
*
22+
* @param \Illuminate\Database\Eloquent\Builder $hasQuery
23+
* @param \Illuminate\Database\Eloquent\Relations\Relation $relation
24+
* @param string $operator
25+
* @param int $count
26+
* @param string $boolean
27+
* @return \Illuminate\Database\Eloquent\Builder
28+
*/
29+
protected function addHasWhere(EloquentBuilder $hasQuery, Relation $relation, $operator, $count, $boolean)
30+
{
31+
$query = $hasQuery->getQuery();
32+
33+
// Get the number of related objects for each possible parent.
34+
$relationCount = array_count_values($query->lists($relation->getHasCompareKey()));
35+
36+
// Remove unwanted related objects based on the operator and count.
37+
$relationCount = array_filter($relationCount, function($counted) use ($count, $operator)
38+
{
39+
// If we are comparing to 0, we always need all results.
40+
if ($count == 0) return true;
41+
42+
switch ($operator)
43+
{
44+
case '>=':
45+
case '<':
46+
return $counted >= $count;
47+
case '>':
48+
case '<=':
49+
return $counted > $count;
50+
case '=':
51+
case '!=':
52+
return $counted == $count;
53+
}
54+
});
55+
56+
// If the operator is <, <= or !=, we will use whereNotIn.
57+
$not = in_array($operator, array('<', '<=', '!='));
58+
59+
// If we are comparing to 0, we need an additional $not flip.
60+
if ($count == 0) $not = !$not;
61+
62+
// All related ids.
63+
$relatedIds = array_keys($relationCount);
64+
65+
// Add whereIn to the query.
66+
return $this->whereIn($this->model->getKeyName(), $relatedIds, $boolean, $not);
67+
}
68+
1769
/**
1870
* Create a raw database expression.
1971
*
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php namespace Jenssegers\Mongodb\Relations;
2+
3+
use Illuminate\Database\Eloquent\Builder;
4+
use Illuminate\Database\Eloquent\Collection;
5+
use Illuminate\Database\Eloquent\Relations\HasMany as EloquentHasMany;
6+
7+
class HasMany extends EloquentHasMany {
8+
9+
/**
10+
* Add the constraints for a relationship count query.
11+
*
12+
* @param \Illuminate\Database\Eloquent\Builder $query
13+
* @param \Illuminate\Database\Eloquent\Builder $parent
14+
* @return \Illuminate\Database\Eloquent\Builder
15+
*/
16+
public function getRelationCountQuery(Builder $query, Builder $parent)
17+
{
18+
$foreignKey = $this->getHasCompareKey();
19+
20+
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
21+
}
22+
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php namespace Jenssegers\Mongodb\Relations;
2+
3+
use Illuminate\Database\Eloquent\Builder;
4+
use Illuminate\Database\Eloquent\Collection;
5+
use Illuminate\Database\Eloquent\Relations\HasOne as EloquentHasOne;
6+
7+
class HasOne extends EloquentHasOne {
8+
9+
/**
10+
* Add the constraints for a relationship count query.
11+
*
12+
* @param \Illuminate\Database\Eloquent\Builder $query
13+
* @param \Illuminate\Database\Eloquent\Builder $parent
14+
* @return \Illuminate\Database\Eloquent\Builder
15+
*/
16+
public function getRelationCountQuery(Builder $query, Builder $parent)
17+
{
18+
$foreignKey = $this->getHasCompareKey();
19+
20+
return $query->select($this->getHasCompareKey())->where($this->getHasCompareKey(), 'exists', true);
21+
}
22+
23+
}

tests/RelationsTest.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,79 @@ public function testMorph()
303303
$this->assertInstanceOf('Client', $relations['imageable']);
304304
}
305305

306+
public function testHasManyHas()
307+
{
308+
$author1 = User::create(array('name' => 'George R. R. Martin'));
309+
$author1->books()->create(array('title' => 'A Game of Thrones', 'rating' => 5));
310+
$author1->books()->create(array('title' => 'A Clash of Kings', 'rating' => 5));
311+
$author2 = User::create(array('name' => 'John Doe'));
312+
$author2->books()->create(array('title' => 'My book', 'rating' => 2));
313+
User::create(array('name' => 'Anonymous author'));
314+
Book::create(array('title' => 'Anonymous book', 'rating' => 1));
315+
316+
$authors = User::has('books')->get();
317+
$this->assertCount(2, $authors);
318+
$this->assertEquals('George R. R. Martin', $authors[0]->name);
319+
$this->assertEquals('John Doe', $authors[1]->name);
320+
321+
$authors = User::has('books', '>', 1)->get();
322+
$this->assertCount(1, $authors);
323+
324+
$authors = User::has('books', '<', 5)->get();
325+
$this->assertCount(3, $authors);
326+
327+
$authors = User::has('books', '>=', 2)->get();
328+
$this->assertCount(1, $authors);
329+
330+
$authors = User::has('books', '<=', 1)->get();
331+
$this->assertCount(2, $authors);
332+
333+
$authors = User::has('books', '=', 2)->get();
334+
$this->assertCount(1, $authors);
335+
336+
$authors = User::has('books', '!=', 2)->get();
337+
$this->assertCount(2, $authors);
338+
339+
$authors = User::has('books', '=', 0)->get();
340+
$this->assertCount(1, $authors);
341+
342+
$authors = User::has('books', '!=', 0)->get();
343+
$this->assertCount(2, $authors);
344+
345+
$authors = User::whereHas('books', function($query)
346+
{
347+
$query->where('rating', 5);
348+
349+
})->get();
350+
$this->assertCount(1, $authors);
351+
352+
$authors = User::whereHas('books', function($query)
353+
{
354+
$query->where('rating', '<', 5);
355+
356+
})->get();
357+
$this->assertCount(1, $authors);
358+
}
359+
360+
public function testHasOneHas()
361+
{
362+
$user1 = User::create(array('name' => 'John Doe'));
363+
$user1->role()->create(array('title' => 'admin'));
364+
$user2 = User::create(array('name' => 'Jane Doe'));
365+
$user2->role()->create(array('title' => 'reseller'));
366+
User::create(array('name' => 'Mark Moe'));
367+
Role::create(array('title' => 'Customer'));
368+
369+
$users = User::has('role')->get();
370+
$this->assertCount(2, $users);
371+
$this->assertEquals('John Doe', $users[0]->name);
372+
$this->assertEquals('Jane Doe', $users[1]->name);
373+
374+
$users = User::has('role', '=', 0)->get();
375+
$this->assertCount(1, $users);
376+
377+
$users = User::has('role', '!=', 0)->get();
378+
$this->assertCount(2, $users);
379+
}
380+
306381
}

0 commit comments

Comments
 (0)