Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

WallyJ's avatar

Blade syntax to query all of 3rd Level Eloquent Relationship

If I have set up all of my relationships correctly for Users->Contacts->Deals->Tasks, and I eager load all of a user's contacts into a view with their deals and tasks:

    public function index()
    {
        //Look up contacts associated with the logged in user
        $user_id = auth()->user()->id;
        $contacts = Contact::with(['contactnotes' => function($query) {
            $query->orderBy('updated_at', 'desc');
            }])
            ->with('deals.tasks')
            ->orderBy('contactlastseen', 'desc')
            ->take(10)
            ->where('user_id', '=', $user_id)->get();
        return view('dashboard')
            ->with('contacts', $contacts)
            ->with('tasks', $tasks);
    }

How do I query a list of all tasks within a blade view? Meaning all tasks from all deals for all contacts. Or would I have to define a separate variable for all tasks in the controller? All of them are in the collection I loaded so I should be able to have them already, right?

If I try:

<ul>
     @foreach($contacts->deals->tasks as $task)
           <li>{{ $task->tasktext }}</li>
     @endforeach
</ul>

I receive the error: Property [deals] does not exist on this collection instance.

0 likes
8 replies
tykus's avatar

You need nested loops to iterate over the contacts, then each contact's deals and finally each deal's tasks:

<ul>
@foreach($contacts as $contact)
    @foreach($contact->deals as $deal)
        @foreach($deal->tasks as $task)
            <li>{{ $task->tasktext }}</li>
        @endforeach
    @endforeach
@endforeach
</ul>

Assuming a Contact hasMany Deals, and a Deal hasMany Tasks

WallyJ's avatar

That makes sense, but then how would I reference a property in reverse? For instance, with your suggestion I end up with a list of tasks like:

Task1

Task2

Task3

But what if I then wanted to put the type of deal next to each task (Type is a field in the Deal table/object) so it would look something like:

Task1 - Contract

Task2 - Purchase Order

Task3 - Bulk Sale

And if I wanted to go one more rung up the ladder backwards, how would I do that, so it could also show the contact:

Task1 - Contract - Mark Jones

Task2 - Purchase Order - Ben Burns

Task3 - Bulk Sale - Mike McDonald

And to complicate matters, what if I decide to list the tasks by their due date? Using a @foreach will list each task by their due date in order for each deal, within each contact, not overall.

Sounds like I might need to query all of the tasks within the controller with its own variable to order them as as group correctly. I would also still need to reference the reverse relationships.

tykus's avatar
tykus
Best Answer
Level 104

You have access to the current $deal and $contact from those other loops (in the simplest case)

<ul>
@foreach($contacts as $contact)
    @foreach($contact->deals as $deal)
        @foreach($deal->tasks as $task)
            <li>{{ $task->tasktext }} - {{ $deal->name }} - {{$contact->fullname}} </li>
        @endforeach
    @endforeach
@endforeach
</ul>

If you wanted to sort the Tasks by their due_date, then you really should start from that model instead:

$tasks = Task::orderBy('due_date')
    ->whereHas('deal.contact', function ($builder) use ($user_id) {
        $builder->where('user_id', $user_id);
    }) 
    ->with(['deal.contact' => function ($builder) use ($user_id) {
        $builder->where('user_id', $user_id);
    })  
    ->get(); 

The orderBy('contactlastseen', 'desc') from the original query no longer makes sense so I omitted it. The take(10) is a little more involved in this approach, so again I omitted for clarity.

WallyJ's avatar

Makes sense.

Though, if I run the query in the controller, how do I reference a relationship in reverse?

If I use Task::... How do I reference backwards, in the "where" statement, to limit the tasks to the currently logged-in user? There is no user_id field in the task table. Do I still use with(contacts.deals)? Since I'm starting with the deepest connected relationship?

I've never built queries starting at the far end of the relationship.

tykus's avatar

The whereHas is scoping the Task query to return only tasks where the associated Deal's Contact belongs to $user_id - is $user_id representing the authenticated user? If there is some other property on Task or Deal which scopes the query to the authenticated user, then you could use that instead.

WallyJ's avatar

Sorry. I wrote my "Makes sense" response looking at the thread before I saw your explanation. It may have looked like I responded without reading your reply. And technically I did. :)

Again, sorry for any confusion. I will work through that. Thanks so much for your help!

WallyJ's avatar

I was able to get the $tasks to list the tasks by date.

But when I try to reference the other relationships I get errors using this line (The task text will show fine but I receive errors when I try to add the related fields:

 <li>{{ $task->tasktext }} - {{ $deal->name }} - {{$contact->fullname}} </li>

UPDATE: I just realized that I have to write the relationship syntax in reverse, and use:

{{ $task->deal->contact->contact1firstname }}

in my blade template.

Is that how you would do it?

tykus's avatar

Yes, whenever you changed the query, you no longer needed the nested loops. The only word of caution I would have is the mitigate for non-objects, i.e. if $task->deal is null, or $task->deal->contact is null, then you will get an error. You can use the Laravel data_get helper in cases like this:

{{ data_get($task, 'deal.contact.contact1firstname', '') }}

The empty string can be any default fallback value you choose.

Please or to participate in this conversation.