I have the following table structure
tables
tbl_items
id
sub_category_id //foreign key
table subcategories
tbl_subcategories
id
category_id
name
table categories
tbl_categories
id
name
As you can see from the above the sub_category_id is a foreign key in products table which relates to the id in the subcategories table. the subcategories table have a category_id which is a foreign key from the categories table
Now i wanted to fetch all items belonging to a certain category.
so i have tried
$categories = Categories::where('id',7)->first();
//from categories get subcategoryid
$allsubcategories = Subcategories::where('category_id',$categories->id)->pluck('id')
$allitems = Items::where('status',1)->wherein('sub_category_id',$allsubcategories)
The above works. but is there a neater way to do this?
define a hasManyThrough relationship in the Category model to the Item model.
// on the Category Model
public function items() {
return $this->hasManyThrough(Items::class, Subcategories::class);
}
then you can access all the items belongs to a particular category
// On your Controller
$category = Categories::where('id',7)->first();
$items = $category->items
official documentation - https://laravel.com/docs/5.7/eloquent-relationships#has-many-through
Well you could probably try using Nested Eager Loading, which should be a little more efficent:
//Assuming the relationships are named subCategories() on Categories model & items() on Subcategories model
$cat = Categories::where('id', 7)
->with('subCategories.items')
->get()
->first();
That should load the category w/ all the subCategories and all the items within each subCategories with the least amount of queries.
Related
here is my category table data
id
1
2
6
7
when in my post bale I join with this category table
here is my post table sample data
ID = 1,
name = 'hellow'
category id = 4 (i join with category table but selected category is
deleted)
here is my index SQL query (when categy_id match with the category.id) then only its fetch
$post = DB::table('posts)->join('category','posts.category_id','categories.id')-.paginate(10);
for some reason, the selected category can be deleted so I try to get category deleted post data
here is my query
$cpost = DB::table('posts')->join('categories','posts.category_id', '!=' ,'categories.id')->select('post.*')->paginate(5);
but above query duplicate post data based on available category data
i want all post data which are category id is not matched with in category table id how can i get that ?
Try this. Key is the leftJoin instead of default innerJoin (join).
// posts without assigned or existing category
$posts = \DB::table('posts')
->leftJoin('category','posts.category_id','categories.id')
->whereNull('categories.id')
->paginate(10);
why are you doing a join for this? you already have category id stored in your post table.
$cpost = DB::table('posts')->where('category_id','!=', $category_id)->paginate(5);
Just try it:
$cpost = DB::table('posts')
->join('categories','posts.category_id', '=' ,'categories.id')
->select('post.*', 'categories.*')
->whereNotIn('posts.category_id', DB::raw('select id from categories'))
->paginate(5);
I'm stuck in a problem, I have these 3 models: Order, OrderItem and Product, each Order has many Order Items, and each Order Item has one Prduct but a Product can be ordered several times.
Product has a supplier ID column (each supplier has many Products).
So here are the relationships:
Model: Order:
public function items(){
return $this->hasMany('App\Models\OrderItem','id','order_id');
}
Model OrderItem
public function order(){
return $this->belongsTo('App\Models\Order','order_id','id');
}
public function product(){
return $this->belongsTo('App\Models\Product','item_id','id');
}
Model Product
public function orders()
{
return $this->hasMany('App\Models\OrderItem','item_id','id');
}
What I want is that given a supplier_id, I need to filter Products for that supplier_id, and get the Order Items for that supplier_id and then group them by created date which is only available in the Order Model and not Order Items.
In other words, I want to get all Orders of a given supplier (through the supplier_id column in products) grouped by their creation date but there is no direct relation from Orders to Products I have to get Order Items to get to Products (Orders > Order Items > Products).
I thought about doing the following:
Product::with("orders")->where("supplier_id","=",$supplier_id)->join("orders","orders.id","=","products.orders")
The problem is that products.orders is a relationship (between Product and Order Item) and not a column.
You can do it using DB interface or if you'd like continue using Models you need to use multiple pluck.
You can do something like this:
Product::with(['orders','orders.order'])->where('supplier_id', $supplier_id)->get()->pluck("orders")->flatten()->pluck('order');
Use following snippet:
Product::select(DB::Raw('DATE(created_at) as cr'), ...)->where('supplier_id', $supplier_id)->with('orders.order')->groupBy('supplier_id')->get();
After that, you can loop on the result and group them by date:
$result->groupBy('cr')
So i have 2 tables. Orders and OrderItems. Relationship is each Order can have many OrderItems (orderitem is just a product).
OrderItem Table
id INT PRIMARY KEY
name TEXT
quantity INT
pack_value INT
order_id INT FOREIGN KEY REFERENCES ORDER_ID ON ORDERS TABLE
product_id INT FOREIGN KEY REFERENCES PRODUCT_ID ON PRODUCTS TABLE
Orders Table
order_id INT PRIMARY KEY
status TEXT
user_id INT FOREIGN KEY REFERENCES USER_ID ON USERS TABLE
An order can have the same OrderItem twice or only once. For example like below, 3 items but 2 products:
OrderItem(id=1, product_id=100, name="Pizza", quantity=2, pack_value=10, order_id=7)
OrderItem(id=2, product_id=100, name="Pizza", quantity=10, pack_value=1, order_id=7)
OrderItem(id=3, product_id=555, name="Olives", quantity=5, pack_value=1, order_id=7)
So above there is 2 entries for Pizza and this is the result i get. However i want it to only show 1 entry for Pizza because its the same item. Below is what i want to see:
OrderItem(product_id=100, name="Pizza", quantity=30, pack_value=1, order_id=7)
OrderItem(product_id=555, name="Olives", quantity=5, pack_value=1, order_id=7)
So essentially, if the item only exists once, then do nothing to it. If it exists twice, then make the pack_value=1 and the quantity is the sum of the individual quantity*pack_value. So in example above quantity becomes (2*10 + 10*1 = 30).
The controller method is like below, and so here is where I want to do this:
public function showOrderDetails(Order $order){
return view('orders.show', compact('order'));
}
Also the Order and OrderItem models has the method for the relationships. For example, in the Order Model i have:
public function orderItems(){
return $this->hasMany(OrderItem::class, 'order_id');
}
Thanks, and if you need any extra info i can provide.
SELECT name,
SUM(quantity * pack_value) AS quantity,
1 AS pack_value,
order_id
FROM order_items
GROUP BY name,
order_id;
fiddle
Merge another tables to this code using it as subquery, or add them into FROM clause (adjusting its output list and grouping expression) if needed.
If I have two tables with their respective columns:
=====================
product_categories
=====================
id | name
=====================
=======================
products
=======================
id | category_id | name
=======================
And I have the following PHP functions:
// Returns an array of all rows from the products
function getProducts() { ... }
// returns the corresponding row to the given id of the product category
function getProductCategory($category_id) { ... }
Currently, I am displaying a list of all products in a table with the following headings:
id
Category
Name
Where category is the name of the category corresponding to the category_id of the product.
Currently I am using a foreach loop to iterate through the product and calling the getProductCategory(...) function for each product:
<?php
...
$products = getProducts();
foreach ($products as $product) {
$product_category = getProductCategory($product['category_id']);
...
}
...
?>
Which would be an extensive amount of queries to the database if there are lots of products with many repeated queries.
Would it be good practice if the getProducts() function include all it's category information using a JOIN in the SQL statement?
If you want to get id, category_name and product_name each time, you should make ONE method and avoid to make one select, then a foreach on each product and an other select to get all value.
So try something like this maybe :
function getProductsData() {
$querie = 'SELECT
p.id, p.name,
pc.name as Category
FROM products as p
LEFT JOIN product_categories as pc on pc.id = p.category_id
ORDER BY p.name; -- optionnal
';
// Get the data and return them
// You should get an array with one row =
// id (of the product) / name (of the product) / Category (the name of the category)
}
Yes, Join will be better. It will reduce the number of queries for fetching the category. Otherwise, you can do the separate query for getting the categories as below code, but I will suggest Join.
$products = getProducts();
$categoryIds = [];
foreach ($products as $product) {
$categoryIds[] = $product['category_id'];
...
}
$product_category = getProductCategory($categoryIds);
You have to modify the query in getProductCategory() and add where in condition for comparing category ID
In getProductCategory(), you can implode the category IDs, for adding it into the where in clause.
so here is my database for a book store
books : id ,title
category : id, title
book_category : id , book_id, category_id
book_stock : id , book_id , quantity , price
considering all the relations are defined in the model , i can query book_stock it goes something like this
Stock::with('Book')->get();
but what if i want to get stock of a book in the category = 1
i can use use condition on book
Stock::with('Book' , function($q){
$q->where(['title'=>'abc']);
})->get();
but how can i filter related table of book ?
basically i want to get book_id from book_category where category_id = 1 and then use those ids to filter my books finally get stock
ps : i don't want to use query builder
This will return you all books belonging to category=1 with their stock information:
$categoryId = 1;
$books = Book::with('stock')->whereHas('category', function($query) use ($categoryId) {
return $query->where('id', $categoryId);
})->get();
You can do:
Stock::with('Book.stock', 'Book.category')->get();
You can access any number of nested relations within a with statement.
Related question:
Laravel nested relationships
Armin Sam's answer should also be a viable option.