I want to limit related records from
$categories = Category::with('exams')->get();
this will get me exams from all categories but what i would like is to get 5 exams from one category and for each category.
Category Model
public function Exams() {
return $this->hasMany('Exam');
}
Exam Model
public function category () {
return $this->belongsTo('Category');
}
I have tried couple of things but couldnt get it to work
First what i found is something like this
$categories = Category::with(['exams' => function($exams){
$exams->limit(5);
}])->get();
But the problem with this is it will only get me 5 records from all categories. Also i have tried to add limit to Category model
public function Exams() {
return $this->hasMany('Exam')->limit(5);
}
But this doesnt do anything and returns as tough it didnt have limit 5.
So is there a way i could do this with Eloquent or should i simply load everything (would like to pass on that) and use break with foreach?
There is no way to do this using Eloquent's eager loading. The options you have are:
Fetch categories with all examps and take only 5 exams for each of them:
$categories = Category::with('exams')->get()->map(function($category) {
$category->exams = $category->exams->take(5);
return $category;
});
It should be ok, as long as you do not have too much exam data in your database - "too much" will vary between projects, just best try and see if it's fast enough for you.
Fetch only categories and then fetch 5 exams for each of them with $category->exams. This will result in more queries being executed - one additional query per fetched category.
I just insert small logic inside it which is working for me.
$categories = Category::with('exams');
Step 1: I count the records which are coming in response
$totalRecordCount = $categories->count()
Step 2: Pass total count inside the with function
$categories->with([
'exams' => function($query) use($totalRecordCount){
$query->take(5*$totalRecordCount);
}
])
Step 3: Now you can retrieve the result as per requirement
$categories->get();
Related
I need to make pagination in Laravel, and as I read laravel documentation here is a several way to do it. but in my cases it is a little bit complexity. So let say I've two table in my DB, table1 and table2(many to many relationship), I want to paginate table1 with specific ids. So I mean if table1 contains ids 1,2,3,4,5 ... n, I want to paginate only rows which id is 2 and 3
I have tried :
private function check_wear($type){
$products_id = array(); //here is ids which $type is set as 'ON'
$wears = DB::select("SELECT prod_id FROM wears WHERE ".$type." = 'on' "); //Select specific products ids. (in our case it is **table1**)
foreach ($wears as $wr){
array_push($products_id,$wr->prod_id);
}
return $products_id; //return array with ids which should be paginate
}
public function hat($locale){
$hat_prod_ids = $this->check_wear('coat');
$p=12;
$products = DB::table('products')->paginate($p); // 12 per page
dd($products);
my code only paginate one table with all data, is there any built-in function to somehow write a logic condition? for pagination
If you need to filter your pagination with elements which you've in $hat_prod_ids, you're able to use WhereIn() method, that one checks if your elements (in our case ids) exist in your $hat_prod_ids and returns true or false
$products = DB::table('products')
->whereIn('id',$hat_prod_ids)
->paginate($p);
add that code and now you will be able to paginate only that ids which is matched in your array
I have comments table where has parent_id
This is Comment table sub_comments relation.
public function sub_comments()
{
return $this->hasMany(self::class, 'parent_id');
}
This code return all comments with related all sub-comments
Comment::with('sub_comments')->get();
But I want to get all comments also sub-comments when sub-comments is single. That mean if comment have 2 or more comments for that comment I did not want get that sub-comments.
Now I use this code
$oneSubcommentCommentIds = Comment::has('sub_comments', '=', 1)->pluck('id');
Comment::with([
'sub_comments' => function ($q) use ($oneSubcommentCommentIds) {
$q->whereIn('parent_id', $oneSubcommentCommentIds);
}
])->get();
but this make one additional query.
Try this:
Comment::with('sub_comments')->has('sub_comments', '=', 1)->get();
Update
Your question wasn't clear, I can't imagine another way to doing this without previosly loaded the relationship or the count of the relationship.. so I'd do this:
// First get all your comments with an aditional count field
$comments = Comments::withCount('sub_comments')->get();
// separate the ones with just one sub_comment from the rest
list($oneSubComment, $theRest) = $collection->partition(function ($comment) {
return $comment->sub_comments_count == 1;
});
// Then load the relationship on just the selected elements
$oneSubComment->load('sub_comments');
// re-join the collection
$comments = $oneSubComment->union($theRest);
What am I doing here?
Adding an additional field to each $comment with the relationship count (it should be something like sub_comments_count)
Partition the resulting collection in two parts: the ones with one comment and the rest. Using the partition() method.
Lazy eager loading the collection.
Re-joining the two collections using the union() method.
I'm trying to access the parent value, the posts date, in a nested function.
In my application, a user has many posts, with each post being associated to a product (textbook). Each time someone views a page with the product, a new row is added to the product-views table.
I want to return the cumulative amount of times the users products have been seen. I've been able to get all the users posts, then the associated product, and then the count of all the views of that product.
Now I'd like to add another where() condition to only return views that have occured after the post was created. To do so, I need to get the posts date, e.g. views->product->post, while constructing the query like user->posts->product->views.
// getting all of the users available posts
$user = $request->user()->posts()->available()->with([
// with the textbook (product) associated with the post
'textbook' => function ($q) {
// get the count of textbook-page views
return $q->withCount([
// from the related table views
'views' => function ($q) {
// ===!!! Q !!!=== How do I access the posts (parent (grandparent?)) date, so that I only select the rows that have been viewed after the post was created ===!!!===
->where('date-viewed', '>=', 'posts.date');
},
]);
//some cleanup
}])->distinct()->get()->unique('isbn')->pluck('textbook.views_count')->sum();
How do I go backwards in a nested function to access the posts date?
It looks like as Jonas said in the comments, each with() relationship was a new query, so I ended up creating a hasManyThrough() relationship between the posts and views through the product.
// getting all of the users available posts
$user = $request->user()->posts()->available()->withCount([
'views' => function ($q) {
// ===!!! A !!!=== Fixed ===!!!===
->whereRaw('`date-viewed` >= posts.date');
},
])->distinct()->get()->unique('isbn')->pluck('views_count')->sum();
I have a table (weathers) with several thousands rows, 90000 +- at the moment, each one belonging to one location.
This table can have multiple rows belonging to one location, but I still want just one, the last one for a given location.
My model Location have this relation defined as:
...
public function last_weather() {
return $this->hasOne(\App\Weather::class, 'id_location')->orderBy('weathers.id', 'DESC');
}
...
And on my controller I'm retrieving the last_weather like:
...
Location::with(['last_weather'])->findOrfail(1);
...
The strange thing is that this worked until I have 45000+- rows in the weather table, I have 3200 locations, and the last records for each location that are returned are on 40000+- rows (between id 40000 and 43000 +-, of the weathers table)
I have checked my DB and I have each location updated on the 80000's, but the relation are returning the data from the 40000's. This is not even the first or the last weather for each location.
You can do this in your Location model
public function weathers()
{
return $this->hasMany(\App\Weather::class, 'id_location');
}
public function lastWeather()
{
return $this->weathers()->latest()->first();
}
Then in your controller
$location = Location::findOrfail(1);
then you can access the last weather like this
$location->lastWeather();
UPDATE
Or you can adjust how you eager load weathers
$location = Location::with([
'weathers' => function($query) {
$query->orderBy('id', 'DESC')->first();
},
])
->findOrfail(1);
Order by will return all rows, to only return a single row for each matching condition you need to use Group by
I never used Laravel, but looking at your code I'm guessing your query should look like this:
return $this->hasOne(\App\Weather::class, 'id_location')->groupBy('weathers.id', 'DESC');
I'm having issues getting a proper count total with my Laravel model.
Model Structure
User
Item
ItemLike
A user can have multiple Items, and each of these Items can have multiple ItemLikes (when a user 'likes' the item).
I can easily get the individual ItemLike counts when using an Item model:
return $this->itemLikes()->count();
But I can't figure out how to get the total # of ItemLike's a User has across all the Item's he owns.
EXAMPLE
User A has 3 Items. Each Item has 5 ItemLike's, for a grand total of 15.
I tried using eager loading on the User model like this:
return $this->items()->with('itemlikes')->get()->count();
But that returns 3 (the # of Items)
These are the queries it ran, which appears like the second query is the one I want, yet every way I try it I still get 3 instead of 15
select * from `items` where `items`.`user_id` = '1000'
select * from `item_likes` where `item_likes`.`item_id` in ('1000', '1001', '1002')
After suggestions from others I found 2 solutions to get the result.
Using whereIn:
$itemViewCount = ItemView::
whereIn('item_views.item_id', $this->items()->lists('id'))
->count();
return $itemViewCount;
2 queries for a total of 410μs
Using join:
$itemViewCount = $this->items()
->join('item_views', 'item_views.item_id', '=', 'items.id')
->count();
return $itemViewCount;
2 queries for a total of 600μs
Isn't it just a case of creating a method that would return the number of items for the model. e.g.:
#UserModel
public function nbLikes()
{
$nbLikes = 0;
foreach($this->items() as $item) {
$nbLikes += $item->itemLikes()->count();
}
return $nbLikes;
}
And then User::nbLikes() should return the piece of data you are looking for?
try this:
$query="select count(il.id) from item_likes il,item itm where il.item_id=itm.id and tm.user_id=1000";