Laravel Multiple Relationship for a sum column - php

I have Three Models name Invoices, Invoiceitems and Products.
Each Invoices HasMany Relationship with Invoiceitems Model.
Each Invoiceitems HasOne Relationship with Products Model.
I need to sum of Invoiceitem product amount where the Product has category 4.
Table Structure
Invoice
id | date | total_amt
Invoiceitem
id | invoiceid | product_id | product_amt | quantity | total_amt
Product
id | product_name | category_id
Relationship
Invoice Model
public function invoiceitems()
{
return $this->hasMany('App\Invoiceitems', 'invoiceid', 'id');
}
Invoiceitem Model
public function products()
{
return $this->hasOne('App\Products', 'id', 'product_id');
}
Expected Report
Invoice No | Date | Veg Category Product Amt | NonVeg Category Product Amt | Total Amt
KL0001 | 15-05-2021 | 0.00 | 190.366 | 190.366
KL0002 | 16-05-2021 | 20.00 | 350.000 | 370.000
Currently we use following Helper Function to get Particular category Products Total Amount
function getInvdiscsumamt($inv_id, $prdtype)
{
$totaldisamt = Invoiceitems::Where('invoice_id', $inv_id)->whereHas('products', function ($query) use ($prdtype) {
$query->where('category_id', $prdtype);
})->sum('total_amt');
return $totalpdtamt;
}
How to display particular category products total amount using Elequoent method

You can try one of the aggregate functions that already exist in Eloquent, see https://laravel.com/docs/8.x/eloquent-relationships#other-aggregate-functions, for instance withSum:
$invoiceItem = InvoiceItem::query()
->where('invoice_id', $invId)
->withSum(['products', function($query) use ($productType) {
$query->where('category_id', $productType);
}, 'frequency'])
->first();
Your property will then be available using the property {relation}_{function}_{column}, so in this case products_sum_frequency.
Note that the withSum does not share anything with the with (or whereHas) function, that means that if you use a subselection in your query like ->with(['products', function($query){...}]) that will be a separate selection from the withSum query.
I also recommend to use proper camelcasing in your functions and also your models. I'd also use singular names for all your model class names. So Invoiceitems would become InvoiceItem (models/InvoiceItem.php). This is the default way to define it in laravel as far as I'm aware.

Related

Get results by finding an ID which is two joins away using Eloquent relationships

I need to get rows from one table using an id which is two joins away.
I know I can use join('table_name') but I am trying to use the model names rather than raw table names.
I'm trying to select shipping_shipment.* by joining order_item_join_shipping_shipment then joining order_item, and filtering where order_item.order_id = x
I tried this in the ShippingShipment class, but I can't figure it out.
return $this->hasManyThrough(OrderItem::class, ShippingShipment::class, 'shipment_id', 'order_item_id', 'id', 'id');
There are many items in an order, and many shipments. I need to get the shipments.
There can be more than one shipment per order - items come from various places.
There can be more than one shipment per item - if something is returned and needs shipping again.
The table I want to get rows from, shipping_shipment, is joined to order_item by a join table order_item_join_shipping_shipment. That join table has the order_item_id. I need then to join order_item table so that I can search for order_item.order_id
Table order_item model OrderItem
+-----+---------------+
| id | order_id |
+-----+---------------+
| 6 | 13464 |
| 8 | 13464 |
| 9 | 13464 |
+-----+---------------+
Table order_item_join_shipping_shipment model OrderItemJoinShippingShipment
+-----+---------------+-------------+
| id | order_item_id | shipment_id |
+-----+---------------+-------------+
| 1 | 6 | 12 |
| 1 | 9 | 12 | two items in one shipment
| | | |
| 2 | 8 | 13 |
| 3 | 8 | 14 | one item was returned so shipped again
+-----+---------------+-------------+
Table shipping_shipment don't need describing except to say it has an id column.
If I was to do it with MySQL it would look like this
SELECT ss.*, oiss.order_item_id FROM
order_item_join_shipping_shipment AS oiss
INNER JOIN shipping_shipment AS ss ON (oiss.shipment_id = ss.id)
INNER JOIN order_item AS oi ON (oiss.order_item_id = oi.id)
WHERE oi.order_id = 13464
I noticed you are not using the default table names, so your Models must have the table names explicit, e.g.:
class OrderItem extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'order_item';
}
In the same Model file of the above example, you need to indicate how the relationship works, i.e.:
public function shippingShipments()
{
return $this->belongsToMany(ShippingShipment::class, 'order_item_join_shipping_shipment', 'order_item_id', 'shipment_id');
}
Here you can check in Laravel documentation the whole explanation.
You need to apply the same concept in ShippingShipment Model, so your Model will be something like this:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'order_item';
/**
* The roles that belong to the user.
*/
public function orderItens()
{
return $this->belongsToMany(OrderItem::class, 'order_item_join_shipping_shipment', 'shipment_id', 'order_item_id');
}
}
This way you can get shipments by order item and vice-versa:
//Shipments of Order item 13464
$shipments = OrderItem::find(13464)->shippingShipments()->get();
//Order items of Shipment 1
$orders = ShippingShipment::find(1)->orderItems()->get();
Source: Laravel Documentation
As far as I can tell you are using a pivot table between ShippingShipment and OrderItem. If I understand you correctly you want to get OrderItems that are connected to ShippingShipment, if that is the case this is what you can do:
Make belongs to many relationships in both models, such as:
ShippingShipment:
public function orderItems(){
return $this->belongsToMany(OrderItem::class, 'table_name', 'column_id');
}
OrderItem:
public function shippingShipment(){
return $this->belongsToMany(ShippingShipment::class, 'table_name', 'column_id');
}
And then you can get the desired result by typing this query:
ShippingShipment::find(1)->with('orderItems')->get();
OrderItem::find(13464)->with('shippingShipments')->get();
Note: you can use orderItems:id,order or shippingShipment:id,some_other_field for more optimized query

