Search by

magenxcommerce / module-auto-product-links

magenxcommerce

Automatically fills Magento's native Related Products, Up-Sells and Cross-Sells, plus a Frequently Bought Together link type of its own, from editable rules - with a default rule for each, so the whole catalog is linked on the first cron run. A rule pairs a source condition tree (which products it a

Package info

github.com/magenxcommerce/module-auto-product-links

Type:magento2-module

pkg:composer/magenxcommerce/module-auto-product-links

Statistics

Installs: 0

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

v1.1.1 2026-10-11 11:15 UTC

This package is auto-updated.

Last update: 2026-10-24 21:39:22 UTC


README

Built with AI assistance. This module is built with significant use of Claude Code, under active human review and guidance. It is open source and in active development. AI assistance does not lower the engineering bar: every change must meet the same correctness, security, documentation and maintainability requirements as any other contribution.

No warranty. We are not responsible for any incompatibility. By installing this software, you accept responsibility for its security, upgrades, patches and monitoring.

Fills Magento's native Related Products, Up-Sells and Cross-Sells, plus a Frequently Bought Together link type of its own, from editable rules, on a cron.

Related, up-sell and cross-sell links are written straight into catalog_product_link, so the storefront reads them through the stock related_products / upsell_products / crosssell_products GraphQL fields with no change at all.

Frequently Bought Together is deliberately not cross-sells. Cross-sells are the merchant's add-on suggestions shown in the cart; Frequently Bought Together is what customers actually bought in the same orders. The module registers its own link type for it (magenx_bought_together in catalog_product_link_type) and exposes it as ProductInterface.frequently_bought_together_products, resolved by Magento's own linked-products batch resolver.

Out of the box

Installing the module seeds one active rule per link type, and the module is enabled with dry run off, so the first cron run links the whole catalog with nothing to set up:

Rule Storefront Picks
Default: Related "You May Also Like" the cheapest products in the same category
Default: Up-Sells "We Also Recommend" the most expensive products in the same category
Default: Cross-Sells cart a random pick from the 50 cheapest products in the catalog
Default: Frequently Bought Together "Frequently Bought Together" products most often bought in the same orders

They are ordinary rules in the rule grid, at priority 1000: edit them, switch them off, or add rules of your own beside them (see Priorities below).

"Same category" means the product's most specific categories: a shirt assigned to both Men and Men > Shirts is matched against Shirts only, so socks and coats from Men do not compete for its slots.

The default cross-sell rule chooses by Random products. The pick is stable: it is a hash of rule, product and candidate, so a product keeps the same cross-sells from night to night until the candidates change, and nothing is rewritten (or purged from cache) just because a run happened.

The guarantee

Links you add by hand are never moved, overwritten or deleted.

Every link the module writes gets a row in magenx_auto_link_ledger, and only links with a ledger row are ever touched. A hand-picked link never gets one — including the case where a rule independently computes a product you had already linked yourself, which is subtracted out before anything is claimed. Manual links also keep their positions (auto links are numbered from 1000 up, so yours sort first) and do not count against a rule's link limit.

Every run logs manual_links_skipped, the number of times the module declined to claim someone else's link. That is the standing audit trail for this promise.

What a rule is

Part Meaning
Fills Related / Up-Sells / Cross-Sells / Frequently Bought Together
Apply this rule to these products The source condition tree — which products get links
Link to these products The target condition tree — the candidate pool
Choose products by Similar products, Bought together or Random products
Must match the source product on Attributes a candidate must share with the source (colour, size, manufacturer…), plus shares a category and price band
Price band (%) Half-width of the band, e.g. 20 = within ±20% of the source's price
Maximum links per product Cap on auto links (manual links are extra)
Show best How the survivors are ranked before the cap
Randomly pick from the best Optional N: link a stable random max links out of the best N instead of simply the best
Priority Lower runs first; see below

Random products links each product to a different random handful of the target pool — what cart cross-sells usually are. Match attributes and the price band still narrow the draw (e.g. random products from the same category), randomly pick from the best N limits it to the best N under Show best, and Show best decides the stored order of the picks.

Both condition trees are the stock Magento rule widget — the same attribute picker as a Catalog Price Rule, with any/all and is / is-not / greater-than.

Nothing is hardcoded per link type: a rule declares what it fills, so Bought together can drive Related Products just as well as Cross-Sells. The one restriction is the other way round: a Frequently Bought Together rule must choose by Bought together.

Priorities

Rules of one link type run in priority order, and each product has one owning rule per link type: the first rule that produces links for a product takes it, later rules skip it. A rule that finds nothing for a product leaves it to the next rule, which is how the priority-1000 defaults fill whatever your own rules (priority 0 by default) leave empty.

