API провайдера модулей
API модулей Flute CMS предоставляет интерфейс для управления жизненным циклом модулей через ModuleManager и ModuleServiceProvider.
ModuleManager
ModuleManager — центральный класс для управления модулями. Он автоматически инициализируется при первом обращении: собирает module.json, синхронизирует статусы с БД, сортирует провайдеры и проверяет зависимости.
Получение экземпляра
<?php
use Flute\Core\ModulesManager\ModuleManager;
// Через DI контейнер
$moduleManager = app(ModuleManager::class);
// Через хелпер
$moduleManager = modules();Основные методы
<?php
// Получение всех модулей (включая отключённые)
$allModules = $moduleManager->getModules();
// Получение только активных модулей
$activeModules = $moduleManager->getActive();
// Получение конкретного модуля
$module = $moduleManager->getModule('News');
// Проверка существования модуля
$exists = $moduleManager->issetModule('Shop');
// Получение JSON данных модуля
$moduleJson = $moduleManager->getModuleJson('BansManager');
// Очистка кеша модулей
$moduleManager->clearCache();
// Обновление списка модулей
$moduleManager->refreshModules();
// Время загрузки модулей (для профилирования)
$bootTimes = $moduleManager->getModulesBootTimes();ModuleInformation
Каждый модуль представлен объектом ModuleInformation со следующими свойствами:
<?php
$module = $moduleManager->getModule('News');
// Доступные свойства
$module->key; // Ключ модуля: 'News'
$module->name; // Название: 'News'
$module->description; // Описание модуля
$module->version; // Версия: '1.0.0'
$module->status; // Статус: 'active', 'disabled', 'notinstalled'
$module->createdAt; // Дата установки
$module->dependencies; // Массив зависимостей
$module->providers; // Массив провайдеровСтатусы модулей
| Статус | Константа | Описание |
|---|---|---|
| Активен | ModuleManager::ACTIVE | Модуль установлен и работает |
| Отключён | ModuleManager::DISABLED | Модуль установлен, но отключён |
| Не установлен | ModuleManager::NOTINSTALLED | Модуль не установлен |
Проверка статуса
<?php
class ModuleInfoService
{
protected ModuleManager $moduleManager;
public function __construct(ModuleManager $moduleManager)
{
$this->moduleManager = $moduleManager;
}
/**
* Проверка активности модуля
*/
public function isModuleActive(string $moduleName): bool
{
$activeModules = $this->moduleManager->getActive();
return $activeModules->has($moduleName);
}
/**
* Получение статуса модуля
*/
public function getModuleStatus(string $moduleName): string
{
if (!$this->moduleManager->issetModule($moduleName)) {
return ModuleManager::NOTINSTALLED;
}
$module = $this->moduleManager->getModule($moduleName);
return $module->status;
}
/**
* Получение информации о модуле
*/
public function getModuleInfo(string $moduleName): ?ModuleInformation
{
try {
return $this->moduleManager->getModule($moduleName);
} catch (\Exception $e) {
return null;
}
}
}Работа с зависимостями
<?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 {
// Проверка всех зависимостей модуля
$dependencyChecker->checkDependencies(
$module->dependencies,
$activeModules,
$themeInfo
);
echo "Все зависимости выполнены";
} catch (\Flute\Core\ModulesManager\Exceptions\ModuleDependencyException $e) {
echo "Ошибка зависимостей: " . $e->getMessage();
}В обычном цикле загрузки ModuleManager автоматически проверяет зависимости и отключает проблемные модули.
Composer интеграция
<?php
// Запуск composer install для конкретного модуля
// (если есть app/Modules/<Module>/composer.json)
$moduleManager->runComposerInstall($module);
// Принудительный install для всего проекта
$moduleManager->runComposerInstall(null, true);ModuleServiceProvider
ModuleServiceProvider — базовый класс для всех провайдеров модулей. Он предоставляет методы для загрузки ресурсов модуля.
Базовая структура провайдера
<?php
namespace Flute\Modules\News\Providers;
use Flute\Core\Support\ModuleServiceProvider;
class NewsProvider extends ModuleServiceProvider
{
/**
* Список расширений (опционально)
*/
public array $extensions = [];
/**
* Регистрация сервисов в DI контейнере
* Вызывается первым, до boot()
*/
public function register(\DI\Container $container): void
{
// Регистрация сервисов
$container->set(NewsService::class, \DI\autowire());
}
/**
* Загрузка ресурсов модуля
* Вызывается после register()
*/
public function boot(\DI\Container $container): void
{
// Автоматическая загрузка всех ресурсов
$this->bootstrapModule();
// Загрузка представлений
$this->loadViews('Resources/views', 'news');
// Загрузка стилей
$this->loadScss('Resources/assets/scss/news.scss');
// Условная загрузка админ-пакета
if (is_admin_path() && user()->can('admin')) {
$this->loadPackage(new NewsAdminPackage());
}
}
}Методы загрузки ресурсов
bootstrapModule()
Автоматически загружает все стандартные ресурсы модуля:
<?php
$this->bootstrapModule();
// Эквивалентно вызову:
// $this->loadEntities(); - сущности БД
// $this->loadConfigs(); - конфигурация
// $this->loadTranslations(); - переводы
// $this->loadRouterAttributes();- маршруты из атрибутов
// $this->loadComponents(); - компоненты
// $this->loadWidgets(); - виджетыbootstrapModule() кеширует результаты на время defaultCacheDuration для оптимизации.
Загрузка отдельных ресурсов
<?php
// Загрузка сущностей базы данных из database/Entities
$this->loadEntities();
// Загрузка конфигурации из Resources/config
$this->loadConfigs();
// Загрузка переводов из Resources/lang
$this->loadTranslations();
// Загрузка маршрутов через атрибуты из Http/Controllers
$this->loadRouterAttributes();
// Загрузка компонентов из Components/
$this->loadComponents();
// Загрузка виджетов из Widgets/
$this->loadWidgets();Представления и стили
<?php
// Загрузка представлений
// Первый аргумент — путь относительно модуля
// Второй — namespace для использования в view()
$this->loadViews('Resources/views', 'news');
// Теперь можно использовать:
// view('news::articles.index')
// view('news::partials.sidebar')
// Загрузка SCSS файлов
$this->loadScss('Resources/assets/scss/style.scss');
// Загрузка SCSS для админки
if (is_admin_path()) {
template()->getTemplateAssets()->addScssFile(
$this->getModulePath('Resources/assets/scss/admin.scss'),
'admin'
);
}Компоненты
<?php
// Загрузка одного компонента
$this->loadComponent(NewsCardComponent::class, 'news-card');
// Регистрация нескольких компонентов
$this->registerComponents([
'news-card' => NewsCardComponent::class,
'news-list' => NewsListComponent::class,
'news-featured' => NewsFeaturedComponent::class,
]);Админ-пакеты
<?php
// Загрузка админ-пакета
$this->loadPackage(new NewsAdminPackage());
// Условная загрузка только для админки
if (is_admin_path()) {
$this->loadPackage(new NewsAdminPackage());
}Маршруты
<?php
// Загрузка routes.php вручную (не вызывается автоматически)
$this->loadRoutes();
// Файл должен быть: Resources/routes/routes.phpПолучение путей
<?php
// Получение абсолютного пути к файлу модуля
$configPath = $this->getModulePath('Resources/config/settings.php');
// Результат: /var/www/app/Modules/News/Resources/config/settings.phpПрактические примеры
Полноценный провайдер
<?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
{
// Регистрация сервисов
$container->set(ProductService::class, \DI\autowire());
$container->set(CartService::class, \DI\autowire());
// Регистрация интерфейсов
$container->set(ProductServiceInterface::class, \DI\get(ProductService::class));
}
public function boot(\DI\Container $container): void
{
// Базовая загрузка
$this->bootstrapModule();
// Представления и стили
$this->loadViews('Resources/views', 'shop');
$this->loadScss('Resources/assets/scss/shop.scss');
// Внешние библиотеки
template()->addStyle(url('assets/css/libs/swiper.min.css'));
template()->addScript(url('assets/js/libs/swiper.js'));
// Компоненты
$this->registerComponents([
'product-card' => ProductCardComponent::class,
'cart-widget' => CartWidgetComponent::class,
]);
// Слушатели событий
events()->addListener(
UserRegisteredEvent::NAME,
[UserRegistrationListener::class, 'handle']
);
// Админ-пакет
if (is_admin_path()) {
$this->loadPackage(new ShopPackage());
}
}
}Провайдер с условной загрузкой
<?php
namespace Flute\Modules\BansManager\Providers;
use Flute\Core\Support\ModuleServiceProvider;
class BansManagerProvider extends ModuleServiceProvider
{
public function boot(\DI\Container $container): void
{
// Базовые ресурсы загружаем всегда
$this->loadEntities();
$this->loadConfigs();
$this->loadTranslations();
// Основной сервис
$container->set(BansManager::class, \DI\autowire());
// Фронтенд ресурсы только для фронтенда
if (!is_admin_path()) {
$this->loadViews('Resources/views', 'bans-manager');
$this->loadScss('Resources/assets/scss/main.scss');
$this->loadRouterAttributes();
// Компоненты для фронтенда
$this->registerComponents([
'bans-table' => BansTableComponent::class,
'mutes-table' => MutesTableComponent::class,
]);
}
// Админ ресурсы только для админки
if (is_admin_path()) {
$this->loadPackage(new BansManagerPackage());
template()->getTemplateAssets()->addScssFile(
$this->getModulePath('Resources/assets/scss/admin.scss'),
'admin'
);
}
// Регистрация драйверов
$bansManager = $container->get(BansManager::class);
$bansManager->registerDriver('SourceBans', SourceBansDriver::class);
}
}Обработка ошибок
<?php
public function boot(\DI\Container $container): void
{
try {
$this->bootstrapModule();
$this->loadCustomResources();
} catch (\Exception $e) {
// Логируем ошибку
logs('modules')->error("Ошибка загрузки модуля: " . $e->getMessage());
// В debug режиме пробрасываем исключение
if (is_debug()) {
throw $e;
}
// В production продолжаем работу без этого модуля
}
}Лучшие практики
Разделяйте register() и boot()
register()— только регистрация сервисов в DI контейнереboot()— загрузка ресурсов, регистрация слушателей
Используйте условную загрузку
Загружайте ресурсы только когда они нужны:
if (is_admin_path()) {
$this->loadPackage(new AdminPackage());
}Следуйте порядку загрузки
- Конфигурация
- Сущности БД
- Переводы
- Маршруты
- Компоненты и виджеты
- Слушатели событий
Кешируйте тяжёлые операции
bootstrapModule() автоматически кеширует результаты сканирования директорий.
Логируйте ошибки
Всегда логируйте ошибки инициализации для упрощения отладки.
Работа с DI контейнером
Регистрация сервисов
<?php
public function register(\DI\Container $container): void
{
// Простая регистрация с autowiring
$container->set(MyService::class, \DI\autowire());
// Регистрация интерфейса
$container->set(MyServiceInterface::class, \DI\get(MyService::class));
// Регистрация с алиасом
$container->set('my_service', \DI\get(MyService::class));
// Регистрация с фабрикой
$container->set(MyService::class, function() {
return new MyService(config('my_module.setting'));
});
}Получение сервисов
<?php
// Через DI контейнер
$service = app(MyService::class);
// Через алиас
$service = app('my_service');
// В конструкторе (autowiring)
public function __construct(MyService $service)
{
$this->service = $service;
}