Modules
Modules are components of the Hierarchical Model View Controller application design pattern further explained in chapter
HMVC - Extending the Model-View-Controller Pattern.
Table of Contents
- Overview
- Structure of a HMVC Module
- Module Controller Specifications
- Using Modules as Application Controllers
- Using Modules as Libraries or Widgets / View Partials
- Module Paths
- Loading Module Files and Resources
- Module Configuration
- URI Routing
- Module Assets
Overview
Following below the features and key points of revIgniter's approach to the HMVC pattern:
- Modules can be used as libraries.
- Module controllers can be used as application controllers.
- Modules can be used as widgets or view partials respectively.
- Modules can be nested, that's what "H" in HMVC means anyway.
- Modules can load resources of other modules.
- Controllers can be located in myModule/controllers sub-directories.
- Pass any number of parameters to module controllers using URI style segments and / or an array (widgets only).
- Each module may contain a models directory.
- Models may be loaded from myModule/models sub-directories.
- Each module may contain a config/autoload.lc file.
- Each module may contain a config/config.lc file.
- Each module may contain a config/routes.lc file. Note, this file is mandatory if a module controller is used as application controller.
- Each module may contain an extensions directory, a language directory, a helpers directory, a libraries directory, a models directory and a stacks directory.
Structure of a HMVC Module
All modules are arranged in their own folder in the application/modules directory and may contain configuration files and sub-directories like the main MVC component in the application directory.
application/modules/
|--myModule/
|--config/
|--controllers/
|--extensions/
|--helpers/
|--language/
|--libraries/
|--models/
|--stacks/
|--views/
Please see as an example the very basic module "moduleSample" in application/modules. You can find a real-world example (a JSON feed module) here: https://gitlab.com/rabitt/revigniter-jsonfeed-module
Module Controller Specifications
Provided that the controller is used as an application controller, put all handler names which are likely to serve as uri segments into the global gModuleHandlers.
Provided that the controller is used as a library or widget, put the name of the main handler (the handler named after the module) as well as the index handler name and all names of handlers which are likely to be called by using the rigRunModule() function into the global gModuleHandlers.
To avoid name collisions with standard application controller index handlers the name of your module controller's index handler must be composed of the controller name followed by the word "Index" like: myModuleIndex.
Sample code of a module controller named after the module:
<?lc
# PUT YOUR HANDLER NAMES INTO THE GLOBAL gModuleHandlers AS A COMMA SEPARATED LIST
put "myModule,myModuleIndex,otherHandler" into gModuleHandlers
command myModule
# MAIN HANDLER, THIS HANDLER IS CALLED WHEN THE CONTROLLER IS LOADED
# LOAD REQUIRED LIBRARIES, MODELS, HELPERS ETC. HERE
-- Code...
end myModule
command myModuleIndex
# DEFAULT HANDLER, THIS HANDLER IS CALLED WHEN THE CONTROLLER IS RUN AND NO HANDLER IS SPECIFIED
# REMEMBER TO PUT ALL THE VARIABLES TO BE MERGED WITH VIEWS INTO THE ARRAY VARIABLE gData
-- Code...
end myModuleIndex
command otherHandler
-- Code..., calling _privatehandler for example
end otherHandler
command _privatehandler
# HANDLER NAME NOT USED AS URI SEGMENT / NOT CALLED BY rigRunModule(), SO, THERE IS NO NEED
# TO STORE IT'S NAME IN gModuleHandlers
-- Code...
end _privatehandler
--| END OF myModule.lc
--| Location: ./application/modules/myModule/controllers/myModule.lc
----------------------------------------------------------------------
There is a very basic sample controller in modules/moduleSample/controllers which can serve as application controller as well as widget / view partial.
Note: In case you use module controllers as application controller you must include a routes.lc file in the yourModule/config directory. Routing has no effect on view partials or if modules are used as libraries. Please see the sample routes file in modules/moduleSample/config.
Using Modules as Application Controllers
To use modules as application controllers specify the controller script to be invoked using URI segments. There is no difference to standard controllers. For example to run the sample module as application controller the appropriate URL would read:
example.com/moduleSample
Note: On the basis of the URL there is no way to distinguish between standard application controllers and module controllers used as app controllers. So, to avoid name collisions with app controller names be careful naming your module controllers. Of course this applies to naming modules used as widgets only.
Using Modules as Libraries or Widgets / View Partials
Widgets are, in contrast to modules used as app controllers, intrinsically tied to the modules library. So, please see chapter Modules Library for further information.
Module Paths
Module paths and module controller paths are relative to application/modules/. Keep in mind that module controllers can be located in sub-folders of myModule/controllers and that you must omit the name of the "controllers" directory in module paths. So, paths to controller files can be:
myModule/myController
or (calling in each case the index handler "myControllerIndex"):
myModule/mySubDirectory/myController
To address a particular controller handler instead of the index handler add it's name as segment Like:
myModule/myController/myControllerHandler
Use additional segments to pass parameters:
myModule/myController/myControllerHandler/name/joe
If you name a controller after your module and this controller is not in a sub-directory of myModule/controllers you can omit the controller name and the path would simply be:
myModule
If you don't include a handler name the controller's index handler, myModuleIndex in this case, is called. Calling a particular handler of controller myModule of module myModule would read:
myModule/myOtherHandler
Note: Currently "query string" URLs are not compatible with modules. So, always use segment-based URLs.
Loading Module Files and Resources
You can load models, libraries, helpers etc. like in standard application controllers. To load equivalent module files add the module's name to parameter lists like:
rigLoadModel "moduleModel", , "myModule"
rigLoaderLoadLibrary "Modulelibrary", , "myModule"
get rigLangLoadLang("moduleLang", "french", "myModule")
rigLoadHelper "moduleHelper", "myModule"
get rigLoadConfig("mymoduleconfig", TRUE, TRUE, "myModule")
rigLoadStack "moduleStack", , , "myModule"
rigLoadExtension "com.myDomain.library.myExtension", "myModule"
-- "com.myDomain.library.myExtension" is a folder (containing your "module.lcm" file) located in modules/myModule/extensions/
Auto-loading Module Resources
To autoload module resources, open your application/modules/myModule/config/autoload.lc file and add the item you want loaded to the gModuleAutoload array like:
put "database" into sAutoLibraries[1]
put "myLib" into sAutoLibraries[2]
put sAutoLibraries into gModuleAutoload["libraries"]
You'll find instructions in the autoload file of the sample module corresponding to each type of resource item which can be auto-loaded.
Loading Views
In general module view files are loaded like any other view file using rigLoadView(). Provided that the particular module is used as widget you must keep in mind that you should not set the second parameter of this function to true. To set this parameter is unnecessary anyway because loading the view of a widget always returns data as a string rather than sending it to the browser.
Module Configuration
To configure your module simply add a configuration file "config.lc" to application/modules/myModule/config/ like the one you find in the config directory of the sample module.
Setting a Module's Configuration Value in a Configuration File
To add configuration data related to your module use the module name as array key in a multidimensional array named "gConfig" like:
put "foo" into gConfig["myModule"]["bar"]
Overriding a Global Configuration Value in a Configuration File
To override global configuration data just omit the module's name as array key.
put "french" into gConfig["language"]
Fetching Module Config Items
To retrieve an item from your module's config file, use the standard function and add the name of your module as second parameter like:
rigFetchConfigItem("itemName", "myModule")
Setting a Module Config Item
If you would like to dynamically set a module's config item or change an existing one, you can so using:
rigSetConfigItem("itemName", "itemValue", "myModule")
Where itemName is the gConfig array index you want to change, and itemValue is its value, and the third parameter is the name of your module.
URI Routing
Note, the following applies to modules only if used as application controllers. Routing has no effect on view partials or if modules are used as libraries. In case you use module controllers as application controller you must include a routes.lc file in the yourModule/config directory. This file lets you re-map URI requests to specific controller functions. Typically there is a one-to-one relationship between a module URL string and its corresponding controller page library/handler. The segments in a module URL normally follow this pattern:
example.com/myModule/handler/ID/
or in case the name of the page library is not equal to the module name
example.com/myModule/pagelibrary/handler/ID/
or using a sub-directory in myModule/controllers
example.com/myModule/sub-directory/pagelibrary/handler/ID/
In some instances, however, you may want to remap this relationship so that a different page library/handler is called than the one corresponding to the URL. Store these custom routes which are relative to application/modules/ in the gRoute["yourModule"] array using a key number starting with number 2 like:
put "myModule/mysub-directory/myPagelibrary/myHandler" into gRoute["myModule"][2]["myModule/foo/bar"]
Please see chapter Uri Routing for complete details.
Reserved Routes
There are two reserved routes for modules:
put "myModule/pagelibrary" into gRoute["myModule"][1]["defaultController"]
This route indicates which controller page library should be loaded if the URI contains no data besides the module name. In the above example, the "pagelibrary" page library would be loaded. If the "defaultController" route is empty and there is only one URI segment specifying the name of the module, revIgniter expects that there is a controller named after the module and tries to load myModule/myModule. If no handler is specified the module's index handler is called which must be composed of the controller name followed by the word "Index" so that the route would look like: myModule/myModule/myModuleIndex.
put "" into gRoute["myModule"][1]["404Override"]
This route will tell the Router what URI segments to use if those provided in the URL cannot be matched to a valid route, in other words if the controller specified is missing. If this route is empty the default 404 error page is provided.
Note: Module routes are relative to application/modules/. The gRoute["myModule"] array must be numbered and the key number for reserved routes must be 1. So custom route key numbers start with number 2.
Module Assets
A nice side effect of using the asset helper to deal with module CSS files, JavaScript files and images is that it simplifies distribution of modules including assets. Just organize all related assets (as described in chapter Asset Helper) in a folder preferably named after your module and store it in assets/modules. This way installation of a module with appendant assets is as simple as copying two folders to the right place.