One of the requirements in one of our recent Drupal Commerce 2.x project was that each product should have a sale price field. By default, Commerce 2.x doesn't support this, but adding it is so easy. In this post I will share with you how I implemented this feature. Like anything else in programming there are many different approaches, but to me this seems like the most straightforward.

First thing that you need to know is that each product variation type comes with a Price field. You cannot delete this field, you can only change some settings related to the form and entity display. But you can add a second Price field to the product variation. Using field of the type Price makes the most sense, and I recommend you to avoid using regular text/number fields for this.

So, to cut the story short, you need to add a new field to your product variation. Use a field of the type Price and call it Sale price (machine name should be: field_sale_price).



This field should not be required - it should be optional. It doesn't have to have a value because product can but doesn't need to have a sale price. If a shop administrator enters a price in this field then that should be the product's price, and if this field is empty or if the value is zero then product's price should be equal to the value in the default Price field.

In Drupal Commerce 2.x there is something called Price resolver. Let's just say that this is a class where you can write some code that will change the product's price in some way. For this to work you need to change one thing in the Drupal Commerce 2.x web interface. You must go to your product variation settings and change the Price field format to Calculated price. You also probably need to hide the Sale price field here, since you don't want it to be displayed.



After you've done all this, you can write your own Price Resolver class. The logic is simple. Read value from the sale price field and check if it is empty, equal to zero or if it has some positive value. In the case that the value is positive (higher than zero) then set the product price to be equal to this sale price value. Look at the code bellow to see how easy is to accomplish this. And that code looks so damn simple and beautiful.

commerce_product_saleprice/commerce_product_saleprice.info.yml

name: 'Commerce Product Saleprice' type: module description: 'Drupal Commerce 2.x and sale price implementation.' package: 'Commerce (contrib)' core: 8.x dependencies: - commerce:commerce

commerce_product_saleprice/commerce_product_saleprice.services.yml

services: commerce_product_saleprice.saleprice_resolver: class: Drupal\commerce_product_saleprice\Resolvers\SalePriceResolver arguments: ['@request_stack'] tags: - { name: commerce_price.price_resolver, priority: 600 }

commerce_product_saleprice/src/Resolvers/SalePriceResolver.php

<?php namespace Drupal\commerce_product_saleprice\Resolvers; use Drupal\commerce\Context; use Drupal\commerce\PurchasableEntityInterface; use Drupal\commerce_price\Price; use Drupal\commerce_price\Resolver\PriceResolverInterface; /** * Class SalePriceResolver. * * @package Drupal\commerce_product_saleprice\Resolvers */ class SalePriceResolver implements PriceResolverInterface { /** * {@inheritdoc} */ public function resolve(PurchasableEntityInterface $entity, $quantity, Context $context) { // Make sure that product variation has a field called Sale price. if (!$entity->hasField('field_sale_price')) { return; } if ($entity->get('field_sale_price')->isEmpty()) { return; } /** @var \Drupal\commerce_price\Price $sale_price */ $sale_price = $entity->get('field_sale_price')->first()->toPrice(); $sale_price_number = $sale_price->getNumber(); $sale_price_currency_code = $sale_price->getCurrencyCode(); if (!$sale_price_number) { return; } return new Price($sale_price_number, $sale_price_currency_code); } }

You can also visit my github page and download this code if you don't want to write it.