Laravel How to use hasManyThrough with groupBy? - laravel

is there any way to count a group with a hasManyThrough in Laravel?
I have this database:
projects (id, project)
milestones (id, project_id,milestone)
todos (id,milestone_id,todo)
In my todos I am storing a employee. Now I want to know how many employees are working in project.
My Project Model looks like this:
public function employee()
{
return $this->hasManyThrough('App\Models\Todo', 'App\Models\Milestone');
}
My resource looks like this if I want to see all Todo in my Project:
'employee' => $this->employee()->get(),
How does my resource and/or model have to look like if I want to know how many employee are working in project. I tried something like this (not working):
'employee' => $this->employee()->groupBy('employee')->count(),

There are multiple ways of getting count of relational table
Project::with(array('employee' => function($query) {
$query->groupBy('employee')->count();
}))
->get();
Another way of getting relational count
Project::withCount('employee')->get();

If you only has an employee for a todo, just use withCount():
$projects = Project::withCount(['employees' => function (Builder $query) {
$query->where('employee_id', '!=', null);
}])->get();
And call {relationship}_count to show the result:
foreach($projects as $project) {
echo $project->employees_count;
}

Related

Laravel 5.2 Eloquent ORM to get data from 3 tables

I have the following tables. users, user_details and client_teams. Each user has one details and each user can have many teams. schema for users:
id, name, email,parent_user_id
user_details:
id, user_id, client_team_id
client_teams:
id, user_id, team_name,status
In user_model i have the following relations:
public function userDetails(){
return $this->belongsTo('App\Models\UserDetails','id','user_id');
}
public function clientTeamList(){
return $this->hasMany('App\Models\ClientTeams','user_id','id');
}
In user_details model i have the following relation:
public function clientMemberTeam(){
return $this->belongsTo('App\Models\ClientTeams','client_team_id');
}
I want to be show the list of users who have a specific team ID and created by a specific user. The query that i am using is this:
$userCollections=Users::where([
['users.status','!=','DELETE'],
['users.parent_user_id',$clientId],
['users.id','!=',$loginUser->id]
])
->with([
'userDetails'=>function($query) {
$query->where('client_team_id',1);
}
]);
This is giving me all records for this user, Whereas i want to match by client_team_id and user_id
You need to use whereHas and orWhereHas methods to put "where" conditions on your has queries.
Please look into https://laravel.com/docs/8.x/eloquent-relationships
$userCollections = Users::where([['users.status', '!=', 'DELETE'],
['users.parent_user_id', $clientId],['users.id', '!=', $loginUser->id]
])
->whereHas('userDetails' => function ($query) {
$query->where('client_team_id', 1);
})->get();

Laravel Eloquent with() selecting specific column doesn't return results

Say I have 2 models, Category and POI where 1 Category can have many POIs.
$categoryDetails = Category::with([
'pois' => function ($query) {
$query->where('is_poi_enabled', true);
},
])->findOrFail($id);
The above query returns results from the specific Category as well as its POIs.
However, with the query below:
$query->select('id', 'name')->where('is_poi_enabled', true);
The POIs become empty in the collection.
Any idea why this is happening? When added a select clause to the Eloquent ORM?
While doing a select it's required to fetch the Relationship local or Primary key.
For an example POIs table contains category_id then it's required to select it
Try this:
$categoryDetails = Category::with([
'pois' => function ($query) {
$query->select(['id', 'category_id', 'is_poi_enabled'])
->where('is_poi_enabled', true);
},
])->findOrFail($id);
Good luck!

Laravel "whereHas" with "take"

