Skip to content

Detect N+1 queries: dispatch an event on lazy relation load #590

Closed

Description

@KalimeroMK

When a relation is accessed without with(), Active Record silently runs one query per model, and there is currently no way to observe it.

foreach (Customer::query()->all() as $customer) {
    $customer->getOrders();
}

AbstractActiveRecord::retrieveRelation() is the single entry point for lazy loading, so a BeforeLazyRelationLoad event dispatched from EventsTrait would make this detectable by a listener.

EventDispatcherProvider::set(CustomerEventsModel::class, new Dispatcher(new Provider(
    (new ListenerCollection())->add(
        new LazyLoadGuard(LazyLoadGuardMode::Strict),
        BeforeLazyRelationLoad::class,
    ),
)));

// LogicException: Relation "App\Customer::orders" is lazy loaded.

Modes: Off (default — nothing is logged or thrown; the only cost is one event dispatch per lazy load), Log (PSR-3 warning carrying relation name, per-request count and trace), Strict (throws LogicException). Scoping per model class via only/except, on top of the existing use EventsTrait opt-in. Default behaviour of existing code is unchanged.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions