📜 ⬆️ ⬇️

Understanding the frame extensions for FFCMS - hello world!

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:

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:

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:
  1. Extension type (see above - _extensions.type)
  2. Directory | extension name (see above - _extensions.dir)
  3. 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 { // type = components, dir = news, iface = front, pattern = singleton public function make() { //   } } 

/extensions/components/news/back.php:
 <?php class components_news_back extends \engine\singleton { // type = components, dir = news, iface = back, pattern = singleton public function make() { //   } } 

')

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:

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() { //    } public function _update($v) { switch($v) { // case - / } } public function make() { //   } 


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); //   - 10 ^ (1-1) .. 10 ^ 1 $tpl = template::getInstance()->twigRender('components/hello/view.tpl', $params); //           template::getInstance()->set(template::TYPE_CONTENT, 'body', $tpl); //      content.body } } 

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(); //  URI  ,     $params['local']['second_way'] = $way[0]; //      URI 

Now, by accessing our component by URI /hello/hello-developer.html, we will see the following in the processing results:
ffcms component render result

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.0
Our wonderful forum: help on the forum
Github: zenn1989 / ffcms

Source: https://habr.com/ru/post/242579/


All Articles