How to specify pivot table for hasOne() relationship in Laravel - php

I have a couple of models that I have included pivot tables for to avoid a polymorphic relation.
role table
id
name
description
restriction table
id
rule
status
restriction_role table
id
restriction_id
role_id
Reason for this setup is that both Roles and Restrictions can actually belong to multiple other models. In this case, a Role can only have 1 Restriction. I would normally define this as
class Role extends Eloquent {
public function restrictions()
{
return $this->hasOne('Restriction');
}
}
This obviously does not work because Laravel is unaware of the pivot table connecting this relation. I could easily accomplish this by using a many-to-many relationship instead, but this is not exactly how my model works. Not seeing anything in the documentation for defining this. Any thoughts?

I've solved this by using the belongsToMany relation and defining a custom accessor for it, like so:
public function foo()
{
return $this->belongsToMany(App\Bar::class);
}
public function getFooAttribute()
{
return $this->foo()->first();
}

As #deczo stated in the comments, belongsToMany() is about all that will work here. I recommend returning the first result using the first() method if you require only one result but cannot use a hasOne() relationship.

Related

How do I handle multiple pivot relationships in Laravel?

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.

The relationship of one model

I have a problem with Laravel relationships.
I need to make relationship based on this table:
issuer and friend need to be united. Relationship will return all rows where user id in issuer or in friend. At the moment code looks like this:
return DB::table('contacts')->select()->where('friend', $this->id)->orWhere('issuer', $this->id)->where('status', 'approved');
Previously I used that method, but there are no relationship, 'cause attach() is undefined.
private function contactsIssued() {
return $this->belongsToMany(User::class, 'contacts','issuer', 'friend');
}
private function contactsFriended() {
return $this->belongsToMany(User::class, 'contacts','friend', 'issuer');
}
public function contacts() {
return $this->contactsIssued()->union($this->contactsFriended()->toBase());
}
So, I need to make one relationship that has two foreign columns.
Sorry, my English can be broken, 'cause it's not my native language.
That's look like a one to many relationship but you are using belongsToMany which is many to many relationship and required a pivot table.
if you have user model that has many contact model then in your contact model you should use the one to many relationship by using belongsTo.
but if you have 3 table you didn't say that in you question then please edit you question and provide your models and the relations between them.

Laravel's relationship using Eloquent really correct?

I have a project running Laravel 5.5, and I have the following tables in the database.
USERS (
    ID,
    NAME,
    PERMISSION_ID
)
PERMISSIONS (
    ID,
    NAME
)
If I am not wrong, USERS relationship with PERMISSIONS is 1.1 (USERS has at least 1 permission and a maximum of 1), so it's a OneToOne relationship.
In my user model I made the following relationship.
class User extends Authenticatable
{
    public function permission () {
        return $this->hasOne('App\Models\Permission');
    }
}
But in this way Laravel/Eloquent looks for the relationship in the PERMISSIONS table. Generating a select similar to:
SELECT * FROM PERMISSIONS WHERE PERMISSIONS.USER_ID = 1
I have already consulted the Laravel documentation and saw that I can specify the ForeignKey and LocalKey in the hasOne method. This solves the relationship.
But is it really correct for Laravel/Eloquent by default to fetch the information in the PERMISSIONS table? The correct one would not be to look in the USERS table with PERMISSION_ID?
#Edit
Due to the response of Alexey Mezenin
I researched the difference between hasOne and belongsTo. And I found the following answer:
What is the difference between BelongsTo And HasOne in Laravel
From what I understood, when the foreign key is on the other table the correct one is to use hasOne.
When the foreign key is in the template's own table, the correct one is to use belongsTo.
I'll stay tuned in on these details next time, thanks Alexey Mezenin and jedrzej.kurylo
Your relationship is inverted, you're using hasOne() instead of belongsTo. Use this relationship in the User model:
public function permission()
{
return $this->belongsTo(Permission::class);
}
And in the Permission model you might want to add hasOne() relationship (in case if you would want to use it):
public function user()
{
return $this->hasOne(User::class);
}
But usually, the same permission can be used by many users. If this is the case, just change hasOne to hasMany.

How to establish a hasManyThrough relationship in laravel for roles and permissions?

So I understand how to load the roles for a user and the permissions for a role.
But now I have a user table, a role table, and a permission table. I also have role_user table for linking users and roles. And of course a permission_role for linking permissions and roles.
Now when I want to get all the roles for the user, I simply do something like this:
public function roles()
{
return $this->belongsToMany('Uppdragshuset\AO\Tenant\Models\Role');
}
Similarly I can fetch permissions for roles like so:
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
Now according to the documentation on laravel, I can directly fetch permissions for a user by using the hasManyThrough relation too like so:
public function permissions()
{
return $this->hasManyThrough(Permission::class, Role::class);
}
But this is returning with an error saying:
Unknown column 'role.user_id' in 'field list'
I think I understand why. Laravel is looking for user_id field in the role table but it does not understand that it is a many to many relation and it should look for it in the pivot table.
So what is the way around this? Is there a way around this in Eloquent or will I have to resort to using the query builder? And if yes, how to do the same thing with the query builder?
HasManyThrough can only be used to connect two HasMany relationships. There is no native relationship for your case.
I created a HasManyThrough relationship with unlimited levels and support for BelongsToMany:
Repository on GitHub
After the installation, you can use it like this:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function permissions() {
return $this->hasManyDeep(Permission::class, ['role_user', Role::class, 'permission_role']);
}
}

How should I structure the Eloquent ORM models for multiple intermediate tables in Laravel?

I can't find anywhere the information on how you have several intermediate tables with your Eloquent ORM models. The problem I'm facing is that I have a table for my users, permissions and roles. These are the 4 tables:
Permissions:
id
name
Permission_roles:
id
name
Permission_role_mappings:
id
permission_id
permission_role_id
Permission_role_user_mappings:
id
permission_role_id
user_id
(Well, I also have a users table but the layout of it doesn't matter since the foreign key is in permission_role_user_mapping.)
So the problem is that I want to be able to get the data from the permissions table when calling from the User model. I have some trouble grasping the workflow with Eloquent ORM altogether so if I'm missing something basic which is crucial then please point it out.
According to the documentation it seems that I don't need to create models for the intermediate tables. So how would I specify the relationship from the User class? Could I do something similar to this?
class User extends Eloquent {
public function permission_role()
{
return $this->has_many_and_belongs_to('Permission_Role', 'permission_role_user_mappings');
}
public function permission()
{
return $this->has_many_and_belongs_to('Permission_Role', 'permission_role_user_mappings')->has_many_and_belongs_to('Permission','permission_role_mappings');
}
}
This doesn't seem to be working, this is the error:
User::find(1)->first()->permission()->first();
...
Method [permission] is not defined on the Query class.
I also want to be able to get data by starting from Permission_Role and Permission. I'd prefer that the answer would help me specifying all the models required.
Eloquent relationships are accessed as an object property instead of a function.
User::find(1)->first()->permission;
You can wrap that above statement in the dd function to get a look at it.
This guide on Eloquent Relationships should be helpful
Edit for question in comments about selecting all permissions in the role:
$roles = array();
$permission_roles = User::find(1)->permission_roles()->get();
foreach ($permission_roles as $pr) {
if (! in_array($pr->permissions)) {
$roles[] = $pr->permissions;
}
}
This will get you what you want. However, this will end up doing a lot of queries. It's best to make use of Eager Loading here.

Categories