Devyn
Devyn

Reputation: 427

Laravel Polymorphic get all models associated to a single model

I have an Attachment model which uses the morphs table and morphTo function. I have several models but the main ones include Client, Job, Project and Notes (which is also polymorphic) which can contain several Attachments.

A client is the top-level model. A Client has many Jobs. A Job has many Projects.

I am struggling with a single way to return all attachments of a client, including attachments of each Job, Project, and notes of each job/project under a client.

I currently am running several foreach loops and have a working way, but the queries on the page load range from 60-100 depending on the amount of jobs/projects/notes for each client. I run through each job to check if it has an attachment, if so, I loop through them. Then, I run through $job->notes->attachments and display those. From there, I dive into another foreach loop pulling all the job's projects, pulling the attachments from each project and then pulling all the notes and looping through that.

Is there a way within Laravel to get all of the Attachments that are somehow attached to a single Client without looping through the way I have? If not, is there a way I can optimize my loops so I don't have to request the attachments for each job/job's notes/project/project's notes?

Upvotes: 0

Views: 563

Answers (2)

dparoli
dparoli

Reputation: 9171

My advice is to use eloquent-has-many-deep. As example of you can do with that library you can look at the code of three models related with many to many:

class Role extends Model
{    
    public function users()
    {
        return $this->belongsToMany('App\Models\User')->withTimestamps();
    }

    public function permissions()
    {
        return $this->belongsToMany('App\Models\Permission')->withTimestamps();
    }
}

class Permission extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function roles()
    {
        return $this->belongsToMany('App\Models\Role')->withTimestamps();
    }

    public function users()
    {
        return $this->hasManyDeep('App\Models\User', ['permission_role', 'App\Models\Role', 'role_user']);
    }
}

class User extends Authenticatable
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
    
    public function roles()
    {
        return $this->belongsToMany('App\Models\Role')->withTimestamps();
    }

    public function permissions()
    {
        return $this->hasManyDeep('App\Models\Permission', ['role_user', 'App\Models\Role', 'permission_role']);
    }
}

With these relationships in place and 5 tables involved: users, role_user, roles, permission_role and permissions you can retrieve all the permissions of a User model with a call to $user->permissions, that resolves to only one query with all the joins needed.

Upvotes: 0

Matteus Barbosa
Matteus Barbosa

Reputation: 2743

I do this all the time. You just need a way to

"...get all of the Attachments that are somehow attached to a single Client without looping through..."

You must consider custom joins, using Laravel Eloquent:

//client_id input here
    $client_id = 10;
    $att_from_client = Attachment::join('note', function ($join) {
    $join->on('note.id', '=', 'attachment.object_id')
            ->where('attachment.object_type', 'App\\Note');
    })
    ->join('project', 'project.id', '=', 'note.project_id')
    ->join('job', 'job.id', '=', 'project.job_id')
    ->join('client', 'client.id', '=', 'job.client_id')
    ->where('client.id', $client_id)
    ->get();

dd($att_from_client);

Upvotes: 0

Related Questions