Skip to main content

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.

note

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:

  1. Loading routing parameters into the engine (routes).
  2. Loading module translations into the engine.
  3. Loading custom items in the admin panel.
  4. 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).
warning

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.
note

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.

warning

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.

warning

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:

i18n/ru/mon_test.php
<?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.