---
url: >-
  https://docs.corbitaltech.dev/products/whatsmark-saas/customization/custom-module/hooks-events.md
description: >-
  Technical guide for implementing WordPress-style hooks and event system
  integration in WhatsMarkSaaS custom modules
---

# Hooks & Events

::: warning 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
<?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-intro}

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

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

```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

| 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

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

| 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

```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 {#next-steps-hooks-events}

* **[Helper Functions](./helper-functions.md)** - Use module utility functions

***

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