# magendoo/module-product-labels

> Product labels for Magento 2 - first-class label entities with stable codes, manual assignment via product attribute, rule-computed assignments materialized by an indexer, PLP/PDP badges and GraphQL exposure

`composer require magendoo/module-product-labels`

Canonical URL: https://packagento.com/magendoo/module-product-labels

## At a glance

- **Vendor**: magendoo (https://packagento.com/magendoo.md)
- **Latest version**: v1.1.0 — released 2026-07-21
- **Pricing**: Free
- **Package type**: Magento 2 module
- **Status**: active, accepting new buyers

## Installation

Packagento is licence-gated, so even free packages need a licence on a project before Composer can resolve them.

1. **Sign in or create an account** at https://packagento.com/customer/account/.

2. **Add the package to your account.** Open https://packagento.com/magendoo/module-product-labels and complete the free checkout. A licence is minted automatically.

3. **Create or pick a project, then activate the licence on it.**
   - Projects represent the Magento installs you deploy to. Manage them at https://packagento.com/projects/.
   - Activate the new licence on the project you'll deploy this package to. Activation is what generates the Composer credentials scoped to that project.

4. **Add the project credentials to your Magento codebase.**

   Grab the project's public + private key from https://packagento.com/projects/ (open the project, then its Credentials tab), and add them to `auth.json`:

   ```json
   {
     "http-basic": {
       "packagento.com": {
         "username": "ppk_live_...",
         "password": "psk_live_..."
       }
     }
   }
   ```

   Add the Packagento Composer repository to `composer.json`:

   ```json
   {
     "repositories": [
       { "type": "composer", "url": "https://packagento.com" }
     ]
   }
   ```

5. **Install and apply.**

   ```bash
   composer require magendoo/module-product-labels:*
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```

## What it does

Product labels for Magento 2 - first-class label entities with stable codes, manual assignment via product attribute, rule-computed assignments materialized by an indexer, PLP/PDP badges and GraphQL exposure

## README

Product labels ("Sale", "New", "Staff Pick", …) for Magento 2.4.x, rendered as badges on category
listings and product pages, exposed in GraphQL, and driven by a first-class label entity with a
stable code — not by ad-hoc attribute options.

![Category listing with badges](docs/img/storefront-plp.png)

### Why this module is shaped the way it is

- **Labels are entities, not attribute options.** Every label has a stable `code`
  (e.g. `sale`, `new-arrival`) that stays identical across environments and is the only
  identifier integrations should use. Numeric IDs are local to one installation.
- **Manual and computed assignments never share storage.** Merchandisers assign labels on the
  product form (a multiselect attribute the module never writes). Rule-computed assignments are
  materialized by an indexer into a dedicated table. A rule recalculation can never overwrite a
  merchandiser's selection, and a product import can never wipe computed labels.
- **The storefront reads materialized data only.** Category and search listings are preloaded in
  one batch — two indexed lookups for the whole page, no rule evaluation, no external calls in
  the render path. Related/up-sell blocks and product widgets fall back to two lookups per tile
  (batched preload for those surfaces is on the roadmap).

### Features

- Label definitions with admin grid and form: code, name, text, colors, corner position,
  per-placement visibility (listing / product page), priority, active flag.
- Store-view text overrides (e.g. translate "Sale" per store view).
- Manual assignment via the **Product Labels** multiselect on the product form
  (Product Details section, global scope).
- Two built-in computed rules, recalculated by a real Magento indexer with MView change tracking:
  - **New products** — the product's *Set Product as New From/To* window is active.
  - **On sale** — the product has an active special price window and the special price is below
    the regular price.
- Luma rendering on category listings, search results and product pages (badge overlay,
  four corner positions, stacked when several labels share a corner).
- GraphQL: `magendoo_labels` on every product in the `products` query.
- Full-page-cache aware: pages carry identity tags for the labels they render, listing pages are
  stamped with every label so visibility transitions flush in both directions, manual assignees'
  product pages are flushed when a label's visibility changes, and the indexer flushes exactly
  the products whose computed label set changed.

| Product page | Admin form |
|---|---|
| ![Product page badge](docs/img/storefront-pdp.png) | ![Label edit form](docs/img/label-edit.png) |

### Requirements

- Magento Open Source / Adobe Commerce 2.4.x (`magento/framework` ≥ 103)
- PHP 8.2, 8.3 or 8.4

### Installation

```bash
## from the Magento root
composer require magendoo/module-product-labels     # once published on Packagist

## or manually: copy the module to app/code/Magendoo/ProductLabels, then
bin/magento module:enable Magendoo_ProductLabels
bin/magento setup:upgrade
bin/magento cache:flush
```

The `setup:upgrade` creates three tables (`magendoo_product_label`,
`magendoo_product_label_store`, `magendoo_product_label_assignment`), the
`magendoo_product_labels` product attribute, and registers the **Product Labels** indexer.

### Configuration

**Stores → Configuration → Magendoo → Product Labels**

| Field | Default | Scope | Description |
|---|---|---|---|
| Enable Labels | Yes | store view | Render label badges on the storefront for this scope. |
| Maximum Labels Per Product | 2 | store view | Highest-priority labels win; 0 means unlimited. |

### Managing labels

**Marketing → Promotions → Product Labels**

![Label grid](docs/img/label-grid.png)

Each label has:

- **Code** — stable identifier, lowercase letters/digits/`_`/`-`, unique. Used by GraphQL and
  any integration. Choose it once and don't change it.
- **Name** — internal admin name.
- **Label Text** — default storefront text, plus optional per-store-view overrides.
- **Background / Text Color** — 6-digit hex values.
- **Position** — top-left, top-right, bottom-left or bottom-right corner of the product image.
- **Show on Product Listings / Product Page** — per-placement visibility.
- **Priority** — lower renders first; also decides which labels survive the per-product cap.
- **Rule** — `None` (manual only), `New products`, or `On sale` (special price below regular
  price).

#### Manual assignment

Edit a product → Product Details → **Product Labels** multiselect. This is the merchandiser's
surface; the module never writes this attribute. The attribute is global scope: one manual
selection per product across all stores (per-store display text still varies via overrides).

#### Computed assignment

Set a label's Rule to *New products* or *On sale* and the **Product Labels** indexer
materializes matching products per store view:

```bash
bin/magento indexer:reindex magendoo_product_labels    # full rebuild
bin/magento indexer:status magendoo_product_labels
```

In *Update by Schedule* mode (default changelog subscriptions: product entity, product datetime
and decimal attribute values) product edits are picked up incrementally by the standard `index`
cron group — no custom cron. Saving a label whose rule or active flag changed invalidates the
indexer automatically.

A product's storefront labels are the **union** of its manual and computed labels, filtered by
placement, ordered by priority, and capped by *Maximum Labels Per Product*.

#### Honest limitations (Release 1)

_(README truncated for .md surface. Full README on https://packagento.com/magendoo/module-product-labels.)_

## Recent Versions

| Version | Released |
|---|---|
| v1.1.0 | 2026-07-21 |
| v1.0.0 | 2026-07-21 |

## Dependencies

### Require

| Package | Constraint |
|---|---|
| magento/framework | >=103.0 |
| magento/module-backend | >=102.0 |
| magento/module-catalog | >=104.0 |
| magento/module-catalog-rule | >=101.2 |
| magento/module-eav | >=102.0 |
| magento/module-store | >=101.0 |
| magento/module-ui | >=101.0 |
| php | ~8.2.0\|\|~8.3.0\|\|~8.4.0 |

### Suggest

| Package | Constraint |
|---|---|
| magento/module-catalog-graphql | Required for the magendoo_labels GraphQL field (the schema extends its ProductInterface) |
| magento/module-graph-ql | Exposes product labels in the GraphQL products query |

## Quality

Latest release (v1.1.0) fails the Packagento QA pipeline. Verdicts below are per-cell (Magento line × PHP version) for the matrixed tools, and run-once for the static / security tiers.


### Compatibility

Each Magento line is installed on its supported PHP versions, then the module is built (DI compile + static-content deploy). Cells show passed / failed / untested; staircase gaps render as `–`.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Pass | Pass | – | – |
| 2.4.8 | – | Pass | Pass | – |
| 2.4.9 | – | – | Pass | not tested |


### Code Quality

Advisory checks against the module's source. Never affect the Compatibility verdict — a phpcs finding can't make a module incompatible.

#### Static Analysis

Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Warning | 2 | 2 warnings (ruleset: Magento2) |
| PHPMD | Warning | 21 | 21 rule violations (UnusedPrivateField:21) |
| Cpd | Pass | 0 |  |
| Composer validate | Info | 7 | valid; 7 advisory notes (composer validate --strict) |

#### PHPStan

Type-checks the module against a real Magento install. Re-runs per Magento + PHP version because resolvable symbols differ between releases.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 4 | 4 | – | – |
| 2.4.8 | – | 4 | 4 | – |
| 2.4.9 | – | – | 4 | N/A |


### Tests

Unit and integration suites run per Magento + PHP cell. Test failures speak to the module's behaviour, not its compatibility with a line, so they're reported here separately.

#### Unit Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | – | – |
| 2.4.8 | – | N/A | N/A | – |
| 2.4.9 | – | – | N/A | N/A |

#### Integration Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | – | – |
| 2.4.8 | – | N/A | N/A | – |
| 2.4.9 | – | – | N/A | N/A |


### Security

Dependency-advisory audit (composer audit) plus a source malware scan. A malware detection fails the version outright.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| Composer audit | Pass | 0 |  |
| Malware scan | Pass | 0 |  |

## Licence and pricing

Free. A licence is still minted on checkout and bound to your project for Composer access — no payment step.

Refundable within 14 days of first purchase via https://packagento.com/account/refunds/.

## Install via Claude Code or any MCP client

The Packagento MCP server can run the licence + project + Composer steps above in one tool call:

```
purchase_and_install_packages(
  composer_names=["magendoo/module-product-labels"],
  project_id="proj_xxx"
)
```

This handles cart, checkout, licence minting, project activation, and writes auth.json credentials. Connect a client with `claude mcp add packagento https://mcp.packagento.com`. Full setup at https://packagento.com/docs/mcp-setup.

## Vendor

magendoo is a Magento 2 vendor on Packagento. See https://packagento.com/magendoo.md for their full catalogue.

