Skip to Content
ModulesModule Structure

Module Structure

A module in Flute CMS is a standalone package that adds new functionality. Each module resides in a separate folder within app/Modules/ and has a standard structure.

File Structure

A typical module looks like this:

    • module.json
      • BlogProvider.php

What Goes Where

DirectoryPurpose
Providers/Module entry point. Services are registered and resources are loaded here.
Http/Controllers/Controllers handling HTTP requests. Routes are defined via attributes.
database/Entities/Entities for working with the database via Cycle ORM.
Resources/config/Module settings. Available via config('filename.key').
Resources/lang/Translations for different languages.
Resources/views/Blade templates for displaying pages.
Components/Interactive Yoyo components (work without page reload).
Widgets/Blocks that can be placed on pages via the admin panel.
Services/Module business logic (optional folder).
Middleware/Request filters (permission checks, validation, etc.).

Not all folders are mandatory. Minimally, only module.json and Providers/ are required.


Module Manifest (module.json)

Every module must have a module.json file in the root. This is the module’s “passport” — without it, the system won’t know about the module.

Minimal Example

{ "name": "Blog", "version": "1.0.0", "description": "Blog module for publishing articles", "providers": [ "Flute\\Modules\\Blog\\Providers\\BlogProvider" ] }

Full Example with Dependencies

If your module requires a specific PHP version, other modules, or composer packages:

{ "name": "Shop", "version": "2.0.0", "description": "Online store with cart and checkout", "authors": ["Ivan Petrov"], "providers": [ "Flute\\Modules\\Shop\\Providers\\ShopProvider" ], "dependencies": { "php": ">=8.2", "flute": ">=1.0.0", "modules": { "Payments": ">=1.0.0" }, "extensions": ["curl", "gd"], "composer": { "guzzlehttp/guzzle": "^7.0" } } }

Dependency Options

TypeDescriptionExample
phpMinimum PHP version">=8.2"
fluteMinimum Flute CMS version">=1.0.0"
modulesOther modules that must be active{"Payments": ">=1.0.0"}
extensionsPHP extensions["curl", "gd"]
composerComposer packages{"guzzlehttp/guzzle": "^7.0"}
themeRequired theme{"standard": ">=1.0.0"}

If dependencies are not met, the module is automatically disabled. Check logs if the module isn’t working.


Module Provider

The provider is the main class of the module. It tells the system which services to register and which resources to load.

Basic Provider

<?php namespace Flute\Modules\Blog\Providers; use Flute\Core\Support\ModuleServiceProvider; class BlogProvider extends ModuleServiceProvider { /** * Module name (must match folder name) */ protected ?string $moduleName = 'Blog'; /** * Service registration. * Called ALWAYS, even if the module is disabled. * Register classes in the DI container here. */ public function register(\DI\Container $container): void { // Register service for working with articles $container->set(ArticleService::class, \DI\autowire()); } /** * Module initialization. * Called ONLY for active modules. * Load resources and configure the module here. */ public function boot(\DI\Container $container): void { // Load all resources with one command $this->bootstrapModule(); } }

What bootstrapModule() Does

The bootstrapModule() method is “magic” that automatically finds and loads all module resources:

Database Entities

Looks for classes in database/Entities/ and registers them in Cycle ORM. After that, you can work with the database via rep(Article::class)->findAll().

Configuration

Files from Resources/config/ become available via config(). For example, blog.php is available as config('blog.posts_per_page').

Translations

Files from Resources/lang/ru/messages.php are registered for localization. Use __('messages.welcome') in code.

Controllers with Routes

Scans folders Http/Controllers/, Controllers/, and Submodules/*/Controllers/. Routes are taken from #[Get], #[Post] attributes, etc.

Yoyo Components

Classes from Components/ are automatically registered. Component ArticleCard.php becomes available as @yoyo('article-card').

Widgets

Classes from Widgets/ implementing WidgetInterface can be used on pages.

If you need a routes.php file for manual route definition, add to boot():

$this->loadRoutesFrom('routes.php');

Manual Resource Loading

Sometimes you need more precise control. Instead of bootstrapModule(), you can load resources individually:

public function boot(\DI\Container $container): void { // Only DB entities $this->loadEntities(); // Only configuration $this->loadConfigs(); // Only translations $this->loadTranslations(); // Views with custom namespace // Now you can use: view('myblog::pages.index') $this->loadViews('Resources/views', 'myblog'); // Routes from file $this->loadRoutesFrom('routes.php'); // SCSS styles (compiled automatically) $this->loadScss('Resources/assets/scss/blog.scss'); // Components and widgets $this->loadComponents(); $this->loadWidgets(); }

Module Lifecycle

When Flute starts, modules go through several stages:

Module Discovery

The system scans the app/Modules/ folder and finds all module.json files.

Status Check

Each module is checked against the database. Statuses:

  • active — module is working
  • disabled — module is disabled by administrator
  • notinstalled — module found but not yet installed

Dependency Check

For each active module, dependencies from module.json are checked. If something is wrong (missing PHP extension, missing dependent module), the module is automatically disabled.

Registration (register)

The register() method is called for ALL modules. Services are registered in the DI container here.

Initialization (boot)

The boot() method is called only for ACTIVE modules. Resources are loaded and setup is performed here.


Caching

For performance, Flute caches file scanning results. By default, the cache lives for 1 hour.

When to Clear Cache

  • Added a new controller or component
  • Changed module folder structure
  • Added new translations

How to Clear Cache

// Clear cache of all modules (module list, statuses) app(\Flute\Core\ModulesManager\ModuleManager::class)->clearCache(); // Clear cache of a specific module (files, translations, components) $provider->clearFileCache();

Change Cache Duration

public function boot(\DI\Container $container): void { // Cache for 2 hours instead of 1 $this->setCacheDuration(7200); $this->bootstrapModule(); }

Module Extensions

If a module is large, it can be split into extensions:

class ShopProvider extends ModuleServiceProvider { /** * Extensions are loaded automatically */ public array $extensions = [ CartExtension::class, PaymentExtension::class, DeliveryExtension::class, ]; }

Each extension is a class implementing ModuleExtensionInterface:

<?php namespace Flute\Modules\Shop\Extensions; use Flute\Core\ModulesManager\Contracts\ModuleExtensionInterface; class CartExtension implements ModuleExtensionInterface { public function register(): void { // Cart services registration } public function boot(): void { // Cart initialization } }

For extensions, only register() is called automatically. boot() is NOT called automatically.


Naming Conventions

To ensure modules are compatible and code is readable, follow these rules:

WhatHow to NameExample
Module FolderStudlyCaseBlog, UserProfile, PaymentGateway
ClassesStudlyCaseArticleController, PaymentService
MethodscamelCasegetArticles(), processPayment()
Template Fileskebab-casearticle-card.blade.php, user-profile.blade.php
ConstantsUPPER_CASEMAX_ARTICLES, DEFAULT_LIMIT
DB Tablessnake_case with prefixblog_articles, shop_orders

The module folder name must match the name field in module.json and the $moduleName property in the provider.