Laravel 5.2 Eloquent Order Query by Eagerly Loaded Data - laravel

Is it possible to order the results of an Eloquent query by the eagerly loaded data. To be clear I get the right data, it is paginated properly, and works in every way except it isn't ordered the way I need it to be. In this case I'd like to sort users by profile.firstname.
Something like this:
$results = User::where('id', '!=', $user->id)
->whereNotIn('id', $ids)
->with([
'profile' => function ($query) {
$query->addSelect(['id', 'first_name', 'last_name']);
},
])
->orderBy('profile.first_name', 'desc')
->paginate();
The data is used in an Ionic application and is used for an infinite scroll so I need to have the data in the format below ordered by first_name prior to being received on the client, but the above example doesn't work.
DATA needed for Ionic View
user: {
id: '1',
username: 'aUsername'
email: 'anEmail'
profile: {
id: '2',
user_id: '1',
first_name: 'aFirstName',
last_name: 'aLastName',
phone_number: '999-999-9999'
}
}
...
Solution (with flattened result set)
$results = User::join('user_profiles', 'users.id', '=', 'user_profiles.user_id')
->select('users.id', 'users.username', 'user_profiles.first_name', 'user_profiles.last_name')
->where('users.id', '!=', $user->id)
->whereNotIn('id', $existingContactIds)
->where('users.username', 'like', $request->input('username') . '%')
->orderBy('user_profiles.first_name')
->paginate();
Solution (with proper result format)
$results = User::where('users.id', '!=', $user->id)
->whereNotIn('users.id', $existingContactIds)
->where('users.username', 'like', $request->input('username') . '%')
->with([
'profile' => function ($query) use ($columns) {
$query->addSelect('user_profiles.first_name', 'user_profiles.last_name');
},
])
->select('users.id', 'users.username')
->join('user_profiles', 'users.id', '=', 'user_profiles.user_id')
->orderBy('user_profiles.first_name')
->paginate();

For ordering by another table, you need to perform a join.
Chain in a join for your profile table
-> join('profiles', 'profiles.id', '=', 'users.profile_id')
You'll then be able to orderBy first_name (though not profile.first_name).
It isn't possible to orderBy eager loaded tables. If you look at the SQL Laravel produces, you'll see that it firsts gets all the results from the users tables then passes the IDs of just the results (so a subset of all results if you're using pagination) to the profiles table to get relevant data.
You could sometimes avoid the join by using the Collection method sortBy on the results, but this won't work when coupled with pagination as sortBy only works on the results, so will be ordering a subset of data only (but sortBy does support the dot notation you've tried using for your orderBy).

Im seeing your code and I think it should be ->orderBy('profile->first_name', 'desc') or try this other way ->orderBy('first_name', 'desc')

Try like this
$results = User::where('id', '!=', $user->id)
->whereNotIn('id', $ids)
->with([
'profile' => function ($query) {
$query->addSelect(['id', 'first_name', 'last_name']);
$query->orderBy('first_name','DESC');
},
])
->paginate();

Related

Laravel, query on joined table after join

The functionality:
I'm displaying a list of responses (user is related to a response), and i want to be able filter by user name
I have an assignment response table with a user id in it
i am joining users to this table based on that id
i have a search filter for these responses
I want to be able to search by user name
Is it possible to do something like this;
$responses = AssignmentResponse::query()->where('assignment_id', '=', $request->assignment)
->with([
'likes',
'user'
]);
//FILTERS
if ($request->queryString) {
$responses->where('user.name', 'LIKE', '%' . $request->queryString . '%');
}
return $responses->get();
Simply alter the relationship with a callback:
return AssignmentResponse::where('assignment_id', $request->assignment)
->with('likes')
->when(
$request->filled("queryString"),
function ($q) use ($request) {
$q->whereHas("user", fn ($q) => $q->where("name", "like", "%$request->queryString%"))
->with(["user" => fn ($q) => $q->where("name", "like", "%$request->queryString%")]);
}
)
->get()
Note both with() and whereHas() are used to ensure only matching values are returned. A conditional clause is used in place of your if statement.

Get multiple average values in raw expression - Laravel MongoDB jenssegers

I am using Laravel with MongoDB and jenssegers package. I have a collection called ProductDetails in which I have three fields for which I need to retrieve the average value: price, margin and weight. I can make three different queries but I would like to get that in one query. I think it would be cleaner.
I tried using raw expressions but I cannot make it work. I can't even retrieve the average of a single value. I have seen many people using mongoDB aggregation but I don't see why and how it would be useful here.
Here's what I'd love to achieve:
$productDetails = ProductDetails::select('AVG(price) as avg_price','AVG(margin) as avg_margin','AVG(weight) as avg_weight')
->where('id', '=', $id)
->where('active', '=', true)
->get();
It works when I try to retrieve the values (not the average) but the select does not work with AVG() which is why I wanted to use raw expressions
This is an unfruitful attempt:
$productDetails = ProductDetails::raw(function($collection)
{
return $collection ->find([
'name' => 'First product',
'avg_price' => ['$avg' => "price"]
]);
})
you just have to use selectRaw :
$productDetails = ProductDetails::selectRaw('AVG(price) as avg_price, AVG(margin) as avg_margin, AVG(weight) as avg_weight')
->where('id', '=', $id)
->where('active', '=', true)
->get();
be careful about the ' location, it should be around the hole statement in select raw expressions.
but :
->where('id', '=', $id)
this statment will make the query return only one row, I think you mean
->where('product_id', '=', $id)
is not?

