belongsToMany with multiple forieng key in Laravel - laravel

By default, laravel support two foreing_key in a pivot table. And both attach and detach methods work properly. But in case we have triple foreign key in a pivot table, what is the proper way to retrieve and insert data to the pivot table.
Tables
Orders:
id bill_number
Products
id name
Colors
id name
order_details:
id order_id product_id color_id price
My Models:
class Order extends Model
{
public function client() {
return $this->belongsTo('App\Client');
}
public function items() {
return $this->belongsToMany('App\Product', 'order_items');
}
}
class Product extends Model
{
//
public function orders() {
return $this->belongsToMany('App\Order', 'order_items');
}
}
class Color extends Model
{
//
public function orders() {
return $this->belongsToMany('App\Order', 'order_items');
}
}
Now I want to get or insert order_items with the following attribute:
order_id product_id color_id price
1 1 1 800
Products:
Shirt
Pant
Jacket
Color:
Gray
White
Black
Now I want to list all items of order with product name, color and price;
If I want to insert an order of shirt with black color and price of 800, how should I figure it out? And Also If I list all order items with color and prices which method should I use?

You could use an intermediate table model. Then you can define your relationship with color on that model.

A little bit "hacky" but you can use withPivot and wherePivot.
class Product extends Model
{
public function orders() {
return $this->belongsToMany('App\Order', 'order_items')->withPivot('color_id', 'price');
}
}
Then you may insert a new record like this:
$product->orders()->attach($orderId, ['color_id' => 1, 'price' => 800]);
And query it with:
$products = Product::whereHas('orders', function ($query) {
$query->where('color_id', '=', 1);
})->get();

I would use a double pivot table using a same order_id for order_product and order_color. Another solution is using a Polymorphic Relationship https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationships

I finally made it with sql join as bellow:
class Order extends Model
{
public function items() {
return $this->belongsToMany('App\Product', 'order_details')->withPivot('id', 'price')
->join('colors', 'order_details.color_id', '=', 'colors.id')
->select('products.*', 'colors.name as color_name');
}
}

Related

Laravel apply where clause to belongsToMany query using with() method

I have a products table with brand_id, and a category_products table with, product_id and category_id.
My Products model :
class Product extends Model
{
public function categories() {
return $this->belongsToMany(Category::class)->withTimestamps();
}
}
My Brands Model :
class Brand extends Model
{
public function products() {
return $this->hasMany(Product::class)->with(['categories']);
}
}
My question is, How can I fetch the products from a Brand instance that belongs to certain category?
$brand = App\Brand::find(1);
/*
I want to be able to do something in the likes of :
*/
$brand->products_which_are_under_this_category
Remove with(['categories']) method from inside products() and write a query like that.
$brand->products()->with(['categories' => function ($query) {
$query->where('category_id', CATEGORY_ID);
}])->get();
hope this can help.

Retrieve Models with invalid Category Subcategory relations

I have three Models Record, Category and Subcategory. The Record table stores category_id and subcategory_id as foreign keys. There is a pivot table "category_subcategory".
I would like to retrieve all Records that have an "invalid Category Subcategory relation" in an elegant and performant way via a custom function in the Model.
What do I mean by "invalid Category Subcategory relations":
a) Record has as Categroy and Subcategory. But the Subcategory doesn't belong to the Category (No entry in pivot table)
b) Record has a Category but no Subcategory (subcategory_id = NULL). Because the Category itself has Subcategories, the subcategory_id of the Record should be NULL
c) Record has a Category and a Subcategory, but the Cateory itself has no Subcategories, therefore the Record should have the subcategory_id = NULL
With this custom function in the Model I would like to be able to do sth like this in a Controller:
Records::withInvalidCategorySubcategoryRelation()->get(); //or similar
rather than going through endless foreach loops in the Controller like
$records = Record::all();
foreach($records as record){ ...
Any suggestions are much appreciated!
Here are my Model Classes:
class Record extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function subcategory()
{
return $this->belongsTo(Subcategory::class);
}
}
class Category extends Model
{
public function subcategories()
{
return $this->belongsToMany(Subcategory::class);
}
}
class Subcategory extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Laravel offers whereNotExists that can be helpful here.
Add the following function to Record model:
// Adjust the func name as per your test ;)
public function scopeInvalidRecords($query)
{
return $query->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
});
}
In Controller:
Record::invalidRecords()->get();
// It'll give you all the invalid records(invalid as per your definition)
Hope it is helpful!
Thanks to #Zeshan Khattak I was able to get what I needed. Now I get the "invalid" Records where a) b) c) is met
public function scopeInvalidRecords($query)
{
return $query->whereExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
})->orWhereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotNull('subcategory_id');
}

