Back to Top

How to change price of a product in cart in Shopware 6

Updated 17 June 2021

In this blog, you are going to learn “How to change the price of a product in the cart in Shopware 6 at the storefront.”
I hope you know the directory structure of Shopware 6 plugin, if you don’t know, see here- https://docs.shopware.com/en/shopware-platform-dev-en/internals/directory-structure.

 In this example, the prices are fetched from a database table. You have already created your own working plugin with a custom entity for those prices.  If you don’t know that’s done have a look at how to create a custom entity.

Entity:- ChangePriceEntity.php

<?php declare(strict_types=1);

namespace Webkul\Test\Cart\Checkout\ChangePrice;

use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityIdTrait;

class ChangePriceEntity extends Entity
{
    use EntityIdTrait;

    /**
     * @var ProductEntity
     */
    protected $product;

    /**
     * @var string
     */
    protected $productId;

    /**
     * @var float
     */
    protected $price;

    public function getProduct(): ProductEntity
    {
        return $this->product;
    }

    public function setProduct(ProductEntity $product): void
    {
        $this->product = $product;
    }

    public function getProductId(): string
    {
        return $this->productId;
    }

    public function setProductId(string $productId): void
    {
        $this->productId = $productId;
    }

    public function getPrice(): float
    {
        return $this->price;
    }

    public function setPrice(float $price): void
    {
        $this->price = $price;
    }
}

Changing the price

Change the price of the product in the cart, you should use the collector pattern. For this, you need to create your own cart collector.
The collector compares the product IDs of the products in the cart with the product IDs from the custom table. If there’s any match, the price has to be overwritten.

All adjustments are done in the method, where the product items already own a name and a price. If no product in the cart matches your condition, you can early return in the method. Afterward, in addition to this, you have to create a new line item for the new discount. For the latter, you want the line item to not be stackable and it shouldn’t be removable either.

Start your headless eCommerce
now.
Find out More
<?php declare(strict_types=1);

namespace Webkul\Test\Cart\Checkout;

use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartBehavior;
use Shopware\Core\Checkout\Cart\CartDataCollectorInterface;
use Shopware\Core\Checkout\Cart\CartProcessorInterface;
use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Webkul\Test\Cart\Checkout\ChangePrice\ChangePriceeEntity;

class OverwrittenPriceCollector implements CartDataCollectorInterface, CartProcessorInterface
{
    /**
     * @var EntityRepositoryInterface
     */
    private $overwritePriceRepository;

    /**
     * @var QuantityPriceCalculator
     */
    private $calculator;

    public function __construct(
        EntityRepositoryInterface $overwritePriceRepository,
        QuantityPriceCalculator $calculator
    ) {
        $this->overwritePriceRepository = $overwritePriceRepository;
        $this->calculator = $calculator;
    }

    public function collect(CartDataCollection $data, Cart $original, SalesChannelContext $context, CartBehavior $behavior): void
    {
        // get all product ids of current cart
        $productIds = $original->getLineItems()->filterType(LineItem::PRODUCT_LINE_ITEM_TYPE)->getReferenceIds();

        // remove all product ids which are already fetched from the database
        $filtered = $this->filterAlreadyFetchedPrices($productIds, $data);

        if (empty($filtered)) {
            return;
        }

        $criteria = new Criteria();
        $criteria->addFilter(new EqualsAnyFilter('productId', $filtered));

        // fetch prices from database
        $prices = $this->overwritePriceRepository->search($criteria, $context->getContext());;

        foreach ($filtered as $id) {
            $key = $this->buildKey($id);

            $price = null;
            // find price for the current product id
            foreach ($prices as $current) {
                if ($current->getProductId() === $id) {
                    $price = $current;
                    break;
                }
            }

            // we have to set a value for each product id to prevent duplicate queries in next calculation
            $data->set($key, $price);
        }
    }

    public function process(CartDataCollection $data, Cart $original, Cart $toCalculate, SalesChannelContext $context, CartBehavior $behavior): void
    {
        // get all product line items
        $products = $toCalculate->getLineItems()->filterType(LineItem::PRODUCT_LINE_ITEM_TYPE);

        foreach ($products as $product) {
            $key = $this->buildKey($product->getReferencedId());

            // no overwritten price? continue with next product
            if (!$data->has($key) || $data->get($key) === null) {
                continue;
            }

            /** @var OverwrittenPriceEntity $price */
            $price = $data->get($key);

            // build new price definition
            $definition = new QuantityPriceDefinition(
                $price->getPrice(),
                $product->getPrice()->getTaxRules(),
                $product->getPrice()->getQuantity()
            );

            // build CalculatedPrice over calculator class for overwritten price
            $calculated = $this->calculator->calculate($definition, $context);

            // set new price into line item
            $product->setPrice($calculated);
            $product->setPriceDefinition($definition);
        }
    }

    private function filterAlreadyFetchedPrices(array $productIds, CartDataCollection $data): array
    {
        $filtered = [];

        foreach ($productIds as $id) {
            $key = $this->buildKey($id);

            // already fetched from database?
            if ($data->has($key)) {
                continue;
            }

            $filtered[] = $id;
        }

        return $filtered;
    }

    private function buildKey(string $id): string
    {
        return 'price-overwrite-'.$id;
    }
}

The respective services.xml, which registers the collector in the first instance. You can learn more about the service. The cart repository is not writable so cart interface help the to edit the cart. Line item service help to remove and add the product in the cart. You can learn about this from the docs of Shopware.

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="Webkul\Test\Cart\Checkout\OverwrittenPriceCollector">
            <argument type="service" id="overwritten_price.repository" />
            <argument type="service"               
            id="Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator"/>
            <tag name="shopware.cart.processor" priority="4500" />
            <tag name="shopware.cart.collector" priority="4500" />
        </service>
    </services>
</container>

In the service file, you have to use the tag name shopware.cart.processor and set priority to 4500.
The quantity price calculator function calculates the price according to the quantity of the product.

I hope it will help you. Thanks for reading. Happy Coding 🙂
Thank You.

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


2 comments

  • Jan Brinkmann
    • Diwakar Rana (Moderator)
  • Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home