I have a regular pivot table with 2 keys. However, I also have a 3rd column where I want to store a different key with a one to many relationship. Is this possible to have?
Example:
Pivot table:
Organization 1 | Organization 2 | Relation type
1 | 2 | 1
1 | 3 | 2
In this case organization number 1 has a relation with organization number 2 with the relation type being number 1. Organization number 1 also has a relation with organization number 3 with relation type 2.
Now is my question, how do I set up that additional one to many relationship on the pivot table?
What you have here is a ternary relationship. You are saying that an organisation A relates with an organisation B and a relationship type. This is a very uncommon use case because in the vast majority of cases ternary relationships can be simplified to binary ones. You need a very deep inspection of your data model to determine whether your case can be simplified, but assuming that it can't here's my suggestions.
It's worth checking the eloquent docs in particular under Defining Custom Intermediate Table Models for this. Note that this requires Laravel 5.4+ to work.
The following should work:
class OrganisationOrganisationLink extends Pivot {
public relationType() {
return $this->belongsTo(RelationType::class); //You need to specify the foreign key correctly as a 2nd parameter
}
}
Then in your original model:
class Organisation extends Model {
public relatedOrganisation() {
return $this->belongsToMany(self::class)->using(OrganisationOrganisationLink::class);
}
}
Then when making practical use of this you can e.g. do:
$organisation = Organisation::with('relatedOrganisation')->first();
echo "Got ".$organisation->name." which relates to "
.$organisation->relatedOrganisation->first()->name
." with relationship type "
$organisation->relatedOrganisation->first()->pivot->relationshipType()->value('name');
Of course the fields I've assumed may not exist but hopefully you get the idea.
Related
I'm building a DB for a software where authentication is coupled with the companys LDAP Server.
I now have the two tables
AD_Groups
and
AD_Users
Which are joined in the table
AD_UsersXAD_Groups
I already learnt about establishing relationships in eloquent.
The many to many relationship is exemplified in the official docs by this:
https://laravel.com/docs/5.8/eloquent-relationships#many-to-many
Now, as you can see, the following feature of eloquent won't help me much:
"To define this relationship, three database tables are needed: users, roles, and role_user. The role_user table is derived from the alphabetical order of the related model names, and contains the user_id and role_id columns."
I therefore need to override this derived name by using the second parameter, as described here:
"As mentioned previously, to determine the table name of the relationship's joining table, Eloquent will join the two related model names in alphabetical order. However, you are free to override this convention. You may do so by passing a second argument to the belongsToMany method:
return $this->belongsToMany('App\Role', 'role_user');
But as seen in the above example from the docs, the infamous "snake case" is still applied to the name.
However, I'm affraid this might not work for my case.
Admittedly, AD_UsersXAD_Groups is pretty ugly, and I fear that eloquent/lumen will not be able to correctly identify its elements and apply the snake case rule correctly.
But I don't know for sure, and therefore I'm asking you what will be the most likely to work.
Using AD_UsersXAD_Groups or AD_UserXAD_Group
Because you have an "x", the Eloquent magic will never be able to match your table automatically.
You can override the table name in the relationship in your User model. You can also specify the keys if they are not Eloquent's expected "group_id" and "user_id":
function groups() {
return $this->belongsToMany(GroupModel::class, 'AD_UsersXAD_Groups', 'user_id_key', 'group_id_key')
}
And in your Group model you could do this to reverse it
function users() {
return $this->belongsToMany(UserModel::class, 'AD_UsersXAD_Groups', 'group_id_key', 'user_id_key')
}
I have 3 tables namely A, B and C.
All 3 of them need to make use of a notes table(i.e. A has many notes and a note belongs to A. The same follows for remaining 2 tables).
So I created 3 more tables: A_note, B_note and C_note(as mentioned here: multiple tables need one to many relationship).
Is there a way to do CRUD operations on these tables(A_note, B_note and C_note) similar to the way we handle pivot table in many-to-many relationship?
Please suggest a way forward.
You should be using polymorphic relationships for this. It allows multiple models to make use of a single table.
Have a look at the documentation here
In your particular case, each of your tables would reference a noteable_id column and a noteable_type.
The noteable_id will contain the id of the (A/B/C) model.
The noteable_type will contain the string name of the model (A/B/C).
The (A/B/C) models will now get a new attribute:
/**
* (A/B/C) Model(s)
*/
public function notes()
{
return $this->morphMany('App\Notes', 'noteable');
}
And the note model will initiate it's polymorphic properties against the attribute name used to identify your polymorphic ids and types:
/**
* Note Model
*/
public function noteable()
{
return $this->morphTo();
}
Now you can simply call ->noteable on the (A/B/C) models, and they all share 1 table without the need of another pivot table for each table.
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 the following relation in my Users model
public function events($user_id)
{
return $this->find($user_id)->hasManyThrough('Event', 'Event_Member', 'user_id', 'id');
}
And the three tables
User
id|name|...
1 |bob |
2 |mike|
Event
id|name |location
1 |meeting|office
Event_Member
user_id|event_id|status
1 |1 |Owner
2 |1 |Guest
The hasManyThrough relationship gives me the information from the Event table, but I also need the status information from the pivot table, how can this be done?
What you have looks like a many-to-many relation rather than a has-many-through one. You have two tables connected through a pivot, not three parent-child-grandchild tables.
I would strongly suggest changing the relationship in both models. In the user model, it would look like this:
public function events()
{
return $this->belongsToMany('Event', 'event_members');
}
Note that a pivot table is typically called model1_model2 in alphabetical order - that is, event_user in this case. I assumed your pivot table is called event_members, if not just change it. Also note that I removed the user_id since the user model already knows which user it's using.
Then you can access pivot data easily, e.g. like this:
foreach ($user->events as $event) {
echo $event->pivot->status;
}
I'm trying to create a polymorphic relationship with multiple pivot tables. I have a table of requirements that can be assigned to accounts, roles, trips, and countries. This needs to be a many to many relationship because the same requirement could apply to multiple countries and/or trips and/or accounts etc.
I then need a table listing outstanding requirements for the user. For example: if a user has a certain account and there are requirements related to that account, then those requirements would be added to the user's list of requirements.
One solution I have is to first assign the requirements to the accounts, roles, trips, and countries using Pivot tables in a Many to Many relationship. Then using a polymorphic relationship I would connect the user to whichever pivot tables relate.
But I don't know how to do this or if it is even possible?
Here are my tables:
user_requirements
- id
- user_id
- requireable_id
- requireable_type
account_requirement
- id
- account_id
- requirement_id
role_requirement
- id
- role_id
- requirement_id
trip_requirement
- id
- account_id
- requirement_id
country_requirement
- id
- account_id
- requirement_id
Laravel 4.1 now has support for polymorphic many to many relationships.
Example below shows how I have implemented sharing Photos with both Products and Posts.
DB Schema
photos
id integer
filename string
alt string
photoable
id integer
photoable_id integer
photoable_type string
Models
Photo Model
class Photo extends Eloquent
{
public function products(){
return $this->morphedByMany('Product', 'photoable');
}
public function posts(){
return $this->morphedByMany('Post', 'photoable');
}
}
Product Model
class Product extends Eloquent
{
public function photos(){
return $this->morphToMany('Photo', 'photoable');
}
}
Post Model
class Post extends Eloquent
{
public function photos(){
return $this->morphToMany('Photo', 'photoable');
}
}
With the above, I can access all photos which are attached to a product as follows:
$product = Product::find($id);
$productPhotos = $product->photos()->all();
I can also iterate over to display all photos as any collection of models.
foreach ($productPhotos as $photo)
{
// Do stuff with $photo
}
The above can be replicated almost exactly to your requirements.
create a requirements table
create a requireable table
In Requirement model, declare all morphedByMany relationships
In Country, Trip, Role etc. declare morphToMany relationships
nb - I've typed this out freehand in S/O with no code editor, so there will probably be a typo, error or two - but concept remains the same.
A polymorphic relation in Laravel 4 is intended for single MODEL associations, therefore you cannot achieve what you are trying to build with this method. This is due to the fact that a pivot table doesn't represent a Model.