How to select instances with multiple relations? - laravel

I have some models Featured_Course_Request, Course_Request, Response and Teacher. Featured_Course_Request hasOne Course_Request and Course_Request hasMany Response by Teacher.
I want to get the only Featured_Course_Requests on which logged in teacher has not responded (have no Response by logged in teacher.) How can I do it?
I am trying to achieve it with the following code but it is not giving correct output.
$featured_course_request = Featured_Course_Resquest::whereRaw('remaining_coins >= coins_per_click')->where('status', '=', 'open')
->whereHas('courseRequest', function($q) use ($teacher){
$q->whereHas('responses', function($qe) use ($teacher){
$qe->where('teacherID', '!=', $teacher->id);
});
});

You can target nested relations with the dot syntax: 'courseRequest.responses' further more you'll need whereDoesntHave instead of whereHas:
$featured_course_request = Featured_Course_Resquest::whereRaw('remaining_coins >= coins_per_click')
->where('status', '=', 'open')
->whereDoesntHave('courseRequest.responses', function($q) use ($teacher){
$qe->where('teacherID', '=', $teacher->id);
})
->get();

Related

Product filtering problems with Laravel Query Builder

I try to implement ajax based filtering in my ecommerce project homepage for searching a product. I am using Laravel Query Builder. My Query for filtering products is given below-
$result= DB::table('products')
->leftjoin('products_description','products.products_id','products_description.products_id')
->leftjoin('image_categories', 'products.products_image', '=', 'image_categories.image_id')
->leftjoin('products_to_categories','products.products_id','products_to_categories.products_id')
->leftjoin('categories','products_to_categories.categories_id','categories.categories_id')
->when($category_slug, function($q) use ($category_slug) {
return $q->where('categories.categories_slug', $category_slug);
})
->where('products_name','like',"%{$querval}%")
->where('image_categories.image_type','=','ACTUAL')
->orderby('products.products_id','DESC')
->take(5)
->get();
I get every product twice in search result. don't know why. A sample response is given in this picture.
Can anyone help me to optimize my query for getting the desired result?
join with categories table only when you have to.
and select your columns strictly. then group by selected columns.
$result= DB::table('products')
->leftjoin('products_description','products.products_id','products_description.products_id')
->leftjoin('image_categories', 'products.products_image', '=', 'image_categories.image_id')
->when($category_slug, function($q) use ($category_slug) {
return $q->leftjoin('products_to_categories','products.products_id','products_to_categories.products_id')
->leftjoin('categories','products_to_categories.categories_id','categories.categories_id')
->where('categories.categories_slug', $category_slug);
})
->where('products_name','like',"%{$querval}%")
->where('image_categories.image_type','=','ACTUAL')
->orderby('products.products_id','DESC')
->select(['products_name','products.id','image_path'])
->groupBy(['products_name','products.id','image_path'])
->take(5)
->get();
you can use from groupBy to avoid comming repeated data.
try this one:
$result= DB::table('products')
->leftjoin('products_description','products.products_id','products_description.products_id')
->leftjoin('image_categories', 'products.products_image', '=', 'image_categories.image_id')
->when($category_slug, function($q) use ($category_slug) {
return $q->where('categories.categories_slug', $category_slug);
})
->where('products_name','like',"%{$querval}%")
->where('image_categories.image_type','=','ACTUAL')
->orderby('products.products_id','DESC')
->groupBy('products_name','products.products_id')
->take(5)
->get();

Laravel: Use order by in combination with whereHas

I want to retrieve a collection of data, which is ordered by the start_date of the relation
Basically I want to achieve this, with Laravel Models (the code below works perfectly)
$posts = DB::table('posts')
->leftJoin(
'threads',
'posts.id',
'=',
'threads.postable_id'
)
->where('threads.postable_type', '=', 'App\Post')
->orderBy('threads.start_date')
->paginate($request->input('limit', 2));
So in this case, I'm fetching ALL Posts and those are ordered by the start_date of the thread relation.
Those are not my actual tables but this works perfectly!
Because I'm using https://laravel.com/docs/8.x/eloquent-resources this is not the ideal solution to retrieve sorted data.
So instead I want to use the orderBy clause somewhere here
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now());
});
But I just cannot make this work. I've tried this
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
and I also appended this to the actual relation:
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
If you look at your code:
$posts = Post::whereHas('thread', function ($query) {
$query->where('end_date', '>=', Carbon::now())
->orderBy('start_date');
});
the whereHas will only return Post associate with a thread which the function return true.
Try this:
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
This will fetch all Post with Thread only if they have at least one Thread and then orderBy the start_date of the Thread.
You don't have to do the whereHas function because when you call ->with('thread') it'll use you this :
public function thread(): MorphOne
{
return $this->morphOne('App\Thread', 'postable')->orderBy('start_date');
}
whereHas doesnt retrieve the relationship.
If you need even more power, you may use the whereHas and orWhereHas methods to define additional query constraints on your has queries, such as inspecting the content of a comment: Laravel whereHas
Don't do :
$posts = Post::with('thread')->orderBy('thread.start_date');
If there is no thread on some post, post without thread will be fetch with value null for their key thread and you will have an unexpected result when you try to orderBy.
First of all I want to thank Elie Morin for his help but I found out that I definitely need to use joins for that task.
In my example, I wanted to order the main query (posts) by the relation's start_date
Doing what you suggested
$posts = Post::with('thread')->has('thread')->orderBy('thread.start_date')->get();
Would only order the thread by start_date and not the ENTIRE query.
Which is why I came up with something like this:
$posts = Post::has('thread')
->select('posts.id')
->leftJoin(
'thread',
'posts.id',
'=',
'thread.postable_id'
)
->where('thread.postable_type', '=', 'App\Post')
->where('thread.end_date', '>=', Carbon::now())
->orderBy('thread.start_date')
->with('thread');
return PostResource::collection($posts->paginate(2));

