Get the quantity and name of an object between related tables - php

I'm taking data from my Animals table, which has a relationship with the Categories table. My problem is that I need to count how many products per category are registered.
Doing a search here on Stackoverflow, I started using the code below that is returning the quantity of animals per category ([Animal, quantity]).
$data = DB::table('animals')
->select(
DB::raw('category_id as category'),
DB::raw('count(*) as number'))
->groupBy('category')
->get();
$array[] = ['Name', 'Quantity'];
foreach($data as $key => $value)
{
$array[++$key] = [$value->category, $value->number];
}
$cat = json_encode($array);
dd($cat);
Using "dd", I see that the data below is correct, but the category_id is coming, I am not sure how to get this id and put the category name for that id.
"[["Category","Quantity"],[1,10],[2,14],[3,30],[4,26],[5,1]]"
Example: [2,14] this refers to category_id 2 which has the name: mammal. So I would have 14 animals registered in the mammal category.
I would like the result to be like this:
"[["Category","Quantity"],[birds,10],[mammals,14],[reptiles,30],[amphibians ,26],[fish,1]]"
How can I handle this id related to the category name?

Join your category table and get name from there, I suppose it should look like:
$data = DB::table('animals')
->join('category', 'animals.category_id', '=', 'category.id')
->select(
DB::raw('category.id as category_id'),
DB::raw('category.name as category_name'),
DB::raw('count(*) as number'))
->groupBy('category')
->get();
More about joins

You can use the method "withCount" from the model, it helps you to count the number of results from a relationship
$posts = App\Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
See the documentation https://laravel.com/docs/7.x/eloquent-relationships#counting-related-models

Related

Not able to fetch data from primary table in laravel

I have 3 tables i.e category, sub category and products. I have passed category id to sub category and then sub category id to product so that i can get the whole data as per requirements. The problem in i an able to get the data from the secondary tables that has joins statement not from the primary table.Here is my code of query. Any help from you would be appreciated
public function showProduct()
{
$data = DB::table('category')
->join('sub_category','category.id',"=",'sub_category.category_id')
->join('product','sub_category.id', '=', 'product.sub_category_id')
->get();
return $this->success('date' ,$data);
}
use leftjoin
$data = DB::table('category')
->leftjoin('sub_category','category.id',"=",'sub_category.category_id')
->leftjoin('product','sub_category.id', '=', 'product.sub_category_id')
->get();

Laravel multiple select filter for categories, only get one select as output

In laravel 8, I am filtering blog articles by category. However when I select multiple categories in my select menu. I do get the proper request . for example: articles/category/?category_ids=3,4
But it will only output one selected filter. If I select 2 filters it just selects that next filter as if I only selected that one. (I also use Axios but the request is done proper, so its in my Controller)
Here is my code I tried:
$data['articles'] = Article::whereHas('categories', function ($query) use($category_ids){
$query->whereHas('category_id', '=', $category_ids)->where('premium',0);
;})->get();
I also tried:
$data['articles'] = Article::whereHas('categories', function ($query) use($category_ids){
$query->whereIn('category_id', [$category_ids])->where('premium',0);
;})->get();
So how do I get to query both or more category id's ?
I am using a pivot table:
Articles can have many Categories
Categories can have many Articles
I use article_category as a pivot table
When checking for relationship existence in many-to-many relations, the check is still to be done against the id in the categories table.
Try this
$category_ids = collect(explode(',', $request->category_ids))
->map(fn($i) => trim($i))
->all();
$data['articles'] => Article::whereHas('category', fn($query) =>
$query->whereIn('categories.id', $category_ids)
->where('categories.premium', 0)
)->get();
You can explode the categories and then make the query like this.
$categories = explode(',',$request->categories);
$data['articles'] = Article::whereHas('categories', function ($query) use($categories){
$query->whereIn('category_id', $categories)->where('premium',0);
})->get();

Using Laravel How can get a specific sizes products for a category?

I have category migration like this:
id
title
parent_id
Then I store products in another migration table like this
id
category_id
title
price
Then I store products in another migration table stocks like this:
id
product_id
size (like M/L/1 Year etc)
qty
I can get all category products using following query:
$category = Category::first();
$products = $category->products;
My question is how Can get a specific sizes products? like I want to get all M Sizes Products of a single category?
Updated Answer:
$category = Category::first();
$products = $category->products()
->whereHas('stocks', function($q){
$q->where('size', '=', 'M');
})
->get();
First you get all the categories.
$category = Category::first(); // You can get all the categories or in where condition whatever your need
$size = "M" //Comming from the request
For products where has categories and size as mentioned
I hope you have relation defined for stock in product model.
$product = $category->whereHas('products', function($query) use ($size){
return $query->whereHas('stock', function($stock) use ($size){
return $stock->where('size', $size);
});
})->with(['products'=>function($q) use ($size){
return $q->whereHas('stock', function($stock) use ($size) {
return $stock->where('size', $size);
});
}])->get();
This will gives you category which are only having products which has size provided in the request.
You can do,
$category = Category::first();
$products = $category->products()
->whereHas('stocks', function($q){
$q->where('size', '=', 'M');
})
->get();

