Laravel Relationship Union: One Relationship with two tables in a model - laravel

Lets supose this scenery:
We have 3 models -> User, Role and Permission.
Relationships
User has one to many relationship with Role.
Role has many to many relationship with Permission
User has many to many relationship with Permission.
The final purpose to the User -- Permisson relationships is to override the permission assigned to the user through his role. Just an exception for this User.
I am trying to to something like this:
public function permissions () {
return $this->hasManyThrough('App\Permission', 'App\Role')->merge($this->permissionOverrides);
}
In this case when I try to eager load the model permissions Laravel throws an error 'cause permissions is not detected as a relationships.
In the other hand, what if we change User -- Role relationship to many to many?
Thanks a lot for your knowledge!
EDIT
Role Relationships
public function permissions() {
return $this->belongsToMany('App\Permission')->withTimestamps();
}
Permission relationships
public function roles() {
return $this->belongsToMany('App\Role')->withTimestamps();
}
User relationships
public function roles() {
return $this->belongsToMany('App\Role')->withTimestamps();
}
public function permissionOverrides() {
return $this->belongsToMany('App\Permission')->withTimestamps();
}
The question is how to make the permissions() relationship merging the role permissions and the user permissions.
BETTER EXPLANATION
Due to misunderstanding shown in comments I will try to exlain this better. I had set all the relations between App\User and App\Role; I also had set the the relationship between App\Role and App\Permission. I can do right now:
$user->role->permissions;
For obtaining this I have configured the pivot table permission_role which stores the relation.
Thats not the case. What I want is to add one more var to the equation. Now I want to have the ability to override the role permissions adding a relationship between App\User and App\Permission. I have the pivot table permission_user and the relation stablished:
public function permissionsOverride() {
return $this->belongsToMany('App\Permission');
}
The REAL QUESTION is how to obtain all the permissions (both role permissions and overrided permissions in only ONE relationship). I mean merging both relationships.
Why I want to merge relationships? I could do a regular function to do this, but I would want to eager load this relation, Laravel way.

You can try like this, ManyToMany relation between User And Role, similarly ManyToMany relation between Role and Permission.
User Model
public function roles() {
return $this->belongsToMany('App\Role', 'user_role')->withTimestamps();
}
public function permissions() {
return $this->belongsToMany('App\Permission', 'permission_user')->withTimestamps();
}
Role Model
public function users() {
return $this->belongsToMany('App\User', 'user_role')->withTimestamps();
}
public function permissions() {
return $this->belongsToMany('App\Permission', 'permission_role')->withTimestamps();
}
Fetch Data
$user = User::with('permissions','roles','roles.permissions')->find(1);
$permissions = $user->permissions;
foreach($user->roles as $role){
$permissions->merge($role->permissions); //merger collection
}
dd($permissions->all());
For details you can look https://laravel.com/docs/5.6/eloquent-relationships#many-to-many
And for Collection you can look this https://laravel.com/docs/5.6/collections#method-merge

Related

Eloquent Relationship on the same model

I am using the User model and want to reference other users on a One to Many relationship.
With two models, this would be done by a Many to Many but this attempt at it is obviously wrong:
public function relatedUsers()
{
return $this->belongsToMany(User::class, 'related_user', 'user_id', 'user_id');
}
Is there a better way I can achieve my goal? I don't need an inverse method.
You can use hasMany() or belongsTo() (according to your need) relation for same model relationship
Define hasMany relationship in User model:
public function relatedUsers() {
return $this->hasMany('User','user_id');
}
Example:
Consider you have one User object
$user = User::where('id',$id)->first();
If you want to access related records
$related_users = $user->relatedUsers; // this will return all related users for particular object

How to create relationship in Laravel 5 which will collect records of own model mentioned in pivot table?

I have 3 tables users, companies and pivot table with user_id, company_id.
I can't get users, which belongs to my company inside User model.
Tried like
belongsToMany('App\User','companies_users','company_id','user_id' );
but I get relation with wrong users.
Since you are having a belongsToMany relationship between the User and Company, the User belongs to more than one Company. To get users of the companies of a particular User will not be straight forward. If you are sure that is exactly what you want, then do this:
//inside the User model
public function companies()
{
return $this->belongsToMany('Company');
}
//inside the User model
public function companiesusers()
{
$users= new Illuminate\Database\Eloquent\Collection;
foreach($this->companies as $company)
{
$users = $users->merge($company->users->get());
}
return $users->unique();
}
//inside the Company model
public function users()
{
return $this->belongsToMany('User');
}
Then you can get a user's companiesusers like so:
User::first()->companiesusers();

Polymorphic relations in Laravel

