Hooks & Events
Minimum Version Requirement
This feature requires WhatsMarkSaaS v2.0.0 or higher. Ensure your installation meets this requirement before proceeding.
Overview
WhatsMarkSaaS implements a WordPress-style hook system that allows modules to integrate with core functionality without modifying core code. This guide covers actions, filters, lifecycle hooks, and custom hook creation.
Hook System Architecture
The hook system provides two types of hooks:
| Hook Type | Purpose | Return Value |
|---|---|---|
| Actions | Execute code at specific points | None (void) |
| Filters | Modify and return data through pipeline | Modified value |
Registering Hooks
registerHooks() Method
Hooks must be registered in the main module class registerHooks() method:
<?php
namespace Modules\OrderTracker;
use Corbital\ModuleManager\Support\Module;
class OrderTracker extends Module
{
/**
* Register WordPress-style action and filter hooks.
*
* @return void
*/
public function registerHooks(): void
{
// Register actions
add_action('after_activate', [$this, 'onActivate']);
add_action('order_created', [$this, 'handleOrderCreated']);
// Register filters
add_filter('dashboard_widgets', [$this, 'addDashboardWidget']);
add_filter('order_status_options', [$this, 'addCustomStatus']);
}
/**
* Handle module activation.
*/
public function onActivate($data): void
{
\Log::info('OrderTracker module activated', $data);
}
/**
* Handle order creation event.
*/
public function handleOrderCreated($orderData): void
{
// Send notification, log event, trigger webhook, etc.
}
/**
* Add custom dashboard widget.
*/
public function addDashboardWidget($widgets): array
{
$widgets[] = [
'name' => 'Order Summary',
'component' => 'order-tracker::widgets.summary',
];
return $widgets;
}
/**
* Add custom order status.
*/
public function addCustomStatus($statuses): array
{
$statuses['on_hold'] = 'On Hold';
return $statuses;
}
}Actions
Actions execute code at specific points without returning a value.
Registering Actions
Signature:
add_action(string $tag, callable $callback, int $priority = 10, int $acceptedArgs = 1): voidParameters:
| Parameter | Type | Description |
|---|---|---|
$tag | string | Hook name |
$callback | callable | Function to execute |
$priority | int | Execution order (default: 10, lower = earlier) |
$acceptedArgs | int | Number of arguments callback accepts |
Example:
public function registerHooks(): void
{
add_action('after_activate', function ($data) {
\Log::info('Module activated', $data);
});
add_action('order_created', function ($order) {
// Send notification
$this->sendOrderNotification($order);
}, 20);
}Firing Actions
Signature:
do_action(string $tag, mixed ...$args): voidExample:
// In controller
public function store(Request $request)
{
$order = Order::create($validated);
// Fire action with order data
do_action('order_created', $order);
return redirect()->route('order-tracker.index');
}Action with Multiple Parameters
// Register action
add_action('order_status_changed', function ($order, $oldStatus, $newStatus) {
\Log::info("Order {$order->id} status changed from {$oldStatus} to {$newStatus}");
}, 10, 3);
// Fire action
do_action('order_status_changed', $order, $oldStatus, $newStatus);Filters
Filters modify and return data through a pipeline.
Registering Filters
Signature:
add_filter(string $tag, callable $callback, int $priority = 10, int $acceptedArgs = 1): voidExample:
public function registerHooks(): void
{
add_filter('dashboard_widgets', function ($widgets) {
$widgets[] = [
'name' => 'Order Statistics',
'component' => 'order-tracker::widgets.stats',
];
return $widgets;
});
add_filter('order_total', function ($total, $order) {
// Apply discount
if ($order->has_discount) {
$total *= 0.9; // 10% discount
}
return $total;
}, 10, 2);
}Applying Filters
Signature:
apply_filters(string $tag, mixed $value, mixed ...$args): mixedExample:
// Get widgets with filter applied
$widgets = apply_filters('dashboard_widgets', []);
// Calculate total with filters
$total = apply_filters('order_total', $order->total, $order);Filter Pipeline
Multiple filters on the same hook execute in priority order:
// Priority 10 (executes first)
add_filter('order_total', function ($total) {
return $total * 1.1; // Add 10% tax
}, 10);
// Priority 20 (executes second)
add_filter('order_total', function ($total) {
return round($total, 2); // Round to 2 decimals
}, 20);
// Result: (100 * 1.1) rounded = 110.00
$total = apply_filters('order_total', 100);Lifecycle Hooks
WhatsMarkSaaS provides built-in lifecycle hooks for module events:
Available Lifecycle Hooks
| Hook | Trigger Point | Arguments |
|---|---|---|
before_activate | Before module activation | $moduleName |
after_activate | After module activation | $moduleName |
before_deactivate | Before module deactivation | $moduleName |
after_deactivate | After module deactivation | $moduleName |
before_install | Before module installation | $moduleName |
after_install | After module installation | $moduleName |
before_uninstall | Before module uninstallation | $moduleName |
after_uninstall | After module uninstallation | $moduleName |
Using Lifecycle Hooks
public function registerHooks(): void
{
// Log activation
add_action('after_activate', function ($moduleName) {
\Log::info("Module {$moduleName} activated");
});
// Cleanup on deactivation
add_action('before_deactivate', function ($moduleName) {
\Cache::forget("module:{$moduleName}:cache");
});
// Setup on installation
add_action('after_install', function ($moduleName) {
// Create default settings
$this->createDefaultSettings();
});
// Cleanup on uninstallation
add_action('before_uninstall', function ($moduleName) {
// Remove module data
$this->cleanupModuleData();
});
}Creating Custom Hooks
Define Custom Actions
In your module controller:
public function processOrder($orderId)
{
$order = Order::findOrFail($orderId);
// Allow modules to modify before processing
do_action('before_order_process', $order);
// Process order
$this->process($order);
// Allow modules to execute after processing
do_action('after_order_process', $order);
}Other modules can hook into these actions:
// In another module
add_action('before_order_process', function ($order) {
// Validate inventory
$this->checkInventory($order);
});
add_action('after_order_process', function ($order) {
// Send confirmation email
$this->sendConfirmation($order);
});Define Custom Filters
In your module:
public function calculateShipping($order)
{
$baseShipping = 10.00;
// Allow modules to modify shipping cost
$shipping = apply_filters('order_shipping_cost', $baseShipping, $order);
return $shipping;
}Other modules can modify shipping:
// In shipping module
add_filter('order_shipping_cost', function ($cost, $order) {
if ($order->total > 100) {
return 0; // Free shipping over $100
}
return $cost;
}, 10, 2);Hook Priority System
Priority Levels
Priority determines hook execution order:
| Priority | When to Use |
|---|---|
1-9 | Critical, must execute first |
10 | Default priority (standard hooks) |
11-19 | After default hooks |
20+ | Low priority, cleanup tasks |
Priority Example
// Execute first (priority 5)
add_filter('order_total', function ($total) {
return $total + 5; // Add shipping
}, 5);
// Execute second (priority 10, default)
add_filter('order_total', function ($total) {
return $total * 1.1; // Add tax
});
// Execute third (priority 15)
add_filter('order_total', function ($total) {
return round($total, 2); // Round result
}, 15);
// Result: ((100 + 5) * 1.1) rounded = 115.50
$total = apply_filters('order_total', 100);Conditional Hook Registration
Register Hooks Based on Conditions
public function registerHooks(): void
{
// Only register if another module is active
if (module_enabled('Notifications')) {
add_action('order_created', [$this, 'sendNotification']);
}
// Only register for specific tenants
if (tenant_setting('enable_custom_status')) {
add_filter('order_status_options', [$this, 'addCustomStatus']);
}
// Only register in production
if (app()->environment('production')) {
add_action('order_created', [$this, 'sendToAnalytics']);
}
}Hook Documentation
Documenting Custom Hooks
Document all custom hooks your module provides:
/**
* Fires after an order is successfully created.
*
* @since 1.0.0
*
* @param \Modules\OrderTracker\Models\Order $order The created order object.
*/
do_action('order_created', $order);
/**
* Filters the order status options.
*
* @since 1.0.0
*
* @param array $statuses Array of status options (key => label).
* @return array Modified status options.
*/
$statuses = apply_filters('order_status_options', $statuses);Hook Reference
Common WhatsMarkSaaS Hooks
Module Lifecycle:
before_activateafter_activatebefore_deactivateafter_deactivate
Application:
app_module_headers- Add scripts/styles to headerapp_module_footers- Add scripts to footerdashboard_widgets- Filter dashboard widgets
Custom Module Hooks:
Create a HOOKS.md file in your module documenting all hooks:
# OrderTracker Hooks
## Actions {#actions-example}
### order_created
Fires after an order is created.
**Parameters:**
- `$order` (Order) - The created order object
**Example:**
```php
add_action('order_created', function($order) {
\Log::info("Order {$order->id} created");
});order_status_changed
Fires when an order status changes.
Parameters:
$order(Order) - The order object$oldStatus(string) - Previous status$newStatus(string) - New status
Filters
order_total
Filters the order total amount.
Parameters:
$total(float) - The order total$order(Order) - The order object
Returns: (float) Modified total
---
## Best Practices
### Hook Naming Conventions
1. **Use descriptive names** - `order_created` not `oc`
2. **Use underscores** - `order_status_changed` not `orderStatusChanged`
3. **Prefix module hooks** - `ordertracker_order_created`
4. **Be consistent** - Follow existing naming patterns
### Performance Considerations
1. **Avoid heavy operations** - Keep hook callbacks lightweight
2. **Use priority wisely** - Don't force early execution without reason
3. **Cache results** - Cache expensive filter results
4. **Limit hook count** - Don't create unnecessary hooks
### Error Handling
```php
add_action('order_created', function ($order) {
try {
$this->sendNotification($order);
} catch (\Exception $e) {
\Log::error('Failed to send order notification', [
'order_id' => $order->id,
'error' => $e->getMessage(),
]);
}
});Troubleshooting
Hooks not firing
Solutions:
- Verify
registerHooks()is called - Check hook tag spelling matches exactly
- Verify module is activated
- Check priority order
Filters not modifying data
Solutions:
- Ensure filter callback returns modified value
- Verify filter is registered before being applied
- Check priority order (lower priority executes first)
Next Steps
- Helper Functions - Use module utility functions
This guide is for WhatsMarkSaaS v2.0.0+ custom module development.