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

csprick's avatar

Accessing properties through relationships

I'm working on a gradebook and have a bunch of related tables - assignments, students, grades. Assignment hasMany(grades), Student hasMany(grades), Grade belongsTo(assignment), Grade belongsTo(student)

I'm passing in $assignment to a view. In the view, I'm accessing:

{{$assignment->grades->where('user_id',3)}}

and get this, which is the right data:

[{
"id":1,
"user_id":3,
"assignment_id":1,
"score":"78",
"mark":"Good",
"feedback":"That was pretty good",
"attempt":1,
"late":false,
"created_at":"2019-09-29 10:27:53",
"updated_at":"2019-09-29 10:27:53"
}]

I'd like to show just the value of 'score' or 'feedback'. I keep getting errors about "property [score] does not exist on this collection instance" if I try to access score like: {{$assignment->grades->where('user_id',3)->score}}

I'm sure this is a simple syntax error, what am I doing wrong?

Here's some of the route parameters from ignition. I'm on Laravel 6.0.4

{
  "id": 1,
  "name": "Session 1 Quiz",
  "description": "Session 1 FLO quiz",
  "mean": "0",
  "sd": "0",
  "pct": "0",
  "instance_id": 1,
  "type_id": 1,
  "created_at": "2019-09-08 08:55:03",
  "updated_at": "2019-09-08 08:55:03",
  "grades": [
    {
      "id": 1,
      "user_id": 3,
      "assignment_id": 1,
      "score": "78",
      "mark": "Good",
      "feedback": "That was pretty good",
      "attempt": 1,
      "late": false,
      "created_at": "2019-09-29 10:27:53",
      "updated_at": "2019-09-29 10:27:53"
    },
    {
      "id": 2,
      "user_id": 9,
      "assignment_id": 1,
      "score": "22",
      "mark": "unsatisfactory",
      "feedback": "Try again, that was hopeless.",
      "attempt": 1,
      "late": false,
      "created_at": "2019-09-29 10:32:53",
      "updated_at": "2019-09-29 10:32:53"
    }
  ],
0 likes
9 replies
tykus's avatar
tykus
Best Answer
Level 104

This returns a Collection, not an individual instance;

$assignment->grades->where('user_id',3)

if you expect an individual instance, then chain on the first() Collection method before score:

$assignment->grades->where('user_id',3)->first()->score

If you expect a Collection of Grades, then you will need to map over the Collection to get only the attributes you need:

$assignment->grades->where('user_id',3)->map(function ($grade) {
    return [
        'score' => $grade->score,
        'feedback' => $grade->feedback,
    ];
});

You would need to iterate over the resulting collection to display each score/feedback in your view.

While all of the above is valid, I wonder why you are filtering a Collection by user_id in the first place . Is there an opportunity to use the query to return only the results you needed.

2 likes
csprick's avatar

Thanks for helping tykus. I tried to add first() as you said and now I get: Trying to get property 'score' of non-object

I'm actually iterating over multiple users/students:

@foreach($assignment->instance->users as $student)

    <div class="row">

        <div class="col-2 bg-light">{{$student->name}} {{$student->surname}}</div>

        <div class="col-1">{{$student->fan}}</div>

        <div class="col-1">

            <a class="btn btn-success btn-sm" href="{{route('grades.create',['assignment'=>$assignment->id, 'student'=>$student->id])}}" role="button">Add</a>

            <a class="btn btn-primary btn-sm disabled"  href="{{route('grades.edit',['grade' => $student->grade])}}" role="button">Edit</a>

        </div>

        <div class="col-1">{{$assignment->grades->where('user_id',$student->id)->first()->score}}</div>

    </div>

    @endforeach

I might eventually have multiple entries of a grade for an assignment/user combination, so getting a collection and looking at either the latest or iterating over all of them seems reasonable as long as I can get to the individual elements in grade.

tykus's avatar

I tried to add first() as you said and now I get: Trying to get property 'score' of non-object

The result was an empty Collection.

Why don't you eager-load the grade with the student rather than managing two separate collections?

csprick's avatar

Yes, an empty collection on one of the other students. Obviously I'm new to this, and it did seem complicated the way I was doing it.

Can you please explain eager loading in this case? Everything is related, so I thought I could just traverse up and down the relations?

csprick's avatar

The structure is a topic (instance) that has multiple assignments and multiple students enrolled in that topic. I want to show for a particular assignment, a list of all the students enrolled in the topic and the grade details (score, feedback, ...) for that assignment (if they exist), and leave them blank if they don't exist.

So in assignment.show, how would you eager load all of the required info on students and grades for that assignment?

How would eager loading of this avoid the problem of a student not having a grade?

-clem's avatar

How about just checking if the grade exists before trying to display it?

@if($assignment->grades->where ('user_id', $student->id)->first()->score)
    <div>{{$assignment->grades->where('user-id', $student->id)->first()->score}}</div>
@else
   <div>No grade</div>

It can be made prettier with a bit more forethought as to what you pass into the view, but you get the idea.

csprick's avatar

Thanks clem. But I'm not sure that's working. I'm getting the "trying to get property of non-object" again. This is even if I take off ->score and even ->first()->score from the conditional. I even tried @if(count(...)>0). I think it must be evaluating the result of the if even if it's not true?

When I remove the {{$assignment->...}} from the div following the if (replace with "grade exists"), it shows properly, so I'm sure that the expression in the true area gets evaluated regardless of the conditional - but not displayed if the conditional is false.

@if(count($assignment->grades->where ('user_id', $student->id)))
            <div>{{$assignment->grades->where('user-id', $student->id)->first()->score}}</div>
        @else
           <div>No grade</div> 
        @endif
csprick's avatar

Thanks for all the help guys. I've learned a bunch of syntax.

I think I'm going to change my approach and instead of listing all of the students and trying to match up grades to those who have them, I'll create two lists that are formatted differently. One for those with grades and one for those without.

-clem's avatar

I think it must be evaluating the result of the if even if it's not true?

You're right, I always forget that. To check if it exists you want to use isset(). Same way you used count()

Sounds like a new approach would be good, but this is still useful to know!

1 like

Please or to participate in this conversation.