PHPackages                             spryker/product-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. spryker/product-bundle

ActiveLibrary

spryker/product-bundle
======================

ProductBundle module

7.27.1(7mo ago)02.7M↓19.2%316proprietaryPHPPHP &gt;=8.3CI passing

Since Feb 21Pushed 2mo ago6 watchersCompare

[ Source](https://github.com/spryker/product-bundle)[ Packagist](https://packagist.org/packages/spryker/product-bundle)[ RSS](/packages/spryker-product-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (35)Versions (91)Used By (16)

ProductBundle Module
====================

[](#productbundle-module)

[![Latest Stable Version](https://camo.githubusercontent.com/f95376d248d52eb1e32f9bda045025443053661ffef8b984edde952771465dfe/68747470733a2f2f706f7365722e707567782e6f72672f737072796b65722f70726f647563742d62756e646c652f762f737461626c652e737667)](https://packagist.org/packages/spryker/product-bundle)[![Minimum PHP Version](https://camo.githubusercontent.com/9c50dc780fa576f5c39b4feff00c05345c1471be0808881a09e750b91220dc54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344253230382e332d3838393242462e737667)](https://php.net/)

Product bundles are two or more existing products combined into a new product for store display and sales purposes. Typically bundles consist of concrete products, because all items in the bundle need to be potential order items (i.e. have stock). The new (bundled) product does not physically exist in the bundled state. A bundle, when bought will still be handled as separate items in the order management system. ProductBundle provides all these product bundle features for combining multiple concrete products to a single one, and selling it.

Installation
------------

[](#installation)

```
composer require spryker/product-bundle

```

The following information describes how to install the newly released ´Product´ module (01/2017). These instructions are only relevant if you need to add this module to an already installed version of the Framework. If you have not yet installed the Spryker Framework, ignore these instructions as include this module in all versions released after January 2017.

Make sure you updated modules to:

Calculation: &gt;=2.2.0, ProductOption: &gt;=3.1.0, SalesAggregator: &gt;=3.1.0, Tax: &gt;=3.1.0 , Cart: &gt;= 2.4.0. Those all minor releases are BC.

### Plugin configuration

[](#plugin-configuration)

Plugin configuration is the process of incorporating the module into the project by registering the bundle plugins.

**To Register the bundle's plugins**:

1. In `\Pyz\Zed\Calculation\CalculationDependencyProvider::getCalculatorStack`, add `CalculateBundlePricePlugin` and position it after the `ExpenseTaxWithDiscountsCalculatorPlugin` or before `GrandTotalWithDiscountsCalculatorPlugin`.
2. In `\Pyz\Zed\Cart\CartDependencyProvider::getCartPreCheckPlugins`, replace the CheckAvailabilityPlugin with the `CartBundleAvailabilityPreCheckPlugin`.
3. In `\Pyz\Zed\Cart\CartDependencyProvider::getExpanderPlugins`, add `CartItemWithBundleGroupKeyExpanderPlugin` and position it after `CartItemProductOptionPlugin` or `CartItemPricePlugin` if these options are not used.
4. In `\Pyz\Zed\Cart\CartDependencyProvider::getPostSavePlugins`, add `CartPostSaveUpdateBundlesPlugin`as the last line.
5. In `\Pyz\Zed\Cart\CartDependencyProvider::getExpanderPlugins`, add `CartItemWithBundleGroupKeyExpanderPlugin` as the last line.
6. In `\Pyz\Zed\Checkout\CheckoutDependencyProvider::getCheckoutPreConditions`, add `ProductBundleAvailabilityCheckoutPreConditionPlugin`.
7. In `\Pyz\Zed\Oms\OmsDependencyProvider::getReservationHandlerPlugins`, add `ProductBundleAvailabilityHandlerPlugin`.
8. In `\Pyz\Zed\Product\ProductDependencyProvider::getProductConcreteAfterUpdatePlugins`, add `ProductBundleProductConcreteAfterCreatePlugin`.
9. In `\Pyz\Zed\Product\ProductDependencyProvider::getProductConcreteAfterUpdatePlugins`, add `ProductBundleProductConcreteAfterUpdatePlugin`.
10. In `\Pyz\Zed\Product\ProductDependencyProvider::getProductConcreteReadPlugins`, add `ProductBundleProductConcreteReadPlugin`.
11. In `\Spryker\Zed\ProductBundle\Communication\Plugin\Sales\ProductBundleOrderSaverPlugin::saveOrder`, add `ProductBundleOrderSaverPlugin`.
12. In `\Spryker\Zed\ProductBundle\Communication\Plugin\SalesAggregator\ProductBundlePriceAggregatorPlugin::aggregate`, add `ProductBundlePriceAggregatorPlugin`.
13. In `\Pyz\Zed\Stock\StockDependencyProvider::getStockUpdateHandlerPlugins`, add `ProductBundleAvailabilityHandlerPlugin`.

#### Plugin descriptions

[](#plugin-descriptions)

- `CalculateBundlePricePlugin` - calculates a bundle price.
- `CartBundleAvailabilityPreCheckPlugin` - a cart pre-check plugin to check bundle availability. It replaces `CheckAvailabilityPlugin`.
- `CartItemWithBundleGroupKeyExpanderPlugin` - a cart expander plugin which extracts bundle items.
- `CartPostSaveUpdateBundlesPlugin` - does a cleanup on unused bundles in a quote.
- `CartItemWithBundleGroupKeyExpanderPlugin` - changes the current item group key to include bundle identifier information.
- `ProductBundleAvailabilityCheckoutPreConditionPlugin` - checks product bundle availability when placing an order (final checkout step).
- `ProductBundleAvailabilityHandlerPlugin` - an availability handler which updates bundle availability every time a bundled item moves to reserved state.
- `ProductBundleProductConcreteAfterUpdatePlugin` - is a plugin which persists the product bundle and is used by the Product Management bundle.
- `ProductBundleProductConcreteReadPlugin` - writes product bundle data into `ProductConcreteTransfer`.
- `ProductBundleOrderSaverPlugin` - saves bundle related information when an order with the bundle is placed.
- `ProductBundlePriceAggregatorPlugin` - aggregates product information for the sale bundle.
- `ProductBundleAvailabilityHandlerPlugin` - a stock handler plugin which updates a bundle's available stock when a bundle or bundled product changes.

### Database migrations

[](#database-migrations)

Database migration is the process of adjusting the DB settings to incorporate the new plugin's activity.

**To configure the database migration**:

1. Create a sequence called spy\_product\_bundle\_pk\_seq:

```
CREATE SEQUENCE "spy_product_bundle_pk_seq";

CREATE TABLE "spy_product_bundle"
(
    "id_product_bundle" INTEGER NOT NULL,
    "fk_bundled_product" INTEGER NOT NULL,
    "fk_product" INTEGER NOT NULL,
    "quantity" INTEGER DEFAULT 1 NOT NULL,
    "created_at" TIMESTAMP,
    "updated_at" TIMESTAMP,
    PRIMARY KEY ("id_product_bundle")
);

ALTER TABLE "spy_product_bundle" ADD CONSTRAINT "spy_product_bundle-fk_bundled_product"
    FOREIGN KEY ("fk_bundled_product")
    REFERENCES "spy_product" ("id_product")
    ON UPDATE CASCADE
    ON DELETE CASCADE;

ALTER TABLE "spy_product_bundle" ADD CONSTRAINT "spy_product_bundle-fk_product"
    FOREIGN KEY ("fk_product")
    REFERENCES "spy_product" ("id_product")
    ON UPDATE CASCADE
    ON DELETE CASCADE;

```

2. Drop old tables/fields.

```
DROP TABLE IF EXISTS "spy_sales_order_item_bundle_item" CASCADE;`

`ALTER TABLE "spy_sales_order_item_bundle"

  DROP COLUMN "tax_rate",

  DROP COLUMN "bundle_type";

```

`DROP TABLE IF EXISTS "spy_product_to_bundle" CASCADE;`

### Yves/Project changes

[](#yvesproject-changes)

The following information describes the modifications that need to be done to Yves. You can find the Product module demo implementation and all code for Yves in the current Spryker demoshop.

### Cart

[](#cart)

The way the cart stores the quantity of items has changed.

The number of items in the cart is now returned by `$this->cartClient->getItemCount()`.

**To implement the change to the cart**:

1. In `\Pyz\Yves\Cart\Plugin\Provider\CartServiceProvider:register`, change:

```
public function register(Application $app)
{
    $app['cart.quantity'] = $app->share(function () {
        return $this->getClient()->getItemCount();
    });
}

```

2. Cart operations must be updated to cover product bundle logic as follows: instead of `CartOperationHandler` use `\Pyz\Yves\Cart\Handler\ProductBundleCartOperationHandler` (you can take this from the demoshop).
3. Twig changes:

- `src/Pyz/Yves/Cart/Theme/default/cart/parts/cart-item.twig` and `src/Pyz/Yves/Cart/Theme/default/cart/index.twig` - now handle product bundles.
- Project has received a new module called `ProductBundle` in which the bundle grouper is currently stored. This groups items for presentation in `\Spryker\Yves\ProductBundle\Grouper\ProductBundleGrouper`

As result, views where items are displayed have also to be changed in `\Pyz\Yves\Cart\Controller\CartController::indexAction`:

```
$cartItems = $this->getFactory()
          ->createProductBundleGroupper()
          ->getGroupedBundleItems($quoteTransfer->getItems(), $quoteTransfer->getBundleItems());

```

### Checkout

[](#checkout)

The checkout summary step has changed and needs adjustment to the cart item listing as follows:

In `\Pyz\Yves\Checkout\Process\Steps\SummaryStep`, inject the grouper and cart client into the `SummaryStep`, and update `getTemplateVariables` method.

```
/**
    * @param \Generated\Shared\Transfer\QuoteTransfer $quoteTransfer
    *
    * @return array
    */
   public function getTemplateVariables(AbstractTransfer $quoteTransfer)
   {
       return [
           'quoteTransfer' => $quoteTransfer,
           'cartItems' => $this->productBundleGrouper->getGroupedBundleItems(
               $quoteTransfer->getItems(),
               $quoteTransfer->getBundleItems()
           ),
       ];
   }

```

`srs/Pyz/Yves/Checkout/Theme/default/checkout/partials/summary-item.twig``src/Pyz/Yves/Checkout/Theme/default/checkout/summary.twig` have new item rendering structures, take samples from the demoshop.

### Customer

[](#customer)

The customer controller now uses the product bundle grouper.

**Change the following**In: `\Pyz\Yves\Customer\Controller\OrderController::getOrderDetailsResponseData` do:

```
$bundleItemGrouper = $this->getFactory()->createProductBundleGroupper();
       $items = $bundleItemGrouper->getGroupedBundleItems(
           $orderTransfer->getItems(),
           $orderTransfer->getBundleItems()
       );

       return [
           'order' => $orderTransfer,
           'items' => $items
       ];

```

Take the new implementation for listing order items, including `src/Pyz/Yves/Customer/Theme/default/order/partials/order-items.twig`.

Documentation
-------------

[](#documentation)

[Spryker Documentation](https://docs.spryker.com)

###  Health Score

65

—

FairBetter than 99% of packages

Maintenance75

Regular maintenance activity

Popularity41

Moderate usage in the ecosystem

Community35

Small or concentrated contributor base

Maturity94

Battle-tested with a long release history

 Bus Factor5

5 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~40 days

Recently: every ~22 days

Total

80

Last Release

211d ago

Major Versions

3.3.0 → 4.0.02017-12-20

4.12.0 → 5.0.02019-05-30

5.0.0 → 6.0.02019-06-27

4.12.1 → 6.0.12019-07-17

6.1.0 → 7.0.02019-11-21

PHP version history (8 changes)4.1.1PHP &gt;=7.1

7.0.0PHP &gt;=7.2

7.9.0PHP &gt;=7.3

7.10.0PHP &gt;=7.4

7.14.0PHP &gt;=8.0

7.17.0PHP &gt;=8.1

7.20.0PHP &gt;=8.2

7.27.0PHP &gt;=8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/10738957?v=4)[Spryker Bot](/maintainers/spryker-bot)[@spryker-bot](https://github.com/spryker-bot)

---

Top Contributors

[![dereuromark](https://avatars.githubusercontent.com/u/39854?v=4)](https://github.com/dereuromark "dereuromark (122 commits)")[![voitovtihran](https://avatars.githubusercontent.com/u/39267485?v=4)](https://github.com/voitovtihran "voitovtihran (70 commits)")[![m7moud](https://avatars.githubusercontent.com/u/3217954?v=4)](https://github.com/m7moud "m7moud (66 commits)")[![demkos](https://avatars.githubusercontent.com/u/2093777?v=4)](https://github.com/demkos "demkos (32 commits)")[![kraal-spryker](https://avatars.githubusercontent.com/u/42177964?v=4)](https://github.com/kraal-spryker "kraal-spryker (27 commits)")[![bm13kk](https://avatars.githubusercontent.com/u/1164904?v=4)](https://github.com/bm13kk "bm13kk (26 commits)")[![AsonUnique](https://avatars.githubusercontent.com/u/20453760?v=4)](https://github.com/AsonUnique "AsonUnique (23 commits)")[![helen-laktionova](https://avatars.githubusercontent.com/u/29577856?v=4)](https://github.com/helen-laktionova "helen-laktionova (22 commits)")[![stereomon](https://avatars.githubusercontent.com/u/1382877?v=4)](https://github.com/stereomon "stereomon (22 commits)")[![tamasnyulas](https://avatars.githubusercontent.com/u/3429362?v=4)](https://github.com/tamasnyulas "tamasnyulas (21 commits)")[![gechetspr](https://avatars.githubusercontent.com/u/42143273?v=4)](https://github.com/gechetspr "gechetspr (21 commits)")[![PhilinTv](https://avatars.githubusercontent.com/u/376033?v=4)](https://github.com/PhilinTv "PhilinTv (16 commits)")[![denis-gnusov](https://avatars.githubusercontent.com/u/53435720?v=4)](https://github.com/denis-gnusov "denis-gnusov (14 commits)")[![gerner-spryker](https://avatars.githubusercontent.com/u/30629375?v=4)](https://github.com/gerner-spryker "gerner-spryker (13 commits)")[![pushokwhite](https://avatars.githubusercontent.com/u/4017411?v=4)](https://github.com/pushokwhite "pushokwhite (11 commits)")[![limeeugenia](https://avatars.githubusercontent.com/u/25685017?v=4)](https://github.com/limeeugenia "limeeugenia (11 commits)")[![olhalivitchuk](https://avatars.githubusercontent.com/u/77281282?v=4)](https://github.com/olhalivitchuk "olhalivitchuk (10 commits)")[![zssamoylov](https://avatars.githubusercontent.com/u/43746999?v=4)](https://github.com/zssamoylov "zssamoylov (9 commits)")[![geega](https://avatars.githubusercontent.com/u/1426310?v=4)](https://github.com/geega "geega (7 commits)")[![a-sabaa](https://avatars.githubusercontent.com/u/1667759?v=4)](https://github.com/a-sabaa "a-sabaa (7 commits)")

### Embed Badge

![Health badge](/badges/spryker-product-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/spryker-product-bundle/health.svg)](https://phpackages.com/packages/spryker-product-bundle)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
