Laravel - How to chain Eloquent relationship - Eager Loading - php

I'm using Laravel 5.4, Laravel Roles from here and Eloquent relationships.
I'm trying to retrieve the users of a company along with their roles.
User::find(1)->roles gets me the user's roles whose id =1
Company::find(1)->users gets me all the users that belongs to the company whose id =1 (without the roles of users).
Company::find(1)->users->roles returns an error Property [roles] does not exist on this collection instance.
Questions
Is it possible to do what I want to do ?
If so, how should I do it ?
User.php
class User extends Authenticatable
{
use HasRoleAndPermission;
public function company()
{
return $this->belongsTo('App\Company');
}
public function user()
{
return $this->belongsTo(User::class);
}
}
HasRoleAndPermission.php
trait HasRoleAndPermission
{
public function roles()
{
return $this->belongsToMany(config('roles.models.role'));
}
}
Company.php
class Company extends Model
{
public function users() {
return $this->hasMany('App\User');
}
}

$company = Company::with('users.roles')->find(1);
Will load all the users, and all the roles for each user.
Update
According to your comment, you don't want the company data. Just users and roles Using eager loading and relationship existence.
$users = User::with('roles')
->whereHas('company' => function($query){
$query->where('name', '=', 'company'); //If you don't have the company ID
})->get();
Without relationship existence
$users = User::where('company_id', 1)->with('roles')->get();

1 company has many users.
1 users has many roles.
You are trying to get the roles of a collection of users (the property only exists for one user) thus, the property doesn't exists for the collection.
If you want to get all the roles of all users in the company, you might try the above code:
$roles = [];
Company::find(1)->users->foreach(function($user) {
$roles = array_merge($roles, $user->roles);
});
--------- edit ---------
For eager loading the roles of users, you must use with, as suggested by #Ohgodwhy, but I'd refactor a little:
$users = User::with('roles')->where('company_id', $companyId)->get();
Now you have the array of users eager loading their roles. You still can't access directly $users->roles, you must first get a user, only then get its roles.

Related

Laravel Many To Many Get element that I dont have a relationship with

In laravel Many to Many relationship like the one in the Laravel Documentation example.
https://laravel.com/docs/9.x/eloquent-relationships#many-to-many
users
id - integer
name - string
roles
id - integer
name - string
role_user
user_id - integer
role_id - integer
In the Users model I have
public function roles()
{
return $this->belongsToMany(Role::class);
}
In the Roles model I have
public function users()
{
return $this->belongsToMany(User::class);
}
How can I get all Role that I don't have a relationship to.
I tried thing like
$this->availableRoles = Role::doesntHave('users')->get();
But this give me all the Role that no user at all have
Any hint on this.
If you already have a User instance (I assume that is what $this is?), then you can simply filter the relationship:
$this->availableRoles = Role::whereDoesntHave('users', function ($subQuery) {
return $subQuery->where('users.id', $this->id);
})->get();
This should return all Role elements, unless that Role has an association with the User ($this, $user, auth()->user(), etc.; adjust as required).
An alternative approach, filter out Role based on existing Roles:
$this->availableRoles = Role::whereNotIn('id', $this->roles->pluck('id'))->get();
This approach get's the roles.id for the User instance.
You can take a look at query efficiency and use the one that works best, but either approach should work fine.
Note: If $this is not a User instance, adjust:
$user = User::find($userId); // or `auth()->user()`, etc.
$availableRoles = Role::whereDoesntHave('users', function ($subQuery) use ($user) {
return $subQuery->where('users.id', $user->id);
})->get();
// OR
$availableRoles = Role::whereNotIn('id', $user->roles->pluck('id'))->get();

Reverse contains() in laravel?

I'm new to laravel,
I'm trying to get a row from a table according to it's Many-to-Many relationship with another table.
You know when you pass multiple ids in contain() to check if they have relationship with that row or not, like this:
$row->contains([4,6,9]);
i want to reverse this, where i can use the ids [4,6,9] to get any row from the other table linked with them.
Let's say you have this two models: User and Role that makes many-to-many relationship.
If you defined your relationships properly:
/** User.php */
public function roles()
{
return $this->belongsToMany(Role::class);
}
-
/** Role.php */
public function users()
{
return $this->belongsToMany(User::class);
}
Then you could use the relationship to accomplish what you are trying to do.
From the related object:
$user = User::find(1);
// getting all the roles attached with a user:
$roles = $user->roles;
// getting all the roles attached with a user that has certain ids:
$roles = $user->roles()->whereIn('id', [2, 4, 6])->get();
Also, you could find your desired related model using the collection instance that return the relationship:
$user = User::find(1);
// getting all the roles attached with a user:
$roles = $user->roles;
// getting a specific role attached to a user:
$specific_role = $roles->where('id', 6)->first();

