OrderBy on Eloquent whereHas relationship - laravel

I have a simple page which lists counties and there related items under headings. These items need to be approved, hence the whereHas method.
I currently have the following eloquent query;
$counties = County::whereHas('items', function ($query) {
$query->where('approved', 1);
})->get();
The items returned are currently ordered by their primary field id (it would seem), however I want to list these items alphabetically by their name field.
I have tried the following query, but this does change anything. Any advice would be appreciated?
$counties = County::whereHas('items', function ($query) {
$query->where('approved', 1)->orderBy('name');
})->get();

$counties = County::whereHas('items', function ($query) {
$query->where('approved', 1);
})->orderBy('name')->get();
I don't think you can order on the subquery, it should be before the ->get

when you want to display the result , try this :
#foreach($counties as $county)
#foreach($county->items->orderBy('name') as $item)
{{ $item->name }}
#endforeach
#endforeach
Or in your County Models :
public function approvedItems(){
return $this->hasMany(Item::class)->where('approved', 1)->orderBy('name');
}
and then :
controller :
$counties = County::whereHas('approvedItems')->get();
view :
#foreach($counties as $county)
#foreach($county->approvedItems as $item)
{{ $item->name }}
#endforeach
#endforeach
Try to work with your models and relationships for having the lightest controller you can, you will gain in lisibility

