PHPackages                             orangecat/module-product-lists - 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. [Admin Panels](/categories/admin)
4. /
5. orangecat/module-product-lists

ActiveMagento2-module[Admin Panels](/categories/admin)

orangecat/module-product-lists
==============================

Saved product lists (order templates) for logged-in B2B customers in Magento 2

0.0.2(2d ago)02↑2900%OSL-3.0PHPPHP &gt;=8.1

Since Jun 5Pushed 2d agoCompare

[ Source](https://github.com/olivertar/m2_productlists)[ Packagist](https://packagist.org/packages/orangecat/module-product-lists)[ RSS](/packages/orangecat-module-product-lists/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (2)Dependencies (2)Versions (3)Used By (0)

Orangecat\_ProductLists
=======================

[](#orangecat_productlists)

Saved product lists (order templates) for logged-in B2B customers — create, manage, and reorder from named lists.

**Module:** `Orangecat_ProductLists`**Version:** 1.0.0 **License:** OSL-3.0 **Author:** Oliverio Gombert

---

Table of Contents
-----------------

[](#table-of-contents)

1. [Overview](#overview)
2. [Theme Compatibility](#theme-compatibility)
3. [Requirements](#requirements)
4. [Installation](#installation)
5. [What Gets Installed](#what-gets-installed)
6. [Configuration](#configuration)
7. [Store Admin Guide](#store-admin-guide)
8. [Buyer Guide](#buyer-guide)
9. [Developer Guide](#developer-guide)
10. [REST API](#rest-api)
11. [Frontend Routes Reference](#frontend-routes-reference)
12. [DevOps &amp; Integrator Notes](#devops--integrator-notes)

---

Overview
--------

[](#overview)

`Orangecat_ProductLists` lets logged-in customers maintain multiple named product lists — essentially saved order templates. Each list holds products with their configured options (color, size, etc.) and quantities, which can be added to cart in one click.

The module handles:

- Creating, renaming, and deleting multiple product lists per customer
- Adding products (simple and configurable) to a list from the PLP, PDP, related products, and upsell blocks
- Updating item quantities within a list
- Adding all or selected items from a list to the shopping cart
- Exporting a list to CSV (SKU, price, qty, name, options, URL)
- Paginated "My Lists" dashboard and per-list detail page in the customer account
- AJAX-based list selector modal when multiple lists exist
- Optional AJAX mode for silent add-to-list without page reload

### Position in the Orangecat B2B Dependency Chain

[](#position-in-the-orangecat-b2b-dependency-chain)

```
Orangecat_Core (via composer: orangecat/core)
  └── Orangecat_ProductLists               ← this module (standalone — no Company dependency)

```

`Orangecat_ProductLists` is independent of `Orangecat_Company`. It works for any logged-in customer, whether they belong to a company or not.

### The `buy_request` Field

[](#the-buy_request-field)

Each `product_list_item` row stores a `buy_request` JSON blob — the same data structure Magento uses for cart line items. This preserves the full configurable option selection (e.g. `{"super_attribute":{"93":52,"142":167},"qty":2}`). `ProductListItem::getBuyRequestObject()` deserializes this blob into a `DataObject` ready to pass to `Cart::addProduct()`, overriding the stored qty with the item's dedicated `qty` column.

---

Theme Compatibility
-------------------

[](#theme-compatibility)

ThemeStatusNotes**Luma**SupportedStandard `.phtml` templates. `add-to-list` button injected into PDP (`product.info.addto`), PLP (`category.product.addto`), related, and upsell blocks. List selector is a jQuery modal (`addto.phtml` + `productlists_addto.js`). Less CSS from `web/css/source/_module.less`.**Breeze Evolution**SupportedSame templates and JS as Luma — Breeze bundles them via `breeze_default.xml`. Additional Less from `web/css/breeze/_default.less`.**Hyvä**SupportedAlpine.js templates (`-hyva.phtml`). `hyva_*` layout handles active. `hyva_default.xml` removes the Luma modal block. PLP uses a dedicated Hyvä modal component (`list-modal-hyva.phtml`) registered as a JS block dependency. CSS from `web/css/hyva/module.css` and `view/frontend/tailwind/tailwind-source.css`.Hyvä templates follow the `view/frontend/templates/` convention (not `view/hyva/`).

---

Requirements
------------

[](#requirements)

DependencyVersion / NotesMagento 2.4.xTested on 2.4.8-p5PHP&gt;= 8.1`orangecat/core`Composer dependency`Magento_Customer`Customer session`Magento_Catalog`Product repository`Magento_Checkout`Cart model`Magento_ConfigurableProduct`Configurable option resolution---

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

[](#installation)

### Via Git Submodule (recommended for this project)

[](#via-git-submodule-recommended-for-this-project)

```
# From repo root
git submodule add git@github.com:olivertar/m2_productlists.git app/code/Orangecat/ProductLists
git submodule update --init --recursive
```

### Enable the Module

[](#enable-the-module)

Run inside the PHP container (`reward shell`):

```
bin/magento module:enable Orangecat_ProductLists
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush
```

---

What Gets Installed
-------------------

[](#what-gets-installed)

### Database Tables

[](#database-tables)

#### `product_list`

[](#product_list)

ColumnTypeNotes`list_id``int unsigned`Primary key, auto-increment`customer_id``int unsigned`FK → `customer_entity.entity_id` (CASCADE DELETE)`list_name``varchar(255)`Default: `"Main"``description``text`Optional list description, nullable`created_at``timestamp`Set on insert, not updatedIndex on `customer_id`. Deleting a customer cascades to all their lists.

#### `product_list_item`

[](#product_list_item)

ColumnTypeNotes`item_id``int unsigned`Primary key, auto-increment`list_id``int unsigned`FK → `product_list.list_id` (CASCADE DELETE)`product_id``int unsigned`Catalog product entity ID`qty``decimal(12,4)`Default: `1``buy_request``text`JSON blob with configurable options and qty`added_at``timestamp`Set on insert, not updatedIndexes on `list_id` and `product_id`. Deleting a list cascades to all its items.

### EAV Attributes

[](#eav-attributes)

None.

### Data Patches

[](#data-patches)

None. No default records, roles, or CMS pages are created.

---

Configuration
-------------

[](#configuration)

**Path:** `Stores > Configuration > Orangecat > Product List`

### General Settings

[](#general-settings)

LabelConfig pathDefaultDescriptionEnable Product List`productlists/general/enable_productlists`NoMaster switch. When disabled, the "My Lists" nav link is hidden and all frontend routes become inaccessible.Enable Ajax Product List`productlists/general/enable_ajaxwishlist`NoWhen enabled, clicking "Add to List" silently posts via AJAX without page reload.```
productlists/general/enable_productlists
productlists/general/enable_ajaxwishlist

```

Both settings are configurable at Default, Website, and Store view scope.

---

Store Admin Guide
-----------------

[](#store-admin-guide)

There is no Admin UI for managing individual product lists or customer list items. Lists are owned and managed entirely by customers via the frontend.

Admin access is limited to:

- **Enabling/disabling the feature:** `Stores > Configuration > Orangecat > Product List`
- **Viewing data directly:** via database (`product_list`, `product_list_item` tables)

To grant a role access to the configuration section, assign the ACL resource `Orangecat_ProductLists::config` (visible under `Stores > Settings > Configuration`).

---

Buyer Guide
-----------

[](#buyer-guide)

### Accessing My Lists

[](#accessing-my-lists)

After logging in, navigate to **Account Dashboard &gt; My Lists** or go directly to:

```
https://your-store.test/product_lists/index/index

```

The "My Lists" link appears in the customer account sidebar when `Enable Product List` is set to Yes.

### Creating a List

[](#creating-a-list)

From the My Lists page, click **Add List**. Enter a name and optional description in the modal dialog, then save.

### Adding Products to a List

[](#adding-products-to-a-list)

**From a product listing page (PLP) or product detail page (PDP):**

Click the **Add to List** button. If multiple lists exist, a modal appears to select the target list or create a new one inline. For configurable products, the required options (size, color, etc.) must be selected first — an error is shown if they are not.

If only one list exists and AJAX mode is enabled, the product is added silently.

**Duplicate handling:** Adding the same product with the same option selection to a list increments the existing item's quantity rather than creating a duplicate row.

### Viewing a List

[](#viewing-a-list)

Click any list name from My Lists to open its detail page:

```
https://your-store.test/product_lists/index/view?list_id=N

```

The detail page shows product thumbnails, names, configured options, unit prices, and a quantity field per item. A search box filters items by product name within the list.

### Updating Quantities

[](#updating-quantities)

Edit the quantity field for any item on the list detail page and click **Update** to save all changes at once.

### Removing Items

[](#removing-items)

Click **Remove** next to any item to delete it from the list immediately.

### Adding to Cart

[](#adding-to-cart)

From the list detail page:

- **Add All to Cart** — adds every item to the cart, preserving configured options and quantities. Products remain in the list.
- **Add Selected to Cart** — check the items to add, optionally override qty, then click **Add Selected to Cart**.

After a successful add, the browser redirects to the cart.

### Exporting a List

[](#exporting-a-list)

From the list detail page, click **Export** to download a CSV file with columns: SKU, Price, Qty, Name, Options, URL.

### Deleting a List

[](#deleting-a-list)

From My Lists, click **Delete** next to a list. A confirmation dialog is shown. Deleting a list permanently removes it and all its items.

---

Developer Guide
---------------

[](#developer-guide)

### Module Structure

[](#module-structure)

```
Orangecat/ProductLists/
├── Api/
│   ├── ConfigInterface.php                    isEnabled(), isAjaxEnabled()
│   ├── ProductListsProviderInterface.php       getLists(), getListCollection(), getItemsForList()
│   └── Data/
│       ├── ProductListInterface.php            List data contract
│       └── ProductListItemInterface.php        Item data contract (incl. getBuyRequestObject)
├── Block/
│   ├── ListAddto.php                           Modal/AJAX config block for Luma/Breeze
│   ├── ProductListsIndex.php                   My Lists dashboard (paginated)
│   ├── ProductListsView.php                    List detail (items, search, pagination)
│   └── Catalog/Product/
│       ├── ProductList/Item/AddTo/ProductList.php  PLP add-to button block
│       └── View/AddTo/ProductList.php              PDP add-to button block
├── Controller/
│   ├── Index/
│   │   ├── Index.php       GET  — My Lists dashboard
│   │   ├── View.php        GET  — List detail (validates ownership)
│   │   └── Export.php      GET  — CSV download (validates ownership)
│   ├── Item/
│   │   ├── Add.php         POST — Add product to list (AJAX or redirect)
│   │   ├── Remove.php      POST — Remove item from list (AJAX or redirect)
│   │   ├── Update.php      POST — Bulk update item quantities
│   │   ├── AddAll.php      POST — Add all list items to cart
│   │   └── AddSelected.php POST — Add selected items to cart
│   └── Productlists/
│       ├── Get.php         AJAX GET  — Return customer's lists as JSON
│       ├── Update.php      AJAX POST — Create or rename a list
│       └── Delete.php      AJAX POST — Delete a list
├── Helper/Data.php          getItemProduct(), getItemImageProduct(), getConfiguredOptions(), getListParams()
├── Model/
│   ├── Config.php
│   ├── ProductList.php
│   ├── ProductListItem.php                     getBuyRequestObject() merges JSON + qty column
│   ├── ProductListsProvider.php               Session-aware; getLists() result is cached per request
│   └── ResourceModel/                          Standard AbstractResource + Collection pairs
├── Plugin/WishlistAddAction.php               around Magento\ProductLists\Controller\Index\Add
└── view/frontend/
    ├── layout/                                 Luma, Hyvä (hyva_*), and Breeze (breeze_*) handles
    ├── templates/                              Luma/Breeze .phtml + Hyvä -hyva.phtml variants
    └── web/
        ├── js/productlists.js                 My Lists CRUD UI (jQuery modal, delete confirm)
        ├── js/productlists_addto.js           Add-to-List button behavior (modal, AJAX, configurable validation)
        ├── css/source/_module.less            Luma Less
        ├── css/breeze/_default.less           Breeze Less
        └── css/hyva/module.css                Hyvä CSS

```

### Key Interfaces

[](#key-interfaces)

#### `ConfigInterface`

[](#configinterface)

```
isEnabled(int|null $storeId = null): bool
isAjaxEnabled(int|null $storeId = null): bool
```

#### `ProductListsProviderInterface`

[](#productlistsproviderinterface)

```
getLists(): array                          // All lists for logged-in customer (cached)
getListCollection(): ?ListCollection       // Raw paginatable collection
getItemsForList(int $listId): ItemCollection
```

#### `ProductListInterface`

[](#productlistinterface)

```
getListId(): int|null
getCustomerId(): int|null
setCustomerId(int $customerId): $this
getListName(): string|null
setListName(string $name): $this
getDescription(): string|null
setDescription(string|null $description): $this
getCreatedAt(): string|null
```

#### `ProductListItemInterface`

[](#productlistiteminterface)

```
getItemId(): int|null
getListId(): int|null
setListId(int $listId): $this
getProductId(): int|null
setProductId(int $productId): $this
getQty(): float|null
setQty(float $qty): $this
getBuyRequest(): string|null              // Raw JSON
setBuyRequest(string|null $buyRequest): $this
getBuyRequestObject(): DataObject         // Deserialized; qty column takes precedence over JSON qty
getAddedAt(): string|null
```

### Observers

[](#observers)

This module registers no observers. There is no `etc/events.xml`.

### Plugins

[](#plugins)

ClassTargetHookPurpose`Plugin\WishlistAddAction``Magento\ProductLists\Controller\Index\Add``around`Validates configurable options are selected before add; returns AJAX-friendly JSON when `ajax=1`. **Note:** the target class name `Magento\ProductLists` is non-standard — verify it resolves correctly in your Magento installation.### JS Components

[](#js-components)

#### Frontend

[](#frontend)

FilePurpose`js/productlists.js`My Lists dashboard — creates/edits lists via AJAX modal, deletes with confirmation dialog, DOM-only row removal on delete`js/productlists_addto.js`Add-to-List button behavior — list selector modal, new-list creation inline, configurable option validation (select dropdowns and swatch options), AJAX add modeBoth components use RequireJS AMD format and work on Luma and Breeze. Hyvä uses Alpine.js templates instead.

#### Admin JS

[](#admin-js)

None.

### Email Templates

[](#email-templates)

This module sends no transactional emails. There are no email template files.

### ACL Resources

[](#acl-resources)

Resource IDTitleLocation`Orangecat_ProductLists::config`Multi Wishlist`Stores > Settings > Configuration`### Adding Custom Logic

[](#adding-custom-logic)

- **Extend the provider:** Implement or decorate `ProductListsProviderInterface` via `di.xml` preference to change how lists are loaded (e.g., filter by company, add sharing across company users).
- **Extend item behavior:** Override `ProductListItemInterface` preference to add custom buy-request transformations before items are added to cart.
- **Add new add-to-list placements:** Inject a block extending `Orangecat\ProductLists\Block\Catalog\Product\ProductList\Item\AddTo\ProductList` into any layout handle with `Helper\Data::getListParams($product)` to generate the required `data-post` attribute.

---

REST API
--------

[](#rest-api)

This module exposes no REST API endpoints. There is no `etc/webapi.xml`.

---

Frontend Routes Reference
-------------------------

[](#frontend-routes-reference)

Route ID: `product_lists` (front name: `product_lists`)

RouteControllerMethodAccess`/product_lists/index/index``Controller\Index\Index`GETLogged-in customers only`/product_lists/index/view?list_id=N``Controller\Index\View`GETLogged-in owner of the list`/product_lists/index/export?list_id=N``Controller\Index\Export`GETLogged-in owner of the list`/product_lists/item/add``Controller\Item\Add`POSTLogged-in customers only`/product_lists/item/remove``Controller\Item\Remove`POSTLogged-in owner of the item`/product_lists/item/update``Controller\Item\Update`POSTLogged-in owner of the list`/product_lists/item/addall``Controller\Item\AddAll`POSTLogged-in owner of the list`/product_lists/item/addselected``Controller\Item\AddSelected`POSTLogged-in owner of the list`/product_lists/productlists/get``Controller\Productlists\Get`GET (AJAX only)Logged-in customers only`/product_lists/productlists/update``Controller\Productlists\Update`GET/POST (AJAX only)Logged-in customers only`/product_lists/productlists/delete``Controller\Productlists\Delete`POST (AJAX only)Logged-in owner of the listAll ownership checks are enforced by filtering collections on both `customer_id` and the requested entity ID — no separate authorization layer is needed.

---

DevOps &amp; Integrator Notes
-----------------------------

[](#devops--integrator-notes)

### Deployment Checklist

[](#deployment-checklist)

```
# After deploying or updating this module:
bin/magento module:enable Orangecat_ProductLists
bin/magento setup:upgrade          # creates product_list and product_list_item tables
bin/magento setup:di:compile       # regenerates interceptors for the Plugin
bin/magento setup:static-content:deploy -f
bin/magento cache:flush

# Enable the feature in config:
bin/magento config:set productlists/general/enable_productlists 1
bin/magento cache:flush
```

### Integration Token Scope

[](#integration-token-scope)

This module has no REST API. No integration token permissions are required to consume it. Direct database access to `product_list` / `product_list_item` requires no ACL resource.

For Admin panel config access, grant `Orangecat_ProductLists::config`.

### Disabling Without Uninstalling

[](#disabling-without-uninstalling)

```
bin/magento module:disable Orangecat_ProductLists
bin/magento setup:upgrade
bin/magento cache:flush
```

Disabling hides the "My Lists" nav link (`ifconfig` guard on the layout block) and stops all frontend routes from resolving, but leaves the database tables and data intact.

### Data Integrity Notes

[](#data-integrity-notes)

- Deleting a customer triggers a cascade delete of all their `product_list` rows, which in turn cascades to all `product_list_item` rows for those lists.
- Deleting a product does **not** cascade to `product_list_item` (no FK enforced on `product_id`). Orphaned items pointing to deleted products are skipped gracefully at render and cart-add time.
- The `buy_request` JSON blob may become stale if a configurable product's attribute options change after an item was saved. The cart-add action will reject an invalid option combination with a `LocalizedException`.
- No unique constraint exists on `(list_id, product_id)` — the application layer deduplicates by matching `super_attribute` values and incrementing qty instead of inserting.

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance99

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~1 days

Total

2

Last Release

2d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/959440?v=4)[Oliverio Gombert](/maintainers/olivertar)[@olivertar](https://github.com/olivertar)

---

Top Contributors

[![olivertar](https://avatars.githubusercontent.com/u/959440?v=4)](https://github.com/olivertar "olivertar (21 commits)")

---

Tags

b2bwishlistmagento2orangecatproduct-lists

### Embed Badge

![Health badge](/badges/orangecat-module-product-lists/health.svg)

```
[![Health](https://phpackages.com/badges/orangecat-module-product-lists/health.svg)](https://phpackages.com/packages/orangecat-module-product-lists)
```

###  Alternatives

[redchamps/module-clean-admin-menu

It will merge all third party extensions menu items to single menu item named 'Extensions'.

165438.8k](/packages/redchamps-module-clean-admin-menu)[kiwicommerce/module-cron-scheduler

Easily set up and manage cron jobs from the backend with a beautiful and managed timeline feature. Find the actual load on CPU/Memory by cron job execution.

74606.1k](/packages/kiwicommerce-module-cron-scheduler)[smile/module-seller

Smile Retailer Suite - Seller Module

13539.8k4](/packages/smile-module-seller)[myparcelnl/magento

A Magento 2 module that creates MyParcel labels

1859.0k](/packages/myparcelnl-magento)[kiwicommerce/module-customer-password

Magento 2 - Customer Password

1356.8k](/packages/kiwicommerce-module-customer-password)[vasileuski/magento2-module-admin-search

Magento 2 module that enhances the admin panel's global search by enabling fast and efficient searches through main entities with improved visibility and ACL support.

253.1k](/packages/vasileuski-magento2-module-admin-search)

PHPackages © 2026

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