Skip to content

Commit c6ac1e6

Browse files
LaravelFreelancerNLDeploy
and
Deploy
authored
184 support wheredoesnthaverelation methods (#187)
* qa: confirmed proper functioning of several eloquent relationship query methods * fix: table alias was set to null in returndocs --------- Co-authored-by: Deploy <[email protected]>
1 parent d849565 commit c6ac1e6

File tree

4 files changed

+298
-31
lines changed

4 files changed

+298
-31
lines changed

docs/compatibility-list.md

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ update with join
111111
delete / truncate
112112

113113
### Debugging
114-
dd / dump / toSql / ddRawSql / dumpRawSql / toRawSql
114+
dd / dump / toSql / ddRawSql? / dumpRawSql / toRawSql?
115115

116116
## <a name="eloquent"></a>Eloquent
117117
The methods listed below are **specific** to Eloquent.
@@ -120,17 +120,17 @@ in the chapter above._
120120

121121
### Model CRUD
122122
all / first / firstWhere / firstOr / firstOrFail /
123-
firstOrCreate / firstOrNew? /
124-
find / findOr / fresh? / refresh? /
125-
create / createOrFirst / fill / save / update / updateOrCreate /
126-
upsert / replicate / delete / destroy / truncate / softDeletes /
127-
trashed? / restore? / withTrashed? / forceDelete
128-
isDirty? / isClean / wasChanged / getOriginal /
129-
pruning / query scopes? / saveQuietly / deleteQuietly /
130-
forceDeleteQuietly / restoreQuietly
123+
firstOrCreate / firstOrNew /
124+
find / findOr? / fresh / refresh /
125+
create / createOrFirst / fill? / save / update / updateOrCreate /
126+
upsert / replicate? / delete / destroy / truncate / softDeletes? /
127+
trashed? / restore? / withTrashed? / forceDelete /
128+
isDirty? / isClean? / wasChanged? / getOriginal? /
129+
pruning? / query scopes? / saveQuietly? / deleteQuietly? /
130+
forceDeleteQuietly? / restoreQuietly?
131131

132132
#### Model comparison
133-
is / isNot
133+
is? / isNot?
134134

135135
### Relationships
136136
- One To One
@@ -142,30 +142,35 @@ is / isNot
142142

143143
belongsTo / belongsToMany /
144144
morphOne / morphTo / morphMany / morphMany / morphedByMany /
145-
ofMany / latestOfMany / oldestOfMany
146-
hasOne / hasMany / hasX / hasOneThrough / hasManyThrough /
147-
throughX /
148-
whereBelongsTo /
149-
as /
150-
withTimestamps /
145+
ofMany? / latestOfMany? / oldestOfMany? /
146+
has / hasOne / hasMany / hasOneThrough? / hasManyThrough? /
147+
through? / whereBelongsTo? /
151148

152149
#### Pivot functions
153-
withPivot /
154-
wherePivot / wherePivotIn /wherePivotNotIn /
155-
wherePivotBetween / wherePivotNotBetween /
156-
wherePivotNull / wherePivotNotNull / orderByPivot /
157-
using
150+
as? / withPivot? /
151+
wherePivot? / wherePivotIn? /wherePivotNotIn? /
152+
wherePivotBetween? / wherePivotNotBetween? /
153+
wherePivotNull? / wherePivotNotNull? / orderByPivot? /
154+
using? / withTimestamps?
158155

159-
enforceMorphMap / getMorphClass / getMorphedModel / resolveRelationUsing
156+
enforceMorphMap? / getMorphClass? / getMorphedModel? / resolveRelationUsing?
160157

161-
#### Query relationships
162-
has / orHas / whereHas / whereRelation / doesntHave /
163-
whereDoesntHave / whereHasMorph / whereDoesntHaveMorph
158+
#### Querying Relationship Existence
159+
has / orHas / whereHas / orWhereHas / whereRelation / orWhereRelation /
160+
whereMorphRelation / orWhereMorphRelation
161+
162+
#### Querying Relationship Absence
163+
doesntHave / orDoesntHave /
164+
whereDoesntHave / orWhereDoesntHave / whereDoesntHaveRelation / orWhereDoesntHaveRelation /
165+
whereMorphDoesntHaveRelation / orWhereMorphDoesntHaveRelation
166+
167+
#### Querying Morph To Relationships
168+
whereHasMorph / orWhereHasMorph / whereDoesntHaveMorph / orWhereDoesntHaveMorph / whereMorphedTo? / whereNotMorphedTo?
164169

165170
#### Aggregating related models
166-
withCount / loadCount /
167-
withSum / loadSum / withExists / morphWithCount /loadMorphCount /
168-
loadMorphCount
171+
withCount / loadCount? /
172+
withSum? / loadSum? / withExists / morphWithCount? /loadMorphCount? /
173+
loadMorphCount?
169174

170175
#### Eager loading
171176
with / without / withOnly / constrain /

src/Query/Concerns/HandlesAliases.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public function getTableAlias(string|Expression $table): float|int|null|string
8080
$table = 'Expression' . spl_object_id($table);
8181
}
8282

