ideas icon indicating copy to clipboard operation
ideas copied to clipboard

hasOneThrough on same table would add alias for join

Open GertjanRoke opened this issue 4 years ago • 3 comments

Hello, I think the hasOneThrough relationship should be a bit smarter then it is right now, maybe this also applies for the hasManyThrough relationship but never looked at that one.

Idea:

I'm talking about when you set up a hasOneThrough relationship on one table so the first table is the same as the second table you pass through, so maybe Laravel can maybe check if they are the same and if so add an alias for the join clause.
I think the example below will make it more clear.

Example:

So imagine that you have a page with subpages and they are both stored in the same table called pages and you also have a comment that is stored in a table called comments.

(!) Important to know is that a subpage can not have subpages.

So the tables will look something like this:

pages table:
id
parent_id
name
...

comments table:
id
page_id
message
votes_counter
...

And the models will have this relationships setup:

// Page model
public function parent()
{
    return $this->hasOne(Page::class, 'parent_id');
}

// Comment model
public function page()
{
    return $this->hasOne(Page::class);
}

Now you have two pages Questions about SMTP and How to setup SMTP on Apple and we have a comment that has over 100 upvotes that is connected to the How to setup SMTP on Apple page.

Let's say that on the page where you want to show all comments that has a voting of 100 or more and want to show the parent of the subpage that the comment is connected to.

So the code in most cases would look something like this:

$comments = Comment::where('votes_counter', '>=', 100)->with('page.parent')->get();
// or
$comments = Comment::where('votes_counter', '>=', 100)->with('page.parent')->whereHas('page.parent')->get();

foreach($comments as $comment) {
    echo $comment->page->parent->name;
}

So to show this you need three queries and in most systems that's fine, but if you have a lot of traffic it is not so handy.

Of course you also need to go through two relations to get the name you really wanted, but now you also loaded all the data of the subpage that the comment is connected to and you not even showing it somewhere.

// Query on the comments table
SELECT * FROM comments WHERE `votes_counter` >= 100

// Queries on the pages table
SELECT * FROM pages WHERE `id` IN(3, 4, 5)
// And the next to get the parent
SELECT * FROM pages WHERE `id` IN(1, 2) // 1 and 2 are the parent pages of 3, 4 and 5

So that's why I thought it would be great to use the hasOneThrough relation to load the parent page and save fetching all the data I don't even need.
I understand you can also do this with a join clause yourself but it feels nicer to stick to the options the framework gives and it's maybe a bit better to read.


I will do my best to respond as soon as I can if you have any questions.

GertjanRoke avatar Dec 24 '19 20:12 GertjanRoke

Same situation here.

bianchi avatar Dec 16 '20 14:12 bianchi

+1 same situation here too. If I find a workaround I will post it, maybe someone else has already found a way?

rokaslit6 avatar Jun 22 '21 14:06 rokaslit6

Solved my use case by using accessors - https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators

rokaslit6 avatar Jul 02 '21 14:07 rokaslit6