Hooks Integration
Hooks allow your addon to extend and modify Mumara Campaigns behavior without changing core files. This guide covers implementing both module hooks (event-driven) and output hooks (UI injection).
Hook Files
Hook implementations go in the hooks/ directory:
hooks/
├── hooks.php # Main hooks file
├── contacts.php # Contact-related hooks
├── campaigns.php # Campaign-related hooks
└── ui.php # Output/UI hooks
Loading Hooks
Load hook files in your service provider:
// Providers/YourAddonServiceProvider.php
public function boot(): void
{
$this->registerHooks();
// ... other boot logic
}
protected function registerHooks(): void
{
$hooksPath = module_path($this->moduleName, 'hooks');
foreach (glob($hooksPath . '/*.php') as $file) {
require_once $file;
}
}
Module Hooks
Module hooks respond to events in the application.
Basic Hook Registration
// hooks/hooks.php
<?php
// React to contact addition
add_hook('AddContact', 5, function ($vars) {
$contact = $vars['contact'];
$list = $vars['list'];
// Validate email
$result = validateEmail($contact->email);
// Update contact with validation status
$contact->update([
'email_valid' => $result['valid'],
'email_score' => $result['score'],
]);
});
// React to broadcast completion
add_hook('CompleteBroadcast', 10, function ($vars) {
$campaign = $vars['campaign'];
$stats = $vars['stats'];
// Log completion
\Log::info("Campaign {$campaign->id} completed", [
'sent' => $stats['sent'],
'bounced' => $stats['bounced'],
]);
// Send notification
notifyUser($campaign->user_id, 'Campaign completed');
});
Hook with Background Processing
For resource-intensive operations:
// hooks/campaigns.php
<?php
add_hook('StartBroadcast', 5, function ($vars) {
$campaign = $vars['campaign'];
// Queue heavy processing
dispatch(new \Addons\EmailValidator\Jobs\ValidateCampaignEmails($campaign));
});
Modifying Data with Hooks
Some hooks allow you to modify data:
// Modify contact data before import
add_hook('AutomationImportContacts', 5, function ($vars) {
$contacts = $vars['contacts'];
$list = $vars['list'];
// Filter out invalid emails
$validContacts = array_filter($contacts, function ($contact) {
return isValidEmailFormat($contact['email']);
});
// Return modified data
return ['contacts' => $validContacts];
});
Common Module Hooks
Contact Hooks
// When a contact is added
add_hook('AddContact', 5, function ($vars) {
$contact = $vars['contact'];
$list = $vars['list'];
$user = $vars['user'];
});
// When a contact is edited
add_hook('EditContact', 5, function ($vars) {
$contact = $vars['contact'];
$changes = $vars['changes'];
});
// When a contact is deleted
add_hook('DeleteContact', 5, function ($vars) {
$contactId = $vars['contact_id'];
$listId = $vars['list_id'];
});
// After import completes
add_hook('ImportList', 5, function ($vars) {
$list = $vars['list'];
$importedCount = $vars['count'];
$user = $vars['user'];
});
Campaign Hooks
// When broadcast starts sending
add_hook('StartBroadcast', 5, function ($vars) {
$campaign = $vars['campaign'];
});
// When broadcast completes
add_hook('CompleteBroadcast', 5, function ($vars) {
$campaign = $vars['campaign'];
$stats = $vars['stats'];
});
// When email is sent
add_hook('EmailSentPostProcess', 5, function ($vars) {
$email = $vars['email'];
$recipient = $vars['recipient'];
$campaign = $vars['campaign'];
});
Tracking Hooks
// When email is opened
add_hook('EmailOpened', 5, function ($vars) {
$contact = $vars['contact'];
$campaign = $vars['campaign'];
$timestamp = $vars['opened_at'];
});
// When link is clicked
add_hook('LinkClicked', 5, function ($vars) {
$contact = $vars['contact'];
$campaign = $vars['campaign'];
$link = $vars['url'];
});
// When email bounces
add_hook('EmailBouncedPostProcess', 5, function ($vars) {
$email = $vars['email'];
$bounceType = $vars['type'];
$reason = $vars['reason'];
});
Output Hooks
Output hooks inject HTML into specific page locations.
Injecting into Page Head
// hooks/ui.php
<?php
// Add CSS/meta tags to <head>
add_hook('HeadEnd', 5, function ($vars) {
return '<link rel="stylesheet" href="' . asset('css/emailvalidator.css') . '">';
});
// Add preload hints
add_hook('HeadTop', 1, function ($vars) {
return '<link rel="preconnect" href="https://api.emailvalidator.com">';
});
Injecting into Page Body
// Add JavaScript before </body>
add_hook('BodyEnd', 10, function ($vars) {
return '<script src="' . asset('js/emailvalidator.js') . '"></script>';
});
// Add loading indicator after <body>
add_hook('BodyTop', 5, function ($vars) {
return '<div id="email-validator-loader" style="display:none;"></div>';
});
Adding to Navigation
// Add menu item to primary navigation
add_hook('PrimaryMenu', 10, function ($vars) {
$user = $vars['user'] ?? auth()->user();
if (!$user) {
return '';
}
return <<<HTML
<li class="kt-menu__item">
<a href="/email-validator" class="kt-menu__link">
<span class="kt-menu__link-icon">
<i class="flaticon-email"></i>
</span>
<span class="kt-menu__link-text">Email Validator</span>
</a>
</li>
HTML;
});
Page-Specific Content
// Add content to specific pages
add_hook('addPageHtml', 10, function ($vars) {
$route = $vars['route'] ?? '';
// Only show on contacts page
if ($route !== 'contact.index') {
return '';
}
$html = <<<HTML
<div class="alert alert-info">
<strong>Tip:</strong> Validate your contacts' emails to improve deliverability.
<a href="/email-validator">Validate Now</a>
</div>
HTML;
return [
'html' => $html,
'selector' => '.kt-portlet__body',
'action' => 'prepend'
];
});
Dynamic Actions
The addPageHtml hook supports multiple placement actions:
| Action | Description |
|---|---|
prepend | Insert as first child of selector |
append | Insert as last child of selector |
before | Insert before the selector element |
after | Insert after the selector element |
add_hook('addPageHtml', 10, function ($vars) {
$route = $vars['route'] ?? '';
if (str_starts_with($route, 'broadcasts.')) {
return [
'html' => '<button class="btn btn-info" id="validate-list">Validate Recipients</button>',
'selector' => '.kt-portlet__head-toolbar',
'action' => 'append'
];
}
return '';
});
Alert Bar Notifications
// Add system-wide alert
add_hook('AlertBar', 5, function ($vars) {
$user = auth()->user();
if (!$user) {
return '';
}
$credits = $user->email_validation_credits ?? 0;
if ($credits > 100) {
return '';
}
return <<<HTML
<div class="alert alert-warning alert-dismissible">
<strong>Low Credits:</strong> You have only {$credits} validation credits remaining.
<a href="/email-validator/credits">Purchase More</a>
<button type="button" class="close" data-dismiss="alert">×</button>
</div>
HTML;
});
Hook Priority
Priority determines execution order (lower numbers execute first):
// Execute first (priority 1)
add_hook('AddContact', 1, function ($vars) {
// Validate email format
});
// Execute second (priority 5)
add_hook('AddContact', 5, function ($vars) {
// Check against blocklist
});
// Execute last (priority 100)
add_hook('AddContact', 100, function ($vars) {
// Log the addition
});
Priority Guidelines:
| Priority | Use Case |
|---|---|
| 1-3 | Critical operations that must run first |
| 5-10 | Standard processing |
| 10-50 | Secondary operations |
| 50+ | Logging, notifications, cleanup |
Conditional Hooks
Based on User Role
add_hook('PrimaryMenu', 10, function ($vars) {
$user = auth()->user();
// Only show for admins
if (!$user || $user->role_id !== 1) {
return '';
}
return '<li>Admin-only menu item</li>';
});
Based on Package/Plan
add_hook('AddContact', 5, function ($vars) {
$user = $vars['user'];
$package = $user->package;
// Only validate for premium packages
if (!$package || !$package->email_validation_enabled) {
return;
}
// Perform validation
validateEmail($vars['contact']->email);
});
Based on Addon Settings
add_hook('StartBroadcast', 5, function ($vars) {
// Check if feature is enabled
if (!config('emailvalidator.auto_validate_campaigns')) {
return;
}
// Perform validation
});
Error Handling
add_hook('AddContact', 5, function ($vars) {
try {
$result = externalApiCall($vars['contact']->email);
// Process result
} catch (\Exception $e) {
// Log error but don't break the flow
\Log::error('Email validation failed', [
'email' => $vars['contact']->email,
'error' => $e->getMessage()
]);
// Optionally notify admin
if (config('emailvalidator.notify_on_error')) {
notifyAdmin('Validation API Error', $e->getMessage());
}
}
});
Hook Utilities
Check if Hook Exists
if (hook_exist('CustomHookName')) {
// Hook has registered callbacks
}
Get All Hook Results
// Get all results from a hook
$results = hook_get_all('CustomHook', ['data' => $myData]);
foreach ($results as $result) {
// Process each callback's result
}
Get First Result
// Get first non-empty result
$result = hook_get('CustomHook', ['data' => $myData]);
Creating Custom Hooks
Your addon can define its own hooks for other addons to use:
// In your controller or service
public function processValidation(string $email): array
{
// Before validation hook
listen_hook('BeforeEmailValidation', [
'email' => $email,
'user' => auth()->user()
]);
// Perform validation
$result = $this->validate($email);
// After validation hook
run_hook_in_background('AfterEmailValidation', [
'email' => $email,
'result' => $result,
'user' => auth()->user()
]);
return $result;
}
Other addons can then hook into your events:
// In another addon's hooks.php
add_hook('AfterEmailValidation', 10, function ($vars) {
$email = $vars['email'];
$result = $vars['result'];
// React to validation
if (!$result['valid']) {
// Handle invalid email
}
});
Best Practices
- Keep hooks lightweight - Offload heavy processing to queued jobs
- Always return empty string - For output hooks when not applicable
- Use appropriate priorities - Don't use priority 1 unless necessary
- Handle errors gracefully - Don't let hook failures break core functionality
- Check conditions early - Return early if hook doesn't apply
- Document your custom hooks - If creating hooks for others to use