Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

184 support wheredoesnthaverelation methods #187

Merged
merged 2 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 33 additions & 28 deletions docs/compatibility-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ update with join
delete / truncate

### Debugging
dd / dump / toSql / ddRawSql / dumpRawSql / toRawSql
dd / dump / toSql / ddRawSql? / dumpRawSql / toRawSql?

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

### Model CRUD
all / first / firstWhere / firstOr / firstOrFail /
firstOrCreate / firstOrNew? /
find / findOr / fresh? / refresh? /
create / createOrFirst / fill / save / update / updateOrCreate /
upsert / replicate / delete / destroy / truncate / softDeletes /
trashed? / restore? / withTrashed? / forceDelete
isDirty? / isClean / wasChanged / getOriginal /
pruning / query scopes? / saveQuietly / deleteQuietly /
forceDeleteQuietly / restoreQuietly
firstOrCreate / firstOrNew /
find / findOr? / fresh / refresh /
create / createOrFirst / fill? / save / update / updateOrCreate /
upsert / replicate? / delete / destroy / truncate / softDeletes? /
trashed? / restore? / withTrashed? / forceDelete /
isDirty? / isClean? / wasChanged? / getOriginal? /
pruning? / query scopes? / saveQuietly? / deleteQuietly? /
forceDeleteQuietly? / restoreQuietly?

#### Model comparison
is / isNot
is? / isNot?

### Relationships
- One To One
Expand All @@ -142,30 +142,35 @@ is / isNot

belongsTo / belongsToMany /
morphOne / morphTo / morphMany / morphMany / morphedByMany /
ofMany / latestOfMany / oldestOfMany
hasOne / hasMany / hasX / hasOneThrough / hasManyThrough /
throughX /
whereBelongsTo /
as /
withTimestamps /
ofMany? / latestOfMany? / oldestOfMany? /
has / hasOne / hasMany / hasOneThrough? / hasManyThrough? /
through? / whereBelongsTo? /

#### Pivot functions
withPivot /
wherePivot / wherePivotIn /wherePivotNotIn /
wherePivotBetween / wherePivotNotBetween /
wherePivotNull / wherePivotNotNull / orderByPivot /
using
as? / withPivot? /
wherePivot? / wherePivotIn? /wherePivotNotIn? /
wherePivotBetween? / wherePivotNotBetween? /
wherePivotNull? / wherePivotNotNull? / orderByPivot? /
using? / withTimestamps?

enforceMorphMap / getMorphClass / getMorphedModel / resolveRelationUsing
enforceMorphMap? / getMorphClass? / getMorphedModel? / resolveRelationUsing?

#### Query relationships
has / orHas / whereHas / whereRelation / doesntHave /
whereDoesntHave / whereHasMorph / whereDoesntHaveMorph
#### Querying Relationship Existence
has / orHas / whereHas / orWhereHas / whereRelation / orWhereRelation /
whereMorphRelation / orWhereMorphRelation

#### Querying Relationship Absence
doesntHave / orDoesntHave /
whereDoesntHave / orWhereDoesntHave / whereDoesntHaveRelation / orWhereDoesntHaveRelation /
whereMorphDoesntHaveRelation / orWhereMorphDoesntHaveRelation

#### Querying Morph To Relationships
whereHasMorph / orWhereHasMorph / whereDoesntHaveMorph / orWhereDoesntHaveMorph / whereMorphedTo? / whereNotMorphedTo?

#### Aggregating related models
withCount / loadCount /
withSum / loadSum / withExists / morphWithCount /loadMorphCount /
loadMorphCount
withCount / loadCount? /
withSum? / loadSum? / withExists / morphWithCount? /loadMorphCount? /
loadMorphCount?

#### Eager loading
with / without / withOnly / constrain /
Expand Down
4 changes: 4 additions & 0 deletions src/Query/Concerns/HandlesAliases.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public function getTableAlias(string|Expression $table): float|int|null|string
$table = 'Expression' . spl_object_id($table);
}

if ($this->isTableAlias($table)) {
return $table;
}

if (!isset($this->tableAliases[$table])) {
return null;
}
Expand Down
130 changes: 130 additions & 0 deletions tests/Eloquent/MorphToTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Illuminate\Database\Eloquent\Builder;
use LaravelFreelancerNL\Aranguent\Testing\DatabaseTransactions;
use TestSetup\Models\Character;
use TestSetup\Models\Location;
Expand Down Expand Up @@ -35,3 +36,132 @@
expect($location->capturable)->toBeInstanceOf(Character::class);
expect($location->capturable->id)->toEqual('TheonGreyjoy');
});

test('whereHasMorph', function () {
$locations = Location::whereHasMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('_key', 'TheonGreyjoy');
},
)->get();

expect(count($locations))->toEqual(1);
});

test('orWhereHasMorph', function () {
$locations = Location::where(function (Builder $query) {
$query->whereHasMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('id', 'TheonGreyjoy');
},
)
->orWhereHasMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('id', 'DaenerysTargaryen');
},
);
})->get();

expect(count($locations))->toEqual(6);
});

test('whereMorphRelation ', function () {
$locations = Location::whereMorphRelation(
'capturable',
Character::class,
'_key',
'TheonGreyjoy',
)
->get();

expect(count($locations))->toEqual(1);
});

test('orWhereMorphRelation', function () {
$locations = Location::where(function (Builder $query) {
$query->whereMorphRelation(
'capturable',
Character::class,
'id',
'TheonGreyjoy',
)
->orWhereMorphRelation(
'capturable',
Character::class,
'id',
'DaenerysTargaryen',
);
})->get();

expect($locations->count())->toEqual(6);
});

