What is pbShop
pbShop is a shop for sites built on PageBlocks 3.x: products (which are equally tariffs and services) with options and variants, categories, stock, cart, delivery, orders, prices in several currencies, payments, subscriptions, and events the site hangs its own logic on.
It is not a standalone application. Without PageBlocks installed the component logs an error and switches itself off: tables and the field constructor, routing, controllers, file templates, migrations and the template engine all come from there. pbShop reinvents none of it — it brings the shop domain logic and two ways to call it.
One result — three paths
This is the main idea, and it is worth starting here, because the rest of the documentation is split along exactly this line.
// Templater — a MODX template
[[!pbProducts? &parent=`mugs` &tpl=`card` &limit=`12`]]
// Templater — Fenom in a file template
{'!pbProducts' | snippet: ['parent' => 'mugs', 'tpl' => 'card', 'limit' => 12]}
// Developer — a controller, no snippet needed at all
$products = $shop->products()->inCategory('mugs')->limit(12)->get();
return $this->render('catalog.tpl', ['products' => $products]);Three paths, one service. None of them is more "correct" than the others, and none goes through another: the templater is never obliged to open an IDE, the developer is never obliged to use snippets. A project can be built entirely the first way, entirely the second, or mixed — for an agency that is the normal case, not an exotic one.
Logic lives in the service
A snippet is a ten-line adapter. Everything it can do is available through a direct call, and the other way round: a capability needed in a typical scenario must have a one-line call. Anything that exists only in the snippet is a defect, not a convenience.
Where to go next
| You | Start with |
|---|---|
| Know HTML and CSS, work inside MODX templates | Quick start |
| Write routes and controllers, work in an IDE | For developers |
What is inside
Catalogue. Nested categories, a product in several categories, friendly URLs with a canonical address, options and specifications, variants with their own price, weight, stock and SKU, a variant generator, filtering by options, and a "from" price for composite products.
Cart and checkout. A guest cart on a cookie, merged into the member cart on login, promo codes, delivery priced against the actual contents, one-click purchase, data-processing consent, and registration at checkout through pbAuth.
Money. Amounts are always integers in minor units — no floats anywhere. Prices are fixed per currency and are never converted at a rate. A driver must verify its own callback signature, the service checks the amount on top of that, and nobody takes the amount from a callback on trust.
Orders and subscriptions. There is no separate "subscription" entity: a subscription is an order with a period, an expiry date, an auto-renewal flag and a stored card token. When to renew and when to expire is decided by the site's cron, not by the component.
Fiscal receipts. The gateway issues the receipt itself where it can; for every other way money arrives there is a cash-register driver with a document queue and an idempotency key.
What pbShop does not do
Login and registration — that is pbAuth or the site itself. Header links come from settings, and failing that from pbAuth's named routes; with neither, no link is shown at all. A "Log in" link leading to a 404 is worse than no link.
Scheduling likewise: dueForRenewal(), renew(), expire(), back-in-stock mailing and picking up unfinished receipts are all called by the shop's cron. The component provides the methods but does not decide when they fire.