Laravel model with preloaded relation

1.6k Views Asked by At

I have a DB table that stores my job titles so as not to duplicate them, and the Job model has relationships internally, so when I use the Eloquent Model I have to always call or load the JobTitles model over and over again. Is there a way to always use the Job Model with pre-loaded JobTitles inside?

    class Job extends Model
    {
        use Notifiable;

        protected $fillable = [ ... ];

        protected $hidden = [
        'token',
        ];


    
        public function title()
        {
            return $this->belongsTo('App\Models\JobTitle','job_title_id');
        }

        public function people()
        {
            return $this->belongsToMany('App\Models\Person','job_person','job_id','person_id');
        }
        
    }

This is JobTitle model

class JobTitle extends Model
{
    use Notifiable;

    protected $table = "job_titles";

    protected $primaryKey = 'job_title_id';

    protected $fillable = [
        'name',
    ];

    protected $hidden = [
    'token',
    ];


    public function jobs()
    {
        return $this->hasMany('App\Models\Job','job_title_id');
    }

    
}

Now my code inside of the controller looks like this:

 $job = Job::all()->load('title');

It is working fine but when I am calling jobs from people

 $personJobs = Person::find(1)->jobs()->load('title')->get();

gives error, Any ideas how this is done?

3

There are 3 best solutions below

1
On BEST ANSWER

To always use the Job Model with pre-loaded JobTitles inside. you can add $with attribute in your job model:

class Job extends Model
{
   use Notifiable;
   
   protected $with = ['title'];

}
0
On

When calling a relation by the method $person->jobs() you will end up with a query builder, so you can't call the model methods like load() since you dont have an instance of a model.

It would be more efficient to call for the relations before getting the first result (nested eager loading)

$person = Person::with('jobs.title')->find(1);
$personJobs = $person->jobs;

If you dont want to query the person data from the database you should use whereHas

$personJobs = Job::whereHas('people', function($personQueryBuilder) {
    $personQueryBuilder->where('id',1);
})->with('title')->get();
5
On

load() is executed after the query has been executed. It's called lazy-loading. Eager loading is using with().

Eager loading is generally faster than lazy loading (I could be wrong on this one, correct me if I'm wrong).

Also, using jobs() or any other relationship, this will return the relationship object itself, while accessing it via ->Jobs will execute the relationship query and will return a collection (if many) or model (if one).

The reason why $personJobs = Person::find(1)->jobs()->load('title')->get(); doesn't work is because you're essentially saying "The jobs hasMany relationship will load the "title" which won't work because hasMany doesn't have the load() function.

So if you want to solve this, you can do Person::with('jobs.title')->find(1) or Person::find(1)->load('jobs.title')