Using HasManyThrough in Laravel

I have the following structure:
orders
id
name
products
id
name
items
id
order_id
product_id
quantity
The relationships are as follows:
Order
public function items(){
return $this->hasMany(Item::class)
}
public function products(){
return $this->hasManyThrough(Product::class, Item::class);
}
Item
public function order(){
return $this->belongsTo(Order::class);
}
public function product(){
return $this->belongsTo(Product::class);
}
Product
public function items(){
return $this->hasMany(Item::class);
}
I wish to get all products for the order doing something like this:
$order->items()->products()->get() using the hasManyThrough method, but I must be doing it wrong since it tries to look for item_id in the products table.
I realized that I do not need many through, I just need to use items table as a pivot table, defining the relationship on the Order model just like this:
public function products(){
return $this->belongsToMany(Product::class, 'items');
}

Laravel | Using Eloquent hasManyThrough

I have a table called invoiceDetails that has item_id as foreign key from another table called items which has category_id as foreign key from table called categories.
I want to do that following using eloquent:
$result = InvoiceDetail::groupBy('item_id')
->selectRaw('sum(qty) as qty, item_id')->with('item', 'category')->get();
but I am getting error:
Call to undefined relationship [category] on model [App\InvoiceDetail].
Here's my relation inside Category model:
public function invoiceDetail() {
return $this->hasManyThrough('App\InvoiceDetail', 'App\Item', 'category_id', 'item_id');
}
Any suggestions?
Not sure you would even need a hasManyThrough relation here, unless you want to fetch all InvoiceDatail objects belonging to all items which in turn belong to the Category. That part is not clear from your question.
But in your example you are fetching items with their category from distinct item_id.
The reason this is not working is because you are trying to fetch the category relation from the InvoiceDetail object, which does not exist.
->with('item', 'category')
You want to load the Category based on the item relation, not based on the InvoiceDetail, try the dot notation (given that you did define the other relations)
->with('item.category')
Relations should be like this:
class InvoiceDetail extends Model
{
public function item()
{
return $this->belongsTo(\App\Item::class);
}
}
class Item extends Model
{
public function invoiceDetails()
{
return $this->hasMany(\App\InvoiceDetail::class);
}
public function category()
{
return $this->belongsTo(\App\Category::class);
}
}
class Category extends Model
{
public function items()
{
return $this->hasMany(\App\Item::class);
}
public function invoiceDetails()
{
return $this->hasManyThrough(\App\InvoiceDetail::class, \App\Item::class, 'category_id', 'item_id');
}
}
You would want to use the hasManyThrough if, for example, you have a Category and you want to load all the InvoiceDetails directly.
dd($category->invoiceDetails);

Laravel 4 eloquent

Is there any way I can do this with eloquent?
$orders = Customer::with('orders','orders.shop')->where('orders.shop.location','=','Japan')->get()
Customers, orders and shop are tables where 1 customer has many orders and each order has one shop only.
Location is a column in the shop table
I keep getting an error stating orders.shop.location is a column not found.
Anyone can help? Thanks in advance.
You need to defined relationship in your model classes.
Customer model:
public function orders()
{
return $this->hasMany('Order');
}
Order model:
public function customer()
{
return $this->belongsTo('Customer');
}
Then if you want orders of a special customer you just have to do :
$orders = Customer::find($id)->orders;
Or find the user attatched to an order:
$user = Order::find($id)->user;
You can also use the same kind of relation between your Shop and Order model and do something like this:
$orders = Order::with(array('shop' => function($query)
{
$query->where('location', '=', 'japan');
}))->get();
Which should give you all orders for a shop located in japan.
More informations about this type of request:
http://laravel.com/docs/eloquent#eager-loading
in CostumerModel you need set a relationship (One To Many):
public function order()
{
return $this->hasMany('OrderModel', 'foreign_key_in_orderTable');
}
in OrderModel too:
public function costumer()
{
return $this->belongsTo('CostumerModel', 'foreign_key_in_orderTable');
}
then in OrderModel one more relationship with Shop (One To One):
public function shop()
{
return $this->hasOne('ShopModel', 'foreign_key');
}
Now in ShopModel (One To One):
public function order()
{
return $this->belongsTo('OrderModel', 'local_key');
}
query:
$orders = Customer::with('costumer', 'shop')->where('location','=','Japan')->get();

Resources