Laravel 5.1 constrain and eager load multiple nested relations - php

I have four models: OwnerCompany, Owner, User, and Role.
I want to get all OwnerCompanys eager loading their Owner and also eager loading its Users who have the Role with the name 'admin'.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();
This loads in the models but doesn't constrain the users, they are all loaded.

Not tested . I think you are missing like or equal operator.
OwnerCompany::with('owner.users')->whereHas('owner.users.roles', function ($query) {
$query->where('name','like','admin');
})->get();

I try this, if work then let me know. not tested
1).
OwnerCompany::with('owner.users.roles')->whereHas('owner', function ($query) {
$query->whereHas('users', function($qr){
$qr->whereHas('roles', function($q){
$q->where('name', 'admin');
});
});
})->get();
OR
2).
OwnerCompany::with('owner.users.roles')->whereHas('owner.users.roles', function ($query) {
$query->where('name', 'admin');
})->get();

If I got it right, you want each OwnerCompany object to contain the Owner object, which will contain "admins". I would do something like this.
$ownerCompany = OwnerCompany::all();
return $ownerCompany->each(function ($company) {
$company->owner;
$company->owner->users->each(function ($user) {
return $user->hasRole('admin');
});
});
and inside User class:
public function role()
{
return $this->belongsTo(Role::class);
}
public function hasRole($role)
{
if ($this->role()->where('name', $role)->first()) {
return $this;
}
return null;
}
Unfortunately, I can't test it right now, so If you have any trouble let me know.

Related

Filter a collection in Laravel

i want to filter a collection in order to return only items with fullname that is like the one given in parmaters
public function searchFriend($fullName){
return Auth::user()->friends->filter(function ($item) use ($fullName) {
return false !== stristr($item->friend->full_name, $fullName);
});
}
the function actually is not returning the correct results.
Instead of accessing the collection directly, you can do the filtering in SQL.
public function searchFriend($fullName){
return Auth::user()
->friends()
->where('full_name', 'like', '%'.$fullName.'%')
->get();
}
If you, for whichever reason, need to do it on the collection, then the problem with your current code is that $item represents the friend, so you're checking $item->friend->full_name instead of $item->full_name.
public function searchFriend($fullName){
return Auth::user()
->friends
->filter(function ($item) use ($fullName) {
return false !== stristr($item->full_name, $fullName);
});
}
I guess friend is a relationship to the User model so it could be
public function searchFriend($fullName)
{
return Auth::user()->friends()->whereHas('friend', function ($query) use ($fullName) {
$query->where('full_name', 'like', "%{$fullName}%");
})->get();
}
If you have first_name and last_name in User model then you can use concat.
public function searchFriend($fullName)
{
return Auth::user()->friends()->whereHas('friend', function ($query) use ($fullName) {
$query->whereRaw("concat(first_name, ' ', last_name) like ?", ["%{$fullName}%"]);
})->get();
}

using 'And Where' in Many to Many relation

I have two models which have a many-to-many relationship.
class User extends Model
{
function cars()
{
return $this->belongsToMany(Car::class);
}
}
class Car extends Model
{
function users()
{
return $this->belongsToMany(User::class);
}
}
I want to get users who used a specific set of cars:
$car_selected = [1, 3, 6];
$users = User::when(count($car_selected) > 0, function ($q) use ($car_selected) {
$q->whereIn('cars.id', $car_selected);
})
->get();
This gives too many results because of the 'whereIn' condition; what I want is 'whereAnd' something.
I tried this, but no luck.
$users = User::when(count($car_selected) > 0, function ($q) use ($car_selected) {
foreach($car_selected as $xx) {
$q->where( 'cars.id', $xx);
}
})
->get();
How can I get all users which have a relationship to cars 1, 3, and 6?
Your code provided doesn't make a lot of sense, but based on your explanation, you want to find a user who has a relationship with cars 1 and 3 and 6. Using whereIn() gets you users with relationships with cars 1 or 3 or 6.
Your attempt with multiple where() filters wouldn't work, as this would be looking for a single row in the pivot table with multiple cars, which obviously wouldn't be possible. Instead, you need to nest multiple whereHas() relationship filters into a single where() group like this:
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})
->with(['cars' => function ($q) use ($car_selected) {
$q->whereIn('car_id', $car_selected);
}])
->get();
This is all assuming you've correctly set up your relationships and tables per Laravel standards.
Demo code is here: https://implode.io/anjLGG
You can use the whereHas method to query relationship:
use Illuminate\Database\Eloquent\Builder;
User::whereHas('cars', function (Builder $builder) use($car_selected)
{
$builder->whereIn( 'cars.id', $car_selected);
})->get();
For more infos, check the doc
as #miken32 explained, I need to nest multiple whereHas() relationship filters into a single where() group.
#miken32 proposed following..
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})
->with(['cars' => function ($q) use ($car_selected) {
$q->whereIn('car_id', $car_selected);
}])
->get();
And I guess following is enough .
$users = User::where(function ($q) use ($car_selected) {
foreach ($car_selected as $car) {
$q->whereHas('cars', function ($query) use ($car) {
$query->where('car_id', $car);
});
}
})->get();
Thanks.

