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

mdev11's avatar

hasOneThrough returns the first row not the related row

I have these tables:

grades:

id | grade
 1  |   1
 2  |   2

services:

id | service
1  | service1
2  | service2

comments:

id | comment
1   | comment1
2   | comment2
3   | comment3  

comments_services:

id | service_id | grade_id(nullable) | comment_id
1  |      1     |         null       |      1
2  |      1     |           2        |      2
3  |     2      |            3       |      3 

In comments model:

public function services(){
     return $this->belongsToMany(Service::class, 'comments_services', 'comment_id', 'service_id', 'id')- 
   		>with('grade');
}

In services model:

public function grade(){
    return $this->hasOneThrough(Grade::class, Service::class, 'service_id', 'id', 'id', 'grade_id');
}		

The issue is with the grade function it should return the related grade but it returns the first row instead.

This is the query generated:

select `grade_id``, `grade`, `comments_services`.`service_id` as `laravel_through_key` from `grades` inner join `comments_services` on `comments_services`.`grade_id` = `grades`.`id` where `comments_services`.`service_id` in (1, 2, 6, 10)

So if there are multiple comments with service_id in (1,2,6,10) it would return the first one.

I think the solution would be to include the comment_id in the query or condition with comments_services.id.

Also, I tried to split this relation instead of using hasOneThrough:

//comments model
public function services(){
    return $this->belongsToMany(Service::class, 'comments_services', 'comment_id', 'service_id', 'id')->with('ser');
}	

//services model
public function ser(){
	 return $this->hasOne(Service::class, 'service_id', 'id')->with('grade')
}

//comments_services model
public function grade(){
	 return $this->belongsTo(Grade::class, 'grade_id', 'id')
}

This is not working either.

CommentController:

public function edit(Request $request, Comment $comment){
    return view('comments.edit', compact('comment'));
}

In the case of hasOneThrough this would be returned for all comments with grades:

"grade" => array:3 [▼
    "id" => 1
    "grade" => "1"
    "laravel_through_key" => 2
  ]
0 likes
9 replies
AungHtetPaing__'s avatar

@mdev11 I think your hasOneThrough key convention doesn't seem correct. Your tables are more likely many-to-many 3 tables relationship. So may be try with many-to-many relationship

//services model
public function grades() {
	return $this->belongsToMany(Grade::class);
}

//grade model
public function services() {
	return $this->belongsToMany(Service::class);
}
mdev11's avatar

@AungHtetPaing__

This returns all the records within commens_services with grade_id

[
    {"id":1,"grade":"2","pivot":{"service_id":,"grade_id":1}},
    {"id":5,"grade":"3","pivot":{"service_id":,"grade_id":5}}
]
AungHtetPaing__'s avatar

@mdev11 yeah is that not what you want?

The issue is with the grade function it should return the related grade but it returns the first row instead.

So I assumed you are trying to get grade data from service like this $service->grade

mdev11's avatar

@AungHtetPaing__

That's right but it should only return 1.

So for example for comments_services.comment_id = 3 it should return the grade.id = 3 only not 3 & 2

AungHtetPaing__'s avatar

@mdev11 ah you also want to check condition with comment_id. How about thinking comment_services table as model since you have three foreign key in table.

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class CommentService extends Model
{
    public function grade() {
		return $this->belongsTo(Grade::class);
	}
}

$commentService = CommentService::where('service_id', $serviceId)->where('comment_id', $commentId)->get();

$commentService->grade;
mdev11's avatar

@AungHtetPaing__

Would it work with route model binding?

public function edit(Request $request, Comment $comment){
    return view('comments.edit', compact('comment');
}
AungHtetPaing__'s avatar

@mdev11 what route model binding do you mean?

public function edit(Request $request, Comment $comment){
	$commentService = CommentService::where('service_id', $serviceId)->where('comment_id', $comment->id)->get();
	$commentService->grade;
    return view('comments.edit', compact('comment');
}
mdev11's avatar

@AungHtetPaing__

What about multiple comments?

public function create(Request $request, Application $application){
      $comments = $application->comments->load('author', 'decision', 'services.grades');

      return view('comments.add', compact('comments'));
}

Please or to participate in this conversation.