Product Group and Add-on Hooks
The seven hooks over groups, categories, add-ons and requirements.
Overview
The structures around products live here: the groups that hold them, the add-ons sold beside them and the requirements asked for during an order.
Two shapes surprise. Group options arrive as an array or as text depending on the context. And the category gate receives not one record but a batch, which may include deletions.
Reference
Changing the group data
Runs before a product group or category is saved.
Hook::add('filter:product.group_save_data', 10, function (&$set_data, $category, $detail) {
// Options may be an array or text: check the type first.
if (!is_array($set_data['options'] ?? null)) return;
$set_data['options']['acme_tag'] = Acme::tagFor($category);
});Following a group being saved
Runs after a product group or category is saved.
Hook::add('action:product.group_saved', 10, function ($id, $is_new, $category) {
// On a constant category the id can be ZERO.
if ($id) Acme::refreshGroupMenu($id);
});Stopping a group deletion
Runs before a product group or category is deleted.
Hook::add('gate:product.group_delete', 10, function ($id, $category, $detail) {
if (Acme::groupHasProducts($id)) return 'A group holding products cannot be deleted.';
return null;
});Stopping a batch category operation
Runs before add-on or requirement categories are saved. What arrives is not one record but a batch: one request can hold creations, updates and deletions together.
addon or requirement.Hook::add('gate:product.category_save', 10, function ($type, $categories) {
// The batch may hold DELETIONS too: walk all of them.
foreach ($categories as $op)
if (($op['action'] ?? '') === 'delete' && Acme::categoryInUse((int) ($op['id'] ?? 0)))
return 'A category in use cannot be deleted.';
return null;
});Changing the add-on data
Runs before a product add-on is saved.
Hook::add('filter:product.addon_save_data', 10, function (&$addonData, $detail) {
$addonData['status'] = Acme::allowedAddon($addonData) ? ($addonData['status'] ?? 0) : 0;
});Stopping an add-on deletion
Runs before a product add-on is deleted.
Hook::add('gate:product.addon_delete', 10, function ($id, $detail) {
if (Acme::addonInUse($id)) return 'An add-on live on a customer cannot be deleted.';
return null;
});Stopping a requirement deletion
Runs before a product requirement is deleted. Requirements are what a customer is asked for during an order, so removing one touches orders already waiting.
Hook::add('gate:product.requirement_delete', 10, function ($id, $detail) {
if (Acme::pendingOrdersNeed($id)) return 'Orders still waiting use this requirement.';
return null;
});Pitfalls
The options field arrives as an array on a constant group record and as text otherwise. Reading it as an array without checking raises an error; test the type on the first line.
One request can hold creations, updates and deletions together. A check that only looks at creations misses the deletion sitting in the same request.
Related Articles
- Product Hooks
- Service Hooks
- How Hooks Work
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.