Skip to content

Commit 0865e3f

Browse files
authored
Merge pull request #4 from nullthoughts/dev
Shorthand methods
2 parents b16665b + 0a1fbbc commit 0865e3f

File tree

3 files changed

+76
-9
lines changed

3 files changed

+76
-9
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Laravel Latest Relation
2-
Eloquent macros for querying the latest HasMany relationship in Laravel
2+
Eloquent macros for querying the latest HasMany relationship in Laravel.
3+
4+
More information on the problem and solutions: [Dynamic scope on latest record in Laravel's HasMany relationships, Part 1: solving with Subqueries - nullthoughts.com](https://nullthoughts.com/development/2019/10/08/dynamic-scope-on-latest-relationship-in-laravel/)
35

46
## Installation
57
Install via composer:
@@ -10,6 +12,20 @@ Use the Builder methods inside a whereHas closure:
1012

1113
### Latest:
1214

15+
#### whereLatestRelation($relation, $column, $value)
16+
**Query**
17+
```php
18+
$users = User::whereLatestRelation('logins', 'device_type', 'desktop');
19+
```
20+
21+
**Dynamic Scope**
22+
```php
23+
public function scopeUsingDevice($query, $device)
24+
{
25+
return $query->whereLatestRelation('logins', 'device_type', $device);
26+
}
27+
```
28+
1329
#### whereLatest($column, $value)
1430
**Query**
1531
```php
@@ -20,7 +36,7 @@ $users = User::whereHas('logins', function ($query) {
2036

2137
**Dynamic Scope**
2238
```php
23-
public function scopeByCondition($query, $condition)
39+
public function scopeUsingDevice($query, $device)
2440
{
2541
return $query->whereHas('logins', function ($query) use ($device) {
2642
$query->whereLatest('device_type', $device);
@@ -42,7 +58,7 @@ $users = User::whereHas('logins', function ($query) {
4258

4359
**Dynamic Scope**
4460
```php
45-
public function scopeByCondition($query, $condition)
61+
public function scopeHavingDeviceType($query)
4662
{
4763
return $query->whereHas('logins', function ($query) {
4864
$query->latestRelation()->whereNotNull('device_type');
@@ -53,6 +69,8 @@ public function scopeByCondition($query, $condition)
5369
### Earliest:
5470

5571
```php
72+
$users = User::whereLatestRelation('logins', 'device_type', 'desktop');
73+
5674
$users = User::whereHas('logins', function ($query) {
5775
$query->whereEarliest('device_type', 'desktop');
5876
});

src/ServiceProvider.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
namespace LaravelLatestRelation;
44

55
use InvalidArgumentException;
6-
use Illuminate\Database\Query\Builder;
6+
use Illuminate\Database\Query\Builder as QueryBuilder;
7+
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
78
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
89

910
class ServiceProvider extends BaseServiceProvider
1011
{
1112
public function boot()
1213
{
13-
Builder::macro('relation', function (string $type = 'max') {
14+
QueryBuilder::macro('relation', function (string $type = 'max') {
1415
if(! $where = $this->wheres[0] ?? null) {
1516
throw new InvalidArgumentException('The relation methods should only be called from within a whereHas callback.');
1617
}
@@ -22,20 +23,32 @@ public function boot()
2223
});
2324
});
2425

25-
Builder::macro('earliestRelation', function () {
26+
QueryBuilder::macro('earliestRelation', function () {
2627
return $this->relation('min');
2728
});
2829

29-
Builder::macro('latestRelation', function () {
30+
QueryBuilder::macro('latestRelation', function () {
3031
return $this->relation('max');
3132
});
3233

33-
Builder::macro('whereEarliest', function ($column, $value) {
34+
QueryBuilder::macro('whereEarliest', function ($column, $value) {
3435
return $this->earliestRelation()->where($column, $value);
3536
});
3637

37-
Builder::macro('whereLatest', function ($column, $value) {
38+
QueryBuilder::macro('whereLatest', function ($column, $value) {
3839
return $this->latestRelation()->where($column, $value);
3940
});
41+
42+
EloquentBuilder::macro('whereEarliestRelation', function ($relation, $column, $value) {
43+
return $this->whereHas($relation, function($query) use ($column, $value) {
44+
return $query->whereEarliest($column, $value);
45+
});
46+
});
47+
48+
EloquentBuilder::macro('whereLatestRelation', function ($relation, $column, $value) {
49+
return $this->whereHas($relation, function ($query) use ($column, $value) {
50+
$query->whereLatest($column, $value);
51+
});
52+
});
4053
}
4154
}

tests/Unit/EloquentLatestRelationTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,42 @@ public function where_earilest()
174174

175175
$this->assertCount(0, $users);
176176
}
177+
178+
/**
179+
* @test
180+
*/
181+
public function where_latest_relation()
182+
{
183+
$users = User::whereLatestRelation('logins', 'device_type', 'mobile')->get();
184+
185+
$this->assertCount(2, $users);
186+
$this->assertSame('Cameron Frye', $users->first()->name);
187+
$this->assertSame('mobile', $users->first()->lastLogin->device_type);
188+
$this->assertTrue($users->first()->lastLogin->created_at->isYesterday());
189+
$this->assertSame('Ed Rooney', $users->last()->name);
190+
$this->assertSame('mobile', $users->last()->lastLogin->device_type);
191+
$this->assertTrue($users->last()->lastLogin->created_at->isToday());
192+
193+
$users = User::whereLatestRelation('logins', 'device_type', 'desktop')->get();
194+
195+
$this->assertCount(1, $users);
196+
$this->assertSame('Ferris Bueller', $users->first()->name);
197+
$this->assertSame('desktop', $users->first()->lastLogin->device_type);
198+
}
199+
200+
/**
201+
* @test
202+
*/
203+
public function where_earilest_relation()
204+
{
205+
$users = User::whereEarliestRelation('logins', 'device_type', 'mobile')->get();
206+
207+
$this->assertCount(3, $users);
208+
209+
$users = User::whereEarliestRelation('logins', 'device_type', 'desktop')->get();
210+
211+
$this->assertCount(0, $users);
212+
}
177213
}
178214

179215
class User extends Model

0 commit comments

Comments
 (0)