-
Notifications
You must be signed in to change notification settings - Fork 0
Module Structure
Understanding how Razy modules are organized on disk.
Every Razy module follows a strict directory convention. The framework uses this structure to auto-discover controllers, views, API handlers, web assets, and plugins. Modules live under either a distributor's modules/ folder or the global shared/module/ folder.
modules/vendor/my-module/
├── module.php # Module metadata (required)
├── default/ # Default version folder (required)
│ ├── package.php # Package configuration (required)
│ ├── controller/ # Controller closures
│ │ ├── my_module.php # Main controller (class name match)
│ │ ├── my_module.index.php # Route handler "index"
│ │ ├── my_module.detail.php # Route handler "detail"
│ │ └── api/ # API command handlers
│ │ ├── getData.php # API command "getData"
│ │ └── submit.php # API command "submit"
│ ├── view/ # Template files
│ │ ├── main.tpl # Main template
│ │ ├── list.tpl # List template
│ │ └── include/ # Included partials
│ │ └── alert.tpl
│ ├── webasset/ # Web-accessible static files
│ │ ├── css/
│ │ ├── js/
│ │ └── img/
│ └── plugin/ # Plugin closures
│ └── my_plugin.php
└── 1.0.0/ # Tagged version (optional)
├── package.php
├── controller/
└── view/
The root-level module.php defines module identity. It sits outside any version folder and provides metadata used by the distributor.
return [
'module_code' => 'vendor/my-module',
'name' => 'My Module',
'author' => 'Author Name',
'description' => 'Module description',
'version' => '1.0.0',
];
The module_code must be in vendor/package format, matching the directory path.
Each version folder contains a package.php that defines what gets loaded at runtime:
return [
'module_code' => 'vendor/my-module',
'author' => 'Author Name',
'description' => 'Package description',
'version' => '1.0.0',
'api_name' => 'mymod', // Cross-module API identifier
'require' => [
'vendor/other' => '>=1.0.0',
],
];
The controller/ directory contains PHP closure files that the framework auto-loads. Files are named following a strict convention:
| File Pattern | Purpose | Example |
|---|---|---|
{class}.php |
Main controller — extends Controller, defines lifecycle hooks |
my_module.php |
{class}.{name}.php |
Route handler closure — registered via addRoute() / addLazyRoute()
|
my_module.index.php |
api/{command}.php |
API command handler — registered via addAPICommand()
|
api/getData.php |
demo/{name}.php |
Custom sub-folders for logical grouping | demo/overview.php |
The main controller file returns an anonymous class extending Controller:
return new class extends Controller {
protected function __onInit(Agent $agent): bool
{
$agent->addRoute('/', 'index');
$agent->addAPICommand('getData', 'api/getData');
return true;
}
};Route handler files return a Closure that receives captured URL parameters:
// controller/my_module.index.php
return function () {
$template = $this->loadTemplate('main');
echo $template->output();
};The view/ folder holds .tpl template files used by the Razy Template Engine. Templates are loaded via $this->loadTemplate('name') in the controller, which resolves to view/name.tpl.
You can organize templates into subdirectories. The include/ subfolder is a convention for partial templates referenced by the INCLUDE block type.
The webasset/ directory contains publicly accessible static files (CSS, JS, images). These are served directly by the web server via URL rewriting. Asset paths are managed through the module's package.php asset configuration and the ModuleInfo::getAssets() method.
The plugin/ directory contains plugin closure files that extend the Template Engine or Collection system. Plugins are loaded via $this->registerPluginLoader() in the controller. See the Plugin-System page for details.
Modules support multiple versions via named version folders:
modules/vendor/my-module/
├── module.php
├── default/ ← default version
│ ├── package.php
│ └── ...
├── 1.0.0/ ← tagged version
│ ├── package.php
│ └── ...
└── 2.0.0/ ← another tagged version
├── package.php
└── ...
The dist.php configuration selects which version to load:
'modules' => [
'*' => [
'vendor/my-module' => '*', // Latest version
'vendor/my-module' => 'default', // Default folder
'vendor/my-module' => '1.0.0', // Specific tag
],
],
See Packaging-Distribution for full version management details.
| Location | Type | Description |
|---|---|---|
shared/module/ |
Shared | Available to all distributors. Enabled via global_module or autoload_shared in dist.php
|
sites/{dist}/modules/ |
Distributor-specific | Only available to that distributor |
modules/ |
Local development | Used during development, referenced by dist.php
|
demo_modules/core/route_demo/
├── module.php
└── default/
├── package.php
├── controller/
│ ├── route_demo.php # Main controller
│ ├── route_demo.main.php # /route handler
│ ├── route_demo.article.php # /article/:id handler
│ ├── route_demo.product.php # /product/:slug handler
│ ├── route_demo.search.php # /search handler
│ ├── route_demo.user.php # /user/:id handler
│ ├── route_demo.tag.php # /tag/:name handler
│ └── route_demo.code.php # /code/:lang handler
└── view/
└── main.tpl
Each .{name}.php file corresponds to a route action registered in the main controller's __onInit via $agent->addRoute().