Blocks

Relevant for platform versions 3.x.

For detailed information on using blocks for FL2.x, please read the link.

Purpose of Use

To add content to platform pages, you need to use blocks - view handler classes. Let's consider the general structure of a block, each stage of its creation, including integration on the page. Blocks can be created in modules as well as in add-ons.

namespace plugins\example\views;

use bff\view\Block;

class ExampleBlock extends Block
{
    public function init()
    {
        // Block initialization
    }

    public function data()
    {
        // Processing block data used in the template
    }
}

Initialization

The block class is created in the views directory and inherits from the base block bff\view\Block. The init() method is mandatory and initializes the block.

public function init()
{
    // Call the base initialization
    parent::init();
    // Declare a template for rendering the block
    $this->setTemplate('tpl/example', $controller);
    // Set the multilingual block title
    $this->setTitle($this->extension->lang('Example'));
}

The setTemplate($tpl, $controller) method sets the template for the block, where

  • $tpl - subdir/tpl_name is the template name and location in the tpl directory, for a block in a plugin, also specify the name of the template placement folder tpl/subdir/tpl_name, for a block in a module - only the subdirectory and template name subdir/tpl_name;
  • $controller - the block controller, which can be a module module_name or a plugin.
$this->extension = $this->app->plugin('plugin_alias');
$this->setTemplate('tpl/subdir/tpl_name', $this->extension);

Block Data

The data() method defines the data that will be passed to the template for displaying the block.

public function data()
{
    // Base class data
    $data = parent::data();

    // Block data
    $data['title'] = $this->lang('Example block');
    $data['list'] = $this->model->getList();

    return $data;
}

Block Template

In the view template plugins/example/tpl/example.php, you can use the variables initialized in the data() method.

/**
 * Example block
 * @see plugins\example\views\ExampleBlock
 * @var $title string
 * @var $list array
 */
?>
<div>
    <h1><?= $title ?></h1>
    <?php foreach($list as $item){ ?>
        <?= $item['key'] ?>
    <?php } ?>
</div>

Block Integration

To insert the block on the page, use the blocks() method and specify the key and the class name of the ExamplePage handler.

public function blocks()
{
    $this->addBlock('example', ExampleBlock::class);
}

The addBlock($key, $block, $callback, $opts) method adds the ExampleBlock block to the current page handler object. It accepts the following parameters:

  • $key: the block key.
  • $block: the class, block object, or a Closure function.
  • $callback: optional parameter, a closure that will be called after the block initialization. The parameters are the added block object and the key.
  • $opts: optional parameter, an array of additional options.

During the rendering process of the page template, the block is displayed as a variable with the key example.

Pages

To create pages, we use a view handler class that inherits from the base view page bff\view\Page.
Pages can be created in both modules and addons.
Let's look at the page structure and main methods.

namespace plugins\example\views;

use bff\view\Page;

class ExamplePage extends Page 
{
    public function init()
    {
       // Initialize the page 
    }

    public function data()
    {
        // Process the page data used in the template
    }

    public function blocks()
    {
        // Add blocks
    }

    public function settingsForm($form)
    {
        // Page settings in the admin panel
    }    
    
    public function seo()
    {
         // Page SEO settings 
    }
}

Initialization and data of the page are similar to the init() and data() methods of blocks.

Page Blocks

In the blocks() method, blocks of the page are added.

public function blocks()
{
    // Add an existing block to the page 
    $this->addBlock('example', ExampleBlock::class, function (ExampleBlock $block) {
        $block->itemId = $this->itemId;
    }););
    // Declare a block
    $this->addTemplateBlock('filter', 'tpl/filter', $this->extension, function (bff\view\Block $block) {
        $block->setTitle($this->extension->lang('Filter'));
        ...
    })
}

Adding a block addBlock() is described in detail in the "Blocks" section. When adding a block, you can pass page parameters in $callback.
In the page handler class, you can declare a block class in the method addTemplateBlock($key, $template, $controller, $callback):

  • $key - block key,
  • $template - template name and location in the tpl directory,
  • $controller - block controller,
  • $callback - closure that will be called after the block is initialized, with the added block object as a parameter.

You can display blocks individually as variables in the template $example and $filter.

Page Settings

In the settingsForm($form) method, a settings form for a page or block in the admin panel interface of the project is created.

$form is an object constructor form bff/tpl/admin/Form that allows the display of all field types.

/** @var bool */
public $enabled = false;
/** @var string */
public $intro = '';
...
public function settingsForm($form)
{
    $form->checkbox('enabled', $this->extension->lang('Enabled'), $this->enabled);
    $form->wysiwyg('intro', $this->extension->lang('Page Text'));
}

In this case, the method creates a checkbox called enabled and a wysiwyg editor called intro. These settings are set as class parameters of the page handler. You can edit the field data in the admin panel under the "Design / Page Settings / Example Page" menu.

Settings_form

You can disable the block or change its placement on the page in the "Order" tab.

SEO parameters for the page are specified in the seo() method.

Page Template

The page template page.php is set in the init() method during the initialization of the handler class:

public function init()
{
    ...
    $this->setTemplate('tpl/page', $this->extension);
}

In the page template, the following are available for rendering:

  • Page data from the data() method
  • Blocks as template variables, such as $example, $filter
  • Page settings, such as $enabled, $intro
/**
 * Example page
 * @see plugins\example\views\ExamplePage
 * @var $title string
 * @var $enabled boolean
 * @var $intro string
 * @var $example plugins\example\views\ExampleBlock
 * @var $filter string 
 */ 
?>
<?php if($enabled){ // Class setting from settingsForm()  ?>
    <div>
        <h1><?= $title // Class data from data() ?></h1>  
        <p><?= $intro ?></p>
        <?= $exampleBlock // Example of outputting the ExampleBlock block ?>
        <?= $filterBlock ?>
    </div>
<?php } ?>

Displaying the Page

The display of the page is done by adding a route in a module or plugin. In the start() method of the /example/ route, specify the view class as the handler:

protected function start()
{
    ...
    $this->routeAdd(
    'plugin_alias-example', # route id
    [
        'pattern'  => '/example/',
        'callback' => ExamplePage::class,
    ]);
}   

Extending Pages

You may notice that many pages implement a contract.
A contract is an interface, an abstract type of dependency that is registered in the application container and allows for extension and replacement of the implementation of the original platform pages.

Let's consider this possibility with the example of the contacts page. The contacts contract is located in a module and looks like this:

namespace modules\contacts\views\contracts;

use bff\view\Page;

/**
 * Contact page contract
 * @mixin Page
 */
interface ContactPage
{
}

It is located in the views namespace in the contracts directory and can use methods from the bff\view\Page class.
In a theme or plugin, you can extend the platform's contacts page and create your own ContactPageExtended contact page.

namespace plugins\contacts_extended\views;

use modules\contacts\views\ContactPage;
use modules\contacts\views\contracts\ContactPage as ContactPageContract;

class ContactPageExtended extends ContactPage implements ContactPageContract
{
    ...
}

You can override the specific implementation in the start method.

use modules\contacts\views\contracts\ContactPage as ContactPageContract;
use plugins\contacts_extended\views\ContactPageExtended;
...
protected function start()
{
    ...
    $this->app->singleton(
        // Contract of the original contacts page in the platform
        ContactPageContract::class, 
        // New class for the contacts page in the plugin
        ContactPageExtended::class 
    );
}
...