83+
if ($this->isTableAlias($table)) {
84+
return $table;
85+
}
86+
8387
if (!isset($this->tableAliases[$table])) {
8488
return null;
8589
}

tests/Eloquent/MorphToTest.php

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

3+
use Illuminate\Database\Eloquent\Builder;
34
use LaravelFreelancerNL\Aranguent\Testing\DatabaseTransactions;
45
use TestSetup\Models\Character;
56
use TestSetup\Models\Location;
@@ -35,3 +36,132 @@
3536
expect($location->capturable)->toBeInstanceOf(Character::class);
3637
expect($location->capturable->id)->toEqual('TheonGreyjoy');
3738
});
39+
40+
test('whereHasMorph', function () {
41+
$locations = Location::whereHasMorph(
42+
'capturable',
43+
Character::class,
44+
function (Builder $query) {
45+
$query->where('_key', 'TheonGreyjoy');
46+
},
47+
)->get();
48+
49+
expect(count($locations))->toEqual(1);
50+
});
51+
52+
test('orWhereHasMorph', function () {
53+
$locations = Location::where(function (Builder $query) {
54+
$query->whereHasMorph(
55+
'capturable',
56+
Character::class,
57+
function (Builder $query) {
58+
$query->where('id', 'TheonGreyjoy');
59+
},
60+
)
61+
->orWhereHasMorph(
62+
'capturable',
63+
Character::class,
64+
function (Builder $query) {
65+
$query->where('id', 'DaenerysTargaryen');
66+
},
67+
);
68+
})->get();
69+
70+
expect(count($locations))->toEqual(6);
71+
});
72+
73+
test('whereMorphRelation ', function () {
74+
$locations = Location::whereMorphRelation(
75+
'capturable',
76+
Character::class,
77+
'_key',
78+
'TheonGreyjoy',
79+
)
80+
->get();
81+
82+
expect(count($locations))->toEqual(1);
83+
});
84+
85+
test('orWhereMorphRelation', function () {
86+
$locations = Location::where(function (Builder $query) {
87+
$query->whereMorphRelation(
88+
'capturable',
89+
Character::class,
90+
'id',
91+
'TheonGreyjoy',
92+
)
93+
->orWhereMorphRelation(
94+
'capturable',
95+
Character::class,
96+
'id',
97+
'DaenerysTargaryen',
98+
);
99+
})->get();
100+
101+
expect($locations->count())->toEqual(6);
102+
});
103+
104+
test('whereDoesntHaveMorph', function () {
105+
$locations = Location::whereDoesntHaveMorph(
106+
'capturable',
107+
Character::class,
108+
function (Builder $query) {
109+
$query->where('id', 'DaenerysTargaryen');
110+
},
111+
)->get();
112+
113+
expect(count($locations))->toEqual(1);
114+
});
115+
116+
test('orWhereDoesntHaveMorph', function () {
117+
$locations = Location::where(function (Builder $query) {
118+
$query->whereHasMorph(
119+
'capturable',
120+
Character::class,
121+
function (Builder $query) {
122+
$query->where('alive', true);
123+
},
124+
)
125+
->orWhereDoesntHaveMorph(
126+
'capturable',
127+
Character::class,
128+
function (Builder $query) {
129+
$query->where('age', '<', 20);
130+
},
131+
);
132+
})->get();
133+
134+
expect(count($locations))->toEqual(6);
135+
});
136+
137+
138+
test('whereMorphDoesntHaveRelation', function () {
139+
$locations = Location::whereMorphDoesntHaveRelation(
140+
'capturable',
141+
Character::class,
142+
'id',
143+
'TheonGreyjoy',
144+
)->get();
145+
146+
expect(count($locations))->toEqual(5);
147+
});
148+
149+
test('orWhereMorphDoesntHaveRelation', function () {
150+
$locations = Location::where(function (Builder $query) {
151+
$query->whereMorphDoesntHaveRelation(
152+
'capturable',
153+
Character::class,
154+
'id',
155+
'DaenerysTargaryen',
156+
)
157+
->orWhereMorphDoesntHaveRelation(
158+
'capturable',
159+
Character::class,
160+
'age',
161+
'<',
162+
20,
163+
);
164+
})->get();
165+
166+
expect(count($locations))->toEqual(1);
167+
});

tests/Eloquent/RelationshipQueriesTest.php

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
<?php
22

3+
use Illuminate\Database\Eloquent\Builder;
34
use LaravelFreelancerNL\Aranguent\Testing\DatabaseTransactions;
45
use TestSetup\Models\Character;
6+
use TestSetup\Models\House;
7+
use TestSetup\Models\Location;
58

69
uses(
710
DatabaseTransactions::class,
@@ -17,15 +20,140 @@
1720
expect(count($characters))->toEqual(1);
1821
});
1922

