I have 3 model named User, Product and Review. I used one to many relationship between User and Product, on the other hand I use one to many between Product and Review.
I can find related product from user model and find related reviews from product model.
How can I find related product reviews from user model?
You should use has-many-through relationship.
https://laravel.com/docs/9.x/eloquent-relationships#has-many-through
The "has-many-through" relationship provides a convenient way to access distant relations via an intermediate relation.
Related
Say I have four tables:
users
groups
activities
group_activities
Where a group can have any number of activities and an activity can belong to any number of groups through their intermediate table group_activities, and a user belongs to one group via users.group_id. I want to correctly model a relationship between users and activities so that a user can have any one activity, but only if the group that user belongs to has a relation to that activity.
HasOneThrough doesn't seem to work here, since the group the user is related through has multiple activities. HasManyThrough doesn't work since the user can only have one.
I want to properly model this relationship so that it can be picked up for selection automatically via a Nova relationship field, but I'm struggling to figure out exactly how I would do so. My first thought is a HasOneThrough relation with some set of subqueries, but I can't quite piece together where to start.
How would I do this, or conversely, is it possible via Eloquent's automatic relationship system at all?
To ensure we are on the same idea of your relationships:
The relationship between Groups and Activities is a Many To Many relationship (Many To Many - Larvel Documentation).
The group_activities table is the pivot table.
The relationship between users and groups is a One To Many relationship One To Many - Larvel Documentation and the inverse of it One To Many (Inverse) - Larvel Documentation.
To actually answer your question:
If you want to use a shortcut from users to their activities, the Has Many Through is the correct way. If a group can have arbitrary many activities, and a user belongs to one group, the user will be associated to these arbitrary many activities through the group -hence Has Many Through. Note that this is not really a separate relationship though, it's just a convient shortcut.
If you wan't to associate a user with a single Activity directly, you need to to this via a separate One to Many relationship between Users and Activities.
I'm not entirely sure if I interpret your question correctly, so the following is just an assumption, but do you want to ensure a user can only be associated to an activity thats also associated with the user group? So to restrict possible activites by group? If that is the case, you'd simply need to check if the selected activity is in the activities associated with the users group:
With your relationships set up like this:
class User {
public function activity(){
return $this->belongsTo('App\Activity');
}
public function possibleActivities(){
return $this->hasManyThrough('App\Activity','App\Group');
}
}
you can check and associate activities like this:
if( $user->possibleActivities()->contains( $activity ) ){
$user->activity()->associate($activity);
}
I'm curious why the Eloquent relationship for hasMany has a different signature than for belongsToMany. Specifically the custom join table name-- for a system where a given Comment belongs to many Roles, and a given Role would have many Comments, I want to store the relationship in a table called my_custom_join_table and have the keys set up as comment_key and role_key.
return $this->belongsToMany('App\Role', 'my_custom_join_table', 'comment_key', 'role_key'); // works
But on the inverse, I can't define that custom table (at least the docs don't mention it):
return $this->hasMany('App\Comment', 'comment_key', 'role_key');
If I have a Role object that hasMany Comments, but I use a non-standard table name to store that relationship, why can I use this non-standard table going one way but not the other?
hasMany is used in a One To Many relationship while belongsToMany refers to a Many To Many relationship. They are both distinct relationship types and each require a different database structure - thus they take different parameters.
The key difference is that in a One To Many relationship, you only need the two database tables that correspond to the related models. This is because the reference to the relation is stored on the owned model's table itself. For instance, you might have a Country model and a City model. A Country has many cities. However, each City only exists in one country. Therefore, you would store that country on the City model itself (as country_id or something like that).
However, a Many To Many relationship requires a third database table, called a pivot table. The pivot table stores references to both the models and you can declare it as a second parameter in the relationship declaration. For example, imagine you have your City model and you also have a Car model. You want a relationship to show the types of cars people drive in each city. Well, in one city people will drive many different types of car. However, if you look at one car type you will also know that it can be driven in many different cities. Therefore it would be impossible to store a city_id or a car_id on either model because each would have more than one. Therefore, you put those references in the pivot table.
As a rule of thumb, if you use a belongsToMany relationship, it can only be paired with another belongsToMany relationship and means that you have a third pivot table. If you use a hasMany relationship, it can only be paired with a belongsTo relationship and no extra database tables are required.
In your example, you just need to make the inverse relation into a belongsToMany and add your custom table again, along with the foreign and local keys (reversing the order from the other model).
Try to understand with text and a figure.
One to One(hasOne) relationship:
A user has(can have) one profile. So, a profile belongs to one user.
One to many(hasMany):
A user has many(can have many) articles. So, many articles belong to one user.
Many to many(BelongsToMany):
A User can belong to many forums. So, a forum belongs to many users.
I have a model Status which have a name attribute.
Further I have two (later maybe n) other Models which can have a Status assigned by a belongsTo() relationship.
Now I want to have constraints that model A could only have specific statuses and model B other specific statuses.
My problem is that I don´t know which would be a good design to enforce these constraints.
Of course I could have different models for statuses for A and B but I already have the status table and model B was added later.
I thought maybe I could add a model AStatus and BStatus and so on, which would have a foreign key to a Status, but then I don´t know how I would express this relationship in Laravel Eloquent.
Another option would be to add a attribute to the existing Status table which indicates which Model is allowed to have this status. But this doesn't feels like a good idea, because then I could not express that multiple Models could have the same status.
I hope you can help me!
Say I have models User and Post
Clearly, User hasMany Post
But now I want to have subscriptions.
Do I create a second relationship user/post relationship that is HABTM in addition to the relationship they already have?
I think that you need to create the relation User belongsTo Subscriptions.
I do not see that having a relationship HABTM
You could do this with two different hasMany associations, a new belongsTo association, or a new HABTM association. It really depends on how you want to organize and access your data.
To answer the question you posed in the comments, yes, it's possible to have multiple relationships between the same set of Models. Please read: Multiple relations to the same model from the Cake book.
Of course you can add more than one relation on the same model. You han have: User hasMany Post
User hasMany Subscription
Post belongsTo User
Subscription belongsTo User
On both direction you can get all users's posts and posts that belong to users.
I am trying to get my head around some relationships between models in Laravel.
I would like to define the relationship between the following models:
User - the users
Campaign - a campaign
Call - a phone call
Lead - Lead/client
Sale - A sale
Appointment - A scheduled phone call.
This is the way the relationship should be:
A user can be assigned to many campaigns.
A user can have many calls.
One user can have many appointments.
One call belongs to one user.
One call belongs to one campaign.
A campaign can have many calls.
A campaign can have many sales
A lead can be assigned to many campaigns.
A lead can have many sales
One lead belongs to one user in one campaign.
One lead can have many calls
One sale belongs to one campaign
One sale belongs to one user
One appointments belongs to one lead.
I uncertain about how to setup the relationship. Its easy with the one-to-one or many-to-one.
But what about this:
Call->User (one-to-one)
Call->Campaign (one-to-one)
Campaign->Call (one-to-many)
User->Call (one-to-many)
Hopefully I managed to explain it clear enough. Thanks.
I'd recommend reading the Laravel docs on relationships again.
Taking the Call/User relationship as an example, here's what you're looking at:
User --------> Call
(one) (many)
This is clearly a one to many relationship. And if you want to set up both sides of this relationship in terms of an Eloquent model, the relationships are this:
A User hasMany Calls
A Call belongsTo a User
These terms are taken directly from the documentation, in the section entitled One To Many.
So, in code...
User Model
public function Calls()
{
$this->hasMany("Call");
}
(notice that I've made the method name plural - this is my own preference, since using $user->Calls will return a collection if items).
Call Model
public function User()
{
$this->belongsTo("User");
}
Rather than taking the two sides of a relationship in isolation, as you seem to have done in your analysis, consider both sides of the relationship at the same time.