Laravel Query Through Relationship

I am creating a search in laravel where customers can search for vehicles.
Table 1
Vehicle
VIN
PLATE
make_and_model_id
Table 2
Vehicle Makes and Models
id
make
model
Relationship in Table 1: Vehicle
public function vehicle_make_and_model_fk()
{
return $this->belongsTo('App\Models\VehicleMakeAndModel', 'vehicle_make_and_model_id');
}
So I am searching for VIN or Plate. That works fine.
I also am Searching for a Make and Model name which is a foreign key.
I pulled in the related table using with which works fine.
Now how to search through the columns of Make and Model Table?
if($request->ajax()) {
$search = $request->search_query;
$vehicles = Vehicle::with('vehicle_make_and_model_fk')
->where(function ($query) use ($search) {
$query->where('plate', 'LIKE', '%'.$search.'%')
->orWhere('vin', 'LIKE', '%'.$search.'%')
->orWhere('vehicle_make_and_models.make', 'LIKE', '%'.$search.'%');
})
->limit(5)
->get();
echo json_encode($vehicles);
exit;
}
To filter the relationship, you need to use a closure in your with()
For example:
$vehicles = Vehicle::query()
->with([
'vehicle_make_and_model_fk' => function ($query) use ($search) {
$query->where('make', 'like', "%$search%")
->orWhere('model', 'like', "%$search%");
}
])
->where(function ($query) use ($search) {
$query->where('plate', 'like', "%$search%")
->orWhere('vin', 'like', "%$search%");
})
->limit(5)
->get();
Eloquent Relationships - Constraining Eager Loads
$vehicles = Vehicle::where('plate','LIKE','%'.$search.'%')
->orWhere('vin','LIKE','%'.$search.'%')
->with('vehicle_make_and_model_fk')
->orwhereHas('vehicle_make_and_model_fk',
function($query) use($search){
$query
->where('vehicle_make_and_models.make','like',$search.'%')
->orWhere('vehicle_make_and_models.model','LIKE','%'.$search.'%');
}
)
->limit(5)
->get();
This query worked. It would search and bring results from vehicle table and would go to makes and models table and bring results from there as well. Based on - Laravel Eloquent search inside related table

How create a subquery with eloquent and laravel with softdeletes

I am using Laravel 7 and Vue.js 2.
I want to retrieve the tasks that are not assigned to a specific user.
I made the following code that works correctly.
$tasks_user = TaskUser::select('task_id')
->where('user_id', $id)
->get();
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', $tasks_user)
->distinct()
->get();
By the way to be more elegant I decided to transform the above code into a single query as follows:
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', function($q) use ($id)
{
$q->select('task_id')
->from('task_user')
->where('user_id', $id)
->get();
})
->distinct()
->get();
Unfortunately I discovered that the above query didn't work because it doesn't considers softdeletes.
For example, if the user with id 3 was related with the task 7 but now that row has been deleted with softdeletes in the table task_user, the first code returns also the task with id 7 (correctly) and the second one not (uncorrectly).
So finally, I must do a single query that works as the first code.
Can help?
You can actually combine both approaches. whereNotIn accepts also an Eloquent Query, it doesnt need to be a callback. Try this:
$userRelatedTasksQuery = TaskUser::select('task_id')
->where('user_id', $id);
$tasks = Task::select('tasks.id as id', 'tasks.name as name', 'tasks.description as description')
->join('task_user', 'tasks.id', '=', 'task_user.task_id')
->whereNotIn('task_user.task_id', $userRelatedTasksQuery)
->distinct()
->get();
Be sure to not use get() at the end of the $userReleatedTasksQuery, as you want the eloquent query instance, not the result.

Sort results where "seen" matches 2

I have a table for stories with fields id, user_id, file_name, seen (1-2)
Stories: id, user_id, file_name, seen
I would like to create a list that takes all the stories but with a primary order where seen corresponds to 2 (still to be seen then)
I have this simple query as a test, what should I do?
$users = App\Models\User::whereHas('stories', function ($query) {
})->get();
You can try next:
$users = App\Models\User::whereHas('stories')->with(['stories' => function($query) {
$query->orderBy(DB::raw('stories.seen=2'), 'desc')
}])->get();
Or
$users = App\Models\User::whereHas('stories')
->select('*')
->join('stories', 'stories.user_id', '=', 'users.id')
->orderBy(DB::raw('stories.seen=2'), 'desc')
->get();

Resources