Platform
Storefront menus API in Go
A Go package that keeps the storefront’s menus: a header, a footer, a sidebar, each a tree of items an operator arranges by hand. A storefront reads a menu at /x/navigation/menus/{handle} with every item already resolved to a path, so it renders the tree without knowing what each item points at.
- Platform module
- 4 settings
- 7 tests
- 10 API operations
- ext/navigation
What it does
In the package’s own words, it is “the storefront’s menus: a header, a footer, a sidebar, each a tree of items an operator arranges by hand.” An item points at a product, a collection, a category, a content page, the home page, or any URL.
An item names what it points at by handle rather than by id, so a menu survives a re-import and reads sensibly in an export. The public route turns each handle into a storefront path with the four path patterns on the Plugins screen, and the item’s kind travels with it for a storefront that wants to know.
A menu is saved whole, because that is how it is edited: dragged into shape and saved, not one item at a time. Every store starts with a header and a footer, so a storefront can ask for them by handle before anyone has opened the Menus screen.
Configuration
Nothing has to be set. The four paths say where each kind of page lives on the storefront; set them on the Plugins screen, which is the setting that takes effect.
| Setting | Environment variable | Required | What it does |
|---|---|---|---|
ProductPathProduct page path | — | No | The product page’s path with {slug} in it, /products/{slug} by default. |
CollectionPathCollection page path | — | No | The collection page’s path, /collections/{slug} by default. |
CategoryPathCategory page path | — | No | The category page’s path, /categories/{slug} by default. |
PagePathContent page path | — | No | The content page’s path, /pages/{slug} by default. Each panel field has a default of its own, which stands in front of Config, so a path set only in Config is never read; see the limits. |
The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own main() you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.
Setting it up
- Install the modulePass navigation.New to gocommerce.New, as below, or start the reference binary with -menus. It adds two tables and gives each store a header and a footer menu.
- Check the pathsIf the storefront’s product, collection, category or page addresses are not the defaults, set their patterns on the Plugins screen under Menus.
- Arrange a menuOn Content › Menus, add items, drag them into shape, up to four levels, and save. The save is one PUT /api/admin/x/navigation/menus/{id}/items with the whole tree.
- Read it on the storefrontGET /x/navigation/menus/header answers the menu with its items nested and each one’s url resolved. GET /x/navigation/menus lists every menu’s handle and title.
main.go
import (
"github.com/itswadesh/gocommerce/core"
"github.com/itswadesh/gocommerce/ext/navigation"
)
app, err := gocommerce.New(cfg,
// The four paths are set on the Plugins screen.
navigation.New(navigation.Config{}),
) The package doc has no example of its own; this is how the reference binary’s -menus flag installs it. cfg is your gocommerce.Config. Import path github.com/itswadesh/gocommerce/ext/navigation.
How it works
-
Six kinds of item
url, home, product, collection, category and page. Home resolves to /, a url is served as written, and the other four put the item’s handle into their path pattern in place of {slug}. Every item but Home needs a target.
-
A tree saved whole
Saving a menu deletes its items and writes the new tree in one transaction. Item ids are not kept: a tree dragged into a new shape is a new tree.
-
Header and footer from day one
Where a store has none, it gets a header (Home, Shop, Contact) and a footer (Search, Contact us, Privacy policy). A store opened later gets the same; a store that built its own keeps them.
-
Menus as a spreadsheet
GET /api/admin/x/navigation/export writes every menu as CSV: menu, menu_title, path, kind and target, with the trail of titles in one cell, such as Shop / Shirts. The file’s row order is the menu’s order.
-
An import that replaces only what it names
POST /api/admin/x/navigation/import rewrites each menu the file names and leaves the others alone. ?dry_run=1 rehearses and rolls back. A row it cannot place is skipped with its line number; files are capped at 8 MB. Export needs data.export, import data.import.
-
Rights of its own
menus.read for managers and staff, menus.write for managers, so a store can hand its navigation to somebody without handing over the products.
What it does not do
Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.
- Targets are not checkedA handle is not looked up when a menu is saved or read. A link to a product that was renamed or deleted is served as it is, and leads to a page that is not there.
- The switch does not hide menusThe public routes do not check the plugin’s switch. Turning Menus off on the Plugins screen leaves /x/navigation/menus answering.
- Config paths are ignoredEach path field on the Plugins screen has a default, and the module reads the panel first, so a path set only in Config never takes effect. Set the paths on the Plugins screen.
- Four levels deepA menu nests at most four levels. A deeper tree is refused on save, and a deeper row in a CSV file is skipped.
- A title, a kind and a targetThat is all an item holds. There is no icon, picture or new-tab flag, and item titles are not translated.
- Every menu is publicGET /x/navigation/menus lists every menu’s handle and title to anyone, so a menu kept for later is visible by its handle.
FAQ
Questions about the Menus module
What does the storefront read?
GET /x/navigation/menus/{handle}, such as header or footer, on the host that serves your GoCommerce API. It answers the menu with its items nested, each with its title, kind, target and resolved url, and an empty children list on a leaf.
Why point at a handle and not an id?
So a menu survives a re-import of the catalogue and reads sensibly in an export. The handle is the product’s, collection’s, category’s or page’s slug, put into the matching path pattern.
Can I edit menus in a spreadsheet?
Yes. Export them as CSV, edit the rows, and import the file back. Each menu the file names is replaced whole; a menu it does not name is not touched. Run it with ?dry_run=1 first to see what it would do.
How deep can a menu go?
Four levels, which in the package’s words is what a header bar can render without becoming a filesystem browser.
Source
Everything on this page is read from ext/navigation in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.
Try it against a store of your own
The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.