1

I am working on a project where I need to export product sold report in Excel format. I am using Maatwebsite/Laravel-Excel

I have three Model.

product.php

class products extends Model
{
    protected $table='products';
    public $timestamps = false;

    protected $fillable  = ['image', 'asin','price','strategy_id'];

    public function orderDetails()
    {
        return $this->hasMany(order_details::class, 'SKU', 'asin');
    }

}

orders.php

class orders extends Model
{
    protected $table = 'orders';
    public $timestamps = false;

    protected $fillable = ['id','date','quantity','totalAmount'];

    public function orderDetails()
    {
        return $this->hasMany(order_details::class);
    }
}

order_details.php

class order_details extends Model
{
    protected $table = 'order_details';

    protected $fillable = ['id','order_id','SKU','unitPrice','quantity','totalPrice'];

    public function order()
    {
        return $this->belongsTo(orders::class);
    }

Now I want to calculate how many time the each product sol in the last 30 days, 60 days and 90 days.

Points to note

  1. products.asin = order_detils.SKU
  2. The order_detail table does not have order date column.
  3. One order can have many products with quantity more than 1.

My current query is:-

$products = products::query();

// Some where clauses/filters

$products = $products->get();

foreach($products as $product)
{
    // Getting the order_details which has this product
    $orderIds = order_details::where('SKU','=',$product->asin)->pluck('order_id');

    $product->sales30days = orders::whereIn('id', $orderIds)->whereBetween('date', [Carbon::now()->subDays(30), Carbon::now()])->sum('quantity');
    $product->sales60days = orders::whereIn('id', $orderIds)->whereBetween('date', [Carbon::now()->subDays(60), Carbon::now()])->sum('quantity');
    $product->sales90days = orders::whereIn('id', $orderIds)->whereBetween('date', [Carbon::now()->subDays(90), Carbon::now()])->sum('quantity');
    $product->sales120days = orders::whereIn('id', $orderIds)->whereBetween('date', [Carbon::now()->subDays(120), Carbon::now()])->sum('quantity');
    $product->totalSold = orders::whereIn('id', $orderIds)->sum('quantity');
}

The above query is giving me the results I need, but is taking a lot of time and is not performance friendly. I have more than 100k products.

  1. I there any solution to optimize this query?
  2. Can I add pagination like $products->paginate(100) after this foreach loop?
1
  • I commented on one of the answers. Your starting point can be better. You are starting with getting all the products. Instead start with orders that's date <= 120 days. You won't need all these products. You will get the "only" product's which are in the orders that are date <= 120 days. It will be filtered version on data layer. Then you can iterate them on application layer. Commented Jul 4, 2020 at 11:12

1 Answer 1

2

The issue is that you are making MANY queries and it will inevitably be slow. This solution should have considerably better performance, since you only make two queries.

$orders = orders::with(['orderDetails'])->get();
$now = Carbon::now();
$quantities = [];

foreach($orders as $order) {
    $daysOld = $order->date->diffInDays($now);

    foreach ($order->orderDetails as $details) {
        if (!isset($quantities[$details->SKU])) {
            $quantities[$details->SKU]['30'] = 0;
            $quantities[$details->SKU]['60'] = 0;
            $quantities[$details->SKU]['90'] = 0;
            $quantities[$details->SKU]['120'] = 0;
            $quantities[$details->SKU]['total'] = 0;
        }

        if ($daysOld <= 30) {
            $quantities[$details->SKU]['30'] += $details->quantity;
        }

        if ($daysOld <= 60) {
            $quantities[$details->SKU]['60'] += $details->quantity;
        }
        
        if ($daysOld <= 90) {
            $quantities[$details->SKU]['90'] += $details->quantity;
        }

        if ($daysOld <= 120) {
            $quantities[$details->SKU]['120'] += $details->quantity;
        }

        $quantities[$details->SKU]['total'] += $details->quantity;
    }
}

return products::all()->map(function ($product) use ($quantities) {
    $product->sales30days  = $quantities[$product->asin]['30'];
    $product->sales60days  = $quantities[$product->asin]['60'];
    $product->sales90days  = $quantities[$product->asin]['90'];
    $product->sales120days = $quantities[$product->asin]['120'];
    $product->salesTotal   = $quantities[$product->asin]['total'];

    return $product;
}); 
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.