Shopware 6 Sales Counter: installation and configuration guide
Install, configure and customise the sales counter on Shopware 6.5, 6.6 and 6.7 product pages.
This guide covers the installation, configuration and customisation of the DfSalesCounter plugin, which displays on every product page how many times a product has already been sold, based on the real orders of your Shopware 6 shop.
Requirements
- Shopware 6.5.x, 6.6.x or 6.7.x in a self-hosted installation. Shopware Cloud (SaaS) does not accept server plugins.
- PHP 8.1 or higher.
- A storefront theme derived from the Shopware Storefront theme, or a custom theme that keeps the standard Twig blocks of the buy widget.
- Command line access is recommended for theme compilation, but installation through the administration works too.
Installation
ZIP upload from the administration
- In the Shopware administration, open Extensions, then My extensions.
- Click Upload extension and select the
DfSalesCounter-1.0.0.zipfile. - Once the plugin is listed, click Install, then activate it with the toggle.
- Recompile the theme from Content, Themes, selecting your theme and then Recompile theme. This step is needed only once, because the plugin ships a storefront stylesheet.
Command line
Drop the DfSalesCounter folder into custom/plugins/ of your installation, then run:
bin/console plugin:refresh
bin/console plugin:install --activate DfSalesCounter
bin/console theme:compile
bin/console cache:clear
On an environment with a deployment pipeline, theme compilation is usually already part of the standard steps.
Configuration
The configuration page lives in Extensions, My extensions, the … button on the right of DataFirefly Sales Counter, then Configure. The selector at the top of the page lets you pick the sales channel the configuration applies to: each channel can have its own threshold, its own wording and its own placement.
General tab
- Enable sales counter: main switch. When off, no query runs and no badge is rendered.
- Counting mode: Quantity sold adds up every ordered quantity of the product. Number of orders counts the distinct orders that included the product. The first mode emphasises volume, the second one emphasises how many different customers were convinced.
- Orders taken into account: All orders gives the raw figure. Exclude cancelled orders drops those whose state machine state is
cancelled. Paid orders only keeps only orders with a transaction inpaidorpaid_partiallystate. - Minimum threshold before display: below this value no badge appears. The default is 5. A threshold of 0 is treated as 1, the badge is never rendered for a product with no sales.
- Period in days: restricts counting to the last X days, based on the order date. The value 0 means a lifetime total.
- Aggregate sales across all variants: adds up the sales of the parent product and all of its variants. Recommended on a fashion or size-based catalogue, to be turned off when each variant matches a distinct use case.
- Count only orders of the current sales channel: prevents a B2B storefront or an export channel from inflating the figures shown on the consumer shop.
Display tab
- Position on the product page: Under the product name, Under the price, or Under the buy box, meaning at the bottom of the box, below the add to cart button.
- Visual style: Badge renders a bordered pill, Simple text renders a line with no frame, Banner renders a full width block with a coloured side bar.
- Icon: flame, cart, check, or none. Icons are inline SVG, no icon font is loaded.
- Accent colour: left empty, the primary colour of the theme is used. When filled, it feeds the
--df-sales-counter-accentCSS variable on the badge element. - Thousands separator: narrow space, comma, dot or none. Useful as soon as counters go past a thousand.
- Custom text: see the next section.
- Cache lifetime in seconds: 900 by default. The value 0 disables the cache and queries the database on every product page view.
Customising the wording
Global text from the configuration
The Custom text field accepts a sentence with the %count% placeholder where the number should appear. Example: This model has shipped %count% times this month. That text is shared across every language of the sales channel. It is sanitised before rendering, which allows simple markup such as <strong> but blocks any script.
Per-language wording through snippets
Leave the Custom text field empty to drive the wording language by language. Open Settings, Shop, Snippets, then search for dfSalesCounter. Four keys are available:
dfSalesCounter.badge.quantitySingularanddfSalesCounter.badge.quantityPlural, used in quantity sold mode.dfSalesCounter.badge.ordersSingularanddfSalesCounter.badge.ordersPlural, used in number of orders mode.
Each value accepts the %count% placeholder. English, French, Spanish, German and Italian translations ship with the plugin. A value edited in the snippet manager takes precedence over the plugin one, including after an update.
How the figure is computed
The plugin reads product-type order line items, joined to the order and its state. The computation runs as a single aggregated query, with no background job and no dedicated table.
- In quantity mode, the query sums the quantity column of the order line items.
- In order mode, it counts distinct order identifiers.
- Only the live version of orders is taken into account, working versions created during a credit note or an order edit are ignored.
- With variant aggregation enabled, the plugin first resolves the family of the displayed product, parent product and variants, then filters on the whole set of identifiers.
If the result is below the configured threshold, no extension is added to the product and the template renders nothing. The badge therefore does not exist in the HTML, which avoids any leftover display through a theme CSS rule.
Cache and freshness
The result is stored in the Symfony application cache pool, under a key combining the product identifier, the sales channel and a signature of the options that influence the computation. Changing the counting mode, the order scope, the period or the aggregation options changes that signature and therefore invalidates previous values automatically.
On every order placed, the plugin purges the cache of the products contained in that order, along with that of their parent product. The counter therefore reflects the sale without waiting for the configured lifetime to expire.
On a modest catalogue the cache lifetime can be set to 0 with no noticeable consequence: the query hits indexed columns. On a large catalogue with heavy traffic, keep a lifetime of several minutes.
Advanced rendering customisation
The plugin extends the product page buy widget and adds its badge in three standard Twig blocks, depending on the chosen placement: the product name block, the price container block and the buy container block. The badge itself is rendered by a dedicated component template, storefront/component/df-sales-counter/badge.html.twig, which exposes two overridable blocks, one for the icon and one for the text.
From a theme or a plugin, the extension is accessible in Twig on the page product under the name dfSalesCounter. It exposes the raw number, the formatted number, the placement, the style, the icon, the accent colour, the custom text and the counting mode. You can therefore render the counter somewhere other than the buy box, for instance in a product information tab, by fetching the extension and including the component.
Styles are defined in Resources/app/storefront/src/scss/base.scss around the df-sales-counter, df-sales-counter__icon and df-sales-counter__text classes, with one modifier per visual style. Any rule of your theme compiled after the plugin one takes precedence, without having to modify the plugin.
Troubleshooting
No badge appears
Check in order: the plugin is activated, the enable switch is on for the right sales channel, the product has reached the configured threshold, and the selected order scope does not exclude all your orders. A threshold of 5 with a Paid orders only scope on a test shop whose orders are never marked as paid will never produce any display.
The badge appears unstyled
The theme was not recompiled after activation. Run bin/console theme:compile or use the recompile button in the administration.
The figure seems frozen
The cache lifetime has not elapsed yet. Clear the application cache with bin/console cache:pool:clear cache.app, or temporarily set the lifetime to 0 to validate the computation.
The badge is not in the right place
A heavily customised theme may have removed or renamed the Twig blocks of the buy widget. Try another placement in the configuration, or include the component manually in your template by fetching the product extension.
Update and uninstall
An update is performed by uploading the new ZIP then clicking Update, followed by a theme recompilation if the version contains style changes. The configuration is kept.
On uninstall, a checkbox offers to keep user data. Unchecked, every configuration key of the plugin is removed. The plugin creates no table and runs no migration, so uninstalling leaves nothing in the database beyond its configuration.
Configuration key reference
Every key is prefixed with DfSalesCounter.config. and can be handled through the Admin API or the system:config:set command:
active, booleancountMode, valuesquantityorordersorderScope, valuesall,notCancelledorpaidminThreshold, integerperiodDays, integeraggregateVariants, booleanscopeToSalesChannel, booleanposition, valuesafterName,afterPriceorafterBuystyle, valuesbadge,inlineorbannericon, valuesnone,flame,cartorcheckaccentColor, hexadecimal stringthousandSeparator, valuesspace,comma,dotornonecustomText, stringcacheTtl, integer in seconds