diff --git a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php index e064689a8f54..8e7da47a7276 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php +++ b/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php @@ -24,11 +24,13 @@ trait QueriesRelationships /** * Add a relationship count / exists condition to the query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation * @param string $operator * @param int $count * @param string $boolean - * @param \Closure|null $callback + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @return $this * * @throws \RuntimeException @@ -79,7 +81,7 @@ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', ? * @param string $operator * @param int $count * @param string $boolean - * @param \Closure|null $callback + * @param (\Closure(\Illuminate\Database\Eloquent\Builder<*>): mixed)|null $callback * @return $this */ protected function hasNested($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null) @@ -121,9 +123,11 @@ public function orHas($relation, $operator = '>=', $count = 1) /** * Add a relationship count / exists condition to the query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation * @param string $boolean - * @param \Closure|null $callback + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @return $this */ public function doesntHave($relation, $boolean = 'and', ?Closure $callback = null) @@ -145,8 +149,10 @@ public function orDoesntHave($relation) /** * Add a relationship count / exists condition to the query with where clauses. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @param string $operator * @param int $count * @return $this @@ -161,8 +167,8 @@ public function whereHas($relation, ?Closure $callback = null, $operator = '>=', * * Also load the relationship with same condition. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|null $callback + * @param string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder<*>|\Illuminate\Database\Eloquent\Relations\Relation<*, *, *>): mixed)|null $callback * @param string $operator * @param int $count * @return $this @@ -176,8 +182,10 @@ public function withWhereHas($relation, ?Closure $callback = null, $operator = ' /** * Add a relationship count / exists condition to the query with where clauses and an "or". * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @param string $operator * @param int $count * @return $this @@ -190,8 +198,10 @@ public function orWhereHas($relation, ?Closure $callback = null, $operator = '>= /** * Add a relationship count / exists condition to the query with where clauses. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @return $this */ public function whereDoesntHave($relation, ?Closure $callback = null) @@ -202,8 +212,10 @@ public function whereDoesntHave($relation, ?Closure $callback = null) /** * Add a relationship count / exists condition to the query with where clauses and an "or". * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|null $callback * @return $this */ public function orWhereDoesntHave($relation, ?Closure $callback = null) @@ -214,12 +226,14 @@ public function orWhereDoesntHave($relation, ?Closure $callback = null) /** * Add a polymorphic relationship count / exists condition to the query. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types * @param string $operator * @param int $count * @param string $boolean - * @param \Closure|null $callback + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @return $this */ public function hasMorph($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', ?Closure $callback = null) @@ -292,7 +306,7 @@ protected function getBelongsToRelation(MorphTo $relation, $type) * Add a polymorphic relationship count / exists condition to the query with an "or". * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types + * @param string|array $types * @param string $operator * @param int $count * @return $this @@ -305,10 +319,12 @@ public function orHasMorph($relation, $types, $operator = '>=', $count = 1) /** * Add a polymorphic relationship count / exists condition to the query. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types * @param string $boolean - * @param \Closure|null $callback + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @return $this */ public function doesntHaveMorph($relation, $types, $boolean = 'and', ?Closure $callback = null) @@ -320,7 +336,7 @@ public function doesntHaveMorph($relation, $types, $boolean = 'and', ?Closure $c * Add a polymorphic relationship count / exists condition to the query with an "or". * * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types + * @param string|array $types * @return $this */ public function orDoesntHaveMorph($relation, $types) @@ -331,9 +347,11 @@ public function orDoesntHaveMorph($relation, $types) /** * Add a polymorphic relationship count / exists condition to the query with where clauses. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @param string $operator * @param int $count * @return $this @@ -346,9 +364,11 @@ public function whereHasMorph($relation, $types, ?Closure $callback = null, $ope /** * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or". * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @param string $operator * @param int $count * @return $this @@ -361,9 +381,11 @@ public function orWhereHasMorph($relation, $types, ?Closure $callback = null, $o /** * Add a polymorphic relationship count / exists condition to the query with where clauses. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @return $this */ public function whereDoesntHaveMorph($relation, $types, ?Closure $callback = null) @@ -374,9 +396,11 @@ public function whereDoesntHaveMorph($relation, $types, ?Closure $callback = nul /** * Add a polymorphic relationship count / exists condition to the query with where clauses and an "or". * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|null $callback + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder, string): mixed)|null $callback * @return $this */ public function orWhereDoesntHaveMorph($relation, $types, ?Closure $callback = null) @@ -387,8 +411,10 @@ public function orWhereDoesntHaveMorph($relation, $types, ?Closure $callback = n /** * Add a basic where clause to a relationship query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -407,8 +433,10 @@ public function whereRelation($relation, $column, $operator = null, $value = nul /** * Add an "or where" clause to a relationship query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -427,8 +455,10 @@ public function orWhereRelation($relation, $column, $operator = null, $value = n /** * Add a basic count / exists condition to a relationship query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -447,8 +477,10 @@ public function whereDoesntHaveRelation($relation, $column, $operator = null, $v /** * Add an "or where" clause to a relationship query. * - * @param \Illuminate\Database\Eloquent\Relations\Relation<*, *, *>|string $relation - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\Relation|string $relation + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -467,9 +499,11 @@ public function orWhereDoesntHaveRelation($relation, $column, $operator = null, /** * Add a polymorphic relationship condition to the query with a where clause. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -484,9 +518,11 @@ public function whereMorphRelation($relation, $types, $column, $operator = null, /** * Add a polymorphic relationship condition to the query with an "or where" clause. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -501,9 +537,11 @@ public function orWhereMorphRelation($relation, $types, $column, $operator = nul /** * Add a polymorphic relationship condition to the query with a doesn't have clause. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this @@ -518,9 +556,11 @@ public function whereMorphDoesntHaveRelation($relation, $types, $column, $operat /** * Add a polymorphic relationship condition to the query with an "or doesn't have" clause. * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo<*, *>|string $relation - * @param string|array $types - * @param \Closure|string|array|\Illuminate\Contracts\Database\Query\Expression $column + * @template TRelatedModel of \Illuminate\Database\Eloquent\Model + * + * @param \Illuminate\Database\Eloquent\Relations\MorphTo|string $relation + * @param string|array $types + * @param (\Closure(\Illuminate\Database\Eloquent\Builder): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param mixed $operator * @param mixed $value * @return $this diff --git a/types/Database/Eloquent/Builder.php b/types/Database/Eloquent/Builder.php index 622f3e281397..61cea1d75508 100644 --- a/types/Database/Eloquent/Builder.php +++ b/types/Database/Eloquent/Builder.php @@ -5,119 +5,176 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\HasBuilder; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Query\Builder as QueryBuilder; -use User; use function PHPStan\Testing\assertType; -/** @param \Illuminate\Database\Eloquent\Builder<\User> $query */ +/** @param \Illuminate\Database\Eloquent\Builder $query */ function test( Builder $query, + User $user, Post $post, ChildPost $childPost, Comment $comment, QueryBuilder $queryBuilder ): void { - assertType('Illuminate\Database\Eloquent\Builder', $query->where('id', 1)); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhere('name', 'John')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereNot('status', 'active')); - assertType('Illuminate\Database\Eloquent\Builder', $query->with('relation')); - assertType('Illuminate\Database\Eloquent\Builder', $query->with(['relation' => ['foo' => fn ($q) => $q]])); - assertType('Illuminate\Database\Eloquent\Builder', $query->with(['relation' => function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query->where('id', 1)); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhere('name', 'John')); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereNot('status', 'active')); + assertType('Illuminate\Database\Eloquent\Builder', $query->with('relation')); + assertType('Illuminate\Database\Eloquent\Builder', $query->with(['relation' => ['foo' => fn ($q) => $q]])); + assertType('Illuminate\Database\Eloquent\Builder', $query->with(['relation' => function ($query) { // assertType('Illuminate\Database\Eloquent\Relations\Relation<*,*,*>', $query); }])); - assertType('Illuminate\Database\Eloquent\Builder', $query->without('relation')); - assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation'])); - assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation' => ['foo' => fn ($q) => $q]])); - assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation' => function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query->without('relation')); + assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation'])); + assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation' => ['foo' => fn ($q) => $q]])); + assertType('Illuminate\Database\Eloquent\Builder', $query->withOnly(['relation' => function ($query) { // assertType('Illuminate\Database\Eloquent\Relations\Relation<*,*,*>', $query); }])); - assertType('array', $query->getModels()); - assertType('array', $query->eagerLoadRelations([])); - assertType('Illuminate\Database\Eloquent\Collection', $query->get()); - assertType('Illuminate\Database\Eloquent\Collection', $query->hydrate([])); - assertType('Illuminate\Database\Eloquent\Collection', $query->fromQuery('foo', [])); - assertType('Illuminate\Database\Eloquent\Collection', $query->findMany([1, 2, 3])); - assertType('Illuminate\Database\Eloquent\Collection', $query->findOrFail([1])); - assertType('Illuminate\Database\Eloquent\Collection', $query->findOrNew([1])); - assertType('Illuminate\Database\Eloquent\Collection', $query->find([1])); - assertType('Illuminate\Database\Eloquent\Collection', $query->findOr([1], callback: fn () => 42)); - assertType('User', $query->findOrFail(1)); - assertType('User|null', $query->find(1)); - assertType('42|User', $query->findOr(1, fn () => 42)); - assertType('42|User', $query->findOr(1, callback: fn () => 42)); - assertType('User|null', $query->first()); - assertType('42|User', $query->firstOr(fn () => 42)); - assertType('42|User', $query->firstOr(callback: fn () => 42)); - assertType('User', $query->firstOrNew(['id' => 1])); - assertType('User', $query->findOrNew(1)); - assertType('User', $query->firstOrCreate(['id' => 1])); - assertType('User', $query->create(['name' => 'John'])); - assertType('User', $query->forceCreate(['name' => 'John'])); - assertType('User', $query->forceCreateQuietly(['name' => 'John'])); - assertType('User', $query->getModel()); - assertType('User', $query->make(['name' => 'John'])); - assertType('User', $query->forceCreate(['name' => 'John'])); - assertType('User', $query->updateOrCreate(['id' => 1], ['name' => 'John'])); - assertType('User', $query->firstOrFail()); - assertType('User', $query->sole()); - assertType('Illuminate\Support\LazyCollection', $query->cursor()); - assertType('Illuminate\Support\LazyCollection', $query->lazy()); - assertType('Illuminate\Support\LazyCollection', $query->lazyById()); - assertType('Illuminate\Support\LazyCollection', $query->lazyByIdDesc()); + assertType('array', $query->getModels()); + assertType('array', $query->eagerLoadRelations([])); + assertType('Illuminate\Database\Eloquent\Collection', $query->get()); + assertType('Illuminate\Database\Eloquent\Collection', $query->hydrate([])); + assertType('Illuminate\Database\Eloquent\Collection', $query->fromQuery('foo', [])); + assertType('Illuminate\Database\Eloquent\Collection', $query->findMany([1, 2, 3])); + assertType('Illuminate\Database\Eloquent\Collection', $query->findOrFail([1])); + assertType('Illuminate\Database\Eloquent\Collection', $query->findOrNew([1])); + assertType('Illuminate\Database\Eloquent\Collection', $query->find([1])); + assertType('Illuminate\Database\Eloquent\Collection', $query->findOr([1], callback: fn () => 42)); + assertType('Illuminate\Types\Builder\User', $query->findOrFail(1)); + assertType('Illuminate\Types\Builder\User|null', $query->find(1)); + assertType('42|Illuminate\Types\Builder\User', $query->findOr(1, fn () => 42)); + assertType('42|Illuminate\Types\Builder\User', $query->findOr(1, callback: fn () => 42)); + assertType('Illuminate\Types\Builder\User|null', $query->first()); + assertType('42|Illuminate\Types\Builder\User', $query->firstOr(fn () => 42)); + assertType('42|Illuminate\Types\Builder\User', $query->firstOr(callback: fn () => 42)); + assertType('Illuminate\Types\Builder\User', $query->firstOrNew(['id' => 1])); + assertType('Illuminate\Types\Builder\User', $query->findOrNew(1)); + assertType('Illuminate\Types\Builder\User', $query->firstOrCreate(['id' => 1])); + assertType('Illuminate\Types\Builder\User', $query->create(['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->forceCreate(['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->forceCreateQuietly(['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->getModel()); + assertType('Illuminate\Types\Builder\User', $query->make(['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->forceCreate(['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->updateOrCreate(['id' => 1], ['name' => 'John'])); + assertType('Illuminate\Types\Builder\User', $query->firstOrFail()); + assertType('Illuminate\Types\Builder\User', $query->sole()); + assertType('Illuminate\Support\LazyCollection', $query->cursor()); + assertType('Illuminate\Support\LazyCollection', $query->cursor()); + assertType('Illuminate\Support\LazyCollection', $query->lazy()); + assertType('Illuminate\Support\LazyCollection', $query->lazyById()); + assertType('Illuminate\Support\LazyCollection', $query->lazyByIdDesc()); assertType('Illuminate\Support\Collection<(int|string), mixed>', $query->pluck('foo')); - assertType('Illuminate\Database\Eloquent\Relations\Relation', $query->getRelation('foo')); + assertType('Illuminate\Database\Eloquent\Relations\Relation', $query->getRelation('foo')); assertType('Illuminate\Database\Eloquent\Builder', $query->setModel(new Post())); - assertType('Illuminate\Database\Eloquent\Builder', $query->has('foo')); - assertType('Illuminate\Database\Eloquent\Builder', $query->has($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orHas($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->doesntHave($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orDoesntHave($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereHas($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->withWhereHas($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereHas($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereDoesntHave($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereDoesntHave($post->users())); - assertType('Illuminate\Database\Eloquent\Builder', $query->hasMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->orHasMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->doesntHaveMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->orDoesntHaveMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereHasMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereDoesntHaveMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereDoesntHaveMorph($post->taggable(), 'taggable')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereRelation($post->users(), 'foo')); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereRelation($post->users(), 'foo')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereMorphRelation($post->taggable(), 'taggable', 'foo')); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereMorphRelation($post->taggable(), 'taggable', 'foo')); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereMorphedTo($post->taggable(), new Post())); - assertType('Illuminate\Database\Eloquent\Builder', $query->whereNotMorphedTo($post->taggable(), new Post())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereMorphedTo($post->taggable(), new Post())); - assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereNotMorphedTo($post->taggable(), new Post())); + assertType('Illuminate\Database\Eloquent\Builder', $query->has('foo', callback: function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->has($user->posts(), callback: function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orHas($user->posts())); + assertType('Illuminate\Database\Eloquent\Builder', $query->doesntHave($user->posts(), callback: function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orDoesntHave($user->posts())); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereHas($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->withWhereHas('posts', function ($query) { + assertType('Illuminate\Database\Eloquent\Builder<*>|Illuminate\Database\Eloquent\Relations\Relation<*, *, *>', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereHas($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereDoesntHave($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereDoesntHave($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->hasMorph($post->taggable(), 'taggable', callback: function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orHasMorph($post->taggable(), 'taggable')); + assertType('Illuminate\Database\Eloquent\Builder', $query->doesntHaveMorph($post->taggable(), 'taggable', callback: function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orDoesntHaveMorph($post->taggable(), 'taggable')); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereHasMorph($post->taggable(), 'taggable', function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereHasMorph($post->taggable(), 'taggable', function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereDoesntHaveMorph($post->taggable(), 'taggable', function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereDoesntHaveMorph($post->taggable(), 'taggable', function ($query, $type) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + assertType('string', $type); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereRelation($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereRelation($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereDoesntHaveRelation($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereDoesntHaveRelation($user->posts(), function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereMorphRelation($post->taggable(), 'taggable', function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereMorphRelation($post->taggable(), 'taggable', function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereMorphDoesntHaveRelation($post->taggable(), 'taggable', function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereMorphDoesntHaveRelation($post->taggable(), 'taggable', function ($query) { + assertType('Illuminate\Database\Eloquent\Builder', $query); + })); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereMorphedTo($post->taggable(), new Post())); + assertType('Illuminate\Database\Eloquent\Builder', $query->whereNotMorphedTo($post->taggable(), new Post())); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereMorphedTo($post->taggable(), new Post())); + assertType('Illuminate\Database\Eloquent\Builder', $query->orWhereNotMorphedTo($post->taggable(), new Post())); $query->chunk(1, function ($users, $page) { - assertType('Illuminate\Support\Collection', $users); + assertType('Illuminate\Support\Collection', $users); assertType('int', $page); }); $query->chunkById(1, function ($users, $page) { - assertType('Illuminate\Support\Collection', $users); + assertType('Illuminate\Support\Collection', $users); assertType('int', $page); }); $query->chunkMap(function ($users) { - assertType('User', $users); + assertType('Illuminate\Types\Builder\User', $users); }); $query->chunkByIdDesc(1, function ($users, $page) { - assertType('Illuminate\Support\Collection', $users); + assertType('Illuminate\Support\Collection', $users); assertType('int', $page); }); $query->each(function ($users, $page) { - assertType('User', $users); + assertType('Illuminate\Types\Builder\User', $users); assertType('int', $page); }); $query->eachById(function ($users, $page) { - assertType('User', $users); + assertType('Illuminate\Types\Builder\User', $users); assertType('int', $page); }); @@ -167,6 +224,15 @@ function test( assertType('Illuminate\Types\Builder\Comment', $comment->newQuery()->create(['name' => 'John'])); } +class User extends Model +{ + /** @return HasMany */ + public function posts(): HasMany + { + return $this->hasMany(Post::class); + } +} + class Post extends Model { /** @use HasBuilder> */ @@ -174,10 +240,10 @@ class Post extends Model protected static string $builder = CommonBuilder::class; - /** @return HasMany */ - public function users(): HasMany + /** @return BelongsTo */ + public function user(): BelongsTo { - return $this->hasMany(User::class); + return $this->belongsTo(User::class); } /** @return MorphTo<\Illuminate\Database\Eloquent\Model, $this> */