Laravel Eloquent deep nested query

I'm still learning Laravel and I can't find the solution for this problem.
I need to get invoices(with expenses) that are related to specific Partner Type.
I tried this:
$p = Project::with(['invoices.partner.partnerType' => function($query){
$query->where('partnerTypeName', 'Lieferant');
}, 'expenses'
])->where('id', $id)
->first();
I want to select invoices for Lieferant, but I get all invoices for one project.
Project Model:
public function invoices()
{
return $this->hasMany('App\Invoice');
}
Invoice Model
public function expenses()
{
return $this->hasMany('App\Expense');
}
public function partner()
{
return $this->belongsTo('App\Partner');
}
Partner Model
public function partnerType()
{
return $this->belongsTo('App\PartnerType');
}
Edit: PartnerType Model
public function partners()
{
return $this->hasMany('App\Partner');
}
Edit 2: Database
Partner(partnerID, name, partnerTypeId)
PartnerType(partnerTypeId, partnerTypeName)
Project(projectID, name)
Invoice(invoiceID, name, projectID, partnerID)
Expenses(expenseID, invoiceID)
If your models look like that.
Should be like :
$p = Project::with(['invoices' => function($query){
$query->where('partnerTypeName', 'Lieferant')
->with(['expenses','partner' => function($q){
$q->with('partnerType');
}]);
}])->where('id', $id)
->first();
return dd($p);
The solution to your problem is to update your query like this:
$p = Project::with(['invoices' => function($query){
$query->with('expenses')->whereHas('partner.partnerType', function($q){
$q->where('partnerTypeName', 'Lieferant');
});
}])
->where('id', $id)
->first();
But a cleaner solution would be using a scope for your problem.
In your Invoice model.
// Invoice.php
public function scopeByPartnerType($query, $partnerType)
{
$query->whereHas('partner.partnerType', function($q) use ($partnerType) {
$q->where('partnerTypeName', $partnerType);
});
}
And then in your Project model, add another relation that will just get Invoices with a particular partner type.
// Project.php
public function lieferantInvoices()
{
return $this->hasMany('App\Invoices')->byPartnerType('Lieferant');
}
Now you can do just this:
$project->find($id)->load('lieferantInvoices');

Getting all related fields to related field in Eloquent

I have this DB structure:
classroom_user is Elqouent many-to-many pivot.
User.php:
public function classrooms() {
return $this->belongsToMany(ClassRoom::class, 'classroom_user', 'classroom_id', 'user_id');
}
ClassRoom.php:
public function users()
{
return $this->belongsToMany(User::class, 'classroom_user', 'user_id', 'classroom_id');
}
I have a $user and want to write Eloquent method which gets all users who are in at least one of classrooms the $useralso is in. Something like $user->classrooms->users. But this falls with error Property [users] does not exist on this collection instance.. How can I do this?
I think you can do it like this:
$users = User::whereHas('classrooms', function($query) {
$query->whereHas('users', function($query) {
$query->where('id', auth()->user()->id);
});
})->get();
You could store this as a scope in your User model:
public function scopeClassmatesOf($query, $user)
{
$query->whereHas('classrooms', function($query) use($user) {
$query->whereHas('users', function($query) use($user) {
$query->where('id', $user->id);
});
})
}
And then call it from a controller like:
$user = auth()->user();
$users = User::classmatesOf($user)->get();
Your asking for users of a collection. $user->classrooms is a collection of classrooms.
If you do $user->classrooms->first()->users you will get all the users from the first classroom.
To get all users from all classrooms, you can do something like;
$users = $user->classrooms->map(function ($classroom) {
return $classroom->users;
});
Note that will return duplicate users and authenticated user him self witin the collection. So, you might need to do some filtering to the result.
This worked for me:
public function classmates() {
$self = $this;
$a = User::with('classrooms')->whereHas('classrooms', function ($query) use ($self) {
$query->whereHas('users', function ($query) use ($self) {
$query->where('users.id', $self->id);
});
});
return $a;
}

Searching from model and related model in laravel5

I want to keyword search from a table and its all related table using Elequent in Laravel5.
My controller is
$clients = Client::with('contacts', 'paymentTerm', 'addressInfo');
if ($q = Request::input("q")) {
$clients->where("clients.name", 'LIKE', "%$q%");
$clients->where("address_info.email", 'LIKE', "%$q%");//This is not working,I want to search from both client and client address_info
}
return $clients->paginate(10);
Client Model ,
public function addressInfo() {
return $this->hasOne('App\Model\ClientAddressInfo');
}
Client Address info,
public function client() {
return $this->belongsTo('App\Model\Client');
}
how can I apply keyword search here?
You can use whereHas to filter by a related model:
$clients->whereHas('addressInfo', function($query) use ($q){
$query->where("email", 'LIKE', "%$q%");
});

Categories