Module discovery, registration, lifecycle management, and dependency resolution. The Module System provides a powerful plugin architecture that allows you to extend the framework's functionality through modular components.
PHP 8 attribute that marks a class as a Forge Engine module. Modules use attributes for configuration rather than extending a base class, providing a clean and modern approach to module definition.
Create a custom module using PHP 8 attributes to mark the class as a Forge Engine module.
<?php
declare(strict_types=1);
namespace App\Modules\Blog;
use Forge\Core\DI\Container;
use Forge\Core\Module\Attributes\Module;
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\Attributes\Compatibility;
use Forge\Core\Module\Attributes\Provides;
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\Repository;
use Forge\Core\Module\LifecycleHookName;
#[
Module(
name: "Blog",
description: "Blog functionality for the application",
version: "1.0.0",
order: 100,
core: false,
isCli: false
),
Compatibility(framework: ">=0.1.0", php: ">=8.3"),
Repository(type: "git", url: "https://github.com/forge-engine/modules"),
Provides(interface: BlogServiceInterface::class, version: "1.0.0")
]
#[Requires]
final class BlogModule
{
public function register(Container $container): void
{
// Register services early in the application lifecycle
$container->bind(BlogService::class, BlogService::class);
$container->bind(PostRepository::class, PostRepository::class);
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Module initialization logic after registration
}
}
Properties available in the Module attribute constructor.
#[Module(
name: "Blog", // Module identifier (required)
version: "1.0.0", // Semantic version (optional)
description: "Blog functionality", // Human-readable description (optional)
order: 100, // Loading order, higher loads first (optional, default: PHP_INT_MAX)
core: false, // Whether this is a core module (optional, default: false)
isCli: false // Whether module provides CLI commands (optional, default: false)
)]
Other attributes used to configure modules.
#[Compatibility(framework: ">=0.1.0", php: ">=8.3")]
#[Repository(type: "git", url: "https://github.com/forge-engine/modules")]
#[Provides(interface: BlogServiceInterface::class, version: "1.0.0")]
#[Requires] // Can specify required modules or interfaces
Register services early in the application lifecycle. This method is called for all modules before any lifecycle hooks.
public function register(Container $container): void
{
// Register services that other modules might depend on
$container->bind(BlogService::class, BlogService::class);
// Register configuration
$container->bind('blog.config', function() {
return new Config([
'posts_per_page' => 10,
'allow_comments' => true,
'moderation_required' => true,
'seo_enabled' => true
]);
});
// Register repositories
$container->bind(PostRepository::class, PostRepository::class);
}
Use attributes to mark methods that should be called at specific points in the application lifecycle.
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Called after all modules have been registered
// Safe to interact with other modules here
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
public function onAfterBoot(): void
{
// Called after the application has booted
// All services are available
}
#[LifecycleHook(hook: LifecycleHookName::BEFORE_REQUEST)]
public function onBeforeRequest(): void
{
// Called before each request is processed
// Useful for request-specific initialization
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_REQUEST)]
public function onAfterRequest(): void
{
// Called after each request is processed
// Useful for cleanup or logging
}
All available lifecycle hook names from the LifecycleHookName enum.
enum LifecycleHookName: string
{
case AFTER_BOOT = 'afterBoot'; // After application boot
case AFTER_MODULE_LOAD = 'afterModuleLoad'; // After modules are loaded
case AFTER_MODULE_REGISTER = 'afterModuleRegister'; // After all modules registered
case AFTER_CONFIG_LOADED = 'afterConfigLoaded'; // After configuration loaded
case APP_BOOTED = 'appBooted'; // After application fully booted
case BEFORE_REQUEST = 'beforeRequest'; // Before each request
case AFTER_REQUEST = 'afterRequest'; // After each request
case AFTER_RESPONSE = 'afterResponse'; // After response sent
}
Complete reference of all available PHP 8 attributes for defining and configuring modules in Forge Engine. These attributes provide a declarative way to configure module metadata, dependencies, compatibility, and behavior.
Required attribute that marks a class as a Forge Engine module and defines its basic properties.
use Forge\Core\Module\Attributes\Module;
#[Module(
name: "Blog", // Required: Module identifier
version: "1.0.0", // Optional: Semantic version
description: "Blog functionality", // Optional: Human-readable description
order: 100, // Optional: Loading order (higher loads first)
core: false, // Optional: Whether this is a core module
isCli: false // Optional: Whether module provides CLI commands
)]
final class BlogModule
{
// Module implementation
}
Defines compatibility requirements for Forge Engine framework and PHP versions.
use Forge\Core\Module\Attributes\Compatibility;
#[Compatibility(
framework: ">=0.1.0", // Forge Engine version constraint
php: ">=8.3" // PHP version constraint
)]
final class BlogModule
{
// Module implementation
}
Specifies the source code repository for the module, used by the PackageManager.
use Forge\Core\Module\Attributes\Repository;
#[Repository(
type: "git", // Repository type (git, svn, etc.)
url: "https://github.com/forge-engine/forge-blog.git" // Repository URL
)]
final class BlogModule
{
// Module implementation
}
Declares that this module provides implementations for specific interfaces. Can be repeated multiple times.
use Forge\Core\Module\Attributes\Provides;
#[Provides(interface: BlogServiceInterface::class, version: "1.0.0")]
#[Provides(interface: PostRepositoryInterface::class, version: "1.0.0")]
#[Provides(interface: CommentServiceInterface::class, version: "1.0.0")]
final class BlogModule
{
// Module provides implementations for these interfaces
}
Declares dependencies on other modules or interfaces. Can be used on the class or specific methods.
use Forge\Core\Module\Attributes\Requires;
#[Requires] // Class-level dependency (module requires other modules)
final class BlogModule
{
#[Requires] // Method-level dependency
public function register(Container $container): void
{
// This method requires other modules to be available
}
}
Defines default configuration values for the module. These values are merged with user configuration.
use Forge\Core\Module\Attributes\ConfigDefaults;
#[ConfigDefaults([
'posts_per_page' => 10,
'allow_comments' => true,
'moderation_required' => true,
'seo_enabled' => true,
'cache_ttl' => 3600
])]
final class BlogModule
{
// Module with default configuration
}
Example from ForgePackageManager module:
#[ConfigDefaults([
'repositories' => [
'packagist' => [
'type' => 'composer',
'url' => 'https://repo.packagist.org'
]
],
'cache_ttl' => 3600,
'auto_update' => false
])]
final class ForgePackageManager
{
// Package manager with default repository configuration
}
Defines a CLI command that this module provides. Used to register commands with the CLI system.
use Forge\Core\Module\Attributes\CLICommand;
#[CLICommand(
name: "blog:install", // Command name
description: "Install the blog module" // Command description
)]
final class InstallCommand
{
// CLI command implementation
}
Defines navigation items for the admin hub interface. Can be repeated multiple times for multiple menu items.
use Forge\Core\Module\Attributes\HubItem;
use Forge\Core\Module\ForgeIcon;
#[HubItem(
label: "Blog Posts", // Menu item label
route: "admin.blog.posts", // Route name
icon: ForgeIcon::POSTS, // Icon constant
order: 10, // Menu order (lower appears first)
permissions: ["blog.manage"] // Required permissions
)]
#[HubItem(
label: "Categories",
route: "admin.blog.categories",
icon: ForgeIcon::CATEGORIES,
order: 20,
permissions: ["blog.manage"]
)]
final class BlogModule
{
// Module adds navigation items to admin hub
}
Marks a method to be called at specific points in the application lifecycle.
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Called after all modules have been registered
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
public function onAfterBoot(): void
{
// Called after the application has fully booted
}
#[LifecycleHook(hook: LifecycleHookName::BEFORE_REQUEST)]
public function onBeforeRequest(): void
{
// Called before each HTTP request
}
Central manager for module discovery, loading, and lifecycle management. The ModuleManager handles dependency resolution, module ordering, and provides methods for enabling, disabling, and updating modules.
Forge Engine uses a ModuleLoader to discover, load, and manage modules throughout the application lifecycle. Modules are loaded based on their forge.json configuration files.
Modules are automatically discovered by scanning the /modules directory for forge.json files.
// The ModuleLoader automatically discovers modules in the modules directory
$loader = new ModuleLoader($this->container);
// Modules are discovered based on forge.json files
// Example forge.json structure:
{
"$schema": "engine/Core/Schema/module-schema.json",
"name": "ForgeBlog",
"version": "1.0.0",
"description": "Blog functionality for Forge Engine",
"type": "module",
"order": 10,
"provides": ["blog", "posts"],
"requires": {
"ForgeAuth": "^1.0.0",
"ForgeUI": "^1.0.0"
},
"lifecycleHooks": {
"register": "register",
"afterModuleRegister": "onAfterModuleRegister"
},
"class": "Modules\\ForgeBlog\\ForgeBlogModule",
"cli": {
"commands": {
"blog:install": "Modules\\ForgeBlog\\Commands\\InstallCommand"
}
},
"compatibility": {
"engine": "^3.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/forge-engine/forge-blog.git"
}
}
The ModuleLoader follows a specific process to load and register modules.
// In ModuleSetup.php - the bootstrap process
public function loadModules(): void
{
$loader = new ModuleLoader($this->container);
// 1. Discover all modules by scanning forge.json files
$modules = $loader->discoverModules();
// 2. Sort modules based on order and dependencies
$sortedModules = $loader->sortModulesByDependencies($modules);
// 3. Load each module class
foreach ($sortedModules as $moduleConfig) {
$moduleClass = $moduleConfig['class'];
$module = new $moduleClass();
// 4. Call register() method on each module
$module->register($this->container);
// 5. Store module instance for later use
$this->modules[$moduleConfig['name']] = $module;
}
// 6. Trigger AFTER_MODULE_LOAD lifecycle hook
$this->eventDispatcher->dispatch(LifecycleHookName::AFTER_MODULE_LOAD);
// 7. Call lifecycle hook methods on each module
foreach ($this->modules as $module) {
$this->callLifecycleHooks($module);
}
}
Modules are loaded in the correct order based on their dependencies.
// Example dependency resolution
$modules = [
'ForgeAuth' => ['order' => 1, 'provides' => ['auth']],
'ForgeUI' => ['order' => 2, 'provides' => ['ui', 'templates']],
'ForgeBlog' => ['order' => 10, 'requires' => ['auth', 'ui']]
];
// The loader will automatically:
// 1. Load ForgeAuth first (order 1, no dependencies)
// 2. Load ForgeUI second (order 2, no dependencies)
// 3. Load ForgeBlog last (order 10, depends on auth and ui)
// If circular dependencies are detected, an exception is thrown
// If required modules are missing, an exception is thrown
Module management in Forge Engine is handled through the PackageManager module and CLI commands, not through a programmatic API.
Use the Forge CLI to manage modules from the command line.
# List all available modules
php forge module:list
# Install a module
php forge module:install ForgeBlog
# Uninstall a module
php forge module:uninstall ForgeBlog
# Enable a module
php forge module:enable ForgeBlog
# Disable a module
php forge module:disable ForgeBlog
# Update a module
php forge module:update ForgeBlog
# Check module status
php forge module:status ForgeBlog
For programmatic module management, use the PackageManager service.
use Forge\Core\PackageManager\Services\PackageManagerService;
$packageManager = $container->make(PackageManagerService::class);
// Get all installed modules
$modules = $packageManager->getInstalledModules();
// Install a module from repository
$result = $packageManager->installModule('ForgeBlog', '1.0.0');
// Uninstall a module
$result = $packageManager->uninstallModule('ForgeBlog');
// Update a module
$result = $packageManager->updateModule('ForgeBlog', '2.0.0');
// Check if module is installed
$isInstalled = $packageManager->isModuleInstalled('ForgeBlog');
// Get module information
$moduleInfo = $packageManager->getModuleInfo('ForgeBlog');
Module configuration is managed through the forge.json file and can be accessed programmatically.
// Access module configuration from within a module
public function register(Container $container): void
{
// Get the current module's configuration
$moduleConfig = $this->getModuleConfig();
// Access specific configuration values
$postsPerPage = $moduleConfig['posts_per_page'] ?? 10;
$allowComments = $moduleConfig['allow_comments'] ?? true;
// Register configuration as a service
$container->bind('blog.config', function() use ($moduleConfig) {
return new Config($moduleConfig);
});
}
// Update module configuration programmatically
$packageManager = $container->make(PackageManagerService::class);
$packageManager->updateModuleConfig('ForgeBlog', [
'posts_per_page' => 20,
'allow_comments' => false
]);
Get information about loaded modules from the container or PackageManager.
use Forge\Core\PackageManager\Services\PackageManagerService;
$packageManager = $container->make(PackageManagerService::class);
// Get information about a specific module
$moduleInfo = $packageManager->getModuleInfo('ForgeBlog');
if ($moduleInfo) {
echo "Module: " . $moduleInfo['name'] . "\n";
echo "Version: " . $moduleInfo['version'] . "\n";
echo "Description: " . $moduleInfo['description'] . "\n";
echo "Type: " . $moduleInfo['type'] . "\n";
echo "Order: " . $moduleInfo['order'] . "\n";
echo "Provides: " . implode(', ', $moduleInfo['provides'] ?? []) . "\n";
echo "Requires: " . json_encode($moduleInfo['requires'] ?? []) . "\n";
echo "Author: " . ($moduleInfo['author'] ?? 'Unknown') . "\n";
echo "License: " . ($moduleInfo['license'] ?? 'Unknown') . "\n";
}
// Get all installed modules
$allModules = $packageManager->getInstalledModules();
foreach ($allModules as $moduleName => $moduleData) {
echo "Module: {$moduleName}\n";
echo " Version: {$moduleData['version']}\n";
echo " Description: {$moduleData['description']}\n";
echo " Status: " . ($moduleData['enabled'] ? 'Enabled' : 'Disabled') . "\n";
}
echo "\n";
}
Get the status of a module.
$status = $manager->getStatus('blog');
switch ($status) {
case 'installed':
echo "Module is installed and ready";
break;
case 'enabled':
echo "Module is enabled and active";
break;
case 'disabled':
echo "Module is disabled";
break;
case 'not_installed':
echo "Module is not installed";
break;
case 'error':
echo "Module has errors";
break;
}
Forge Engine uses a sophisticated module loading system that discovers modules through their forge.json configuration files and loads them using PHP 8 attributes. The system handles dependency resolution, lifecycle hooks, and module validation automatically.
Modules are automatically discovered by scanning the /modules directory for forge.json files.
// The ModuleLoader automatically discovers modules during bootstrap
// No manual discovery needed - just place your module in the /modules directory
// Example module structure:
/modules/
├── ForgeAuth/
│ ├── forge.json # Module configuration
│ ├── src/
│ │ └── ForgeAuthModule.php # Module class
│ └── ...
├── ForgeBlog/
│ ├── forge.json
│ ├── src/
│ │ └── ForgeBlogModule.php
│ └── ...
└── ForgeUI/
├── forge.json
├── src/
│ └── ForgeUIModule.php
└── ...
Modules are validated against the JSON schema during discovery.
// The ModuleLoader validates each forge.json against the schema
// engine/Core/Schema/module-schema.json
// Validation checks include:
// - Required fields (name, version, class)
// - Version format compliance
// - Dependency format
// - Lifecycle hook method existence
// - Class file accessibility
// If validation fails, the module is skipped and an error is logged
Modules are loaded in the correct order based on their dependencies and order values.
// Example dependency resolution process:
$modules = [
'ForgeAuth' => ['order' => 1, 'provides' => ['auth']],
'ForgeUI' => ['order' => 2, 'provides' => ['ui', 'templates']],
'ForgeBlog' => ['order' => 10, 'requires' => ['auth', 'ui']]
];
// The loader will:
// 1. Sort by order field (lower numbers first)
// 2. Resolve dependencies (required modules loaded first)
// 3. Detect circular dependencies
// 4. Throw exception if required modules are missing
// 5. Load modules in resolved order
Common patterns and examples for working with the Module System.
// Example ForgeBlogModule.php
namespace Modules\ForgeBlog;
use Forge\Core\Module\Attributes\Module;
use Forge\Core\Module\Attributes\Compatibility;
use Forge\Core\Module\Attributes\Repository;
use Forge\Core\Module\Attributes\Provides;
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[Module(
name: "ForgeBlog",
version: "1.0.0",
description: "Blog functionality for Forge Engine",
order: 10,
core: false,
isCli: false
)]
#[Compatibility(engine: "^3.0.0")]
#[Repository(type: "git", url: "https://github.com/forge-engine/forge-blog.git")]
#[Provides("blog")]
#[Provides("posts")]
#[Requires("ForgeAuth", "^1.0.0")]
#[Requires("ForgeUI", "^1.0.0")]
class ForgeBlogModule
{
public function register(Container $container): void
{
// Register blog services
$container->bind(BlogService::class, BlogService::class);
$container->bind(PostRepository::class, PostRepository::class);
// Register configuration
$container->bind('blog.config', function() {
return new Config([
'posts_per_page' => 10,
'allow_comments' => true,
'moderation_required' => true,
'seo_enabled' => true
]);
});
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Register routes, event listeners, etc.
// This is called after all modules are registered
$this->registerRoutes();
$this->registerEventListeners();
}
private function registerRoutes(): void
{
// Register blog routes
$router = Container::getInstance()->make('router');
$router->get('/blog', [BlogController::class, 'index']);
$router->get('/blog/{slug}', [BlogController::class, 'show']);
}
private function registerEventListeners(): void
{
$events = Container::getInstance()->make('events');
$events->listen('blog.post.created', function($post) {
// Handle post creation
});
}
}
// forge.json - Module configuration file
{
"name": "ForgeBlog",
"version": "1.0.0",
"description": "Blog functionality for Forge Engine",
"type": "module",
"keywords": ["blog", "posts", "content"],
"homepage": "https://github.com/forge-engine/forge-blog",
"license": "MIT",
"authors": [
{
"name": "Forge Engine Team",
"email": "team@forge-engine.com"
}
],
"support": {
"issues": "https://github.com/forge-engine/forge-blog/issues",
"source": "https://github.com/forge-engine/forge-blog"
},
"require": {
"php": "^8.1",
"forge-engine/core": "^3.0.0",
"forge-engine/forge-auth": "^1.0.0",
"forge-engine/forge-ui": "^1.0.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0"
},
"autoload": {
"psr-4": {
"Modules\\ForgeBlog\\": "src/"
}
},
"extra": {
"forge": {
"class": "Modules\\ForgeBlog\\ForgeBlogModule",
"order": 10,
"core": false,
"isCli": false,
"provides": ["blog", "posts"],
"compatibility": {
"engine": "^3.0.0"
}
}
}
}
// ForgeBlogModule.php - Using attributes for dependencies
use Forge\Core\Module\Attributes\Requires;
use Forge\Core\Module\Attributes\Provides;
#[Module(
name: "ForgeBlog",
version: "1.0.0",
description: "Blog functionality for Forge Engine"
)]
#[Requires("ForgeAuth", "^1.0.0")] // Requires auth module
#[Requires("ForgeUI", "^1.0.0")] // Requires UI module
#[Requires("ForgeDatabase", "^1.0.0")] // Requires database module
#[Provides("blog")] // Provides blog functionality
#[Provides("posts")] // Provides posts functionality
class ForgeBlogModule
{
public function register(Container $container): void
{
// Services are automatically available through dependency injection
$authService = $container->make('ForgeAuth');
$uiService = $container->make('ForgeUI');
$dbService = $container->make('ForgeDatabase');
// Register blog services with dependencies
$container->bind(BlogService::class, function($container) use ($authService, $dbService) {
return new BlogService(
$container->make(PostRepository::class),
$authService,
$dbService
);
});
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Dependencies are guaranteed to be loaded at this point
$this->registerBlogRoutes();
$this->setupBlogDatabase();
}
private function registerBlogRoutes(): void
{
// Use services from required modules via dependency injection
$auth = Container::getInstance()->make('ForgeAuth');
$router = Container::getInstance()->make('router');
// Register protected blog routes
$router->get('/blog', [BlogController::class, 'index']);
$router->get('/blog/{slug}', [BlogController::class, 'show']);
// Admin routes (require auth)
$router->group(['middleware' => $auth->getAuthMiddleware()], function($router) {
$router->get('/admin/blog', [AdminBlogController::class, 'index']);
$router->post('/admin/blog/posts', [AdminBlogController::class, 'create']);
});
}
private function setupBlogDatabase(): void
{
$db = Container::getInstance()->make('ForgeDatabase');
// Create blog tables using the database service
$db->createTable('blog_posts', function($table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->foreignId('author_id')->constrained('users');
$table->timestamps();
});
}
}
// ForgeBlogModule.php - Using lifecycle hooks and events
use Forge\Core\Module\Attributes\LifecycleHook;
use Forge\Core\Module\LifecycleHookName;
#[Module(name: "ForgeBlog", version: "1.0.0")]
class ForgeBlogModule
{
#[LifecycleHook(hook: LifecycleHookName::AFTER_BOOT)]
public function onAfterBoot(): void
{
// Register module-level event listeners
$events = Container::getInstance()->make('events');
// Listen for module system events
$events->listen('module.installed', function($moduleName) {
if ($moduleName === 'ForgeBlog') {
$this->onBlogModuleInstalled();
}
});
$events->listen('module.enabled', function($moduleName) {
if ($moduleName === 'ForgeBlog') {
$this->onBlogModuleEnabled();
}
});
$events->listen('module.disabled', function($moduleName) {
if ($moduleName === 'ForgeBlog') {
$this->onBlogModuleDisabled();
}
});
}
#[LifecycleHook(hook: LifecycleHookName::AFTER_MODULE_REGISTER)]
public function onAfterModuleRegister(): void
{
// Register blog-specific event listeners
$events = Container::getInstance()->make('events');
// Blog post events
$events->listen('blog.post.created', function($post) {
$this->clearPostCache($post->id);
$this->notifySubscribers($post);
$this->updateSearchIndex($post);
});
$events->listen('blog.post.updated', function($post) {
$this->clearRelatedCaches($post);
$this->updateSearchIndex($post);
});
$events->listen('blog.post.deleted', function($post) {
$this->removeFromSearchIndex($post);
$this->cleanupMedia($post);
$this->clearPostCache($post->id);
});
// Category events
$events->listen('blog.category.created', function($category) {
$this->clearCategoryCache($category->id);
});
// Comment events
$events->listen('blog.comment.created', function($comment) {
$this->notifyPostAuthor($comment);
$this->moderateComment($comment);
});
}
private function onBlogModuleInstalled(): void
{
// Create default blog settings
$config = Container::getInstance()->make('config');
$config->set('blog.settings', [
'posts_per_page' => 10,
'allow_comments' => true,
'moderation_required' => true
]);
// Log installation
Container::getInstance()->make('log')->info('ForgeBlog module installed successfully');
}
private function onBlogModuleEnabled(): void
{
// Clear blog-related caches
Container::getInstance()->make('cache')->forget('blog.posts');
Container::getInstance()->make('cache')->forget('blog.categories');
// Rebuild search index
$this->rebuildSearchIndex();
// Log enablement
Container::getInstance()->make('log')->info('ForgeBlog module enabled');
}
private function onBlogModuleDisabled(): void
{
// Clear all blog caches
$this->clearAllBlogCaches();
// Log disablement
Container::getInstance()->make('log')->info('ForgeBlog module disabled');
}
// Helper methods for event handling
private function clearPostCache($postId): void
{
Container::getInstance()->make('cache')->forget("blog.post.{$postId}");
Container::getInstance()->make('cache')->forget("blog.post.{$postId}.html");
}
private function notifySubscribers($post): void
{
$notificationService = Container::getInstance()->make('notification');
$subscribers = $this->getPostSubscribers($post->id);
foreach ($subscribers as $subscriber) {
$notificationService->send($subscriber, 'New blog post: ' . $post->title);
}
}
private function updateSearchIndex($post): void
{
$searchService = Container::getInstance()->make('search');
$searchService->index('blog_posts', $post->id, [
'title' => $post->title,
'content' => $post->content,
'tags' => $post->tags,
'category' => $post->category->name
]);
}
}
Recommended patterns and guidelines for working with the Module System.