Anatomía de un módulo y module.json
La estructura de carpetas, cada campo del manifiesto, y los métodos del ciclo de vida que llama el kernel.
Un módulo es un directorio autocontenido, cargable en tiempo de ejecución e instalable desde un zip. Los módulos de fábrica usan exactamente este contrato.
Estructura de carpetas#
modules/directory/
module.json manifiesto (obligatorio)
src/
DirectoryServiceProvider.php
Models/
Http/Controllers/
Http/Requests/
Support/
database/
migrations/ las tablas del módulo
seeders/
resources/
views/ Blade, sobreescribible por un tema
lang/ las traducciones del módulo
assets/ css y js, YA COMPILADOS
routes/
web.php
admin.php
config/directory.php
El manifiesto#
{
"name": "Directory",
"slug": "directory",
"version": "1.2.0",
"description": "A configurable catalogue.",
"author": { "name": "You", "url": "https://example.com" },
"requires": {
"bondry": ">=1.0 <2.0",
"php": ">=8.2",
"extensions": ["gd"],
"modules": { "payments": ">=1.0" }
},
"provider": "Modules\\Directory\\DirectoryServiceProvider",
"provides": {
"permissions": ["directory.view", "directory.manage"],
"hooks": ["profile.tabs", "admin.menu", "search.sources"]
},
"settings": "admin.directory.settings",
"assets": { "css": ["assets/directory.css"], "js": ["assets/directory.js"] },
"parent": null,
"tested_up_to": "1.4.0",
"official": false,
"licensing": { "product_slug": "your-directory" }
}
| Campo | Significado |
|---|---|
slug |
La identidad. [a-z0-9-], único, y nunca cambia entre versiones. |
version |
Semver. Es lo que impulsa onUpdate. |
requires |
Rangos semver. El kernel se niega a instalar cuando no se cumplen. |
provider |
La clase raíz. Tiene que existir y extender el provider base. |
provides.permissions |
Se registran en la matriz de grupos al instalar. |
settings |
Un nombre de ruta. El listado de módulos enlaza a él cuando el módulo está activo. |
parent |
El slug del módulo que este extiende. Ausente significa que extiende el core. |
tested_up_to |
La versión más nueva del core contra la que probaste. |
official |
Solo se acepta para paquetes firmados por nosotros. |
licensing.product_slug |
Presente solo en un módulo de pago. Ver License API. |
El service provider#
Tu provider extiende Bondry\Kernel\Modules\ModuleServiceProvider e
implementa el ciclo de vida:
interface ModuleContract
{
// Cada request, solo mientras el módulo está activo:
public function register(): void; // bindings del container
public function boot(): void; // rutas, vistas, migraciones, traducciones, hooks
// Llamado por el instalador en tiempo de ejecución, no en cada request:
public function onInstall(): void;
public function onUninstall(bool $purge): void;
public function onEnable(): void;
public function onDisable(): void;
public function onUpdate(string $from, string $to): void;
}
boot() es donde llamas a loadRoutesFrom, loadViewsFrom con tu
namespace, loadMigrationsFrom, loadTranslationsFrom y registras tus
hooks.
register() corre antes de que se resuelva nada: solo bindings, sin base
de datos.
Advertencia
boot()corre en cada request. Cualquier cosa costosa ahí es un costo que tus compradores pagan en cada vista de página, en hosting compartido.
Vistas y el tema#
Registra tus vistas con un namespace:
$this->loadViewsFrom(__DIR__.'/../resources/views', 'directory');
Renderizar directory::item.show resuelve entonces primero a través de la
cadena de temas, así que un comprador puede sobrescribir cualquier
pantalla de tu módulo desde su tema sin editar tus archivos. Ver
Temas.
Flujo de instalación#
Lo que hace el panel con tu zip, en orden:
- lo abre en un directorio temporal aislado;
- lee
module.jsony valida el schema; - revisa
requiresy cualquier conflicto de slug o versión; - verifica la firma cuando
officialestá activado; - lo mueve a
modules/<slug>/y correonInstall(), oonUpdate()cuando ya había una versión ahí; - publica los assets en
public/modules/<slug>/; - lo registra en la tabla de módulos;
- limpia las cachés de rutas, vistas y configuración.