ideas
ideas copied to clipboard
Inverse of Eloquent hasManyThrough relationship
I realized that there was no inverse of the hasManyThrough relationship, even though there's an inverse for everything else.
Consider the following minimalist slack-like messaging system
users:
+----------+--------+
| column | type |
+----------+--------+
| id | int |
| username | string |
+----------+--------+
display_names:
+------------+------+
| column | type |
+------------+------+
| id | int |
| user_id | int |
| channel_id | int |
+------------+------+
messages:
+-----------------+------+
| column | type |
+-----------------+------+
| id | int |
| display_name_id | int |
| message | text |
+-----------------+------+
Now ignoring the fact this is definitely over-engineered. Doing
public function messages()
{
return $this->hasManyThrough(Message::class, DisplayName::class);
}
would generate a query similar to
SELECT
*
FROM
`messages`
INNER JOIN
`display_names` ON `display_names`.`id` = `messages`.`display_name_id`
WHERE
`display_names`.`user_id` = ?
But there's no way to go the reverse. IE: having a message model and getting the user. Like such
SELECT
*
FROM
`users`
INNER JOIN
`display_names` ON `display_names`.`user_id` = `users`.`id`
WHERE
`display_names`.`id` = ?
Admitedly this is a contrived example, and I'm not too sure if there's any other use case than a poorly engineered database like this
Just a point of pedantry... The inverse of a HasManyThrough is ALSO a HasManyThrough. What you're asking for should be doable with standard queries. Something like:
User::with('displayNames')->where('display_name.id', $id)
(disclaimer: This is untested code. Use at your own risk)
@mbitokhov: You have to swap the models in your relationship:
public function messages()
{
return $this->hasManyThrough(Message::class, DisplayName::class);
}
The reverse is just the concatenation of two BelongsTo
relationships:
$message->displayName->user
@fletch3555
I thought so too and it didn't really work out. Doing the inverse of a hasManyThrough with a hasManyThrough would require a display_name_id
inside the users
table.
The inverse of a hasMany
to hasMany
is a belongsTo
to belongsTo
. You're thinking of belongsToMany
being the inverse of a belongsToMany
.
@staudenmeir Thanks for the warning!
So you need two relations, belongsToThrough, and belongsToManyThrough x)
Found this : https://github.com/znck/belongs-to-through
Found this : https://github.com/znck/belongs-to-through
very good
Definitely after 2 years there is no more need for you to solve your problem, but for those that still arrive on this issue, there is also a solution available with Laravel methods. Starting with version 5.8, Laravel has hasOneThrough
method (documentation).
In the case of the problem described here, the solution is not very pretty, but it would be something like this:
// Message::class
public function user()
{
return $this->hasOneThrough(User::class, DisplayName::class, 'id', 'id', 'display_name_id', 'user_id');
}