Skip to main content

Module development

Start by reading existing modules. The service provider is the important part:

  • notes/src/NotesServiceProvider.php
  • signature/src/SignatureServiceProvider.php

notes is a good example for a module with migrations, routes, permissions and resources. signature is a good example for a small module with a custom frontend field.

Build the smallest module that owns the reusable behaviour. Keep customer-specific resources, menus and policies in Skeleton unless the module must ship them for every app.

Every module service provider extends QoreModuleServiceProvider and implements:

public function configure(QoreModuleConfigurator $configurator): void

Minimal Provider

class MyModuleServiceProvider extends QoreModuleServiceProvider
{
public const string MODULE_NAME = 'my-module';

public function configure(QoreModuleConfigurator $configurator): void
{
$configurator
->setName(self::MODULE_NAME)
->useTranslations()
->onRegister(function (QoreModule $module) {
$module
->setTitle(__('my-module::module.title'))
->setDescription(__('my-module::module.description'));
});
}
}

setName() should match the package/module name suffix. If no package name is set, Qore uses qore-next/{name}.

Configurator API

  • setName(string $name): self
  • setPackageName(string $packageName): self
  • useTranslations(): self
  • useMigrations(): self
  • useConfig(): self
  • useRoutes(): self
  • useChannels(): self
  • onRegister(Closure(QoreModule): void $callback): self
  • onEnable(Closure(QoreModule): void $callback): self
  • onDisable(Closure(QoreModule): void $callback): self
  • onBoot(Closure(QoreModule): void $callback): self
  • setSettingsFields(Closure(): Field[] $callback): self
  • onSettingsSubmit(Closure(array $validated): void $callback): self

What The Base Provider Does

In register():

  • creates a configurator using the module root directory;
  • calls your configure();
  • builds a QoreModule;
  • merges config when useConfig() is enabled;
  • registers a publish tag qore-next.{module}.config.

In boot():

  • loads translations from lang when useTranslations() is enabled;
  • loads migrations from database/migrations when useMigrations() is enabled;
  • registers publish tag qore-next.{module}.db;
  • loads routes.php when useRoutes() is enabled;
  • requires channels.php when useChannels() is enabled;
  • runs onRegister();
  • registers the module in qore()->modules().

Enable, Disable and Boot

onRegister() runs when the provider boots and should set title/description.

onEnable() runs when the module is enabled. Use it for permissions or one-time setup.

->onEnable(function () {
$permissions = qore()->permissions()->cruda('notes');
admin_role()->givePermissions(collect($permissions)->values());
})

onDisable() runs when the module is disabled. Use it for cleanup.

onBoot() runs for enabled modules from ModuleService::bootModules(). Use it to register resources, policies, drivers or runtime behaviour.

->onBoot(function () {
Gate::policy(notes_model_class(), notes_policy_class());
qore()->registerResource(notes_resource_instance());
})

Settings

Use settings fields when a module needs database-backed configuration.

->setSettingsFields(fn () => [
PasswordField::make('api_key')
->setDefaultValue(fn () => qore()->settings()->getSetting('my-module.api_key', '')),
])
->onSettingsSubmit(function (array $validated) {
qore()->settings()->setSetting('my-module.api_key', $validated['api_key'] ?? '');
})

Folder Shape

Use only the folders your module needs:

  • src: provider, fields, resources, services, policies.
  • resources/js: custom frontend fields/nodes.
  • resources/css: module styling.
  • database/migrations: migrations loaded/published with useMigrations().
  • config/{module}.php: config loaded/published with useConfig().
  • lang/en, lang/nl: translations loaded with useTranslations().
  • routes.php: routes loaded with useRoutes().
  • channels.php: broadcast channels loaded with useChannels().

Keep reusable code in the module. Keep application-specific resources in Skeleton.