Creating a Module
To develop a module, it's essential to familiarize yourself with the structure of each module. Every module must have a unique key.
The module's key is represented by the folder name and the "key"
parameter in module.json
.
Creating a Module
To create the basic structure of a module, enter the following command:
php flute generate:module
Executing this command will create the basic structure of the module with all necessary directories.
ServiceProvider
Every module should contain ServiceProviders. These classes signify the loading of certain components into the system. Examples of ServiceProvider capabilities include:
- Loading routing parameters into the engine (routes).
- Loading module translations into the engine.
- Loading custom items in the admin panel.
- And so on.
As you might have guessed, ServiceProviders have limitless possibilities but are solely used for initialization.
Example of a module's ServiceProvider implementation:
<?php
namespace Flute\Modules\Monitoring\ServiceProviders;
use Flute\Core\Support\ModuleServiceProvider;
class MonitoringServiceProvider extends ModuleServiceProvider
{
public function boot(\DI\Container $container): void
{
// Loading module translations
$this->loadTranslations('Monitoring');
}
public function register(\DI\Container $container): void
{
}
}
Each ServiceProvider should have 2 methods:
- boot - Executes only when the module is active in the system.
- register - Executes regardless of the module's status (enabled / disabled).
Important Note! The ServiceProvider will not be called if the module is not installed.
Extensions
Each ServiceProvider can have extensions. These are additions to the ServiceProvider that allow you to decompose the provider's logic into some components. Examples include:
- Template renderer extension.
- Route initialization extension.
- Etc.
Actually, ServiceProviders can easily exist without extensions. However, extensions allow breaking down some parts of the code, which enhances the readability of your project.
Example of an extension implementation:
<?php
namespace Flute\Modules\Monitoring\ServiceProviders\Extensions;
use Flute\Modules\Monitoring\Widgets\ServersWidget;
class WidgetExtension implements \Flute\Core\Contracts\ModuleExtensionInterface
{
public function register() : void
{
// In this extension, we register a new widget in the system
widgets()->register(new ServersWidget());
}
}
Each extension must implement the ModuleExtensionInterface
and contain a register()
method.
To initialize our extension in ServiceProvider, we need to add it to the $extensions
array within our ServiceProvider:
<?php
namespace Flute\Modules\Monitoring\ServiceProviders;
use Flute\Modules\Monitoring\ServiceProviders\Extensions\WidgetExtension;
class MonitoringServiceProvider extends ModuleServiceProvider
{
public array $extensions = [
WidgetExtension::class, // We add our extension here
];
/** Rest of the logic */
}
Database Interaction
Modules may require additional tables (or the extension of existing ones) for operation. For this, entities from the Cycle ORM library are used.
Entities
Each entity must have a unique name. All entities should be located in the databases/Entities
folder and start with a capital letter.
You can view examples of entity implementations in app/Core/Database/Entities
.
To create tables in the database for our entities, we need to perform migrations.
Migrations
Migration is the process of creating or deleting tables from the core database of the engine.
Migrations will only be executed if they are executed by Installer
using the importMigrations()
function!
A migration can be created using the command:
php flute generate:migration
After entering the command, a migration file will be created in the specified module.
Example of a migration file:
<?php
class add_servers extends \Spiral\Migrations\Migration
{
/**
* Create tables, add columns or insert data here
*/
public function up()
{
$this->table('sample_table')
->addColumn('id', 'primary')
->addColumn('name', 'string')
->create();
}
/**
* Drop created, columns and etc here
*/
public function down()
{
$this->table('sample_table')->drop();
}
}
To learn more about using migrations, you can read the library's documentation.
Translations
Flute supports multilingualism, which means each module can create its own translations that may later be used in interfaces.
All translations are located in the module's i18n/
directory. The key for each folder inside i18n
is the identifier of the translation. For example, i18n/ru/test.php
and i18n/en/test.php
.
In the system, each translation must have a unique file name, as the file name serves as the translation key.
For instance, if we have a translation file named mon_admin.php
, to call phrases from this translation, you would use:
__('mon_admin.somephrase')
BUT! If we use admin.php
instead of mon_admin.php
, there will be an error.
Try to choose unique names for translation files!
The system automatically determines when to use the appropriate translation. You only need to import them in the ServiceProvider.
<?php
class MonitoringServiceProvider extends ModuleServiceProvider
{
/** Extensions and others */
public function boot(\DI\Container $container): void
{
// We need to call this function, and all translations will automatically initialize in the system.
$this->loadTranslations();
}
/** Rest of the code */
}
All translations should be in the form of a PHP array. Example:
<?php return [
'some' => 'Some phrase',
'key' => [
'what' => 'What??',
'ass' => 'Can we call this in the form of associative arrays??'
]
];
And to call these translations in PHP code, you can use the __()
function:
public function someFunc()
{
// Each key is identified through a dot.
echo __('mon_test.key.ass');
}
To call translations in templates, you can use @t()
:
<div>
Hello @t('mon_test.some')
</div>
Next, let's take a look at what widgets are.