4 level laravel eloquent relationship

I have four tables and i want to make a 4 level relationship. My table looks like this:
------------|------------|------------|------------|
City | Category |Subcategory | Company |
------------|------------|------------|------------|
id | id | id | id |
name | name | name | name |
------------|------------|------------|------------|
I did something like this:
City.php
public function categories()
{
return $this->belongsToMany(Category::class);
}
Category.php
public function subcategories()
{
return $this->belongsToMany(Subcategory::class);
}
Subcategory.php
public function category()
{
return $this->belongsTo(Category::class);
}
But it's not giving the result I need. I have many cities that can have many companies by their category and subcategories. For example I can save city_id, category_id, subcategory_id to company table and get the right companies doing the query:
$company->where('city_id', 1)->where('category_id', 2)->where('subcategory_id', 3)->get();
But I think it's not a best practice. I hope somebody helps. Thanks.
The main question is how can i make a normal relationship from this query:
I have many Cities that can has many categories that has many Subcategories and the subcategories i have companies for the specific city->category->subcategory.
For example in Company model i have city_id, category_id, subcategory_id and when i'm fetching categories for the specific city there will subcategories and in subcategories will be companies.
So the query will be something like:
App\Company::where('city_id', 1)->where('category_id', 2)->where('subcategory_id, 3)->get();
Sorry for bad explanation. I hope you get it...

Right relationships in Laravel

hello, sorry for my english. I will try explain my problem.
For example, we have model Product.
Each product has some options:
product_1 : option_1 = 50, option_2 = 14, option_3 = 23
Whats is the right way?
First - create database, like
id | title | option_1 | option_2 | option_3
Second - create models and tables, like
ProductModel hasMany optionModel
OptionModel belongsToMany ProductModel
tables in databases: Product, Option, Product_Option_Relationships
Third - create some collection in function and table Product_Option_Relationships like
$options = collect([
['id' => '1', 'name' =>'option1'],
['id' => '2', 'name' =>'option2'],
]);
Table: id | product_id | option_id
Or maybe exist normal way, because first - its too big table, when you have 20 options, second - create new model only for information function, i dont now, its normal? Third - too difficult in view show options name.
Thank you, i hope you understand me.
Generally use the one-to-many, many-to-many relationships
And the benefit for that you can freely edit any record without modifying the whole column to apply that on your tables :
First we have products table which is going to require options foreach
so we should have the table options which is going to combine the options in general then we add a new table assignOptionsToProducts which is include keys for both options & products in this case you're going to have many-to-many or one-to-many relationship as you like
Products Table
id | Name
1 | Product A
2 | Product B
Options Table
id | Name
1 | Option A
2 | Option B
AssignOptionsToProducts Table
id | Product_id | Option_id
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
As you can see we assigned the same option many times
And when we want to modify any option you can without modifying each record in other tables and of course you can use each table many times easily
Use the second way. You won't have repeated options. For example:
products
id | name
---|------
1 | Car
2 | Bike
options
id | name
-----|------------
1 | Transport
option_product
option_id | product_id
------------|-------------
1 | 1
1 | 2
Using the other ways, you would have the option Transport twice.
You can use many-to-many relationship and can structure it like so:
Product Model
class Product extends Model {
...
public function options() {
return $this->belongsToMany('App\Product', 'product_options', 'product_id', 'option_id');
}
}
Options Model
class Option extends Model {
...
public function product() {
return $this->belongsToMany('App\Option', 'product_options', 'option_id', 'product_id');
}
}
You will need three tables for this to work:
Table products
id | name
-----------------
1 | iBeacon
2 | Intel Edison
Table options
id | name
----------
1 | Price
2 | Size
Table product_options
id | option_id | product_id
---------------------------
1 | 1 | 2
2 | 2 | 2
You can choose if you want to store a record with the option value in the options table or in the pivot table. I'd place them in the pivot table to keep the options table smaller.
Now you'll be able to assign options to your products like so:
...
// Assing options to a product
$product->options()->sync([$optionID, $optionID]);
// Get product's options
$product->options;
// Get products having certain option
$option->products;
...

Eloquent relation pivot table with table

I read the laravel documentation but I couldn't understand very well.
I have this structure on my database.
PriceTable - which contains info about the period of promotional prices and the default price.
Product - which contains info about products.
and PriceTable_Product - which contains the foreign keys of Product and PriceTable and the respective price.
Example:
PriceTable | PriceTable_Product | Product
id | description | PriceTable_id | Product_id | price | product_id| name
1 | default | 1 | 1 | 5.00 | 1 | test
2 | promotional | 2 | 1 | 3.50 |
And at the Order table I can have multiples products, so I want to know if it is possible to relation Order table, with the pivot table PriceTable_Product, because I need the information of which table belongs the price when the product was sold.
First of all you may define the relations between Product and PriceTable.
Product model (App\Product.php)
<?php
namespace App;
class Product extends Model {
protected $table = 'products';
//if the default primary key isn't 'id' you may use $primaryKey
protected $primaryKey = 'product_id';
public function pricetables() {
return $this->belongsToMany('App\PriceTable');
}
}
PriceTable model (App\PriceTable.php)
<?php
namespace App;
class PriceTable extends Model {
protected $table = 'pricetable';
public function products() {
return $this->belongsToMany('App\Product');
}
}
If you created the relations then you can use:
$product = App\Product::find(1);
foreach ($product->pricetables as $pricetable) {
echo $pricetable->pivot->description;
}

Laravel: use Eloquent belongsto , get data from both tables?

In Controller, I would like to pass the only one variable with specifies column from parent in it. Now,I'm using
View::make('store.product')->with('products', Product::find($id)
->join('design','product.design_id','=','design.id')
->join('user','design.user_id','=','user.id')
->select('user.name','design.title','product.price')
->get();
My question is
1.Is there a better way to do this by using Belongsto?
2.If can do, Is it work the same with Hasmany?
This is my table structure.
User
id | name
1 | 'John'
Design
id | user_id | title
1 | 1 | 'Chill'
2 | 1 | 'Mad'
Product
id | design_id | price
1 | 1 | 20
2 | 1 | 30
And Model be like this
Product belongsto Design ,
Design belongsto User
Add a method to your Users like so for your designs that the user has;
public function designs(){
$this->hasMany('Design');
}
For the designs model add the following methods;
public function user(){
$this->belongsTo('User');
}
public function products(){
$this->hasMany('Product');
}
For your products model
public function design(){
$this->belongsTo('Design');
}
These will set up the relationship allowing you to eager load the data on your models.
This can be done like so;
$variable = Product::with('designs')->find(1); //This will load the product with the related designs
If you want all the designs and the users that belong to the designs do the following;
$variable = Product::with('designs', 'design.user')->find(1); //This will load the designs that relate to the Product and include the user that that design belongs to on the Design model.
To access the properties use the following;
$variable->designs //This will return a collection of all the designs.
$variable->designs->first()->user //This will return the user model that the design belongs to.
An example of displaying the information;
#foreach ($variable->designs as $design)
{{ $design->user->username }}
#endforeach
Please note: i have not tested this code.

Categories