Sort results where "seen" matches 2 - laravel

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();

Related

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.

Select unique records from child with condition on parent relationship

I have two tables, Shows and Episodes, each episode has show_id linking them one to many.
Now I need to get latest 6 episodes, one per show, where show.active is true
I've tried the following code:
$episodes = Episode::select(DB::raw('t.*'))
->from(DB::raw('(SELECT * FROM episodes ORDER BY id DESC) t'))
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('t.show_id')
->take(6)
->get();
Unfortunately, I get the following:
Column not found: 1054 Unknown column 'episodes.show_id' in 'where clause' (SQL: select t.* from (SELECT * FROM episodes ORDER BY id DESC) t where exists (select * from shows where episodes.show_id = shows.id and active = 1) group by t.show_id limit 6)
I've also tried:
$episodes = Episode::where('active', true)
->orderBy('id', 'DESC')
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('show_id')
->take(6)
->get();
It shows no error, but doesn't return latest of each show, groupBy gets the first record, I need the latest
This should work:
$episodes = Episode::where('active', true)
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->groupBy('show_id')
->orderBy('id', 'DESC')
->take(6)
->get();
You can try this
$episodes = Episode::selectRaw('max(id) as id, show_id')
->whereHas('show', function($query) {
$query->where('active', '=', true);
})
->orderBy('id', 'DESC')
->groupBy('show_id')
->take(6)
->get();
You can use a WHERE IN subquery:
$ids = Episode::selectRaw('max(id)')
->whereHas('show', function ($query) {
$query->where('active', '=', true);
})->groupBy('show_id');
$episodes = Episode::whereIn('id', $ids)
->take(6)
->get();

Laravel distinct on join results not working in query builder

I have a posts table that has join query with 4 other tables. 3 of them are one to one relations but the 4th is one to many. I want the query to return only 1 row for each post. What i am trying so far is like this-
$query = DB::table('posts')
->select('posts.*',
'subcategories.subcategory_title_en',
'subcategories.subcategory_title_bn',
'categories.category_title_en',
'categories.category_title_bn',
'users.*',
'postimages.postimage_thumbnail'
)
->join('subcategories', 'subcategories.subcategory_id', '=', 'posts.subcategory_id')
->join('categories', 'categories.category_id', '=', 'subcategories.parent_category_id')
->join('users', 'users.id', '=', 'posts.user_id')
->join('postimages', 'postimages.post_id', '=', 'posts.post_id');
$query->groupBy('posts.post_id');
echo $query->count();
exit();
I have currently 50 posts in database, but the query returns all rows for each postimages, more than 1 row for each post that is. I thought distinct would only show each post_id once? What am i missing here?
I would prefer if someone tells me how to do this with query builder. As this will have a lot of searching on different columns and i want it to be as fast as possible.
This is a simpler version -
$query = DB::table('posts')
->select('posts.*','postimages.postimage_thumbnail')
->join('postimages', 'postimages.post_id', '=', 'posts.post_id')
->groupBy('posts.post_id');
echo $query->count();
exit();
The strange thing is the SQL query that is shown by laravel " select posts.*, postimages.postimage_thumbnail from posts inner join postimages on postimages.post_id = posts.post_id group by posts.post_id" works fine in mysql
You should use groupby
Try this code
$query = DB::table('posts')
->select('posts.*',
'subcategories.subcategory_title_en',
'subcategories.subcategory_title_bn',
'categories.category_title_en',
'categories.category_title_bn',
'users.*',
'postimages.postimage_thumbnail'
)
->join('subcategories', 'subcategories.subcategory_id', '=', 'posts.subcategory_id')
->join('categories', 'categories.category_id', '=', 'subcategories.parent_category_id')
->join('users', 'users.id', '=', 'posts.user_id')
->join('postimages', 'postimages.post_id', '=', 'posts.post_id')->groupBy('posts.post_id');
In config/database.php at "mysql" change : 'strict' => true, to false

Laravel: join Eloquent models

Question. How can I use Eloquent to produce this query:
SELECT campaigns.name, users.name FROM campaigns LEFT JOIN users on campaigns.gamemaster_id = users.id where campaigns.status = 1
Campaigns
id gamemaster_id name status
1 1 campaign1 1
2 2 campaign2 1
Users
id name
1 rick
2 bob
Result
id gamemaster_id name status gamemaster_name
1 1 campaign1 1 rick
2 2 campaign2 1 bob
Campaign model
class Campaign extends Model
{
public function gamemaster()
{
return $this->belongsTo('App\User', 'gamemaster_id');
}
}
My try to make Eloquent, but that fails:
$result = Campaign::where('status', '=', 1)->with('gamemaster')->select('name')->orderBy('users.updated_at', 'desc')->get();
You can do it in two ways, first using the query builder,
$campaigns = DB::table('campaigns')->leftJoin('users', 'campaigns.gamemaster_id', '=', 'users.id')
->select(['campaigns.name as name', 'users.name as gamemaster_name'])
->where('campaigns.status', '=', 1)
->orderBy('users.updated_at', 'desc')
->get();
And with eager loading, you can get the campaigns like below. However, to sort by relation property, you need an another layer which is not that easy to implement.
$campaigns = Campaign::where('status', '=', 1)->with(['gamemaster' => function($query){
$query->select(['id', 'name']);
}]->select('id', 'gamemaster_id', 'name')->get();
But you can use collection functions to easily sort it (However, it will take more execution time)
$campaigns = Campaign::where('status', '=', 1)->with(['gamemaster' => function($query){
$query->select(['id', 'gamemaster_id', 'name', 'updated_at']);
}]->select('id', 'name')->get()->sortByDesc(function ($campaign) {
return $campaign->gamemaster->updated_at;
})
The following query should work:
Campaign::whereStatus(1)
->join('users', 'campaigns.gamemaster_id', '=', 'users.id')
->select('campaigns.name', 'users.name')
->orderBy('users.updated_at', 'desc')
->get()

Laravel 5.2 Eloquent Order Query by Eagerly Loaded Data

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();

Resources