
As I promised earlier, in my previous post on habrahabr today I want to tell you how to write my first extension for the content management system
FFCMS . In this article we will introduce you to the main framework of extensions, which is necessary for interacting with the system - in other words, we will write “hello world” with you in the ffcms view as a component, module and hook.
1. Introduction
First I want to tell you about the structure of the location of extensions in the FFCMS system.
In version 2.0, all extensions are located in the
/extensions
directory with 4 directories depending on the type of extension - components, modules, hooks, api (components, modules, hooks and apicallback respectively).
All system extension data is stored in the system table (database) - _extensions:
- _extensions.type - extension type (enum ('components', 'modules', 'hooks'))
- _extensions.dir - extension name (root directory and class name)
- _extensions.enabled - is the extension included? (1/0)
- _extensions.version and _extensions.compatable - version and compatibility index with system version
- _extensions.path_choice / path_allow / path_deny - special fields for the launch conditions of modules according to certain URI rules
The most important for us are the columns type and dir - depending on them our extension will be initiated. The logical path of initiation will look like this: ROOT. _extension.type. _extension.dir.
As an example, the following directory will be used for type = components and dir = news: / extensions / components / news /.
2. Direction of interaction (interface type pointer)
Each extension (component, module, or hook) can have 2 main implementations, depending on the initiating interface of the system:
- front - the direction of interaction with the user interface
- back - the direction of interaction with the administrator interface
As a result, there can be 2 different implementations in the directory of each extension (2 files with the implementation model): front.php and back.php.
If it is easier to explain (in human language), what the user sees should be processed and displayed in the front.php implementation, and what will be available from the administrative panel is back.php.
As an example, for the extension with type = components and dir = news, 2 main files can be used to implement various functionalities (administrator and user) -
/extensions/components/news/front.php
and
/extensions/components/news/back.php
. An additional implementation can also be created that does not depend on the direction of interaction with this or that interface: cron.php - the implementation of tasks for the execution of tasks on a schedule, but more on that later.
3. Standard for naming extension classes
The name of the extension classes is determined by 3 parameters:
- Extension type (see above - _extensions.type)
- Directory | extension name (see above - _extensions.dir)
- Direction of interaction (front or back)
Based on these parameters for each direction of interaction of the extension, depending on its type and name (location directory), the name of its class can be determined (it is unique in the whole system).
So for our example - type = components, dir = news, for the direction of the implementation of the front the class name should be:
components_news_front
, and for the direction of the back:
components_news_back
.
The extension class implementation should follow the singleton pattern with the static getInstance () method, which should return an object of the initiated class. In order not to represent the implementation, you can inherit the abstract class \ engine \ singleton each time.
Each extension class must have a mandatory dynamic call function: void
make()
.
So, the final framework for our example type = components, dir = news will look like this:
/extensions/components/news/front.php:
<?php class components_news_front extends \engine\singleton {
/extensions/components/news/back.php:
<?php class components_news_back extends \engine\singleton {
')
4. Version Control, Compatibility and Installer
In addition to the simple implementation of extensions, the FFCMS system also provides algorithms that will allow you to use extensions written by you for the entire community. The following system extension control methods can be implemented for all directions of back implementation:
- void _install () - implementation of the system installer (work with the database, language versions or other manipulations)
- void _update (int version_from) - implementation of the extension upgrade method (if you plan to improve your extension - you need a mechanism that will allow you to make changes in the database, language locales, and other areas)
- string _version () - a method that must return the current version of the extension (the system provides an algorithm for checking the version compliance in the database and in the implementation file + outputting notifications about their nonconformity)
- string _compatable () - a method that must return the version of the FFCMS system with which this extension is compatible (the system provides an algorithm for checking the compatibility of the extension and the system)
For our example described in the article above with type = components, dir = news, iface = back, these control methods should look like this:
/extensions/components/news/back.php:
class components_news_back extends \engine\singleton { public function _version() { return '1.0.1'; } public function _compatable() { return '2.0.4'; } public function _install() {
5. Hello, world habrahabr!
Well, since we got acquainted with the frameworks of the extensions, though briefly, it was time to write our “hello world” in the form of an extension for the FFCMS.
As examples we will write 2 implementations of “hello habrahabr” - a component and a module, implementations of a “front” (for the user).
5.1. Hello component
So, our first example is to implement a component named “hello” (type = components, dir = hello, iface = front). To begin with, let's add the initiation of calling our extension to the _extetnsions table (we are developers, not good to us to write the installer before debugging):
INSERT INTO ffcms_extensions (`id`, `type`, `configs`, `dir`, `enabled`, `path_choice`, `path_allow`, `path_deny`, `version`, `compatable`) VALUES (NULL, 'components', '', 'hello', '1', '', '', '', '1.0.1', '2.0.4');
And we will create the necessary file architecture and fill the frame of our component with the code:
/extensions/components/hello/front.php
<?php class components_hello_front extends \engine\singleton { public function make() {
Now users of the site (front interface) can access our component by URI: / hello / (which may look different depending on the on / off multi-lingualism and CNC, for example: /index.php/ru/hello/, / ru / hello / or simply / hello /).
However, at the moment our component does not perform any actions - well, let's set the output of the phrase “Hello habrahabr” to the position of the
{{ content.body }}
template:
<?php use engine\template; class components_hello_front extends \engine\singleton { public function make() { $response = "Hello habrahabr!"; template::getInstance()->set(template::TYPE_CONTENT, 'body', $response); } }
and if you did everything correctly, you will see
something like this with the URI described above.
Now I will show you a few simple manipulations with this example that you will definitely encounter as a result of developing extensions for FFCMS.
Isn't it good that our idea is inside the model? Let's move the contents of the response text to the template and add parsing using the twig random number for this template. Create a template view /templates/default/components/hello/view.tpl and fill it with the contents:
<h1>, !</h1> <hr /> <p> : {{ local.randomval }}</p>
And the extension code will now look like this:
/extensions/components/hello/front.php
<?php use engine\template; use engine\system; class components_hello_front extends \engine\singleton { public function make() { $params = array(); $params['local']['randomval'] = system::getInstance()->randomInt(1);
The main feature of the components is their work, depending on the selected routing URI - so I just have to show you an example of processing URIs in the body of the component.
In the template view, add 1 line:
<p>URL[0] : {{ local.second_way }}</p>
and add the following to the front.php code:
use engine\router; ... $way = router::getInstance()->shiftUriArray();
Now, by accessing our component by URI /hello/hello-developer.html, we will see the following in the processing results:
In the future, you can use the router class capabilities for routing inside your component — yes, there is no explicitly dedicated controller in ffcms (like the mvc model), but nothing prevents you from implementing it.
5.2. Habr module
A modular implementation implies first of all control and interaction with template positions. So, if a component is initiated (its processing is started before class :: instance () -> make ()) only if a specific URI is accessed, then the module can be initiated regardless of routing.
Well, for our habr module (type = modules, dir = habr, iface = front), we first create an initiation point in our _extensions table:
INSERT INTO ffcms_extensions (`id`, `type`, `configs`, `dir`, `enabled`, `path_choice`, `path_allow`, `path_deny`, `version`, `compatable`) VALUES (NULL, 'modules', '', 'habr', '1', '1', '*', '', '1.0.1', '2.0.4');
Just note that later we will also be able to set the routing rules for the module (in simple words - where to work and where not) and now we have chosen the type of rule “1” (where to work?) And indicated the route * (everywhere).
Now we will create the file architecture and model of our module:
/extensions/modules/habr/front.php:
<?php use engine\template; class modules_habr_front extends \engine\singleton { public function make() { $response = 'Hello, habrahabr.ru!'; template::getInstance()->set(template::TYPE_MODULE, 'habr', $response); } }
And in the main template view (/templates/default/main.tpl) we will add the display of the contents of our module:
{{ module.habr }}
And if you have done everything correctly - the cherished phrase will be displayed in the template position. Modules can also use all the functionality of the system (standardization, localization, routing, and others) - you can independently study the capabilities of the system according to the documentation.
6. Conclusion and references
Thus, we got acquainted with the main logic of building the extension framework for the content management system of the FFCMS 2.0 version. I hope that this material was useful for you and simple for your perception.
FFCMS Documentation:
FFCMS API 2.0Our wonderful forum:
help on the forumGithub:
zenn1989 / ffcms