Laravel pagination with specific foreign key - php

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

Related

Getting 2 result from one query with elequent laravel

I want to get 2 result from a filter query in Laravel version 6. I want to get product table information that user can change item exist in page by a select box with name of pageitemcount and can search by a field with name of select. when user select pageitemcount and also search for an name, submit a form and below query is run . When user search anything, available filter of category must be limited to available categories. I want to show distinct product category in additional to product list.
public function productIndex(Request $request)
{
$take = 10;
$query = Product::query();
if ($request->has('pageitem')){
$take = $request->input("pageitem");
}
// Search
if ($request->has('search')){
$query = $query->where('name', $request->input("search"));
}
$availableCategoriesQuery= $query;
// Get product unique product category to show them to user
// that exist in prodcut list that show to user
$availableCategories = $availableCategoriesQuery->select("category_id")->distinct()->get();
// Get product list by pagination
$productList = $query->paginate($take);
return view('admin.manage_product', ['productList' => $productList, 'availableCategories' => $availableCategories]);
}
but when $availableCategories is executed, affected $query variable, and i cant get right result from next line, i get result of $availableCategories in $productList variable. i do'nt want to repeat my code again, how can i deal with?
the result of $availableCategories is completely true, but result of productList is $availableCategories that is paginated.
OK, I think I see the problem. You are trying to reuse the $query variable. You probably want something like this:
I assume that product_categories is the table for your product categories. You can also use the model if you want.
Also, Since the DB has no intrinsic order, you should specify an OrderBy() as well.
$take = $request->input("pageitem", 10);
$productList = Product::paginate($take)->get();
$availableCategories = DB::table('product_categories')->distinct()->get("category_id");

how to compare two collection and get match records

I am using Laravel and I have two different collections that contain ID of products
First one is colorProduct and second is tagProduct
so I want to compare these two and get only same ID of products so how can I do this?
$colorProducts = Color::where('code', $request->color)->get()->first()->products;
$tagProducts = $tag->products->where('shop_id', $shop->id);
$colorAndTagProducts = collect();
foreach ($colorProducts->toBase()->merge($tagProducts)->unique('id')->groupBy('id') as $allProducts) {
if ($allProducts->count() >= 1) {
$colorAndTagProducts[] = $allProducts->first();
}
}
here
$colorAndTagProducts
gives me all records form both collection but I only want same record
I dont know, if I understand correctly, but maybe like this?
I suppose Color and Product are in many to many relationship. And Product and Shop/tag in one to many.
$colorId = Color::where('code', $request->color)->get()->first()->id;
$shopId = $shop->id;
$products = Product::whereHas('colors', function ($query) use ($colorId) {
$query->where('id', $colorId); //id or color_id
})->where('shop_id', $shopId)->get();
intersect()
The intersect method removes any values from the original collection
that are not present in the given array or collection. The resulting
collection will preserve the original collection's keys:
I did it with this method

Sort elements by a value calculated on the go (not stored)

In my project I'm using Laravel 5.5 with Eloquent and Scout drivers to build a sort of search engine API endpoint.
In my scenario I have a SQL table items that has a price_factor property.
The table is also stored inside an Elasticsearch index.
With this value and with the number of the user related with that item, I can calculate the right price of the object.
An easy example is the item with id: 1 has price_factor: 2 and it is related to 5 users.
The right price of the item is 2 * 5 = 10, of course.
Now, I have to query all results and use where conditions, sorting them by that calcolated property, and return paginated results.
For example: get all items with price between 5 and 10, sort them by price and paginate by 10 elements per page.
In Eloquent I will write:
// Set filters to be applied
$filters = [
['price', '>', 5],
['price', '<', 10],
];
// Sort by "update_at", or "price"
$sort = "price";
// Order by "ASC" mode
$order = "ASC";
// Number of rows per page
$rows = 10;
// Get items
$result = Item::orderBy(
$sort,
$order
// Get related user with item record, where has...
)->with(['users'])->whereHas(
// Take users related models
'users',
// Take the model related ("user") and use filters
function($relation_model) use ($filters) {
// Apply where condition with filters
$relation_model->where($filters);
}
// Paginate for the numbers of row requested
)->paginate($rows);
How to do that if price is not a property of table items?
Should I store price inside the table and update it on every new user relation added? (or every removed relation too).
Is this the correct approach?
I was thinking about website like eBay or other real-time auction that have a similar situation of mine: how do you think they have solved?
Thanks in advance.
Assuming that you have a user_items table that keeps track of the items owned by user, I think something like this might work:
$result = Item::selectRaw('items.*, price_factor * (SELECT COUNT(id) FROM user_items WHERE user_items.item_id = items.id) AS price')
->with(['users'])
->havingRaw('price > ? AND price < ?', [5, 10])
->orderBy('price', 'asc')
->paginate($rows);
You can calculate the price on the fly and alias it. Finally you can apply a havingRaw clause to it that will check if the price is between the range it needs to be. Here's a working example of this in action.
There might be better ways to do this. I am also curious to know :)

Eloquent HasMany relationship, with limited record count

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

Counting total distant relationships in Laravel (eager loading?)

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";

Categories