Skip to content

SoftDeletes methods (onlyTrashed, withTrashed) fail on base Builder instances #635

@alies-dev

Description

@alies-dev

Problem

SoftDeletes trait declares @method static for withTrashed(), onlyTrashed(), and withoutTrashed(). These work for static model calls (Member::onlyTrashed()) via Psalm's pseudo_static_methods, but fail on builder instances:

User::query()
    ->whereHas('payment_method', fn(Builder $q) => $q->whereNull('deleted_at'))
    ->onlyTrashed() // ❌ UndefinedMagicMethod
    ->get();

At runtime, SoftDeletingScope registers these as Builder macros via Builder::macro(), so they work on any Builder instance. Psalm doesn't know about this.

Context

PR #632 fixed this for models with custom builders by detecting @method static annotations returning Builder<static> and registering them on the custom builder class. But for models using the base Builder (the common case), there's no equivalent handler.

Suggested fix

Register trait-declared builder methods (like SoftDeletes' withTrashed/onlyTrashed/withoutTrashed) on the base Builder class as well, not just custom builders. This could be done in BuilderScopeHandler or via a new handler registered for Builder::class that checks the model's pseudo_static_methods for builder-returning methods.

The tricky part is that Builder<TModel> is generic — the handler needs to resolve the model from the template parameter to know which trait methods are available.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions