query joins only last row from relationship - laravel

User can apply many times to single order. The last apply is the one I want. My code:
$users = User::whereHas('applies', function ($query) use ($order_id){
$query->where('order_id', $order_id);
})->with(['applies' => function($query) use ($order_id){
$query->orderBy('id', 'desc')->where('order_id', $order_id)->first();
}])->paginate($request->perPage);
This limit the number of applies only in the last user, the other applies records are empty. Removing first() from with() shows all applies in all users. How is that? Please help.
I've tried limit(1), take(1)->get(), take(1), but it also fails.

There is no simple way to limit eager loading.
You can use a HasOne relationship:
public function latestApply() {
return $this->hasOne(Apply::class)->orderBy('id', 'desc');
}
$users = User::whereHas('applies', function ($query) use ($order_id){
$query->where('order_id', $order_id);
})->with(['latestApply' => function($query) use ($order_id){
$query->where('order_id', $order_id);
}])->paginate($request->perPage);
This will still fetch all applies in the background, but only show the latest one for each user.
See also https://stackoverflow.com/a/50571322/4848587 for other possible solutions.

Related

Laravel how to query with Auth ID from another table

I have a forms_table where I query all my results for authenticated user with where Auth::id() in user_id column.
Each of these rows have a boolean column alternation. If this is 1, then it means there is a row in another table called form_alternation where I have the Auth::id(). This means the Auth::id() does not exist in the initial forms_table.
How can I retrieve the rows from the forms_table with where clause where('user_id', Auth::id()) and show the rows where the Auth::id() exists in the form_alternations?
Currently I have this and it shows me 0 rows because the last Auth::id() clause.
$baseQuery->with(['alternated' => function($q){
$q->where('user_id', Auth::id());
}]);
$baseQuery->where('user_id', Auth::id());
As i see it you want your query to do something similar to this (in half pseudo code).
where (forms.user.id = Auth::id() OR (users.alternation = 1 AND alternated.user_id = Auth::id()))
This could be achieved with Eloquent, as i understand your domain, try something like this.
use Illuminate\Database\Eloquent\Builder;
// wrap it in where, to avoid other queries precedence ruining the or clause
$query->where(function (Builder $query) {
// First part, where we assume user id is in forms
$query->where('user_id', Auth::id());
// Second part, check alternation or your relationship
$query->orWhere(function (Builder $query) {
$query->where('alternation', true);
// Use whereHas to check if your forms has the user as a relationship
$query->whereHas('alternated', function (Builder $query) {
$query->where('user_id', Auth::id());
});
});
});
This should provide similar logic to the pseudo example, simply utilizing orWhere() logic and you can check relationships with whereHas().

Eloquent Get Models based on fields in related tables

I have multiple Models related to my User model(eg: profiles, addresses, orders,...etc)
I want to get all the users that were updated in the past 24 hours, based on the update_at field in each of the related tables accordingly.
I have found the following if u have one related table, but in my case I have more than 3 tables that I need to check:
$users = User::with('orders')
->where('updated_at', $valueA)
->whereHas('orders', function($query)
{
$query->where('updated_at', $valueB);
})->get();
I hope someone can help me to know how to apply this for multiple where clauses n=on multiple related tables.
Try the below code,
$users = User::with(['profiles', 'addresses', 'orders'])
->whereDate('updated_at','>', $yesterdayDate)
->orWhereHas('orders', function($query) use ($yesterdayDate){
$query->whereDate('updated_at','>', $yesterdayDate);
})->orWhereHas('addresses', function($query) use ($yesterdayDate) {
$query->where('updated_at','>', $yesterdayDate);
})->orWhereHas('profiles', function($query) use ($yesterdayDate){
$query->where('updated_at','>', $yesterdayDate);
})->get();
If you have direct relation from users to all models like orders, profiles, addresses etc then you can try this.
$valueA = $time // whatever time you want
$users = User::
where('updated_at', $valueA)
->whereHas('orders', function($query) use ($valueA)
{
$query->where('updated_at', $valueA);
})
with('orders')
->whereHas('profiles', function($query) use ($valueA)
{
$query->where('updated_at', $valueA);
})
with('profiles')
->whereHas('addresses', function($query) use ($valueA)
{
$query->where('updated_at', $valueA);
})
with('addresses')
->get();
Thank you.

Eloquent: How to combine eager loading and filter by pivot?