After all rules of a link type have run, auto links on products that no rule claimed any more - the product left a rule's conditions, or its rule was switched off - are removed. A rule that errors keeps its existing links rather than having them cleaned up.

Because ownership depends on the rules above it, Run Now runs every rule of the rule's link type, not the rule alone.

Example rules

  • Related — Choose by Similar products, match on colour + shares a category, show best sellers first.
  • Up-Sells — Choose by Similar products, match on shares a category + price band, price band 60, target tree price greater than {…}, show most expensive first.
  • Cross-Sells — Choose by Random products, no match attributes, target tree narrowed to an Accessories category, show cheapest first. Add randomly pick from the best 30 to draw only from the 30 cheapest accessories.
  • Frequently Bought Together — Choose by Bought together, show strongest match first.

Configuration

Stores → Configuration → Magenx → Auto Product Links (magenx_auto_product_links/…)

Path Default Notes
general/enabled 1 Both crons return immediately when off
general/dry_run 0 Compute and log, write nothing
general/invalidate_cache 1 Broadcast cat_p_<id> after a run
general/max_links_default 12 Matches the storefront PDP rails: 12 per rail, 4 visible at a time
general/auto_position_base 1000 Auto links start here so manual links sort first
limits/target_pool_cap 200000 Memory guard. A rule whose target tree (empty = whole catalog) matches more is skipped and keeps its links
limits/batch_size 100 Source products per transaction
copurchase/lookback_months 6
copurchase/min_support 2 Orders a pair must appear in
copurchase/max_orders_per_run 100000
copurchase/max_items_per_order 50 Very large orders are skipped — see below
cron/copurchase_schedule 0 3 * * *
cron/rules_schedule 30 3 * * * Must run after the mining job

Rules live at Marketing → Promotions → Auto Product Link Rules (ACL Magenx_AutoProductLinks::rules).

Runs are logged to var/log/magenx_auto_product_links.log.

Deploy

bin/magento module:enable Magenx_AutoProductLinks
bin/magento setup:upgrade
bin/magento setup:di:compile          # production mode

setup:upgrade registers the Frequently Bought Together link type and seeds the default rules; the next magenx_auto_link_mine_copurchase / magenx_auto_link_apply_rules cron runs do the rest. To link immediately, open any rule and press Run Now.

Upgrading from 1.0.x: enabled / dry_run defaults only apply where the admin never saved them. If the section was saved with Enable off or Dry Run on, change them there.

Order matters: the mining job must run before the rule job, and the rule job should run after the catalog reindex so it works from current prices.

Caveats

  • Product links are global. catalog_product_link has no store, website or customer-group column, so a rule's store view setting only decides which store's attribute values, prices and orders are read — not who sees the resulting links. There is no way to give two store views different auto links through native links. Customer-group scoping is not offered for the same reason: it would silently write links every group sees.
  • Ranking may not reach the storefront. The order is written as the link position, but Magento's stock GraphQL link resolvers do not guarantee position ordering in every 2.4.x patch. Verify against your own backend before relying on Show best.
  • New links can take hours to appear unless the purge chain is configured: Full Page Cache in Varnish mode, a populated http_cache_hosts, and an edge that forwards PURGE to the storefront's /api/revalidate. Without it the storefront serves its own cached rails until they expire.
  • Very large orders are excluded from mining. The number of pairs an order contributes grows with the square of its line count, so one 200-line order would contribute ~39,800 pairs and drown ordinary baskets.
  • Condition trees are pushed down to SQL, which is what makes a nightly whole-catalog pass viable. A condition on an attribute the SQL builder cannot map is skipped: inside an all/AND group that is harmless, but inside an any/OR group it can quietly match fewer products than expected.
  • Frequently Bought Together needs orders. The first mining run backfills the whole look-back window, but a store with little order history produces few pairs, and a product with none gets no Frequently Bought Together rail.
  • Links are counted at parent level. A configurable's parent product is what gets linked, since a link on an invisible variant renders nothing on the storefront.

Tables

Table Purpose
magenx_auto_link_rule The rules, including both serialized condition trees
magenx_auto_link_ledger Every link the module authored — the manual-link guarantee
magenx_auto_link_copurchase Mined co-purchase pair counts, bucketed by month

The list of already-mined months is kept in Magento's own flag table.

Extending

Add a way of choosing link targets by implementing Magenx\AutoProductLinks\Api\TargetStrategyInterface and contributing it to the strategies argument of Magenx\AutoProductLinks\Model\Strategy\StrategyPool in di.xml. It then appears in the rule form and runs under every existing guard — link caps, ordering, the ledger diff, dry run and cache invalidation — without touching the runner.