Skip to content

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 TypePurposeReturn Value
ActionsExecute code at specific pointsNone (void)
FiltersModify and return data through pipelineModified value

Registering Hooks

registerHooks() Method

Hooks must be registered in the main module class registerHooks() method:

php
<?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:

php
add_action(string $tag, callable $callback, int $priority = 10, int $acceptedArgs = 1): void

Parameters:

ParameterTypeDescription
$tagstringHook name
$callbackcallableFunction to execute
$priorityintExecution order (default: 10, lower = earlier)
$acceptedArgsintNumber of arguments callback accepts

Example:

php
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:

php
do_action(string $tag, mixed ...$args): void

Example:

php
// 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

php
// 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:

php
add_filter(string $tag, callable $callback, int $priority = 10, int $acceptedArgs = 1): void

Example:

php
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:

php
apply_filters(string $tag, mixed $value, mixed ...$args): mixed

Example:

php
// 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:

php
// 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

HookTrigger PointArguments
before_activateBefore module activation$moduleName
after_activateAfter module activation$moduleName
before_deactivateBefore module deactivation$moduleName
after_deactivateAfter module deactivation$moduleName
before_installBefore module installation$moduleName
after_installAfter module installation$moduleName
before_uninstallBefore module uninstallation$moduleName
after_uninstallAfter module uninstallation$moduleName

Using Lifecycle Hooks

php
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:

php
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:

php
// 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:

php
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:

php
// 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:

PriorityWhen to Use
1-9Critical, must execute first
10Default priority (standard hooks)
11-19After default hooks
20+Low priority, cleanup tasks

Priority Example

php
// 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

php
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:

php
/**
 * 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_activate
  • after_activate
  • before_deactivate
  • after_deactivate

Application:

  • app_module_headers - Add scripts/styles to header
  • app_module_footers - Add scripts to footer
  • dashboard_widgets - Filter dashboard widgets

Custom Module Hooks:

Create a HOOKS.md file in your module documenting all hooks:

markdown
# 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:

  1. Verify registerHooks() is called
  2. Check hook tag spelling matches exactly
  3. Verify module is activated
  4. Check priority order

Filters not modifying data

Solutions:

  1. Ensure filter callback returns modified value
  2. Verify filter is registered before being applied
  3. Check priority order (lower priority executes first)

Next Steps


This guide is for WhatsMarkSaaS v2.0.0+ custom module development.

© 2024 - Corbital Technologies. All rights reserved.