I'm having some trouble calculating the price of my carts with eloquent,
here are my tables:
cart_products:
- cart_id
- product_id
- quantity
products:
- price
One cart can have multiple cart_products, and each cart_products have one product associated
I'm making the request from the Cart Model, I'm trying to get the total price of the cart (cart_products.quantity * products.price).
Here is my query:
Cart::select('cart.*', \DB::raw('IFNULL(SUM(products.price*cart_products.quantity), 0) AS cart_price'))
->leftJoin('cart_products', 'cart.id', '=', 'cart_products.cart_id')
->join('products', 'cart_products.product_id', '=', 'products.id');
When I'm doing that, I do get the expected result but all the carts that doesn't contains product are excluded, I would like them to be included.
How could I include them ? Or is there a better way to do it (I saw withCount method but I couldn't make it work properly) ?
Another way would be to setup a virtual relation in your cart model and calculate your cart price like
class Cart extends Model
{
public function price()
{
return $this->hasOne(CartProducts::class, 'cart_id')
->join('products as p', 'product_id', '=', 'p.id')
->groupBy('cart_id')
->selectRaw('cart_id,IFNULL(SUM(products.price*cart_products.quantity), 0) as cart_price');
}
}
To get price data for your carts your can query as
Cart::with('price')->get()->sortByDesc('price.cart_price');
I finally managed to do it another way using raw SQL:
Cart::select('cart.*', \DB::raw('(SELECT IFNULL(SUM(products.price*cart_products.quantity), 0) from cart_products join products on products.id = cart_products.product_id where cart_products.cart_id = cart.id) AS cart_price'));
Thanks to you all for your help !
Related
I'm trying to generate a monthly report in Laravel Livewire
I have a many to many relationship between books and orders with a pivot value for quantity of books in the order.
This is the database designer for the 3 tables :
These are the eloquent relationships in my App\Models\ ... .php
// In Order model
public function books(){
return $this->belongsToMany(Book::class)->withPivot('quantity');
}
// In Book model
public function orders(){
return $this->belongsToMany(Order::class)->withPivot('quantity')->as('orders');
}
Code generating the monthly report data:
public function generate($month)
{
// Gets count of orders and the sum of their totals from month by their status
$this->orderReport = Order::select(DB::raw('COUNT(*) as count,SUM(total_price) as total, status'))
->whereMonth('created_at', $month)
->groupBy('status')
->get()->keyBy('status');
// Gets number of all orders from the month
$this->totalOrders = $this->orderReport->sum('count');
// Gets number of orders and total spending of 10 users with most STATUS_SUCCESSFULL orders for the month
$this->orderUserReport = Order::select(DB::raw('COUNT(*) as count,SUM(total_price) as total, user_id'))
->whereMonth('created_at', $month)
->status(Order::STATUS_SUCCESSFULL)
->groupBy('user_id')
->orderBy('count', 'DESC')
->take(10)->get()->keyBy('user_id');
// THIS IS WHERE IM STUCK
$test = Order::whereMonth('created_at', $month)->status(Order::STATUS_SUCCESSFULL)->with('books')->get();
}
I would like to get a similar result for my products as i do for orders / users. Problem is the quantity of sold products is in the pivot table connecting Books with Orders.
So precisely what I need is the pivot table grouped by book_order.book_id with sums of book_order.quantity only where book_order.order_id is in orders with STATUS_SUCCESSFULL and whereMonth($month).
How would I go about attaining that data?
I had some trouble formulating this question in my mind so if anything is unclear please feel free to comment i'll clarify.
EDIT
$orders = Order::whereMonth('created_at', $month)->status(Order::STATUS_SUCCESSFULL)->with('books')->get();
foreach($orders as $order)
{
foreach($order->books as $book){
if(empty($this->productReport[$book->id])){
$this->productReport[$book->id] = $book->pivot->quantity;
break;
}
$this->productReport[$book->id] += $book->pivot->quantity;
}
}
ksort($this->productReport);
}
This code gives me the result I need but is ugly and inefficient, any way to reproduce this result with Eloquent or Query builder?
The above image is the result from the last snippet [book_id => quantity].
I have two tables "inventory" and "sell_records" relationship. Each product in inventory has many sell records.
I need to order all products by the latest sold record. So, I need to find out the latest sell record for the product from all sell records that the product has and then sort all the products.
This is the script I came up with, but it still doesn't sort as it should be.
$inventory = Inventory::where('inventory.client_id', $user->client_id)
->join('inventory_sell_records', 'inventory_sell_records.product_id', '=', 'inventory.id')
->groupBy('inventory_sell_records.product_id')
->orderByRaw("max(inventory_sell_records.created_at) $order_by")
->paginate(100);
Try using :
$inventory = Inventory::where('inventory.client_id', $user->client_id)
->join('inventory_sell_records', 'inventory_sell_records.product_id', '=', 'inventory.id')
->groupBy('inventory_sell_records.product_id')
->orderBy("inventory_sell_records.created_at", "desc")
->paginate(100);
have an issue with eloquent. Have request
$item = Store::select('id','name')
->with(['product.cart' => function ($query) use($cartWhere) {
$query->where($cartWhere);
}])
->whereHas('product.cart', function ($query) use($cartWhere) {
$query->where($cartWhere);
})
->get();
and middle chain product from product.cart I receive this
select * from `products` where `products`.`store_id` in (2, 3, 4) and `products`.`deleted_at` is null
But why eloquent ignore my ->whereHas filter? Store filtered right, cart filter right, but product ignore it. how I can make that I receive an only product that needs me (exist in cart). Cart table has product_id foreign key
Can I make this via eloquent? or I need to use raw SQL and make this manually?
EDIT v1: Additional info
product table has column store_id
cart table has column product_id,quantity,site_id,user_id,id
$cartWhere variable
$cartWhere['site_id'] =$current_site_id;
$cartWhere['user_id'] = $user_id;
I have a query working perfect but I need it in ORM Laravel.
I just need this query in eloquent Query builder.
SELECT * FROM product where vendor_id in (
SELECT vendor_id from vendor where is_block = 0
);
Scenario : There are two tables. One for Product and other for Vendor.
I want to select product from Product table. Each product belong to a vendor. If vendor is blocked then don't select product.
If is_block attribute is 0 then its mean that vendor is not block. If is_blocked=1 then vendor is blocked. I need to select only products of a vendor whose is_block = 0.
Use the whereHas() method:
Product::whereHas('vendor', function($q) {
$q->where('is_block', 0);
})->get();
Or the whereDoesntHave() method:
Product::whereDoesntHave('vendor', function($q) {
$q->where('is_block', 1);
})->get();
Make sure you've defined the relationship in the Product model:
public function vendor()
{
return $this->belongsTo(Vendor::class);
}
I want to achieve this in Laravel:
SELECT * FROM products JOIN
(SELECT product_id, MIN(price) AS lowest FROM prices GROUP BY product_id) AS q1
ON products.id = q1.product_id
ORDER BY q1.lowest;
I wrote this, but clearly there is something wrong:
$products = new Product();
$products = $products->join(
Price::whereNotNull('price')->select('product_id', DB::raw('min(price) as lowest'))->groupBy('product_id'), 'products.id', '=', 'product_id'
)->orderBy('lowest')->get();
The error I got:
ErrorException in Grammar.php line 39:
Object of class Illuminate\Database\Eloquent\Builder could not be converted to string.
I'm currently using join(DB::raw('(SELECT product_id, MIN(price) AS lowest FROM prices WHERE price IS NOT NULL GROUP BY product_id) AS q1'), 'products.id', '=', 'q1.product_id') as a workaround. Just wondering how to do this in the Eloquent way? Thanks.
If you want to make a single query for efficiency reasons in this case Eloquent is not going to help you much, it is possible but is a hassle. For cases like this you have QueryBuilder.
DB::table('products')
->join(
DB::raw('(SELECT product_id, MIN(price) AS lowest FROM prices GROUP BY product_id) AS q1'),
'products.id', '=', 'q1.product_id'
)
->orderBy('q1.lowest')->get();
If you change get() for getSql() you get the following
select * from `products` inner join
(SELECT product_id, MIN(price) AS lowest FROM prices GROUP BY product_id) AS q1
on `products`.`id` = `q1`.`product_id` order by `q1`.`lowest` asc
Unfortunately as far as I know you can't use a subquery without DB::raw, nevertheless it is not insecure as long as you don't put user input in the query. Even in that case you can use it securely by using PDO.
As for Eloquent, your product model doesn't even have a price field (it probably has a prices() function returning a relationship object) so it makes no sense to get a Product model with a single price asociated to it.
Edit:
You can also eager load the relationship, i.e. (assuming you have the model relationship set as Trong Lam Phan's example)
$products = Product::with('prices')->get();
foreach ($products as $product)
{
$minPrice = $product->prices->min('price');
// Do something with $product and $minPrice
}
This will only run a single query but the min() operation is not done by the database.
In Eloquent, follow me, you need to think a little bit differently from normal mysql query. Use Model instead of complicated query.
First of all, you need to create Product and Price models with relationship between them:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
public function prices()
{
return $this->hasMany('\App\Price');
}
}
class Price extends Model
{
public function product()
{
return $this->belongsTo('\App\Product');
}
}
Then, you need to select all products:
$products = \App\Product::get();
foreach ($products as $product)
{
$minPrice = $product->prices->min('price');
echo 'Min price of product ' . $product->name . ' is: ' . $minPrice;
}
EDIT
If you have a problem with the performance, just get the product's id.
$products = \App\Product::get(['id']);
If you don't like this way of Eloquent, you may use Query Builder like that:
$result = \App\Price::join('products', 'prices.product_id', '=', 'products.id')
->select('product_id', 'name', \DB::raw("MIN(price) AS min_price"))
->groupBy('product_id')
->orderBy('min_price')
->get();
or you can do like that:
\App\Price::join('products', 'prices.product_id', '=', 'products.id')
->selectRaw("name, MIN(price) AS min_price")
->groupBy('product_id')
->orderBy('min_price')
->get()