ProcessWire Hook Guide: Safe Page-Save Validation and Side-Effects
Learn to add page-save validation and side-effects in ProcessWire using hooks in site/ready.php, without editing core files.
12 Jul 2026, 09:39 UTC

The Problem: Enforcing Rules Without Editing Core
ProcessWire stores content as pages, and every content change eventually calls a save operation. When editors create or update pages, you may need to block invalid data—such as an article missing a required custom field—or trigger a side-effect, like updating a summary page or notifying an external service. Modifying core files to achieve this breaks upgrades and makes your site hard to maintain. The practical solution is ProcessWire's hook system: register a callback in site/ready.php that runs when pages are saved, keeping your logic portable and version-controllable.
Desired Outcome
You want to intercept the page-save process to either:
- Validate incoming data and block the save if a business rule fails.
- Run a side-effect after the page is safely stored, such as updating a related page or writing a log entry.
All changes live in site/ready.php or a custom module, never in wire/ core directories.
Prerequisites
- A working ProcessWire 3.0.x installation with administrator access.
- A development copy of the site where hooks can be tested before production.
- Basic PHP knowledge and familiarity with the
$wireAPI and ProcessWire's field/template system.
ProcessWire's hook signatures and event-object behavior can vary slightly across minor 3.x releases. Test on the exact version you plan to deploy.
Choosing the Right Hook
ProcessWire exposes two primary events for page saves:
| Event | Timing | Typical Use |
|---|---|---|
Page::saveReady | Before the page is written to the database | Validation; blocking a save |
Pages::saved | After the page is successfully saved | Side-effects that depend on a committed save |
Use saveReady when you need to reject data. Use Pages::saved when you need to react to a completed save.
Focused Procedure
1. Register a Validation Hook
Open site/ready.php. This file is loaded on every request after ProcessWire's core bootstrap, making it a reliable place for site-wide hooks. Add a hook that runs before an article template page is saved:
$wire->addHookBefore('Page::saveReady', function($event) {
$page = $event->arguments(0);
if($page->template->name !== 'article') return;
if(!$page->title) {
throw new Exception('Article pages must have a title.');
}
});
Here, $event->arguments(0) retrieves the Page object being saved. Throwing an exception halts the save and surfaces the message in the admin editor. Non-article templates return early and are unaffected.
2. Add a Side-Effect Hook with Recursion Protection
To run logic after a save—such as updating a statistics page whenever a product is saved—use Pages::saved. Because saving one page can trigger hooks that save another page, a static flag prevents infinite recursion:
$wire->addHookAfter('Pages::saved', function($event) {
static $running = false;
if($running) return;
$page = $event->arguments(0);
if(!$page instanceof Page || $page->template->name !== 'product') return;
$running = true;
$stats = $event->object->get('template=stats, name=product-cache');
if($stats->id) {
$stats->of(false);
$stats->last_product_save = time();
$stats->save();
}
$running = false;
});
$event->object is the Pages instance, giving you access to the page finder. The $running flag ensures that if saving the stats page fires Pages::saved again, the hook exits immediately.
3. Test in the Admin and via API
Save an article with an empty title. The admin UI should display the exception message and the page should not be stored. Save a valid article and confirm it persists. Then save a page using a different template to verify the hook ignores it.
For API saves, create a simple script or module that calls $pages->save($page). The same exception should be thrown, confirming the hook applies outside the admin.
Expected Checks
- Validation blocks correctly: Invalid data throws an exception; valid data saves.
- Admin and API parity: Both admin edits and programmatic saves trigger the hook.
- No recursion: Saving a product updates the stats page without looping.
- Template isolation: Pages with other templates are unaffected.
- No PHP errors: Check the ProcessWire logs and your PHP error log after test saves.
Recovery Options
If a hook blocks legitimate saves or causes unexpected behavior:
- Comment out or remove the
addHookBeforeoraddHookAfterlines insite/ready.php. - Clear the ProcessWire cache. In the admin, go to Modules > Cache and clear the cache, or delete the contents of
site/assets/cache/if you have filesystem access. - Reload the editor. Normal save behavior should return.
- If the site is version-controlled, restore the previous
site/ready.phpfrom your repository.
Limitations and Verification
Hooks execute on every matching event, so heavy logic inside Pages::saved can slow down bulk operations. Keep side-effects lightweight or queue them. Also, throwing exceptions in saveReady blocks the save entirely; ensure error messages are clear for content editors.
To verify the hook was the cause of any change, temporarily disable it and repeat the save. If behavior returns to normal, the hook is confirmed as the source. Because ProcessWire's internals evolve across minor releases, always test on a staging environment that matches your production version before deploying.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.