Extend Discounts¶
By extending Discounts, you can increase flexibility and control over how promotions are applied to suit your unique business rules. Together with the existing events and the Discounts PHP API, extending discounts gives you the ability to cover additional use cases related to selling products.
Tip
If you prefer learning from videos, two presentations from Ibexa Summit 2025 cover the Discounts feature:
- Konrad Oboza: Introduction to the Discounts system in Ibexa DXP
- Paweł Niedzielski: Extending new Discounts to suit your needs
Create custom conditions and rules¶
With custom conditions you can create more advanced discounts that apply only in specific scenarios.
The logic for both the conditions and rules is specified using Symfony's expression language.
Available expressions¶
The following expressions are available for conditions and rules:
| Type | Name | Value | Available for |
|---|---|---|---|
| Function | get_current_region() |
Region object of the current siteaccess. | Conditions, rules |
| Function | is_in_category() |
true/false, depending if a product belongs to given product categories. |
Conditions, rules |
| Function | is_user_in_customer_group() |
true/false, depending if an user belongs to given customer groups. |
Conditions, rules |
| Function | calculate_purchase_amount() |
Purchase amount, calculated for all products in the cart before the discounts are applied. | Conditions, rules |
| Function | is_product_in_product_codes() |
true/false, depending if the product is part of the given list. |
Conditions, rules |
| Variable | cart |
Cart object associated with current context. | Conditions, rules |
| Variable | currency |
Currency object of the current siteaccess. | Conditions, rules |
| Variable | customer_group |
Customer group object associated with given price context or the current user. | Conditions, rules |
| Variable | amount |
Original price of the product | Rules |
| Variable | product |
Product object | Rules |
Custom expressions¶
You can create your own variables and functions to make creating the conditions easier. The examples below show how to add an additional variable and a function to the available ones:
- New variable:
current_user_registration_date
It's a DateTime object with the registration date of the currently logged-in user.
To add it, create a class implementing the DiscountVariablesResolverInterface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
And mark it as a service using the ibexa.discounts.expression_language.variable_resolver service tag:
1 2 3 | |
- New function:
is_anniversary()
It's a function returning a boolean value indicating if today is the anniversary of the date passed as an argument.
The function accepts an optional argument, tolerance, allowing you to extend the range of dates that are acccepted as anniversaries.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
Mark it as a service using the ibexa.discounts.expression_language.function service tag and specify the function name in the service definition.
1 2 3 4 | |
Two new expressions are now available for use in custom conditions and rules.
Implement custom condition¶
The following example creates a new discount condition. It allows you to offer a special discount for customers on the date when their account was created, making use of the expressions added above.
The tolerance option allows you to make the discount usable for a longer period of time (for example, a day before or after the registration date) to allow more time for the customers to use it.
Create the condition by creating a class implementing the DiscountConditionInterface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
The tolerance option is made available for usage in the expression by passing it in the constructor.
The expression can evaluate to true or false depending on the custom expressions values.
For each custom condition class, you must create a dedicated condition factory, a class implementing the \Ibexa\Discounts\Repository\DiscountCondition\DiscountConditionFactoryInterface inteface.
This allows you to create conditions when working in the context of the Symfony service container.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Mark it as a service using the ibexa.discounts.condition.factory service tag and specify the condition's identifier.
1 2 3 4 | |
You can now use the condition using the PHP API.
To learn how to integrate it into the back office, see Extend Discounts wizard.
Implement custom rules¶
The following example implements a purchasing power parity discount, adjusting product's price in the cart based on buyer's region.
You could use it, for example, in regions sharing the same currency and apply the rule only to them by using the IsInRegions condition.
To implement a custom rule, create a class implementing the DiscountRuleInterface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
As with conditions, create a dedicated rule factory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Then, mark it as a service using the ibexa.discounts.rule.factory service tag and specify the rule's type.
1 2 3 4 | |
You can now use the rule with the PHP API, but to use it within the back office and storefront you need to:
- integrate it into the Discounts wizard
- implement a new value formatter
Custom discount value formatting¶
You can adjust how each discount type is displayed when using the ibexa_discounts_render_discount_badge Twig function by implementing a custom formatter.
You must implement a custom formatter for each custom rule.
To do it, create a class implementing the DiscountValueFormatterInterface and use the ibexa.discounts.value.formatter service tag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
1 2 3 4 | |
Change discount priority¶
You can change the the default discount priority by creating a class implementing the DiscountPrioritizationStrategyInterface and aliasing to it the default implementation.
The example below decorates the default implementation to prioritize recently updated discounts above all the others. It uses one of the existing discount search criterions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 | |