Module Provider API
The Flute CMS Module API provides an interface for managing the module lifecycle via ModuleManager and ModuleServiceProvider.
ModuleManager
ModuleManager is the central class for managing modules. It is automatically initialized upon first access: collects module.json, synchronizes statuses with DB, sorts providers, and checks dependencies.
Getting Instance
<?php
use Flute\Core\ModulesManager\ModuleManager;
// Via DI container
$moduleManager = app(ModuleManager::class);
// Via helper
$moduleManager = modules();Main Methods
<?php
// Get all modules (including disabled)
$allModules = $moduleManager->getModules();
// Get only active modules
$activeModules = $moduleManager->getActive();
// Get specific module
$module = $moduleManager->getModule('News');
// Check if module exists
$exists = $moduleManager->issetModule('Shop');
// Get module JSON data
$moduleJson = $moduleManager->getModuleJson('BansManager');
// Clear module cache
$moduleManager->clearCache();
// Refresh module list
$moduleManager->refreshModules();
// Module boot times (for profiling)
$bootTimes = $moduleManager->getModulesBootTimes();ModuleInformation
Each module is represented by a ModuleInformation object with the following properties:
<?php
$module = $moduleManager->getModule('News');
// Available properties
$module->key; // Module key: 'News'
$module->name; // Name: 'News'
$module->description; // Module description
$module->version; // Version: '1.0.0'
$module->status; // Status: 'active', 'disabled', 'notinstalled'
$module->createdAt; // Installation date
$module->dependencies; // Array of dependencies
$module->providers; // Array of providersModule Statuses
| Status | Constant | Description |
|---|---|---|
| Active | ModuleManager::ACTIVE | Module is installed and working |
| Disabled | ModuleManager::DISABLED | Module is installed but disabled |
| Not Installed | ModuleManager::NOTINSTALLED | Module is not installed |
Checking Status
<?php
class ModuleInfoService
{
protected ModuleManager $moduleManager;
public function __construct(ModuleManager $moduleManager)
{
$this->moduleManager = $moduleManager;
}
/**
* Check if module is active
*/
public function isModuleActive(string $moduleName): bool
{
$activeModules = $this->moduleManager->getActive();
return $activeModules->has($moduleName);
}
/**
* Get module status
*/
public function getModuleStatus(string $moduleName): string
{
if (!$this->moduleManager->issetModule($moduleName)) {
return ModuleManager::NOTINSTALLED;
}
$module = $this->moduleManager->getModule($moduleName);
return $module->status;
}
/**
* Get module information
*/
public function getModuleInfo(string $moduleName): ?ModuleInformation
{
try {
return $this->moduleManager->getModule($moduleName);
} catch (\Exception $e) {
return null;
}
}
}Working with Dependencies
<?php
use Flute\Core\ModulesManager\ModuleDependencies;
$dependencyChecker = $moduleManager->getModuleDependencies();
$module = $moduleManager->getModule('Shop');
$activeModules = $moduleManager->getActive();
$themeInfo = app(\Flute\Core\Theme\ThemeManager::class)->getThemeInfo();
try {
// Check all module dependencies
$dependencyChecker->checkDependencies(
$module->dependencies,
$activeModules,
$themeInfo
);
echo "All dependencies satisfied";
} catch (\Flute\Core\ModulesManager\Exceptions\ModuleDependencyException $e) {
echo "Dependency error: " . $e->getMessage();
}In the normal boot cycle, ModuleManager automatically checks dependencies and disables problematic modules.
Composer Integration
<?php
// Run composer install for specific module
// (if app/Modules/<Module>/composer.json exists)
$moduleManager->runComposerInstall($module);
// Force install for the entire project
$moduleManager->runComposerInstall(null, true);ModuleServiceProvider
ModuleServiceProvider is the base class for all module providers. It provides methods for loading module resources.
Basic Provider Structure
<?php
namespace Flute\Modules\News\Providers;
use Flute\Core\Support\ModuleServiceProvider;
class NewsProvider extends ModuleServiceProvider
{
/**
* List of extensions (optional)
*/
public array $extensions = [];
/**
* Register services in DI container
* Called first, before boot()
*/
public function register(\DI\Container $container): void
{
// Register services
$container->set(NewsService::class, \DI\autowire());
}
/**
* Load module resources
* Called after register()
*/
public function boot(\DI\Container $container): void
{
// Automatically load all resources
$this->bootstrapModule();
// Load views
$this->loadViews('Resources/views', 'news');
// Load styles
$this->loadScss('Resources/assets/scss/news.scss');
// Conditionally load admin package
if (is_admin_path() && user()->can('admin')) {
$this->loadPackage(new NewsAdminPackage());
}
}
}Resource Loading Methods
bootstrapModule()
Automatically loads all standard module resources:
<?php
$this->bootstrapModule();
// Equivalent to calling:
// $this->loadEntities(); - DB entities
// $this->loadConfigs(); - configuration
// $this->loadTranslations(); - translations
// $this->loadRouterAttributes();- routes from attributes
// $this->loadComponents(); - components
// $this->loadWidgets(); - widgetsbootstrapModule() caches results for defaultCacheDuration to optimize performance.
Loading Individual Resources
<?php
// Load database entities from database/Entities
$this->loadEntities();
// Load configuration from Resources/config
$this->loadConfigs();
// Load translations from Resources/lang
$this->loadTranslations();
// Load routes via attributes from Http/Controllers
$this->loadRouterAttributes();
// Load components from Components/
$this->loadComponents();
// Load widgets from Widgets/
$this->loadWidgets();Views and Styles
<?php
// Load views
// First argument — path relative to module
// Second — namespace for use in view()
$this->loadViews('Resources/views', 'news');
// Now you can use:
// view('news::articles.index')
// view('news::partials.sidebar')
// Load SCSS files
$this->loadScss('Resources/assets/scss/style.scss');
// Load SCSS for admin panel
if (is_admin_path()) {
template()->getTemplateAssets()->addScssFile(
$this->getModulePath('Resources/assets/scss/admin.scss'),
'admin'
);
}Components
<?php
// Load one component
$this->loadComponent(NewsCardComponent::class, 'news-card');
// Register multiple components
$this->registerComponents([
'news-card' => NewsCardComponent::class,
'news-list' => NewsListComponent::class,
'news-featured' => NewsFeaturedComponent::class,
]);Admin Packages
<?php
// Load admin package
$this->loadPackage(new NewsAdminPackage());
// Conditional load only for admin panel
if (is_admin_path()) {
$this->loadPackage(new NewsAdminPackage());
}Routes
<?php
// Load routes.php manually (not called automatically)
$this->loadRoutes();
// File must be: Resources/routes/routes.phpGetting Paths
<?php
// Get absolute path to module file
$configPath = $this->getModulePath('Resources/config/settings.php');
// Result: /var/www/app/Modules/News/Resources/config/settings.phpPractical Examples
Full-Featured Provider
<?php
namespace Flute\Modules\Shop\Providers;
use Flute\Core\Support\ModuleServiceProvider;
use Flute\Modules\Shop\Services\ProductService;
use Flute\Modules\Shop\Services\CartService;
use Flute\Modules\Shop\Admin\Package\ShopPackage;
class ShopProvider extends ModuleServiceProvider
{
public function register(\DI\Container $container): void
{
// Register services
$container->set(ProductService::class, \DI\autowire());
$container->set(CartService::class, \DI\autowire());
// Register interfaces
$container->set(ProductServiceInterface::class, \DI\get(ProductService::class));
}
public function boot(\DI\Container $container): void
{
// Basic load
$this->bootstrapModule();
// Views and styles
$this->loadViews('Resources/views', 'shop');
$this->loadScss('Resources/assets/scss/shop.scss');
// External libraries
template()->addStyle(url('assets/css/libs/swiper.min.css'));
template()->addScript(url('assets/js/libs/swiper.js'));
// Components
$this->registerComponents([
'product-card' => ProductCardComponent::class,
'cart-widget' => CartWidgetComponent::class,
]);
// Event listeners
events()->addListener(
UserRegisteredEvent::NAME,
[UserRegistrationListener::class, 'handle']
);
// Admin package
if (is_admin_path()) {
$this->loadPackage(new ShopPackage());
}
}
}Provider with Conditional Loading
<?php
namespace Flute\Modules\BansManager\Providers;
use Flute\Core\Support\ModuleServiceProvider;
class BansManagerProvider extends ModuleServiceProvider
{
public function boot(\DI\Container $container): void
{
// Base resources loaded always
$this->loadEntities();
$this->loadConfigs();
$this->loadTranslations();
// Main service
$container->set(BansManager::class, \DI\autowire());
// Frontend resources only for frontend
if (!is_admin_path()) {
$this->loadViews('Resources/views', 'bans-manager');
$this->loadScss('Resources/assets/scss/main.scss');
$this->loadRouterAttributes();
// Components for frontend
$this->registerComponents([
'bans-table' => BansTableComponent::class,
'mutes-table' => MutesTableComponent::class,
]);
}
// Admin resources only for admin panel
if (is_admin_path()) {
$this->loadPackage(new BansManagerPackage());
template()->getTemplateAssets()->addScssFile(
$this->getModulePath('Resources/assets/scss/admin.scss'),
'admin'
);
}
// Register drivers
$bansManager = $container->get(BansManager::class);
$bansManager->registerDriver('SourceBans', SourceBansDriver::class);
}
}Error Handling
<?php
public function boot(\DI\Container $container): void
{
try {
$this->bootstrapModule();
$this->loadCustomResources();
} catch (\Exception $e) {
// Log error
logs('modules')->error("Error loading module: " . $e->getMessage());
// In debug mode throw exception
if (is_debug()) {
throw $e;
}
// In production continue working without this module
}
}Best Practices
Separate register() and boot()
register()— only register services in DI containerboot()— load resources, register listeners
Use Conditional Loading
Load resources only when needed:
if (is_admin_path()) {
$this->loadPackage(new AdminPackage());
}Follow Loading Order
- Configuration
- DB Entities
- Translations
- Routes
- Components and widgets
- Event listeners
Cache Heavy Operations
bootstrapModule() automatically caches directory scanning results.
Log Errors
Always log initialization errors for easier debugging.
Working with DI Container
Registering Services
<?php
public function register(\DI\Container $container): void
{
// Simple registration with autowiring
$container->set(MyService::class, \DI\autowire());
// Interface registration
$container->set(MyServiceInterface::class, \DI\get(MyService::class));
// Registration with alias
$container->set('my_service', \DI\get(MyService::class));
// Registration with factory
$container->set(MyService::class, function() {
return new MyService(config('my_module.setting'));
});
}Getting Services
<?php
// Via DI container
$service = app(MyService::class);
// Via alias
$service = app('my_service');
// In constructor (autowiring)
public function __construct(MyService $service)
{
$this->service = $service;
}