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

mstarkey's avatar

Property does not exist on this collection instance.

I keep coming up agains this problem it doesn't make any sense as the data I am requesting has data and is nested in a collection, I solved it with another instance by using find(1) in the query but that was for one result with multiple relationships i.e. user->projects.

The following code returns a json object with the correct data.

ReportsController.php

public function report(Request $request)
    {
        $time = explode(" - ", $request->input('time'));
        $client_id = $request->input('client_id');
        $start = $this->change_date_format($time[0]);
        $end = $this->change_date_format($time[1]);

        $entries = TimesheetEntry::with('client','project','timesheetTask','timesheet')
            ->where('client_id', $client_id)
            ->whereBetween('timesheet_entries.updated_at', [$start, $end])
            ->get();
        $items = collect($entries)->groupBy('project_id');

        return $items;

    }

The thing is, is that when I try to render the view with blade I get the ErrorException (Property [client] does not exist on this collection instance.)

report.blade.php

@foreach($items as $item)
      <tr>
             <td>{{ $item->client->name }}</td>
             <td>{{ $item->project->name }}</td>
      </tr>
@endforeach

I have searched everywhere for a definitive answer but nothing even comes close to explaining it. I have read Adam Wathans book on collections and no mention of this issue.

your help would be so welcome at this point.

many thanks

Mark

0 likes
8 replies
Borisu's avatar

Just a wild guess, but since you're grouping by project id, shouldn't you also resolve that in your call? Meaning:

$item->project_id->client->name
mstarkey's avatar

Sadly not the case. I tried a variety of calls and all seem to rely on a function to render the object as a readable array, I added ->first() and got results but only the first item, its a start but I am trying to do the following:

show the total hrs spent on each project for a client between 2 dates.

I am returning some data now using:

ReportsController.php

public function report(Request $request)
    {
        $time = explode(" - ", $request->input('time'));
        $client_id = $request->input('client_id');
        $project_id = $request->input('project_id');
        $start = $this->change_date_format($time[0]);
        $end = $this->change_date_format($time[1]);

        $entries = TimesheetEntry::with('client','project','timesheetTask','timesheet')
            ->where('client_id', $client_id)
            ->whereBetween('timesheet_entries.updated_at', [$start, $end])
            ->get();
        $items = collect($entries)->groupBy('project_id');
        $items->values()->all();

        return view('_modules.admin.reports.report', compact('items','start','end'));

    }
report.blade.php

@foreach($items->flatten() as $item)
       <tr>
              <td>{{ $item->client->name }}</td>
              <td>{{ $item->project->project_name }}</td>
       </tr>
@endforeach

this gives me a list, but I think it may not be an accurate list and doesn't group.

Borisu's avatar

What I don't understand is why you collect a collection... Just use the groupBy on the $entries var. Tomorrow I'll run it in my test env, and tell you if I have the same problem.

Snapey's avatar

do ALL items have client ?

If you are using php7 you should use the null coalesce operator to avoid crashing if you are missing a relation

<td>{{ $item->client->name ?? 'unset'}}</td>
<td>{{ $item->project->project_name ?? 'unset'}}</td>

replace unset with any string

Borisu's avatar

When I try to execute a similar query I get a normal collection of object and can use it directly, without:

$items = collect($entries)->groupBy('project_id');
$items->values()->all();

You can just use your query and finish it with groupBy('project_id')

$entries = TimesheetEntry::with('client','project','timesheetTask','timesheet')
            ->where('client_id', $client_id)
            ->whereBetween('updated_at', [$start, $end])
            ->get()
        ->groupBy('project_id');

Now use the $entries collection to display info in your view.

I would also recommend to use Carbon for your $start, $end vars..

mstarkey's avatar

ok so the solution is as follows

ReportsController.php

public function report(Request $request)
    {
        $time = explode(" - ", $request->input('time'));
        $client_id = $request->input('client_id');
        $start = $this->change_date_format($time[0]);
        $end = $this->change_date_format($time[1]);

        $entries = TimesheetEntry::with('client','project','timesheetTask','timesheet')
            ->whereHas('project', function($q) use ($client_id){
                $q->where('client_id', '=', $client_id);
            })
            ->whereBetween('updated_at', [$start, $end])
            ->get();

        $items = $entries;
        return view('_modules.admin.reports.report', compact(
            'items',
            'start',
            'end'
        ));
    }

The reason for not using Carbon on the controller is down to preference and the fact I am using bootstrap datepicker and thats how the tutorial demonstrated it. I like an easy life at times :)

The whereHas method is a new addition as I found the data collection needed a more robust relationship and that meant removing the timesheet_entry/client relationship as it was already defined in the project table.

The interesting thing is that the groupBy('project_id') doesn't work at all, not sure why as its has never returned a grouped item by project_id, even thought it is in the primary timesheet_entries table.

still need a solution to that one!

mstarkey's avatar
mstarkey
OP
Best Answer
Level 31

I am not sure this the 'right way' to do it, but it works none the less

The controller


$time = explode(" - ", $request->input('time'));
        $client_id = $request->input('client_id');
        $start = $this->change_date_format($time[0]);
        $end = $this->change_date_format($time[1]);

            $entries = TimesheetEntry::with('project')
                ->whereHas('project', function($q) use ($client_id){
                    $q->where('client_id', '=', $client_id);
                })
                ->whereBetween('updated_at', [$start, $end])
                ->get()->groupBy('project_id');

            return view('_modules.admin.reports.report', compact(
                'entries',
                'start',
                'end'
            ));

and the view

@foreach($entries as $item)
                    <tr>
                        <td>{{ $item[0]->project->client->name }}</td>
                        <td>{{ $item[0]->project->project_name }}</td>
                        <td>{{ $item->sum('mon_hrs') + $item->sum('tue_hrs') + $item->sum('wed_hrs') + $item->sum('thu_hrs') + $item->sum('fri_hrs') + $item->sum('sat_hrs') + $item->sum('sun_hrs') }}</td>
                    </tr>
 @endforeach

It took some thinking, and lots of research, but then what doesn't.

I think the docs need to include some blade examples of collection rendering as there is very little out there for this subject.

anyway, this is my best shot so far

1 like

Please or to participate in this conversation.