To keep it eloquent, you can put it in the relation, in the Model class:
public function reviews()
{
return $this->hasMany(Review::class)->orderBy('id','desc');
}
https://laravel.io/forum/09-14-2015-ordering-a-collection-by-the-related-items
Might be late, but hopefully someone stumbles on this (it's the first in google search)

$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

Related

Laravel Sorting (orderBy or sortBy) is no working to sort database results

I am using Laravel 6.x and have been trying to sort orders ascendingly by the time they were created. This is what I tried. I tried changing the positions of orderBy and sortBy, but it didn't change anything.
public function index()
{
$orders = Order::select("*", DB::raw("count(*) as order_count"))
->groupBy(DB::raw("coupon"))->sortBy('created_at', 'desc')
->get();
return view('admin.order.index', compact('orders'));
}
So can you please tell me why this code is not sorting properly? Thank you for your support!
sortBy is used by collection, for a query, you have to use a orderBy :
public function index()
{
$orders = Order::orderByDesc('created_at')
->get();
return view('admin.order.index', compact('orders'));
}
you can also use latest()
public function index()
{
$orders = Order::latest()
->get();
return view('admin.order.index', compact('orders'));
}
You can't use a count a groupBy here, or you would get a single line.
Provide more information about what you are trying to do (Do you want to have the number of order per coupon ?
Edited :
To get your order groupby coupon :
$coupons = Coupon::with(['order' => function($query){
$query->latest();
})->withCount('order')->get();
then you can use it :
#foreach($coupons as $coupon)
Number of Order : {{ $coupon->order_count }}
<br>
Orders :
#foreach($coupon->orders as $order)
{{ $order->[...] }}
#endforeach
#endforeach

Pagination in relations using 'with'

I have some trouble with pagination. I need to take only one category with movies and paginate it. Now I write some code, but I don't think it's optimized.
$category = Category::with(['movies' => function ($query) {
$query->orderBy('id', 'desc')->paginate(18);
}])->where('slug', $slug)->first();
$catMoviesPaginate = $category->movies()->paginate(18);
You can do it with lazy loading as here I mention the code which can help to you.
$category = Category::where('slug', $slug)->first();
$movies= $category->movies()->paginate(18); //lazy loding.
return view('example', compact('category', 'movies'));
You can render the pagination in view file as well.
#foreach ($movies as $movie)
{{ $movie->id }}
#endforeach
{!! $movies->render() !!}
The most efficient way would be to create an inversed relation in movies for categories. Then you query the movies in X category by slug. You should end up with something like this
Movies::whereHas('categories', function($q) use($slug) {
$q->where('slug', $slug)
})->paginate(18);
You can take it a step further and create a scope in the Movies model to simplify your code. With the proper scope it can be as simple as Movies::inCategory($slug)->paginate(18)

accessing collections within collections laravel 5

I have a table of "rosters" that's pretty much strictly foreign keys. It acceses the "schools" table, "courses" table, and "students" table. So essentially, a 'student' takes a 'course' at a 'school'. My RostersController has this
public function show($id)
{
$roster = Roster::where('course_id', $id);
return view('roster')->with('roster', $roster);
}
My Roster Model is:
public function students()
{
return $this->hasMany('App\Student');
}
My student Model is:
public function roster()
{
return $this->belongsTo('App\Roster');
}
my view is this:
#foreach ($roster as $child)
<p>{{$child->id}}</p>
<p>{{$child->students->first_name}}</p>
#endforeach
The rosters table just saves the student_id rather than all of the student's data that is already in the 'students' table. So i'm trying to access the students table from this relation but when i run this, it tells me that anything related to the students table is 'not on this collection'. I know that I could do things this way if i was working with a hasOne relationship, but how can i accomplish this with a hasMany to output the students table value in each row?
You should try this
public function show($id)
{
$roster = Roster::with('students')->where('course_id', $id);
return view('roster')->with('roster', $roster);
}
Try this
Roster.php
public function show($id)
{
$roster = Roster::with('students')->where('course_id', $id)->get(); // load the students
return view('roster')->with('roster', $roster);
}
On the view
#foreach ($roster as $child)
<p>{{$child->id}}</p>
<p>
<!-- Loop through the stundents since this returns an collection of students and not just one -->
#foreach($child->students as $student)
{{$student->first_name}} <br>
#endforeach
</p>
#endforeach
Check this for more information on eager loading
The $child->students is a collection. So, you need to use another foreach loop inside the first one.
Your code should look like this:
#foreach ($roster as $child)
<p>{{$child->id}}</p>
#foreach($child->students as $student)
<p>{{$student->first_name}}</p>
<p>{{$student->age}}</p>
<p>{{$sutdent->another_column_in_students_table}}</p>
#endforeach
<p>{{$child->any_other_column_in_roster_table_if_needed}}</p>
#endforeach
So, your first issue is, that
$roster = Roster::where('course_id', $id);
will just return a QueryBuilder instance, you have to use
$roster = Roster::where('course_id', $id)->get();
then for sure, students is a collection, you have to iterate over it like this:
#foreach ($child->students as $student)
{{ $student->yourProperty}} <br>
#endforeach
Update:
I saw you know already about that when to use get() in query laravel 5

How to pass variable from foreach to view in laravel 5.4?

I want to count each location in my Job table by using location_id in my job table with id in location table. below code, I can count result correctly but I don't know how to pass this variable to the view. Please help?
//my code
public function index(){
$location = Location::all();
$count_location = [];
foreach ($location as $locations){
$count_location = Job::where('location_id', $locations->id)->count();
}
}
Use withCount() and view() to pass location with counted jobs to the view:
public function index(){
return view('view.name', [
'locations' => Location::withCount('jobs')->get()
]);
}
In the view:
#foreach ($locations as $location)
{{ $location->name }} has {{ $location->jobs_count }} jobs
#endforeach
You can return the collection of locations to the view and then loop through each object in the collection like so:
return view('index', [
'locations'=> $locations,
]);
Then in your index.blade.php you can use something like a #foreach or #forelse loop
#foreach ($locations as $location)
{{ $location->id }}
#endfoeach
EDIT
From the looks of it you would be better off defining a relationship between locations and jobs (i.e. a "many to many" or "one to many" relationship). this would allow you to get the counts for jobs at given locations very easily like so:
$location->jobs->count()
Eloquent relationships are explained in the documentation here
https://laravel.com/docs/5.5/eloquent-relationships
It would be more efficient if construct your query to fetch the count of related models instead of looping through all the results.
Have a look at Counting Related Models in the documentations.
For example, to get the count of all jobs related to a location, you could do:
$locations = App\Location::withCount('jobs')->get();
foreach ($locations as $location) {
echo $location->jobs_count;
}
You need to adjust the code according to your models structure.
Do this
public function index(){
$locations = Location::all();
return view('index', compact('locations'));
}
In your Location model make a relationship by adding this
public function jobs(){
return $this->hasMany(Job::class);
}
In your index view do this
#foreach ($locations as $location)
{{$location->jobs->count}}
#endforeach
Please note that Job should be there in your your model

Laravel conditional on relation: return first()

I have a Laravel 5.2 one-to-many relation and I want to return the model and put a condition to relation.
I've tried this:
$categories = Category::with(['descriptions' => function($d) use ($default_language) {
$d->where('language_id', $default_language->id);
}])->get();
It work fine, I just want something else: the relation should not be a collection or array, just a simple object. I want to do something like
$d->where('language_id', $default_language->id)->first();
, just in this case first() is not working. Any ideas?
EDIT
Actually first() is not working properly, it returns first description just for the first object returned, for others it return nothing.
Try this:
$categories = \Jobinja\CMS\Blog\Models\Category::with([
'descriptions' => function ($q) use ($defaultLanguage) {
return $q->where('language_id', $defaultLanguage->id)->take(1);
}
])
->get()
->map(function ($item) {
if ($item->descriptions->isEmpty() === false) {
$item->description = $item->descriptions->first();
}
return $item;
});
and get to description:
foreach ($categories as $category) {
$description = $category->description;
}
You can't do that but you can use first() on a collection later, for example in a view:
#foreach ($categories as $category)
{{ $category->descriptions->first()->name }}
#endforeach
I can say to use instead of first() find() and give it the language_id or $default_language->id and this will try to find in the table first the column id and assign the value. If you have different id column name give it to the find like find(['your_column_name' => '<your_value']).
If you want array to something like ->toArray(). You can test different scenarios in tinker. php artisan tinker
Here is a link to document this -> https://laravel.com/docs/5.3/eloquent#retrieving-single-models

Resources