cashier-stripe: Cashier methods trigger LazyLoadingViolationException

  • Cashier Version: 12.14.0
  • Laravel Version: 8.43.0
  • PHP Version: 8.0.6
  • Database Driver & Version: MySQL 8.0.23

Description:

Laravel’s recently released feature for preventing lazy-loading of Eloquent models triggers a LazyLoadingViolationException when attempting to call hasIncompletePayment().

There are undoubtedly other areas of Cashier, and other first-party Laravel packages, that will trigger similar exceptions. I’m curious what the strategy is to handle situations like this when the offending code is in a vendor package?

Thanks!

Steps To Reproduce:

Call hasIncompletePayment() after setting Model::preventLazyLoading()

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 15 (6 by maintainers)

Most upvoted comments

Thanks @mattkingshott, I took inspiration from your suggestion, and modified it to still be able to detect these kind of issues for other packages, in order to be able to open issues for the packages maintainers and let them know (always useful to report if it can help improve performance for all users).

Model::handleLazyLoadingViolationUsing(function ($model, $relation): void {
  if ($model instanceof Laravel\Cashier\Subscription) {
    return;
  }
});

For anyone following along, here’s the solution I came up with:

Model::preventLazyLoading(! app()->isProduction());

Model::handleLazyLoadingViolationUsing(function($model, $relation) {
    $class = $model->$relation()->getRelated();

    if (Str::startsWith(get_class($class), 'App\\Models')) {
        throw new LazyLoadingViolationException($model, $relation);
    }
});

It works by checking if $relation refers to one of my application models (they all use the App\Models namespace).

If it does, it throws an exception, as it means I’ve introduced lazy loading. By contrast, vendor models that exist outside of the App\Models namespace will be ignored.

Glad it helped.

Maybe then the solution is to use that handle method to only throw exceptions on my own models and silently ignore vendor models such as Subscription.

That sounds like an acceptable way to do it.

Thanks again!

Thanks @mattkingshott, that was super helpful. I forgot about the internal owner calls.

I think there’s nothing we can do about this I’m afraid… Cashier was specifically designed this way and we can’t change this.

Taylor did add a new way to work with the lazy loading warning and that is to log instead of throwing an exception. Check out:

image

Add protected $with = ['subscriptions']; to your billable model. By default this isn’t done because we can’t add this through the trait.