[12.x] allow for custom builders in QueriesRelationships closures#55362
[12.x] allow for custom builders in QueriesRelationships closures#55362zjbarg wants to merge 1 commit intolaravel:12.xfrom
Conversation
|
Thanks for attempting this---however, what you are trying to do is not possible with PHPDocs alone. Given the following example: /**
* Add a relationship count / exists condition to the query with where clauses.
*
* @template TRelatedModel of \Illuminate\Database\Eloquent\Model
+ * @template TRelatedModelBuilder of \Illuminate\Database\Eloquent\Builder<TRelatedModel>
*
* @param \Illuminate\Database\Eloquent\Relations\Relation<TRelatedModel, *, *>|string $relation
- * @param (\Closure(\Illuminate\Database\Eloquent\Builder<TRelatedModel>): mixed)|null $callback
+ * @param (\Closure(TRelatedModelBuilder): mixed)|null $callback
* @param string $operator
* @param int $count
* @return $this
*/
public function whereHas($relation, ?Closure $callback = null, $operator = '>=', $count = 1)method templates can only be used to extract type information from the given parameters. Meaning that:
Also, the existing template type ( $query->whereHas($user->posts(), function ($query) {
// Knows that it's a Post Builder!
assertType('Illuminate\Database\Eloquent\Builder<Illuminate\Types\Builder\Post>', $query);
});When using my published Larastan fork, Larastan is able to correctly determine what the builder type is and it handles custom builders (including nested relations). Here's some examples from the test suite: User::query()->whereHas('accounts', function (Builder $query) {
assertType('Illuminate\Database\Eloquent\Builder<App\Account>', $query);
});
User::query()->withWhereHas('accounts.posts', function (Builder|Relation $query) {
assertType('App\PostBuilder<App\Post>|Illuminate\Database\Eloquent\Relations\BelongsToMany<App\Post, App\Account, Illuminate\Database\Eloquent\Relations\Pivot, \'pivot\'>', $query);
});I upstreamed a PR with this functionality, however, the PR was closed because there's still some limitations with PHPStan...I guess it's a matter of if you want no support at all until it's fully supported or mostly supported with some quirks. In the meantime, if you want your editor and phpstan to know the builder type (without using my fork), you can narrow the type: OrderItem::query()->whereHas('order', function ($q) {
assert($q instanceof OrderBuilder);
return $q->shipped();
});Best, |
The closure types added in #54452 are not accounting for custom query builders, and therefore the following code is triggering a
argument.typeerror because the closure expectsBuilder<Order>