Module Extensions
Module Extensions allow you to extend module functionality by adding UI components, permissions, navigation links, and lifecycle hooks without modifying the module class itself. They extend the AbstractModuleExtension class.
Creating a Module Extension
namespace Extensions\MyExtension;
use App\Abstracts\{AbstractModuleExtension, ModuleRuleBuilder};
class Module extends AbstractModuleExtension
{
// Configuration parameters
protected $show_in_header = true;
// Add UI components during configuration
public function configure(ModuleRuleBuilder $rule_builder): void
{
if ($this->show_in_header) {
$rule_builder->addHeaderLink(
'My Link',
'?page=mymodule&action=custom',
'bi bi-star'
);
}
}
// Called after module bootstrap
public function bootstrap(): void
{
// Initialize extension
}
// Called on each page load
public function init(): void
{
// Page-specific initialization
}
}
Extension Parameters
Extensions can have configurable parameters defined as protected properties:
class Module extends AbstractModuleExtension
{
protected $show_in_list = true;
protected $link_position = 'top-left';
}
Pass parameters when adding the extension:
$rule_builder->addExtension('MyExtension', [
'show_in_list' => false,
'link_position' => 'top-right'
]);
Configuration Hook
The configure() method is called during module initialization. Use it to add UI elements and permissions:
Adding Header Links
public function configure(ModuleRuleBuilder $rule_builder): void
{
$page = $this->module->getPage();
$rule_builder
->addHeaderLink(
'Audit Trail', // Link text
"?page={$page}&action=audit", // URL
'bi bi-database' // Icon class
)
->headerPosition('top-left'); // Position: top-left, top-right, etc.
}
Adding Permissions
public function configure(ModuleRuleBuilder $rule_builder): void
{
$rule_builder->addPermissions([
'audit' => 'View Audit Trail',
'export' => 'Export Data'
]);
}
Checking Permissions
use App\Permissions;
public function configure(ModuleRuleBuilder $rule_builder): void
{
$page = $this->module->getPage();
// Only add link if user has permission
if (Permissions::check($page . ".audit")) {
$rule_builder->addHeaderLink(
'Audit Trail',
"?page={$page}&action=audit",
'bi bi-database'
);
}
}
Lifecycle Hooks
| Hook | When Called | Use Case |
|---|---|---|
configure() |
During module configuration | Add UI elements, permissions, settings |
bootstrap() |
After module initialization (once) | Initialize services, register hooks, load dependencies |
init() |
On each page load | Load page-specific assets, track page views |
bootstrap() Example
Called once after module initialization. Use for one-time setup:
public function bootstrap(): void
{
// Load extension dependencies
require_once __DIR__ . '/AuditModel.php';
// Configure extension settings
\Extensions\Audit\Model::setMaxAuditRecords(100);
// Register global hooks
Hooks::set('model_save', [$this, 'onModelSave']);
// Log initialization
$page = $this->module->getPage();
error_log("Extension loaded for: {$page}");
}
init() Example
Called on every page request. Use for page-specific initialization:
public function init(): void
{
// Load page-specific JavaScript/CSS
Theme::set('javascript', Route::url() . '/Extensions/Audit/Assets/audit.js');
Theme::set('styles', Route::url() . '/Extensions/Audit/Assets/audit.css');
// Track page view
$page = $this->module->getPage();
$action = $_REQUEST['action'] ?? 'home';
error_log("Page view: {$page}/{$action}");
}
Accessing the Parent Module
Extensions have access to the parent module via $this->module->get():
public function myMethod()
{
$module = $this->module->get();
$page = $module->getPage();
$title = $module->getTitle();
$permissions = $module->getPermissions();
// Access module configuration
$config = $module->getConfig();
}
Complete Example: Audit Module Extension
This extension adds audit trail UI and functionality to modules:
namespace Extensions\Audit;
use App\Abstracts\{AbstractModuleExtension, ModuleRuleBuilder};
use App\{Permissions, Get};
class Module extends AbstractModuleExtension
{
public function configure(ModuleRuleBuilder $rule_builder): void
{
// Initialize Auth to check permissions
Get::make('Auth');
$page = $this->module->getPage();
// Add header link if user has permission
if (Permissions::check($page . ".audit")) {
$rule_builder
->addHeaderLink(
'Audit Trail',
"?page={$page}&action=audit",
'bi bi-database'
)
->headerPosition('top-left');
}
// Add audit permission
$rule_builder->addPermissions([
'audit' => 'View Audit Trail'
]);
}
public function bootstrap(): void
{
// Load audit model
require_once __DIR__ . '/AuditModel.php';
// Configure audit settings
\Extensions\Audit\Model::setMaxAuditRecords(100);
\Extensions\Audit\Model::setSessionTimeWindow(18000); // 5 hours
// Log module initialization
$page = $this->module->getPage();
$title = $this->module->getTitle();
error_log("Audit extension loaded: {$page} ({$title})");
}
public function init(): void
{
// Load audit-specific assets for the page
$action = $_REQUEST['action'] ?? 'home';
if ($action === 'audit') {
// Load audit viewer JavaScript
Theme::set('javascript', Route::url() . '/Extensions/Audit/Assets/audit-viewer.js');
}
}
}
UI Customization
Header Link Positions
| Position | Description |
|---|---|
top-left |
Top navigation bar, left side |
top-right |
Top navigation bar, right side |
sidebar |
Left sidebar menu |
Icon Classes
Use Bootstrap Icons classes for header links:
bi bi-database- Database iconbi bi-person- Person iconbi bi-gear- Settings iconbi bi-file-earmark- File icon
See Bootstrap Icons for more options.
Example: Author Extension
Simple extension that adds author tracking UI:
namespace Extensions\Author;
class Module extends AbstractModuleExtension
{
protected $show_username = true;
protected $show_in_list = true;
public function configure(ModuleRuleBuilder $rule_builder): void
{
// No UI elements needed for author tracking
// All functionality is handled in the Model extension
}
public function getDisplayField(): string
{
return $this->show_email ? 'email' : 'username';
}
public function shouldShowInList(): bool
{
return $this->show_in_list;
}
}
Best Practices
- Check permissions - Always verify user permissions before adding UI elements
- Use parameters - Make extensions configurable for different use cases
- Initialize Auth early - Call
Get::make('Auth')in configure() if checking permissions - Load assets conditionally - Only load JavaScript/CSS when needed
- Keep bootstrap light - Heavy initialization should be done in init() or lazily
- Use WeakReference - The parent module is stored as a WeakReference to prevent memory leaks
- Log important events - Use error_log() for debugging and tracking
Combining Module and Model Extensions
Most extensions have both Module and Model components working together:
Extensions/
└── Audit/
├── Module.php # Adds UI (header links, permissions)
├── Model.php # Adds functionality (audit trail tracking)
├── AuditModel.php # Separate model for audit data
└── Controller.php # Handles audit viewer pages
Add both in your module configuration:
// In Module configure()
$rule_builder->addExtension('Audit');
// In Model configure()
$rule_builder->addExtension('Audit');