I have a many-to-many relationship between models. For a given model instance, I know how to filter the related models according to the value of a pivot. For example, in the case of users and roles, I would use:
User->roles()->wherePivot('admin',1);
Similarly, I know how to eager load all roles for a set of users:
User::where('active',1)->with('roles')->get();
What I am trying to do is combine these two concepts. For example, how do I retrieve a set of users with their eagerly loaded roles, where the users are filtered according to a field on the pivot?
I realise I can do this easily enough with raw SQL, but I would prefer to use Eloquent if possible.
You can restrict your eager loaded relations using a closure.
$users = User::with(['roles' => function ($query) {
return $query->wherePivot('admin', 1);
}])
->where('active', 1)
->get();
You can also query relations using whereHas.
$users = User::with('roles')
->whereHas('roles', function ($query) {
return $query->wherePivot('admin', 1);
})
->where('active', 1)
->get();
Pass a closure in to with when eager loading to add clauses to the query:
User::with(['roles' => function ($query) {
$query->where('admin', 1);
}])->where('active', 1)->get();

Where clause inside whereHas being ignored in Eloquent

Im trying to make a query using whereHas with eloquent. The query is like this:
$projects = Project::whereHas('investments', function($q) {
$q->where('status','=','paid');
})
->with('investments')
->get();
Im using Laravel 5.2 using a Postgres driver.
The Project model is:
public function investments()
{
return $this->hasMany('App\Investment');
}
The investments model has:
public function project() {
return $this->belongsTo('App\Project');
}
The projects table has fields id,fields...
The investments table has the fields id,project_id,status,created_at
My issue is that the query runs and returns a collection of the projects which have at least one investment, however the where clause inside the whereHas is ignored, because the resulting collection includes investments with status values different than paid.
Does anyone has any idea of what is going on?
I believe this is what you need
$projects = Project::whereHas('investments', function($q) {
$q->where('status','=','paid');
})->with(['investments' => function($q) {
$q->where('status','=','paid');
}])->get();
whereHas wil check all projects that have paid investments, with will eagerload all those investments.
You're confusing whereHas and with.
The with method will let you load the relationship only if the query returns true.
The whereHas method will let you get only the models which have the relationship which returns true to the query.
So you need to only use with and not mix with with whereHas:
$projects = Project::with(['investments' =>
function($query){ $query->where('status','=','paid'); }])
->get();
Try like this:
$projects = Project::with('investments')->whereHas('investments', function($q) {
$q->where('status','like','paid'); //strings are compared with wildcards.
})
->get();
Change the order. Use with() before the whereHas(). I had a similar problem few weeks ago. Btw, is the only real difference between the problem and the functional example that you made.

Eloquent filter on pivot table created_at

I want to filter the contents of two tables which have an Eloquent belongsToMany() to each other based on the created_at column in the pivot table that joins them. Based on this SO question I came up with the following:
$data = ModelA::with(['ModelB' => function ($q) {
$q->wherePivot('test', '=', 1);
}])->get();
Here I'm using a simple test column to check if it's working, this should be 'created_at'.
What happens though is that I get all the instances of ModelA with the ModelB information if it fits the criteria in the wherePivot(). This makes sense because it's exactly what I'm telling it to do.
My question is how do I limit the results returned based on only the single column in the pivot table? Specifically, I want to get all instances of ModelA and ModelB that were linked after a specific date.
OK, here it goes, since the other answer is still wrong.
First off, wherePivot won't work in whereHas closure. It's BelongsToManys method and works only on the relation object (so it works when eager loading).
$data = ModelA::with(['relation' => function ($q) use ($someDate) {
$q->wherePivot('created_at', '>', $someDate);
// or
// $q->where('pivot_table.created_at', '>', $someDate);
// or if the relation defines withPivot('created_at')
// $q->where('pivot_created_at', '>', $someDate);
}])->whereHas('ModelB', function ($q) use ($someDate) {
// wherePivot won't work here, so:
$q->where('pivot_table.created_at', '>', $someDate);
})->get();
You are using Eager Loading Constraints, which constrain only, like you said, the results of the related table.
What you want to use is whereHas:
$data = ModelA::whereHas('ModelB' => function ($q) {
$q->wherePivot('test', '=', 1);
})->get();
Be aware that ModelB here refers to the name of the relationship.

Resources