Can't get Eloquent's hasManyThrough to work for a basic example

Here are my relationships:
User
id
Collection
id
UserCollection
user_id
collection_id
How can I get something like $user->collections to return all Collection objects that belongs to the user? A UserCollection simply links a User to a Collection. This allows a user to have multiple collections, but also allows a collection to belong to multiple users.
What I'm currently trying is to specify that UserCollection belongs to a User on user_id, and belongs to a Collection on collection_id.
// UserCollection
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function collection()
{
return $this->belongsTo(Collection::class, 'collection_id');
}
Then specifying that a User has many Collections through UserCollection.
// User
public function collections()
{
return $this->hasManyThrough(Collection::class, UserCollection::class);
}
I've also tried explicitly setting the column names of the hasManyThrough relationship, but the join tries to use an id column on the UserCollection model, which does not exist as there is no primary key:
public function collections()
{
return $this->hasManyThrough(Collection::class, UserCollection::class, 'user_id', 'collection_id');
}
You're overcomplicating things here. You don't need hasManyThrough. What you need is belongsToMany() for a many-to-many relationship.
First, get rid of your UserCollection model. You don't need it.
Then change your relations to this:
public function collections(){
return $this->belongsToMany('Collection', 'user_collections');
}
public function users(){
return $this->belongsToMany('User', 'user_collections');
}
For more information take a look at the official docs on relations

Models relate in laravel 4

I hope you are well, I need your help, I am working with Laravel and I need to relate information, I have the User, TypeCost and CostCenter models where relationships are:
user:
hasMany ('TypeCost');
belongsTo ('CostCenter', 'id');
TypeCost:
belongsTo ('User');
CostCenter:
hasMany ('User');
I have my view index in the TypeCost module, there are loads information from the cost of each user, I need is that if the user logged belongs to CostCenter one simply show records of users who belong in CostCenter 1 and so CostCenter for the 2, 3, etc.
This is my Index method that is responsible for displaying the list of expenses:
public function index ()
{
$ type_costs = TypeCost :: paginate ();
return View :: make ('type_costs.index' compact ('type_costs'));
}
//for user model
public function costcenter()
{
$this->belongsTo('CostCenter');
}
//for costcenter model
public function users()
{
$this->hasMany('User');
}
//controller function
public function index ()
{
$cost_centers = Auth::user()->costcenter;
$cost_users = $cost_centers->users;
return View::make ('type_costs.index', compact ('cost_users'));
}
it will return list of users that belongs to CostCenter of logged user.
If the field of costcenter_id in user table is nullable, u can add some validation on controller.

Laravel 4 tables relationship Eloquent

I have four tables:
Users: id, username
Roles: id, name
Domains id, title
DomainsAssignedRolesUsers id, role_id, user_id, domain_id
I want to get all user roles for a domain.
I do:
User::find(1)->domains->first()->roles
But i get all domains roles, not only for my user. Help me to get user roles only for the selected domain/
My relations:
// User model:
public function rolesDomain() {
return $this->belongsToMany('Role', 'domainsAssignedRolesUsers', 'user_id', 'role_id');
}
public function domains() {
return $this->belongsToMany('Domain', 'domainsAssignedRolesUsers');
}
// Role model:
public function domains() {
return $this->belongsToMany('Domain', 'domainsAssignedRolesUsers');
}
// Domain model:
public function roles() {
return $this->belongsToMany('Role', 'domainsAssignedRolesUsers', 'domain_id', 'role_id');
}
public function users() {
return $this->belongsToMany('User', 'domainsAssignedRolesUsers', 'domain_id', 'user_id');
}
You want to get all the roles, from a specific domain, in relation with an user?
So this should do the trick:
User::find(1)->rolesDomain()->where('domain_id', $specificID)->get()
But if you want to only get the roles from the first domain for an user.
User::find(1)->domains()->first()->roles()->get();
And if you only want to retrieve the roles for a user.
User::find(1)->rolesDomain()->get()
And if you only want to retrieve all the roles from each domain in relation with an user.
User::find(1)->domains()->with('roles')->get()
Even if Eloquent documentation has a few example, This orm is really intuitive.
Check out Eloquent Triple Pivot (also on Packagist), it sounds like it does exactly what you want.
You'd set up your Models as User, Domain and Role (a User has many Domains and can have a Role in each). Then look at the docs on the Github page (particularly step 6), and you'd define this on your Domain model:
class Domain extends Eloquent {
...
public function getRolesAttribute() {
return $this->getThirdAttribute();
}
}
Then you can simply call
$user = User::find(1);
$domain = $user->domains->first();
// or
$domain = $user->domains->find(73);
$roles = $domain->roles;
// Or the above can be condensed into just...
$roles = User::findOrFail( 1 )
->domains()
->findOrFail( 73 )
->roles;

Categories