How can I do the job of three foreach loop by one foreach loop?

actually I am new to Laravel. I am doing a project for making a meal management system.
my code in my controller is:
$days=DB::table('days')
->get();
foreach ($days as $day) {
$breakfast[]= DB::table('breakfast_menus')
->leftJoin('breakfast_items', 'breakfast_menus.item_id', '=', 'breakfast_items.id')
->where('breakfast_menus.day_id',$day->id)
->select('breakfast_items.item')
->get();
$lunch[]= DB::table('lunch_menus')
->leftJoin('lunch_items', 'lunch_menus.item_id', '=', 'lunch_items.id')
->where('lunch_menus.day_id',$day->id)
->select('lunch_items.item')
->get();
$dinner[]= DB::table('dinner_menus')
->leftJoin('dinner_items', 'dinner_menus.item_id', '=', 'dinner_items.id')
->where('dinner_menus.day_id',$day->id)
->select('dinner_items.item')
->get();
}
return view('admin.show_menu',compact('days','breakfast','lunch','dinner'));
I want to show the data in a table. Where there will be
<td>day</td>
<td>Breakfast </td>
<td>Lunch</td>
<td>Dinner</td>
You should really use Laravel relationships and do justice with the framework.
Create models for each table.
Define relationship like days has breakfast_manu. See this
Then you will be able to do it like this:
$days = Days::with(['breakfast_menus','lunch_items','dinner_items'])->get();
I can say the way you wrote are very very bad for performance, why don't you use another way round in sql?
$days = DB::table('days')
->leftJoin('breakfast_menus', function($q) {
$q->leftJoin('breakfast_items', 'breakfast_menus.item_id', '=', 'breakfast_items.id')
->where('breakfast_menus.day_id', $day->id);
})
->leftJoin('lunch_menus', function($q) {
$q->leftJoin('lunch_items', 'lunch_menus.item_id', '=', 'lunch_items.id')
->where('lunch_menus.day_id', $day->id);
})
->leftJoin('dinner_menus', function($q) {
$q->leftJoin('dinner_items', 'dinner_menus.item_id', '=', 'dinner_items.id')
->where('dinner_menus.day_id', $day->id);
})
->select('days.id', 'breakfast_items.item as breakfast', 'lunch_items.item as lunch', 'dinner_items.item as dinner')
->get();
I didn't try out the SQL, but you can get the idea.
Then you can foreach $days to show your data

Eloquent User Where Clause with Entrust Library

I'm trying to select all users for a company. But only users who has "admin" role status (Entrust, etc.).
User::where('company_id', Auth::user()->company_id)->hasRole('admin')->get();
The above is throwing an error. Left a bit lost on how to run such a query. Where am I going wrong with this? Very little documentation on Entrust.
You can use plain Eloquent for this. The whereHas method would generate one query:
$users = User::where('company_id', Auth::user()->company_id)
->whereHas('roles', function($query) {
$query->where('name', 'admin');
})->get();
Or, you can just get the roles and then get all of that role's users. This would generate two queries, but ultimately achieve the same thing.
$users = Role::where('name', 'admin')
->first()
->users()
->where('company_id', Auth::user()->company_id)
->get();
Think you need to get all the users having the company_id first
$users = User::where('company_id', Auth::user()->company_id)->get();
Then loop through $users and check hasRole()
foreach ($users as $user) {
if($user->hasRole('admin')){
//user is admin
}
}
UPDATE
This might be a dirty solution but you can try to do a manual query
$admin = DB::table('role_user')
->join('users', 'users.id', '=', 'role_user.user_id')
->join('roles', 'roles.id', '=', 'role_user.role_id')
->where('roles.name', 'admin')->get();

How to select instances for specific 'n' on m:n relationships?

I have two models Teacher and Category. These two have Many to Many relationship.
I want to get those teachers who have one category equal to "OLevels". Which method of eloquent is used for it or is there any other way I can get it?
Is there anyway to get it as:
$teachers = Teacher::where('category', '=', 'OLevels')->get();
You can use whereHas for that:
$category = 'OLevels';
$teachers = Teacher::whereHas('category', function($q) use ($category){
$q->where('name', $category);
})->get();
You could make use of eager loading with constraints
$teachers = Teacher::with(['category' => function($query)
{
$query->where('category', '=', 'OLevels');
}])->get();
Further info in the documentation.

Resources