I'm kinda confused how the eloquent relationships works in Laravel 5.4.
I got a school model that has a "hasMany" relationship with my user model:
public function users()
{
return $this->hasMany('App\User');
}
The user is not required to be linked to a school though, so I haven't put the belongsTo (school) function on my user model.
How should I link the user to a school, when I create the user and how can I pull all users in a specific school into a view, for example?
If the user can only belong to one school, the most straightforward approach would be to add a school_id column to the users table. Since it's not required, you can allow for it to be null. This will allow you to run $school->users to retrieve a list of users for a given school.
I would also recommend adding the belongsTo relationship to the User model, so you can do $user->school to retrieve a user's school when applicable. It's okay that it'll be null for some users.
Related
In my app, you can create lists of roles that are attached to contacts. So you can assign the contact "Bob" the roles of "Gardener" and "Pet Sitter". Then you can create the list "People" and add "Gardener (Bob)" and "Pet Sitter (Bob)" to it.
I have the following tables:
contacts
id
name
roles
id
name
contact_role (pivot)
id
contact_id
role_id
lists
id
name
contact_role_list (pivot)
id
contact_role_id
list_id
Everything was working smoothly until the second pivot table linked to the first pivot table. My pivot tables are (currently) not having any models so I'm not sure if there is a built-in feature to tackle this in Laravel or if I need to think differently.
I currently have this in my List model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(XYZ::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
Is this even close? What do I put where it says XYZ::class?
Ok, so the below is doing what I want, but is there an even better way to do it? The key to solving my problem was to create a Model for ContactRole and changing extends Model to extends Pivot.
I placed this in my List Model:
public function list_roles(): BelongsToMany
{
return $this->belongsToMany(ContactRole::class, 'contact_role_list', 'list_id', 'contact_role_id');
}
And this in my ContactRole Model:
public function contact(): BelongsTo
{
return $this->belongsTo(Contact::class);
}
Now I could reach the contact data by using something like this: List::first()->contact_roles->first()->contact
Any way to use with, pivot or similar to tidy this up even more? Thanks!
I like to approach these issues in terms of Models rather than pivots. I think many new Developers in Laravel get over obsessed with what's going on in the Database which is fine, but theres a lot of Magic going on so you can write very simple code that does a lot of Heavy lifting, so that being said if I fully understand your problem
You have a Contacts Model
This model can have many roles
so in your contacts Model you need a role relationship
public function roles()
{
return $this->belongsToMany(Roles::class);
}
next of course you have a role Model (pun intended)
your each role can have many list
public function lists()
{
return $this->hasMany(List::class)
}
then the idea is now that you have roles on contacts and lists on roles you should be able to have many lists through contact
public function lists()
{
return $this->hasManyThrough(List::class, Role::class);
}
I've done similar things before and for your description it seems like that's the approach you might need to take.
Laravel 7.0
I am trying to set up a system where users can belong to
different models, like Courses or Realms. I call this a
'membership'.
A user who is a member in a Realm or Course always has a
Role within that Realm, like 'amdin', 'editor', or 'guest'
I have models for User, Role, Memberable and the Course/Realm stuff.
Like this:
Role ⇦1:N⇦ Memberable ⇨N:1⇨ User
⇩
poly 1:1
⇩
Realm/Course/YouNameIt
At the moment I am using a wild mix of relations to get
more or less what I want. But my approach seems rather
gross to me, or even wrong.
For example, to get a list of models that I am a member of, I write:
User::find(2)->memberships()->get()
But this gives me a Collection of Memberables, not Realms or
Courses, which kind of sucks. Can this be remedied?
To get my first Realm/Course, I have to write:
User::find(2)->memberships()->first()->memberable // YUCK!
Here are my models:
class User {
public function memberships()
{
return $this->hasMany(Memberable::class)->with('memberable');
}
public function roles()
{
return $this->belongsToMany(Role::class, 'memberables')
->withPivot(['memberable_id', 'memberable_type']);
}
}
class Memberable extends Model
{
public function role()
{
return $this->hasOne(Role::class, 'id', 'role_id');
}
public function memberable()
{
return $this->morphTo('memberable');
}
}
trait HasMembers
{
public function members()
{
return $this->morphToMany(User::class, 'memberable', 'memberables')
->withPivot('role_id')
->join('roles', 'roles.id', 'memberables.role_id')
->addSelect(['role' => Role::select('name')
->whereColumn('role_id', 'roles.id')]);
}
}
class Realm extends Model
{
use HasMembers;
}
class Course extends Model
{
use HasMembers;
}
I know that questions like "is this good" or "is this bad" are difficult to answer, so I'll ask:
Is this so wrong that I should worry?
How could I improve this design?
I'll probably never have more Models that Realms and Courses that can have members. Is it smarter to ditch the whole memberable-polymorphism in favour of two separate tables? It IS nice to keep the flexibility the polymorphic approach offers.
You could just set up more relationships
User belongsToMany Roles
Roles belongsToMany Users
User morphedByMany Realm using Memberable
User morphedByMany Courses using Memberable
User morphedByMany YouNameIt using Memberable
Realm morphToMany User using Memberable
Courses morphToMany User using Memberable
YouNameIt morphToMany User using Memberable
I'd make Memberable a morph pivot.
class Memberable extends MorphPivot { ... }
With this, accessing the relationships isn't hard.
$user->roles // Get an Eloquent Collection made up from $user's roles.
$role->users // Get an Eloquent Collection made up from users that have $role.
$user->realms // Get an Eloquent Collection made up from $user's realms.
$user->courses // Get an Eloquent Collection made up from $user's courses.
$user->youNameIt // Get an Eloquent Collection made up from $user's youNameIt.
$realm->users // Get an Eloquent Collection made up from users associated with $realm.
$course->users // Get an Eloquent Collection made up from users associated with $course.
$youNameIt->users // Get an Eloquent Collection made up from users associated with $youNameIt.
And maybe make User <---> Roles relationship use a separate pivot. If not, then make it a morph relationship as well (User morphedByMany Role using Memberable, Role morphToMany User using Memberable)
The thing is, if you use a central table to morph everything up, it will grow a lot. You're essentially making it do the job of 3-4 pivots (in this example). If your data grows a lot, it might become a problem.
Also, different relationships might need different data from the pivot table but since you're only using a single table, that would complicate things unless you opt to use a json field.
I have users and user_roles tables, and id of user_roles is used as foreign key in users. I fetched data from users using User::with('userRole')->find($user). In returned result userRole is present, however it is empty, instead it was supposed to have data from user_roles for the particular foreign key.
Please, share what can be the possible issues with the functionality or if anyone can explain working of laravel associations in brief.
/* User Model */
public function userRole()
{
return $this->belongsTo('App\UserRole');
}
/* UserRole Model */
public function user()
{
return $this->hasMany('App\User');
}
Thank You,
Many to many relationships in Laravel often require you to have 2 models and 3 tables. One to many relationships require you to have 2 models and 2 tables. One to one relationship also requires you to have 2 models and 2 tables.
Many to many relationship
Let's take User and Role models, since each User can have multiple roles and one Role can be assigned to different users, you will naturally want a Many to many relationship. Now, you will need to create an intermediate table in which you will store the results, why? Because you already defined both User and Role and since it is Many to many none of those objects will have any identifier of the other one inside of them, but rather will have their own identifier in the intermediate table, and this is how Laravel fetches the relationship, it connects Models primary key with foreign key inside of the intermediate table.
One to many relationships
Let's take User and Role models again and let's say that this time, one Role can be assigned to multiple users, but one User can ONLY have 1 Role. Naturally you will have a field role_id inside of your User model and you will connect role_id from users with id from roles.
One to one relationships
Lets take User and Role models again :D Let's say you want to create a separate Role for every user, then you will have 2 models and 2 tables where your users table will have all the users and your roles table will contain id, name, user_id and now when you try to retrieve the relationship, if you define one to one laravel will return only 1 result, no matter if you have multiple same user_id on the roles table, Laravel will return only 1 role because you told him explicitly it's 1-to-1 relationship.
EDIT:
Here is an example of one to one relationship:
/* User Model */
public function userRole()
{
return $this->belongsTo('App\UserRole', 'user_role_id', 'id');
}
#Tim and #Nikola
Thank you, for your efforts mates.
However, I found the reason behind the problem. It was because the wrong naming of userRole function in User model.
I was using foreign key of user_roles as user_roles_id in users table and defined function with name of userRole in User model. This leads to ORM not found the relevant column for attaching user_roles data.
The solution is either I have to change the name of user_roles_id to user_role_id or userRole function name to userRoles. And I choose the first one and it worked fine. :)
For reference on the naming conventions of laravel please refer to Laravel - Database, Table and Column Naming Conventions?.
I am currently building my first App with Laravel and have stumbled upon the problem that I dont know how to setup the relationship Many-to-Many between the Models (User and Group).
I've created a board in which I store the relationship between all users and the Group they are in.
My Problem is that I dont know how to acces and set this up in Laravel.
Im not sure whether I have to user hasMany or belongsToMany.
I am trying to find a method to add a User to Group, so that a new entry will be created in the UserGroups table.
My tables:
User
ID
Name
Email
Group
ID
Name
Creator_ID
UserGroup
User_ID
Group_ID
I appreciate any help, thanks!
If you want to create a many-to-many relationship, it should be belongsToMany, not hasMany.
In the Group model:
public function users()
{
return $this->belongsToMany(Group::class);
}
And in the User model:
public function groups()
{
return $this->belongsToMany(User::class);
}
The pivot table should be called group_user.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
In the model class, use belongsToMany as demonstrated here: https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
To add a user to a group, use attach(), as demonstrated here:
https://laravel.com/docs/5.4/eloquent-relationships#the-create-method (scroll to many to many relations)
Let's say I have three models with this relationsship
USER
hasMany (app/organisation)
ORGANISATION
hasMany (app/order)
ORDER
For one level I could just go
user->organisations()->get()
And get all the organisations tied to the user. But every organisation also have 0..many ORDER entities. So I want to do this:
user->organisations()->orders()->get()
To get a list of all the orders from all the organisations from a certain user. Is that possible this way?
What you can simply do is implement hasManyThrough() relationship on User model.
public function orders()
{
return $this->hasManyThrough(Order::class, organisation::class);
}
then you can query like,
$user->orders()->get();