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);
Related
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;
}
i am using Metable in my project for creating meta for orders
but i have one problem
i want group orders that have same email
this is metable table image : image 1 image 2
i need code like this can work :)
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail");
})->groupBy('meta.value')->get();
and this is meta relation that called by trait 'use Metable' in Order Model:
public function meta(): MorphMany
{
return $this->morphMany($this->getMetaClassName(), 'metable');
}
Thanks
This query may work, we start querying from the Meta model:
Meta::with('metable')
->whereHasMorph('metable', Order::class)
->where('key', 'gmail')
->get()
->groupBy('value')
->map(function ($metaCollection) {
return $metaCollection->map->metable->unique('id');
});
This is untested, but I would give it a try to retrive the models from the Order class:
Order::whereHas('meta', function($query) {
$query->where('key', 'gmail');
})->with(['meta' => function($query) {
$query->groupBy('value')
}])->get();
Try this solution instead:
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail")
$query->groupBy('meta.value');
})->get();
I want to perform an easy query with Eloquent.
I need to get all open tournament ( tournament->type = 1 ) that are in same country than me. Tournament has no country_id, but I need to do it with
$tournament->owner->country_id == Auth::user()->id
So, In my tournament table, I have a user_id that is the owner, and in my model, I have a working relation that get $tournament->owner
Here is my try ( doesn't work )
openTournaments = App\Tournament::with('owner')
->whereHas('owner', function ($query) {
$query->where('id', Auth::user()->country_id);
})
->where('type', config('constants.OPEN_TOURNAMENT'))
->get();
Any Idea how to fix it???
I'm not sure what your schema looks like, but assuming that the user has a country_id associated with it, and a tournament has an owner associated with it, you should be able to just do this:
openTournaments = App\Tournament::with('owner')
->whereHas('owner', function ($query) {
$query->where('country_id', Auth::user()->country_id);
})
->where('type', config('constants.OPEN_TOURNAMENT'))
->get();
Note country_id instead of id.
I've got a situation where I've got Posts, Users and Comments.
Each comment stores a post_id and a user_id. What I want to do is get all of a user's comments on a particular post, so that I can do a call like this:
$comments = Auth::User()->comments(post_id=x)->text
(where I know what x is)
I have:
User->HasMany(comments)
Comments->HasOne(User)
Comments->HasOne(Project)
Project->HasMany(comments)
I feel like there needs to be a where or a has or a wherehas or something thrown in.. the best I can manage is that I pull Auth::User()->comments into an array and then search through the array until I find the matching post ID.. that seems wasteful.
with doesn't apply any join, so you can't reference other table.
You can use this:
// User model
public function comments()
{
return $this->hasMany('Comment');
}
// Comment model
public function scopeForPost($query, $postId)
{
$query->where('post_id', $postId);
}
// then you can do this:
Auth::user()->comments()->forPost($postId)->get();
Alternatively you can eager load comments with constraint:
User::with(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}])->find($someUserId);
// or exactly the same as above, but for already fetched user:
// $user .. or
Auth::user()->load(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}]);
// then you can access comments for $postId just like this:
Auth::user()->comments; // collection
When you need to filter your relations, you just have to do it in your Eloquent query:
$data = User::with('posts', 'comments')
->where('users.id', Auth::User()->id)
->where('posts.id', $postID)
->get();
Then you can
foreach($data->comments as $comment)
{
echo $comment->text;
}
Your Comments table would have foreign keys Post_Id and User_ID
To Access all the comments of a particular post from a particular user , can you try this way?
Comment::select('comments.*')
->where('comments.user_id', Auth::user()->id)
->leftJoin('posts','posts.id','=','comments.post_id')
->leftJoin('users','users.id','=','comments.user_id')
->get();
Am sure there is better way to achieve it, but this should give you desired results.
Note use aliases if you have conflicting column names
Let me know if this worked.
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();