Laravel Eloquent disable automatic eager loading
In the example you provided, the relationship data is "lazy loaded" not "eager loaded". This means the relationship data is not actually loaded until you first access the property.
However, Eloquent can "eager load" relationships at the time you query the parent model. Eager loading alleviates the N + 1 query problem.
To eager load a relationship, you can use eloquent with()
method:
$posts = Post::published()->with('author')->get();
Now each post
will be eagger loaded with the author
model, thus avoiding the N + 1 problem.
You can also specify if you would like to eager load specific columns only:
$posts = Post::published()->with('author:id,name')->get();
And if you want to always load a relationship when retrieving a model, you can define a $with property on the model:
class Post extends Model
{
/**
* The relationships that should always be loaded.
*
* @var array
*/
protected $with = ['author'];
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
More information: https://laravel.com/docs/6.x/eloquent-relationships#eager-loading
It's pretty simple, just add to your model:
public $preventsLazyLoading = true;
Then if you try to lazy load a relation you will get an error
Illuminate\Database\LazyLoadingViolationException : Attempted to lazy load [<ModelName>] on model [<ModelName>] but lazy loading is disabled.
You can also create a parent class for your models, something like
abstract class Model extends \Illuminate\Database\Eloquent\Model
{
public $preventsLazyLoading = true;
}
Then extend it in your models and they automatically will disable lazy load.
For Laravel 8 or later versions
There's another way to disable it in app service provide
// app/Providers/AppServiceProvider.php
public function boot()
{
Model::preventLazyLoading(! app()->isProduction());
}
As it says, it will disable lazy load for all environments except production.
Reference
It's lazy load the example you provided as @LobsterBaz mentioned. If you want to avoid that:
You can check if the relationship is loaded
foreach ($posts as $p) {
if ($p->relationLoaded('author')) echo $p->author->name;
}
But I found this package Laravel Disable Lazy Load which has all the credit for the idea.
That is very simple. By borrowing the idea you can create a Trait yourself to do it overriding the getRelationshipFromMethod.
<?php
namespace App\Traits;
trait DisableLazyLoad
{
public function getRelationshipFromMethod($method)
{
throw new \BadMethodCallException('Relation lazy load has been disabled for performance reasons.');
}
}
Then use the Trait in your Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\DisableLazyLoad;
class Post extends Model
{
use DisableLazyLoad;
}
or install the package an use it in the same way
composer require ybaruchel/laravel-disable-lazyload