Laravel: join Eloquent models - laravel

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

Related

Order by relationship with pagination laravel eloquent

I have the relationship below, an application belongs to a user.
Users table
ID | Name
1 | Danny
2 | Mark
Applications table
ID | user_id
1 | 1
2 | 2
Application.php
public function user()
{
return $this->belongsTo(User::class);
}
I'm trying to sort the application by user's name and paginate the result. I tried the code below, the pagination is working but the order by doesn't have any effect.
$query = Application::query();
$query = $query->with(['user' => function ($query){
$query->orderBy('name', 'DESC');
}]);
$query = $query->paginate(10);
Try using inner join and order by relation column:
$applications = Application::select('*')
->join('authors', 'applications.user_id', '=', 'users.id')
->orderBy('authors.name', 'DESC')
->paginate(10);
Try this:
Application::join('users', 'applications.user_id', '=', 'users.id')
->orderBy('users.name','desc')
->with('users')
->get();

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

Laravel Where Count > N

I have 2 models in my app:
1. Customer.php
2. Car.php
Now I would like to run a query that returns all customers that have less than 2 cars. Where 2 is a number that can be changed by the user.
I have tried this but it didn't work, it just returns all customer records:
$customers = Customer::whereHas("cars", function($query) {
$query->selectRaw("count(*) < ?", [2]);
})
->get();
Edit:
The two models are linked in a pivot table, meaning A customer can have more than 1 car and a Car can belong to more than 1 customer.
Use this:
$customers = Customer::withCount('cars')
->having('cars_count', '<', 2)
->get();
So , here is the result.
Relation in model Customer.php
public function cars()
{
return $this->belongsToMany('App\Car','car_customer','car_id','customer_id');
}
Query to get all customers with N cars:
$userInput = 2;
$data = Customer::with('cars')
->withCount('cars')
->has('cars', '<', $userInput)
->orderBy('cars_count', 'desc')
->get();
Where the $userInput is your 'N'.
This is the best way:
$customers = Customer::has('cars','<', 2)->get();
Have you tried this approach?
$input = 2;
$customers = Customer::whereHas("cars", function($query) use ($input) {
$query->where(DB::raw("count(cars.id)"), "<", DB::raw($input))
})->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

Eloquent "grandfather" relashionship condition

School have books and students.
Book have pages.
How can i get all pages from all books where school_id = 1 ? I tried the code below, but i didn't worked.
Page::whereHas('book.school', function($query) {
$query->where('id', '=', 1);
})->get()->toJson();
Also, what if i'd like to get all pages from book_id = 1 and school_id = 1 ?
I need to use school_id here just to check if the book we are getting the pages is from the same school as the logged student.
Easy way, assuming Book belongsTo School:
Page::whereHas('book', function($query) {
$query->where('school_id', '=', 1);
})->get();
complete solution:
Page::whereHas('book', function($q) {
$q->whereHas('school', function ($q) {
$q->where('id', '=', 1);
});
})->get();

Resources