سؤال

my client just asked me to create a similar products section on a product view page. He wants to display three different products within similar price range as the product currently viewed.

I have a products table (or a Product eloquent model if you want) and these products can have multiple prices, where one can be marked as main price (so I have a one-to-many table product_variants, that means Variants eloquent model).

What is the problem? I would like to build an eloquent query, that can select three additional products based on how closest the main price is.

So for example, we are viewing a product with main price of $40, but in the database we have also a products with these main prices - $45, $38, $37, $50, $35.

So I must show the products closest to the price, so that will be $38 (only 2 dollars difference), $37 (only 3 dollars difference) and one of $45 or $35 (5 dollar difference).

I know that I can do this, but my solution so far (in my head) would be to grab three products from each side (up and down from price), and only then check them wich one is more closer to the price.

I am not on my work PC, but so far this query exists in my mind:

$higher = Product::with(['variants' => function($q) use ($productPrice){
                                           $q->whereMain(1);
                                           $q->where('price', '>=', $productPice);
                                           $q->orderBy('price','asc');
                                       }]->where('id','!=', $productId)->get()->take(3);

Something like this will get me 3 products with higher price than product, and similar with lower prices.

But the magic of closest match would happen in some sort of foreach, where I am checking the substraction of prices and saving the closest ones.

I don't like the solution, because it can be time counsuming, since product view could be highly visited. Yes, I can save the query to Redis cache or something, but still ...

Does anybody have better idea how to achieve these three product more easier?

Thanks

هل كانت مفيدة؟

المحلول 2

Making Tzook's answer more Laravel friendly.

In your Variant model, add the function.

public function scopeOfSimilarPrice($query, $price, $limit = 3)
{
    return $query->orderBy(DB::raw('ABS(`price` - '.$price.')'))->take($limit);
}

Now this functionality is more dynamic and you can use it anywhere and is much easier to use.

Now since we already know your product, I actually think lazy-loading is easier to read and understand.

// Find your product
$product = Product::find(1);

// Eager load variants with closest price
$product->load('variants')->ofSimilarPrice($productPrice);

foreach($product->variants as $variant) {
    echo $variant->details;
    echo $variant->price;
}

نصائح أخرى

select id
from products pr
order by ABS(price-$productPice) ASC
LIMIT 3

I would tell you to use this kind of query to get the closest items, and then maby create eloquents threw the id's found or something.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top