Please, Help me!
I have User model and migration: id, login, password.
3 roles and models (Admin, Teacher, Student) and here more information about user. And user can get several roles (he can be admin and teacher)
And one polymorphic model Role with columns:
user_id
roleable_id
roleable_type
For example, User::find(1)->roles;
And i'd like that result shows two model (admin and teacher).
Pleasee, help me))
You could try to eager load the models by using this in your User model https://gist.github.com/thisdotvoid/3022fee8afa53e45a6b89da3f16b3815. Add the required morph functions and then add three functions to the user model similar to this one (change the name and the model name accordingly):
public function admin()
{
return BelongsToMorph::build($this, 'App\Admin', 'rolable');
}
Then create a mutator function like
function getRolesAttribute()
{
$roles = new Collection();
$roleNames = ['admin', 'teacher', 'student'];
foreach ($roleNames as $role) {
$hasRole = $this->{$role};
if ($hasRole) {
$roles->push($role);
}
}
return $roles;
}

Laravel hasManyThrough with ManyToMany pivot

I am making a game and I have users which have facilities and for this I use ManyToMany
user_facilities
-user_id
-facility_id
But each relation must have a facility level, so I've added facility_levels table and each of this levels must be connected to the ManyToMany relation. So user_facilities now looks like this
user_facilities
-user_id
-facility_id
-level_id
level_id is the connections between the facility which the user owns and which level it is.
My question is how do I connect this in the models?
The User model now has this
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities');
}
And Facility
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities');
}
So how do I get the level of the facility which the user owns?
In blade I hope there is a way I can use something like
{{ $user->facility->level->property }}
level is part of the user_facilities table not of facility
Therefore, you should be able to access the level_id from the many to many relationship of user and facility
One thing you can do is to access the immediate table (also called pivot table).
First, edit your relationship to include the extra attributes.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->withPivot('level_id');
}
public function users()
{ // if you omit this EDIT/UPDATE, you cannot do this:
// $facility->users()->first()->pivot->level_id;
return $this->belongsToMany('App\User', 'user_facilities')
->withPivot('level_id');
}
Take note that when accessing a many to many relationship, Laravel will immediately assign a pivot attribute onto the result which contains details about the pivot table of the two models
Now try accessing the extra column:
$facility = $user->facilities->first();
$level_id = $facility->pivot->level_id;
// now you can use $level_id for finding the level.
$level = Level::find($level_id);
Now, since you can do that, you can also create a model for the many to many relationship of user and facility that will have that property of level_id
Let's create a new model called UserFacility that will extend Pivot.
This will be your Pivot model for many to many relationship of user and facilities.
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserFacility extends Pivot
{
}
Then update your users and facilities relationships as follows.
public function facilities()
{
return $this->belongsToMany('App\Facility', 'user_facilities')
->using('App\UserFacility');
}
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->using('App\UserFacility');
}
Notice that using method.
$userfac = $users->facilities->pivot; // <-- pivot will now be an instance of App\UserFacility
echo $userfac->level_id;
Lastly,
If you don't want the pivot attribute name, you can change it using the as method, chain it after the belongsToMany method, like this:
public function users()
{
return $this->belongsToMany('App\User', 'user_facilities')
->as('UFac')
->using('App\UserFacility');
}
$userfac = $users->facilities->UFac; // <-- you can now access the pivot table using the property `UFac`
echo $userfac->level_id;
It may also be possible that your pivot table has a relationship with a level since it has a level_id. Don't worry, it's possible, just add this function in your UserFacility model.
public function level()
{
return $this->belongsTo('App\Level');
}
Now you can do this!
$user->facilities->first()->UFac->level; // <-- this will be an instance of App\Level
source: https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

A many to many relationship

I've read the docs on relationships and have a few questions.
I have a user and a role table. The relationship is many to many. A user has many roles and a role can belong to many users. So I have set up a pivot table and have used a belongsToMany in each model. But surely a user hasMany roles and a role belongsToMany users.
But when I use hasMany, my queries do not work as expected. Is this just a wording thing and both should be belongsToMany?
I also wanted to know about defining the relationship on each model - do you need to or can it just be defined on users?
The hasMany relationship is for defining one to many relationships and that is why it will break your queries.
I can see why you are thinking along the lines of a user having many roles, but it also perfectly good English to say a user belongs to many roles and that a role belongs to many users.
You don't need to define the relationship on both models if you are only going to query one model and it's relationship. But can you say definitively that you will never need to query the relationship on roles?
I advice to you, study this description:
http://scotch.io/tutorials/php/a-guide-to-using-eloquent-orm-in-laravel
It will be useful for you.
(You should use only "belongToMany" in each model without "hasMany")
User model:
// app/models/User.php
<?php
class User extends Eloquent {
// each user BELONGS to many role
// define our pivot table also
public function roles() {
return $this->belongsToMany('Role', 'users_roles', 'user_id', 'role_id');
}
}
Role model:
// app/models/Role.php
<?php
class Role extends Eloquent {
// DEFINE RELATIONSHIPS --------------------------------------------------
// define a many to many relationship
// also call the linking table
public function users() {
return $this->belongsToMany('User', 'users_roles', 'role_id', 'user_id');
}
}

Resources