wagnerwagner / merx

Merx is a plugin to create online shops with Kirby.
https://merx.wagnerwagner.de
104 stars 10 forks source link

Product Extras (feature / workaround) #7

Closed plagasul closed 4 years ago

plagasul commented 4 years ago

As explained in this forum post I have the need to specify product extras (such as topping for an ice-cream) which differ from variants in that the quantity of the extra may not match the quantity of the product. I may want 3 ice-creams but only 1 with topping. In this sense I cannot add the topping price to the product price, for it would be applied to all items of this product, not only those with the extra

This renders the product variant approach if not useless, at least cumbersome, for it seems I'd need to have a separate products for each combination of product + extra:

ice-cream
ice-cream + chocolate sprinkles
ice-cream + candy

It'd be useful to define an extra along its own quantity and price, and that such extra is included in the product's sum, perhaps something like:

cart()->add([
    'id' => $id,
    'quantity' => $quantity,
    'extras' => // extra1, quantity, price / extra2, quantity, price ...
]);

Perhaps extras should be first defined as separate products then added to main product.

The key request here is that both $item['sum'] and cart->getSum() and even ->getTax(), etc all include the extras.

A workaround would be greatly appreciated.

Thank you

tobiasfabian commented 4 years ago

I’m sorry, we won’t add something like extras to our Cart class in the near future.

I would suggest the following workaround:

Ice cream toppings should be own products. When you put your ice cream (with topping) into the cart you should – at the same time – put the topping product into the cart. When you add the topping product you can add a for key to match the topping with the ice cream.

$cart = cart();
$cart->add([
  'id' => 'vanilla-ice-cream',
]);
$cart->add([
  'id' => 'chocolate-sprinkles',
  'for' => 'vanilla-ice-cream',
]);
plagasul commented 4 years ago

Thank you for the workaround.

Take care.

plagasul commented 4 years ago

@tobiasfabian I've just noticed a possible problem with this workaround.

Since chocolate sprinkles is a product, adding it twice should just incremenet the quantity right?

I can't have two different instances of a product with two different for keys, or can I ?

Danke

tobiasfabian commented 4 years ago

Oops, I think you are right, this won’t work out.

The for key could be an array. But this would be a quite shaky solution.


Maybe every product variant has to be its own product (I think this is the default behavior of shop systems, at least Magento works like this)

You can have a master product e.g. Vanilla Ice Cream. And each master product could have variants/children (e.g. Chocolate Sprinkles).

So you don’t have to create variants for each Ice Cream you could use Virtual Pages.

Untested Example

/site/models/product.php

<?php
class ProductPage extends Kirby\Cms\Page
{
    public function children()
    {
        $variants = [];

        foreach ($this->site()->variants()->toStructure() as $variant) {
            $variants[] = [
                'slug'     => $variant->slug(),
                'num'      => 0,
                'template' => 'product-variant',
                'model'    => 'product-variant',
                'content'  => [
                    'title'  => $variant->title(),
                    'price'  => $this->price()->toFloat() + $variant->additionalPrice()->toFloat(),
                ]
            ];
        }

        return Pages::factory($variants, $this);
    }
}

If the customer wants Vanilla Ice Cream with Chocolate Sprinkles the code would look like this

$cart->add('vanilla-ice-cream/chocolate-sprinkles');
plagasul commented 4 years ago

So you are overriding the children() method of the page class for the product page, and you use ::factory method to return variants as virtual pages. That's very clever, I wouldn't have thought about it

It does not fit my use-case tho, which was not really about ice-cream ( It seemed to me that was a clearer example) but I thought about a way that does fit. This is my cart page

image

My only 'extra' or 'variant' is adding a frame to the print, so I could:

- Add 'Frame' as its own product
- Include 'frames' as key when adding a product to cart, or as a cart.field of the product page.

then

- When user clicks (+) on the frame section of the Cart Page
  - I add +1 Frame product to the cart 
  - I add +1 to the 'frames' key of the associated product (Double Cherry, in the image)

As frames are products, their price is included in cart()'s calculations.

And with the 'Frames' key I can track how many frames for each product, which should make it relatively easy to associate frames and their price, and echo the data in the page, such as adding frames to the product total (they are already added to cart's total)

Any comments appreciated.

I'll come back when I test it.

Thank you

plagasul commented 4 years ago

I confirm my approach works for my case.

'Frame' is created as a separate product, and so it is included in merx calculations.

When adding a frame to the cart, the print this frame belongs to, receives a +1 on a 'frame' attribute, so we can keep track of how many frames are associated to each print among the total ammount of frames in the cart.

Finally it is often needed to filter 'frame' as a product, because we don't really want the customer to buy it separately,

scsskid commented 3 years ago

this issue should be linked in the cookbook