I'm having a relationship:
employees: id | name
employments: id | employee_id | company_id | start_date | end_date | termination_type_id
One employee can have many employments through time. I need to get employees who are employed based on the last/one row in employments and that is whereHas fails me. I've set this in my model Employee.php:
In Employee.php model:
public function latestEmployment() {
return $this->hasMany(Employment::class)->orderBy('start_date', 'DESC')->take(1);
}
On filtering:
$employees->where(function ($query) {
$query->whereHas('latestEmployment', function($subquery) {
$subquery->whereNull('termination_type_id');
}
});
Looks to me that I need somewhere some eager loading because ->take(1) doesn't work that way. Here whereHas takes into account the whole table, no matter what I write next.
Thanks for help.
EDIT:
Doing first(), get(), etc. executes the query in the middle so I cannot use that. Important thing is that the whole query needs to be a Builder because I need a paginate() at the end.
The easiest solution is getting all employees and filtering them afterwards:
public function latestEmployment() {
return $this->hasOne(Employment::class)->orderBy('start_date', 'DESC');
}
Employee::with('latestEmployment')->get()
->where('latestEmployment.termination_type_id', null);
A query that only fetches the relevant employees:
Employee::select('employees.*')
->join('employments', 'employees.id', 'employments.employee_id')
->whereNull('termination_type_id')
->where('start_date', function($query) {
$query->selectRaw('max(start_date)')
->from('employments')
->whereColumn('employee_id', 'employees.id');
})->get();
Try Something like this
Employee::select('employees.*')->join('employments', function ($join) {
$join->on('employees.id', '=', 'employments.employee_id')
->whereNull('emails.termination_type_id');
})->distinct()->paginate(15);

Laravel eloquent table joined to itself, how to get 1 array with all values

I am using a table employee that is joined to itself for the manager.
Employee
id
name
manager_id (FK to Employee)
I am using the following model for Employee:
public function my_employees()
{
return $this->hasMany('App\Employee', 'manager_id');
}
public function my_manager()
{
return $this->belongsTo('App\Employee', 'manager_id');
It is working fine because I can use the my_employees function on an employee and it will get me all the records linked to this employee.
Now I would like to get a table with all the employees where the column manager_id is replaced by manager_name. How can I achieve this with Eloquent?
Not using Eloquent, I do:
$employee = DB::table('employee')
->select('employee.id', 'employee.name', 'employee.manager_id','manager.name AS manager_name')
->join('employee AS manager', 'employee.manager_id','=','manager.id');
This gets me of course what I want but I would like to understand how to do it with Eloquent only.
Thanks.
Exactly the same way.
$employees = Employee::select('employee.id', 'employee.name', 'employee.manager_id', 'manager.name AS manager_name')
->join('employee AS manager', 'employee.manager_id', '=', 'manager.id')
->get();
You could also do this shown below. But then you need to access the manager name from the relationship object.
$employees = Employee::with('my_manager')->get();
foreach($employees as $employee) {
echo $employee->my_manager->name;
}
Edit
You can add any constraint to the eager loaded relationship.
$employees = Employee::with(['my_manager' => function($query) {
$query->select('manager_id', 'name');
}])->get();

laravel search many to many Relashionship

I am testing eloquent for the first time and I want to see if it suit my application.
I have Product table:
id, name
and model:
class Produit extends Eloquent {
public function eavs()
{
return $this->belongsToMany('Eav')
->withPivot('value_int', 'value_varchar', 'value_date');
}
}
and eav table:
id, name, code, field_type
and pivot table:
product_id, eav_id, value_int, value_varchar, value_date
class Eav extends Eloquent {
public function produitTypes()
{
return $this->belongsToMany(
'ProduitType'
->withPivot('cs_attributs_produits_types_required');
}
All this is working.
But I want to search in that relashionship:
e.g: all product that have eav_id=3 and value_int=3
I have tested this:
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->get();
But I get all the product, and eav data only for these who have id=3 and value_int=3.
I want to get only the product that match this search...
Thank you
I know the question is very old. But added the answer that works in the latest versions of Laravel.
In Laravel 6.x+ versions you can use whereHas method.
So your query will look like this:
Produit::whereHas('eavs', function (Builder $query) {
// Query the pivot table
$query->where('eav_id', 3);
})->get()
My suggestion and something I like to follow is to start with what you know. In this case, we know the eav_id, so let's go from there.
$produits = Eav::find(3)->produits()->where('value_int', '3')->get();
Eager loading in this case isn't going to save you any performance because we are cutting down the 1+n query problem as described in the documentation because we are starting off with using find(). It's also going to be a lot easier to read and understand.
Using query builder for checking multiple eavs
$produits = DB::table('produits')
->join('eav_produit', 'eav_produit.produit_id', '=', 'produits.id')
->join('eavs', 'eavs.id', '=', 'eav_produit.eav_id')
->where(function($query)
{
$query->where('eav_produit.value_int','=','3');
$query->where('eavs.id', '=', '3');
})
->orWhere(function($query)
{
$query->where('eav_produit.value_int','=','1');
$query->where('eavs.id', '=', '1');
})
->select('produits.*')
->get();
Making it work with what you already have...
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
$query->orWhere('id', '1')->where('value_int', '1');
}))->get();
foreach($produits as $produit)
{
if(!produit->eavs)
continue;
// Do stuff
}
From http://four.laravel.com/docs/eloquent:
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method
$posts = Post::has('comments')->get();
Using the "has()" method should give you an array with only products that have EAV that match your criteria.
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->has('eavs')->get();

Resources