Laravel Group By relationship column

I have Invoice_Detail model which handles all products and it's quantities, this model table invoice_details has item_id and qty columns.
The Invoice_Detail has a relation to Items model which holds all item's data there in its items table, which has item_id, name, category_id.
The Item model also has a relation to Category model which has all categories data in its categories table.
Question: I want to select top five categories from Invoice_Detail, how?
Here's what I did:
$topCategories = InvoiceDetail::selectRaw('SUM(qty) as qty')
->with(['item.category' => function($query){
$query->groupBy('id');
}])
->orderBy('qty', 'DESC')
->take(5)->get();
But didn't work !!
[{"qty":"11043","item":null}]
Category::select('categories.*',\DB::raw('sum("invoice_details"."qty") as "qty"'))
->leftJoin('items', 'items.category_id', '=', 'categories.id')
->leftJoin('invoice_details', 'invoice_details.item_id', '=', 'items.id')
->groupBy('categories.id')
->orderBy('qty','DESC')
->limit(5)
->get();
This will return you collection of top categories.
Tested on laravel 5.5 and PostgreSQL.
UPD:
To solve this without joins you can add to Categories model this:
public function invoiceDetails()
{
return $this->hasManyThrough(Invoice_Detail::class, Item::class);
}
And to select top 5 categories:
$top = Category::select()->with('invoiceDetails')
->get()->sortByDesc(function($item){
$item->invoiceDetails->sum('qty');
})->top(5);
But first solution with joins will work faster.

Eloquent wherein with join multiple tables

I have 4 tables, items, listings, catitem_item, and item_listing.
items and listing is many to many relationship.
items and catitems is also many to many relationship.
catitems contains list of item's categories.
listings is like location where items located.
example listing shop A can have item chair and item chair have multiple catitem categories.
My goal is to get items which under list of categories such as category 1 AND 2 AND 3 ($cats) and with listing information where this item located.
So if there are 6 listings for chair, it will return 6 chair results.
This is the query I have so far.
$items = DB::table('items')
->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
->join('item_listing', 'item_listing.item_id', '=', 'items.id')
->join('listings', 'item_listing.listing_id', '=', 'listings.id')
->whereIn('catitem_item.catitem_id', $cats)
//->groupBy('items.id')
//->having(DB::raw('count(*)'), '=', count($cats))
->select('items.id', 'items.name', 'items.price', 'items.slug', 'item_listing.listing_id', 'listings.name as listing_name', 'listings.slug as listing_slug')
->get();
Note that the way you are trying to do it, you might get multiple rows per item (once per related listing). A better way would be to have an array of listings per item.
If you use eloquent models and you have setup the relations correctly, you could try the following:
$cats = [1, 2, 3];
$query = Item::with('listings');
foreach ($cats as $cat) {
$query->whereHas('catitems', function($q) use($cat) {
$q->where('id', $cat);
});
}
$items = $query->get();
Now every item shoud have a listings property. For example for the first item you can access the listings the following way:
$item1 = $items[0];
$listings1 = $item1->listings;
Note that whereHas() will probably create a correlated EXISTS subquery for every entry in the $cats array. If that is to slow, you can use a JOIN query like:
$items = Item::with('listings')
->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
->whereIn('catitem_item.catitem_id', $cats)
->groupBy('items.id')
->having(DB::raw('count(*)'), '=', count($cats))
->select('items.*')
->get();
If you don't use eloquent, you can also do the "eager loading" yourself.
$items = DB::table('items')
->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
->whereIn('catitem_item.catitem_id', $cats)
->groupBy('items.id')
->having(DB::raw('count(*)'), '=', count($cats))
->select('items.*')
->get()
->keyBy('id');
foreach ($items as $item) {
$item->listings = [];
}
$itemIds = $items->pluck('id');
$listings = DB::table('listings')
->join('item_listing', 'item_listing.listing_id', '=', 'listings.id')
->whereIn('item_listing.item_id', $itemIds)
->groupBy('listings.id')
->select('listings.*', DB::raw('group_concat(item_listing.item_id) as item_ids'))
->get();
foreach ($listings as $listing) {
$itemIds = explode(',', $listing->item_ids);
foreach ($itemIds as $itemId) {
$items[$itemId]->listings[] = $listing;
}
$listing->forget('item_ids');
}

Categories