test('whereDoesntHaveMorph', function () {
$locations = Location::whereDoesntHaveMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('id', 'DaenerysTargaryen');
},
)->get();

expect(count($locations))->toEqual(1);
});

test('orWhereDoesntHaveMorph', function () {
$locations = Location::where(function (Builder $query) {
$query->whereHasMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('alive', true);
},
)
->orWhereDoesntHaveMorph(
'capturable',
Character::class,
function (Builder $query) {
$query->where('age', '<', 20);
},
);
})->get();

expect(count($locations))->toEqual(6);
});


test('whereMorphDoesntHaveRelation', function () {
$locations = Location::whereMorphDoesntHaveRelation(
'capturable',
Character::class,
'id',
'TheonGreyjoy',
)->get();

expect(count($locations))->toEqual(5);
});

test('orWhereMorphDoesntHaveRelation', function () {
$locations = Location::where(function (Builder $query) {
$query->whereMorphDoesntHaveRelation(
'capturable',
Character::class,
'id',
'DaenerysTargaryen',
)
->orWhereMorphDoesntHaveRelation(
'capturable',
Character::class,
'age',
'<',
20,
);
})->get();

expect(count($locations))->toEqual(1);
});
134 changes: 131 additions & 3 deletions tests/Eloquent/RelationshipQueriesTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
<?php

use Illuminate\Database\Eloquent\Builder;
use LaravelFreelancerNL\Aranguent\Testing\DatabaseTransactions;
use TestSetup\Models\Character;
use TestSetup\Models\House;
use TestSetup\Models\Location;

uses(
DatabaseTransactions::class,
Expand All @@ -17,15 +20,140 @@
expect(count($characters))->toEqual(1);
});

test('has morph', function () {
$characters = Character::has('tags')->get();

expect(count($characters))->toEqual(2);
});


test('orHas', function () {
$characters = Character::has('leads')
->orHas('captured')
->get();

expect(count($characters))->toEqual(4);
});

test('doesntHave', function () {
$characters = Character::doesntHave('leads')->get();

expect(count($characters))->toEqual(40);
});

test('has on morphed relation', function () {
$characters = Character::has('tags')->get();
test('orDoesntHave', function () {
$characters = Character::where(function (Builder $query) {
$query->doesntHave('leads')
->orDoesntHave('conquered');
})
->get();

expect(count($characters))->toEqual(2);
$daenarys = $characters->first(function (Character $character) {
return $character->id === 'DaenerysTargaryen';
});

expect(count($characters))->toEqual(42);
expect($daenarys)->toBeNull();
});

test('whereHas', function () {
$locations = Location::whereHas('leader', function (Builder $query) {
$query->where('age', '<', 30);
})
->distinct()
->pluck('led_by');

expect($locations->count())->toBe(2);
expect($locations[0])->toBe('DaenerysTargaryen');
expect($locations[1])->toBe('SansaStark');
});

test('orWhereHas', function () {
$locations = Location::where(function (Builder $query) {
$query->whereHas('leader', function (Builder $query) {
$query->where('age', '<', 15);
})->orWhereHas('leader', function (Builder $query) {
$query->where('age', '>', 30);
});
})
->distinct()
->pluck('led_by');

expect($locations->count())->toBe(2);
expect($locations[0])->toBe('CerseiLannister');
expect($locations[1])->toBe('SansaStark');
});

test('whereDoesntHave', function () {
$characters = Character::whereDoesntHave('leads', function (Builder $query) {
$query->where('name', 'Astapor');
})->get();

$daenarys = $characters->first(function (Character $character) {
return $character->id === 'DaenerysTargaryen';
});
expect($characters->count())->toBe(42);
expect($daenarys)->toBeNull();
});

test('orWhereDoesntHave', function () {
$houses = House::where(function (Builder $query) {
$query->whereDoesntHave('head', function (Builder $query) {
$query->where('age', '<', 20);
})
->orWhereDoesntHave('head', function (Builder $query) {
$query->whereNull('age');
});
})->get();

expect($houses[0]->name)->toBe('Stark');
expect($houses[1]->name)->toBe('Targaryen');
});



test('whereRelation', function () {
$locations = Location::whereRelation('leader', 'age', '<', 30)
->distinct()
->pluck('led_by');

expect($locations->count())->toBe(2);
expect($locations[0])->toBe('DaenerysTargaryen');
expect($locations[1])->toBe('SansaStark');
});

test('orWhereRelation', function () {
$locations = Location::where(function (Builder $query) {
$query->whereRelation('leader', 'age', '<', 15)
->orWhereRelation('leader', 'age', '>', 30);
})
->distinct()
->pluck('led_by');

expect($locations->count())->toBe(2);
expect($locations[0])->toBe('CerseiLannister');
expect($locations[1])->toBe('SansaStark');
});

test('whereDoesntHaveRelation', function () {
$characters = Character::whereDoesntHaveRelation('leads', 'name', 'Astapor')->get();

$daenarys = $characters->first(function (Character $character) {
return $character->id === 'DaenerysTargaryen';
});

expect($characters->count())->toBe(42);
expect($daenarys)->toBeNull();
});

test('orWhereDoesntHaveRelation', function () {
$houses = House::where(function (Builder $query) {
$query->whereDoesntHaveRelation('head', 'age', '<', 20)
->orWhereDoesntHaveRelation('head', 'age', null);
})->get();

expect($houses[0]->name)->toBe('Stark');
expect($houses[1]->name)->toBe('Targaryen');
});

test('withCount', function () {
Expand Down
Loading