abderrahimghazali/sylius-upsell-plugin

Post-purchase upsell and cross-sell plugin for Sylius 2.x — Frequently Bought Together, one-click add-all-to-cart

Maintainers

Package info

github.com/abderrahimghazali/sylius-upsell-plugin

Type:sylius-plugin

pkg:composer/abderrahimghazali/sylius-upsell-plugin

Statistics

Installs: 20

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v2.1.0 2026-03-28 18:57 UTC

This package is auto-updated.

Last update: 2026-03-28 19:09:40 UTC


README

Sylius Logo

Sylius Upsell Plugin

Post-purchase upsell and cross-sell plugin for Sylius 2.x stores — Frequently Bought Together, checkout upsell modals, and one-click add-all-to-cart.

CI Latest Version PHP Version License Sylius 2.x Symfony 7.x PHPStan Level 5

Screenshots

Admin — Upsell Offers Grid

Admin Offers Grid

Admin — Upsell Offer Form

Admin Offer Form

Shop — Checkout Upsell Modal

Checkout Upsell Modal

Admin — Upsell Analytics Dashboard

Admin Analytics

Features

  • Frequently Bought Together section on every product page with manual or algorithmic product suggestions
  • One-click add-all-to-cart for the entire FBT bundle
  • Algorithmic co-purchase detection from order history with configurable thresholds
  • Checkout upsell modal — intercepts "Place order" with a special offer before completing the order
  • Trigger product targeting — tie offers to specific products in the cart, or use catch-all
  • Discount percentage — upsell product added to cart at discounted price
  • Customizable copy — headline, body, CTA label, and decline label per offer
  • Date scheduling — start and end dates for time-limited offers
  • Priority system — control which offer wins when multiple match
  • Admin CRUD for upsell offers under Marketing, and global FBT settings under Configuration
  • Drag-and-drop reordering of manual FBT relations with optional discount badges
  • Analytics dashboard — impressions, acceptance rate, extra revenue, daily chart (Chart.js)
  • Impression tracking — automatic for both FBT and checkout upsell (shown / accepted / declined)

Requirements

  • Sylius 2.1+
  • Symfony 7.0+
  • PHP 8.2+

Installation

  1. Require the plugin:
composer require abderrahimghazali/sylius-upsell-plugin
  1. Register the bundle in config/bundles.php (if not auto-discovered):
return [
    // ...
    Abderrahim\SyliusUpsellPlugin\SyliusUpsellPlugin::class => ['all' => true],
];
  1. Import routes — create config/routes/sylius_upsell.yaml:
sylius_upsell:
    resource: '@SyliusUpsellPlugin/config/routes.yaml'
  1. Generate and run the migration:
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
  1. Register the Stimulus controllers in assets/shop/controllers.json:
{
    "controllers": {
        "@abderrahimghazali/sylius-upsell-plugin": {
            "fbt": {
                "enabled": true,
                "fetch": "eager"
            },
            "post-purchase": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    }
}
  1. Symlink the plugin assets and rebuild:
# Create the symlink (from your project root)
mkdir -p node_modules/@abderrahimghazali
ln -s ../../vendor/abderrahimghazali/sylius-upsell-plugin/assets node_modules/@abderrahimghazali/sylius-upsell-plugin

# Rebuild assets
yarn encore dev

Entity: UpsellRelation

Field Type Description
sourceProduct ManyToOne (Product) The product page where the FBT section appears
relatedProduct ManyToOne (Product) The product suggested alongside the source
position int Display order (lower = first)
discount int? Optional discount percentage for the related product
createdAt datetime Timestamp of creation

Entity: UpsellOffer

Field Type Description
name string Admin label for the offer
enabled boolean Active/inactive toggle
triggerProduct ManyToOne (Product) The purchased product that triggers this offer
offerProduct ManyToOne (Product) The product offered in the upsell modal
offerVariant ManyToOne (ProductVariant)? Optional specific variant to offer
discountPercent int Discount percentage shown in the modal
headline string Modal heading (default: "Wait! A special offer just for you")
body text? Optional body text
ctaLabel string Accept button text (default: "Yes, add it!")
declineLabel string Decline button text (default: "No thanks")
priority int Higher = shown first when multiple offers match
startsAt datetime? Optional start date
endsAt datetime? Optional end date

Entity: UpsellConfiguration

Field Type Description
enabled boolean Global FBT feature toggle
minCoPurchaseThreshold int Minimum co-purchase count for algorithmic suggestions (default: 3)
maxProductsShown int Maximum products in the FBT section (default: 4)
sectionTitle string Heading text (default: "Frequently bought together")
showDiscountBadge boolean Show/hide discount badges on FBT products
fallbackStrategy string algorithmic, manual_only, or disabled

Architecture

src/
├── Controller/
│   ├── Admin/
│   │   ├── UpsellConfigurationController.php    # Global FBT settings
│   │   └── UpsellOfferController.php            # CRUD for upsell offers
│   └── Shop/
│       ├── ImpressionController.php             # Impression tracking API
│       └── PostPurchaseController.php           # Accept/decline offer API
├── DependencyInjection/
│   ├── Configuration.php
│   └── SyliusUpsellExtension.php                # Prepends resources, grids, hooks
├── Entity/
│   ├── UpsellConfiguration.php
│   ├── UpsellImpression.php
│   ├── UpsellOffer.php
│   ├── UpsellOfferInterface.php
│   ├── UpsellRelation.php
│   └── UpsellRelationInterface.php
├── EventListener/
│   ├── AdminMenuListener.php                    # Marketing menu items
│   └── ProductFormListener.php                  # FBT tab on product form
├── Form/Type/
│   ├── ProductUpsellType.php                    # Product FBT relations form
│   ├── UpsellConfigurationType.php              # Global settings form
│   ├── UpsellOfferType.php                      # Upsell offer form
│   └── UpsellRelationType.php                   # Single relation row form
├── Repository/
│   ├── UpsellImpressionRepository.php           # Analytics queries
│   ├── UpsellOfferRepository.php                # Active offers query
│   └── UpsellRelationRepository.php             # Relations by product
├── Service/
│   ├── FrequentlyBoughtTogetherResolver.php     # FBT logic (manual + algorithmic)
│   ├── PostPurchaseOfferResolver.php            # Best matching offer for an order
│   ├── UpsellAnalyticsService.php               # Impression recording + analytics
│   └── UpsellConfigurationProvider.php          # Cached config access
├── Twig/UpsellExtension.php                     # Twig function bridge
└── SyliusUpsellPlugin.php                       # Bundle class

assets/
├── controllers/
│   ├── fbt-controller.js                        # Add-all-to-cart Stimulus controller
│   └── post-purchase-controller.js              # Upsell modal Stimulus controller
└── package.json

templates/
├── Admin/
│   ├── analytics.html.twig                      # Analytics dashboard with Chart.js
│   ├── configuration.html.twig                  # Global settings page
│   └── upsell_offer/
│       ├── create.html.twig
│       ├── update.html.twig
│       └── grid/field/product.html.twig
└── Shop/
    ├── frequently_bought_together.html.twig     # FBT section on product page
    └── post_purchase_offer.html.twig            # Upsell modal after checkout

Testing

vendor/bin/phpunit

License

MIT. See LICENSE.