From 8489f31de2a79d08db17e9144abc3855f55dc794 Mon Sep 17 00:00:00 2001 From: Kay Wei Date: Tue, 12 Nov 2024 14:41:08 +0800 Subject: [PATCH] Introduce `TFetchMode` generic to enhance PHPDoc support for different PDO fetch modes in Query Builder --- .../Passwords/DatabaseTokenRepository.php | 2 +- src/Illuminate/Cache/DatabaseStore.php | 2 +- src/Illuminate/Database/Capsule/Manager.php | 2 +- .../Database/Concerns/BuildsQueries.php | 7 +- src/Illuminate/Database/Connection.php | 14 ++- .../Database/ConnectionInterface.php | 2 +- src/Illuminate/Database/Eloquent/Builder.php | 6 +- src/Illuminate/Database/Eloquent/Model.php | 2 +- .../Concerns/InteractsWithPivotTable.php | 6 +- .../Eloquent/Relations/MorphToMany.php | 2 +- .../Database/Eloquent/Relations/Relation.php | 4 +- .../DatabaseMigrationRepository.php | 2 +- src/Illuminate/Database/Query/Builder.php | 30 +++-- src/Illuminate/Database/Query/JoinClause.php | 4 +- .../HasDatabaseNotifications.php | 4 +- .../Failed/DatabaseFailedJobProvider.php | 2 +- .../Failed/DatabaseUuidFailedJobProvider.php | 2 +- .../Session/DatabaseSessionHandler.php | 2 +- .../Validation/DatabasePresenceVerifier.php | 4 +- types/Database/Eloquent/Builder.php | 5 +- types/Database/Query/Builder.php | 108 ++++++++++++++---- 21 files changed, 141 insertions(+), 71 deletions(-) diff --git a/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php b/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php index 5449603fbc6b..3435449ae799 100755 --- a/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php +++ b/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php @@ -227,7 +227,7 @@ public function getConnection() /** * Begin a new database query against the table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function getTable() { diff --git a/src/Illuminate/Cache/DatabaseStore.php b/src/Illuminate/Cache/DatabaseStore.php index b8064e4df647..ad3cd95317e1 100755 --- a/src/Illuminate/Cache/DatabaseStore.php +++ b/src/Illuminate/Cache/DatabaseStore.php @@ -424,7 +424,7 @@ public function flush() /** * Get a query builder for the cache table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function table() { diff --git a/src/Illuminate/Database/Capsule/Manager.php b/src/Illuminate/Database/Capsule/Manager.php index cfc47eb5abcf..7a3fa93edbf7 100755 --- a/src/Illuminate/Database/Capsule/Manager.php +++ b/src/Illuminate/Database/Capsule/Manager.php @@ -80,7 +80,7 @@ public static function connection($connection = null) * @param \Closure|\Illuminate\Database\Query\Builder|string $table * @param string|null $as * @param string|null $connection - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public static function table($table, $as = null, $connection = null) { diff --git a/src/Illuminate/Database/Concerns/BuildsQueries.php b/src/Illuminate/Database/Concerns/BuildsQueries.php index bb5d4e86cacb..5f4a65a6ee96 100644 --- a/src/Illuminate/Database/Concerns/BuildsQueries.php +++ b/src/Illuminate/Database/Concerns/BuildsQueries.php @@ -21,6 +21,7 @@ /** * @template TValue + * @template TFetchMode of \PDO::FETCH_*|false * * @mixin \Illuminate\Database\Eloquent\Builder * @mixin \Illuminate\Database\Query\Builder @@ -337,7 +338,7 @@ protected function orderedLazyById($chunkSize = 1000, $column = null, $alias = n * Execute the query and get the first result. * * @param array|string $columns - * @return TValue|null + * @return (TFetchMode is false ? TValue : (TFetchMode is 1|5|8|9 ? object : array))|null */ public function first($columns = ['*']) { @@ -349,7 +350,7 @@ public function first($columns = ['*']) * * @param array|string $columns * @param string|null $message - * @return TValue + * @return (TFetchMode is false ? TValue : (TFetchMode is 1|5|8|9 ? object : array)) * * @throws \Illuminate\Database\RecordNotFoundException */ @@ -366,7 +367,7 @@ public function firstOrFail($columns = ['*'], $message = null) * Execute the query and get the first result if it's the sole matching record. * * @param array|string $columns - * @return TValue + * @return (TFetchMode is false ? TValue : (TFetchMode is 1|5|8|9 ? object : array)) * * @throws \Illuminate\Database\RecordsNotFoundException * @throws \Illuminate\Database\MultipleRecordsFoundException diff --git a/src/Illuminate/Database/Connection.php b/src/Illuminate/Database/Connection.php index 17056a9e4de0..c4022a87c13d 100755 --- a/src/Illuminate/Database/Connection.php +++ b/src/Illuminate/Database/Connection.php @@ -312,7 +312,7 @@ public function getSchemaBuilder() * * @param \Closure|\Illuminate\Database\Query\Builder|\Illuminate\Contracts\Database\Query\Expression|string $table * @param string|null $as - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function table($table, $as = null) { @@ -322,7 +322,7 @@ public function table($table, $as = null) /** * Get a new query builder instance. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function query() { @@ -661,8 +661,9 @@ public function pretend(Closure $callback) /** * Execute the given callback without "pretending". * - * @param \Closure $callback - * @return mixed + * @template TResult + * @param \Closure():TResult $callback + * @return TResult */ public function withoutPretending(Closure $callback) { @@ -682,8 +683,9 @@ public function withoutPretending(Closure $callback) /** * Execute the given callback in "dry run" mode. * - * @param \Closure $callback - * @return array + * @template TResult of array + * @param \Closure():TResult $callback + * @return TResult */ protected function withFreshQueryLog($callback) { diff --git a/src/Illuminate/Database/ConnectionInterface.php b/src/Illuminate/Database/ConnectionInterface.php index 288adb4206e3..34a51136dd9f 100755 --- a/src/Illuminate/Database/ConnectionInterface.php +++ b/src/Illuminate/Database/ConnectionInterface.php @@ -11,7 +11,7 @@ interface ConnectionInterface * * @param \Closure|\Illuminate\Database\Query\Builder|string $table * @param string|null $as - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function table($table, $as = null); diff --git a/src/Illuminate/Database/Eloquent/Builder.php b/src/Illuminate/Database/Eloquent/Builder.php index 3a614e88d161..d6d9fd0067f1 100755 --- a/src/Illuminate/Database/Eloquent/Builder.php +++ b/src/Illuminate/Database/Eloquent/Builder.php @@ -33,7 +33,7 @@ */ class Builder implements BuilderContract { - /** @use \Illuminate\Database\Concerns\BuildsQueries */ + /** @use \Illuminate\Database\Concerns\BuildsQueries */ use BuildsQueries, ForwardsCalls, QueriesRelationships { BuildsQueries::sole as baseSole; } @@ -1797,7 +1797,7 @@ protected function getUnionBuilders() /** * Get the underlying query builder instance. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function getQuery() { @@ -1820,7 +1820,7 @@ public function setQuery($query) /** * Get a base query builder instance. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function toBase() { diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 7afa59933416..8db10ce60f01 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -1607,7 +1607,7 @@ public function newEloquentBuilder($query) /** * Get a new query builder instance for the connection. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function newBaseQueryBuilder() { diff --git a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php index f31d043d7412..089492f1b6a7 100644 --- a/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php +++ b/src/Illuminate/Database/Eloquent/Relations/Concerns/InteractsWithPivotTable.php @@ -538,7 +538,7 @@ public function newExistingPivot(array $attributes = []) /** * Get a new plain query builder for the pivot table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function newPivotStatement() { @@ -549,7 +549,7 @@ public function newPivotStatement() * Get a new pivot statement for a given "other" ID. * * @param mixed $id - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function newPivotStatementForId($id) { @@ -559,7 +559,7 @@ public function newPivotStatementForId($id) /** * Create a new query builder for the pivot table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function newPivotQuery() { diff --git a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php index 1f3c3e57ddb4..968586578388 100644 --- a/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php +++ b/src/Illuminate/Database/Eloquent/Relations/MorphToMany.php @@ -127,7 +127,7 @@ protected function getCurrentlyAttachedPivots() /** * Create a new query builder for the pivot table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function newPivotQuery() { diff --git a/src/Illuminate/Database/Eloquent/Relations/Relation.php b/src/Illuminate/Database/Eloquent/Relations/Relation.php index d5caa8880669..7232693a06ae 100755 --- a/src/Illuminate/Database/Eloquent/Relations/Relation.php +++ b/src/Illuminate/Database/Eloquent/Relations/Relation.php @@ -316,7 +316,7 @@ public function getQuery() /** * Get the base query builder driving the Eloquent builder. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function getBaseQuery() { @@ -326,7 +326,7 @@ public function getBaseQuery() /** * Get a base query builder instance. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function toBase() { diff --git a/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php b/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php index cf020d64db06..7f3922a8347d 100755 --- a/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php +++ b/src/Illuminate/Database/Migrations/DatabaseMigrationRepository.php @@ -199,7 +199,7 @@ public function deleteRepository() /** * Get a query builder for the migration table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function table() { diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 64956bb2324f..59f01b010ef4 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -31,9 +31,12 @@ use function Illuminate\Support\enum_value; +/** + * @template TFetchMode of \PDO::FETCH_* + */ class Builder implements BuilderContract { - /** @use \Illuminate\Database\Concerns\BuildsQueries */ + /** @use \Illuminate\Database\Concerns\BuildsQueries */ use BuildsQueries, ExplainsQueries, ForwardsCalls, Macroable { __call as macroCall; } @@ -1790,7 +1793,7 @@ public function whereNested(Closure $callback, $boolean = 'and') /** * Create a new query instance for nested where condition. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function forNestedWhere() { @@ -3030,7 +3033,7 @@ public function toRawSql() * * @param int|string $id * @param array|string $columns - * @return object|null + * @return (TFetchMode is 1|5|8|9 ? object : array)|null */ public function find($id, $columns = ['*']) { @@ -3045,7 +3048,7 @@ public function find($id, $columns = ['*']) * @param mixed $id * @param (\Closure(): TValue)|list|string $columns * @param (\Closure(): TValue)|null $callback - * @return object|TValue + * @return ($columns is Closure ? TValue|(TFetchMode is 1|5|8|9 ? object : array) : ($callback is Closure ? TValue|(TFetchMode is 1|5|8|9 ? object : array) : (TFetchMode is 1|5|8|9 ? object : array))) */ public function findOr($id, $columns = ['*'], ?Closure $callback = null) { @@ -3530,8 +3533,9 @@ public function doesntExist() /** * Execute the given callback if no rows exist for the current query. * - * @param \Closure $callback - * @return mixed + * @template TResult + * @param \Closure():TResult $callback + * @return true|TResult */ public function existsOr(Closure $callback) { @@ -3541,8 +3545,9 @@ public function existsOr(Closure $callback) /** * Execute the given callback if rows exist for the current query. * - * @param \Closure $callback - * @return mixed + * @template TResult + * @param \Closure():TResult $callback + * @return true|TResult */ public function doesntExistOr(Closure $callback) { @@ -3690,9 +3695,10 @@ protected function setAggregate($function, $columns) * * After running the callback, the columns are reset to the original value. * + * @template TResult * @param array $columns - * @param callable $callback - * @return mixed + * @param callable():TResult $callback + * @return TResult */ protected function onceWithColumns($columns, $callback) { @@ -4086,7 +4092,7 @@ public function truncate() /** * Get a new instance of the query builder. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function newQuery() { @@ -4096,7 +4102,7 @@ public function newQuery() /** * Create a new query instance for a sub-query. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function forSubQuery() { diff --git a/src/Illuminate/Database/Query/JoinClause.php b/src/Illuminate/Database/Query/JoinClause.php index 37a002c57245..c3fd5e1256e2 100755 --- a/src/Illuminate/Database/Query/JoinClause.php +++ b/src/Illuminate/Database/Query/JoinClause.php @@ -125,7 +125,7 @@ public function newQuery() /** * Create a new query instance for sub-query. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function forSubQuery() { @@ -135,7 +135,7 @@ protected function forSubQuery() /** * Create a new parent query instance. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function newParentQuery() { diff --git a/src/Illuminate/Notifications/HasDatabaseNotifications.php b/src/Illuminate/Notifications/HasDatabaseNotifications.php index 10e2386f55ba..998a0f70e875 100644 --- a/src/Illuminate/Notifications/HasDatabaseNotifications.php +++ b/src/Illuminate/Notifications/HasDatabaseNotifications.php @@ -17,7 +17,7 @@ public function notifications() /** * Get the entity's read notifications. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function readNotifications() { @@ -27,7 +27,7 @@ public function readNotifications() /** * Get the entity's unread notifications. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ public function unreadNotifications() { diff --git a/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php index 49cb3b98ae9a..18ced6ec099d 100644 --- a/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseFailedJobProvider.php @@ -163,7 +163,7 @@ public function count($connection = null, $queue = null) /** * Get a new query builder instance for the table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function getTable() { diff --git a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php index b3192f246beb..804c0d1140c2 100644 --- a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php @@ -176,7 +176,7 @@ public function count($connection = null, $queue = null) /** * Get a new query builder instance for the table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function getTable() { diff --git a/src/Illuminate/Session/DatabaseSessionHandler.php b/src/Illuminate/Session/DatabaseSessionHandler.php index 0770c22f46e9..6db8d5666cb3 100644 --- a/src/Illuminate/Session/DatabaseSessionHandler.php +++ b/src/Illuminate/Session/DatabaseSessionHandler.php @@ -284,7 +284,7 @@ public function gc($lifetime): int /** * Get a fresh query builder instance for the table. * - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function getQuery() { diff --git a/src/Illuminate/Validation/DatabasePresenceVerifier.php b/src/Illuminate/Validation/DatabasePresenceVerifier.php index 9229f06b708a..acaa2a740b7d 100755 --- a/src/Illuminate/Validation/DatabasePresenceVerifier.php +++ b/src/Illuminate/Validation/DatabasePresenceVerifier.php @@ -75,7 +75,7 @@ public function getMultiCount($collection, $column, array $values, array $extra * * @param \Illuminate\Database\Query\Builder $query * @param array $conditions - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function addConditions($query, $conditions) { @@ -117,7 +117,7 @@ protected function addWhere($query, $key, $extraValue) * Get a query builder for the given table. * * @param string $table - * @return \Illuminate\Database\Query\Builder + * @return \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> */ protected function table($table) { diff --git a/types/Database/Eloquent/Builder.php b/types/Database/Eloquent/Builder.php index ba46642414eb..93b623e8f7ae 100644 --- a/types/Database/Eloquent/Builder.php +++ b/types/Database/Eloquent/Builder.php @@ -12,7 +12,10 @@ use function PHPStan\Testing\assertType; -/** @param \Illuminate\Database\Eloquent\Builder<\User> $query */ +/** + * @param \Illuminate\Database\Eloquent\Builder<\User> $query + * @param \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> $queryBuilder + */ function test( Builder $query, Post $post, diff --git a/types/Database/Query/Builder.php b/types/Database/Query/Builder.php index 55ae1c28edc0..e4627b703935 100644 --- a/types/Database/Query/Builder.php +++ b/types/Database/Query/Builder.php @@ -7,54 +7,112 @@ use function PHPStan\Testing\assertType; -/** @param \Illuminate\Database\Eloquent\Builder<\User> $userQuery */ -function test(Builder $query, EloquentBuilder $userQuery): void +/** + * @param \Illuminate\Database\Query\Builder<\PDO::FETCH_OBJ> $query + * @param \Illuminate\Database\Eloquent\Builder<\User> $userQuery + */ +function testWithFetchObj(Builder $query, EloquentBuilder $userQuery): void { assertType('object|null', $query->first()); assertType('object|null', $query->find(1)); assertType('int|object', $query->findOr(1, fn () => 42)); assertType('int|object', $query->findOr(1, callback: fn () => 42)); - assertType('Illuminate\Database\Query\Builder', $query->selectSub($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->fromSub($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->from($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->joinSub($userQuery, 'alias', 'foo')); - assertType('Illuminate\Database\Query\Builder', $query->joinLateral($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->leftJoinLateral($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->leftJoinSub($userQuery, 'alias', 'foo')); - assertType('Illuminate\Database\Query\Builder', $query->rightJoinSub($userQuery, 'alias', 'foo')); - assertType('Illuminate\Database\Query\Builder', $query->crossJoinSub($userQuery, 'alias')); - assertType('Illuminate\Database\Query\Builder', $query->whereExists($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->orWhereExists($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->whereNotExists($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->orWhereNotExists($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->orderBy($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->orderByDesc($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->union($userQuery)); - assertType('Illuminate\Database\Query\Builder', $query->unionAll($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->selectSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->fromSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->from($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->joinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<5>', $query->joinLateral($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->leftJoinLateral($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->leftJoinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<5>', $query->rightJoinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<5>', $query->crossJoinSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<5>', $query->whereExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->orWhereExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->whereNotExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->orWhereNotExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->orderBy($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->orderByDesc($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->union($userQuery)); + assertType('Illuminate\Database\Query\Builder<5>', $query->unionAll($userQuery)); assertType('int', $query->insertUsing([], $userQuery)); assertType('int', $query->insertOrIgnoreUsing([], $userQuery)); $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('object', $users); + assertType('array|object', $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('object', $users); + assertType('array|object', $users); assertType('int', $page); }); $query->eachById(function ($users, $page) { - assertType('object', $users); + assertType('array|object', $users); + assertType('int', $page); + }); +} + +/** + * @param \Illuminate\Database\Query\Builder<\PDO::FETCH_ASSOC> $query + * @param \Illuminate\Database\Eloquent\Builder<\User> $userQuery + */ +function testWithFetchArr(Builder $query, EloquentBuilder $userQuery): void +{ + assertType('array|null', $query->first()); + assertType('array|null', $query->find(1)); + assertType('array|int', $query->findOr(1, fn () => 42)); + assertType('array|int', $query->findOr(1, callback: fn () => 42)); + assertType('Illuminate\Database\Query\Builder<2>', $query->selectSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->fromSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->from($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->joinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<2>', $query->joinLateral($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->leftJoinLateral($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->leftJoinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<2>', $query->rightJoinSub($userQuery, 'alias', 'foo')); + assertType('Illuminate\Database\Query\Builder<2>', $query->crossJoinSub($userQuery, 'alias')); + assertType('Illuminate\Database\Query\Builder<2>', $query->whereExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->orWhereExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->whereNotExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->orWhereNotExists($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->orderBy($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->orderByDesc($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->union($userQuery)); + assertType('Illuminate\Database\Query\Builder<2>', $query->unionAll($userQuery)); + assertType('int', $query->insertUsing([], $userQuery)); + assertType('int', $query->insertOrIgnoreUsing([], $userQuery)); + + $query->chunk(1, function ($users, $page) { + assertType('Illuminate\Support\Collection', $users); + assertType('int', $page); + }); + $query->chunkById(1, function ($users, $page) { + assertType('Illuminate\Support\Collection', $users); + assertType('int', $page); + }); + $query->chunkMap(function ($users) { + assertType('array|object', $users); + }); + $query->chunkByIdDesc(1, function ($users, $page) { + assertType('Illuminate\Support\Collection', $users); + assertType('int', $page); + }); + $query->each(function ($users, $page) { + assertType('array|object', $users); + assertType('int', $page); + }); + $query->eachById(function ($users, $page) { + assertType('array|object', $users); assertType('int', $page); }); }