23+
test('has morph', function () {
24+
$characters = Character::has('tags')->get();
25+
26+
expect(count($characters))->toEqual(2);
27+
});
28+
29+
30+
test('orHas', function () {
31+
$characters = Character::has('leads')
32+
->orHas('captured')
33+
->get();
34+
35+
expect(count($characters))->toEqual(4);
36+
});
37+
2038
test('doesntHave', function () {
2139
$characters = Character::doesntHave('leads')->get();
40+
2241
expect(count($characters))->toEqual(40);
2342
});
2443

25-
test('has on morphed relation', function () {
26-
$characters = Character::has('tags')->get();
44+
test('orDoesntHave', function () {
45+
$characters = Character::where(function (Builder $query) {
46+
$query->doesntHave('leads')
47+
->orDoesntHave('conquered');
48+
})
49+
->get();
2750

28-
expect(count($characters))->toEqual(2);
51+
$daenarys = $characters->first(function (Character $character) {
52+
return $character->id === 'DaenerysTargaryen';
53+
});
54+
55+
expect(count($characters))->toEqual(42);
56+
expect($daenarys)->toBeNull();
57+
});
58+
59+
test('whereHas', function () {
60+
$locations = Location::whereHas('leader', function (Builder $query) {
61+
$query->where('age', '<', 30);
62+
})
63+
->distinct()
64+
->pluck('led_by');
65+
66+
expect($locations->count())->toBe(2);
67+
expect($locations[0])->toBe('DaenerysTargaryen');
68+
expect($locations[1])->toBe('SansaStark');
69+
});
70+
71+
test('orWhereHas', function () {
72+
$locations = Location::where(function (Builder $query) {
73+
$query->whereHas('leader', function (Builder $query) {
74+
$query->where('age', '<', 15);
75+
})->orWhereHas('leader', function (Builder $query) {
76+
$query->where('age', '>', 30);
77+
});
78+
})
79+
->distinct()
80+
->pluck('led_by');
81+
82+
expect($locations->count())->toBe(2);
83+
expect($locations[0])->toBe('CerseiLannister');
84+
expect($locations[1])->toBe('SansaStark');
85+
});
86+
87+
test('whereDoesntHave', function () {
88+
$characters = Character::whereDoesntHave('leads', function (Builder $query) {
89+
$query->where('name', 'Astapor');
90+
})->get();
91+
92+
$daenarys = $characters->first(function (Character $character) {
93+
return $character->id === 'DaenerysTargaryen';
94+
});
95+
expect($characters->count())->toBe(42);
96+
expect($daenarys)->toBeNull();
97+
});
98+
99+
test('orWhereDoesntHave', function () {
100+
$houses = House::where(function (Builder $query) {
101+
$query->whereDoesntHave('head', function (Builder $query) {
102+
$query->where('age', '<', 20);
103+
})
104+
->orWhereDoesntHave('head', function (Builder $query) {
105+
$query->whereNull('age');
106+
});
107+
})->get();
108+
109+
expect($houses[0]->name)->toBe('Stark');
110+
expect($houses[1]->name)->toBe('Targaryen');
111+
});
112+
113+
114+
115+
test('whereRelation', function () {
116+
$locations = Location::whereRelation('leader', 'age', '<', 30)
117+
->distinct()
118+
->pluck('led_by');
119+
120+
expect($locations->count())->toBe(2);
121+
expect($locations[0])->toBe('DaenerysTargaryen');
122+
expect($locations[1])->toBe('SansaStark');
123+
});
124+
125+
test('orWhereRelation', function () {
126+
$locations = Location::where(function (Builder $query) {
127+
$query->whereRelation('leader', 'age', '<', 15)
128+
->orWhereRelation('leader', 'age', '>', 30);
129+
})
130+
->distinct()
131+
->pluck('led_by');
132+
133+
expect($locations->count())->toBe(2);
134+
expect($locations[0])->toBe('CerseiLannister');
135+
expect($locations[1])->toBe('SansaStark');
136+
});
137+
138+
test('whereDoesntHaveRelation', function () {
139+
$characters = Character::whereDoesntHaveRelation('leads', 'name', 'Astapor')->get();
140+
141+
$daenarys = $characters->first(function (Character $character) {
142+
return $character->id === 'DaenerysTargaryen';
143+
});
144+
145+
expect($characters->count())->toBe(42);
146+
expect($daenarys)->toBeNull();
147+
});
148+
149+
test('orWhereDoesntHaveRelation', function () {
150+
$houses = House::where(function (Builder $query) {
151+
$query->whereDoesntHaveRelation('head', 'age', '<', 20)
152+
->orWhereDoesntHaveRelation('head', 'age', null);
153+
})->get();
154+
155+
expect($houses[0]->name)->toBe('Stark');
156+
expect($houses[1]->name)->toBe('Targaryen');
29157
});
30158

31159
test('withCount', function () {

0 commit comments

Comments
 (0)