Layout - Jet\MVC_Layout

Right off the bat, let's say that a layout is actually also a view. So they are view scripts, exactly the same way you can pass data/parameters to them and work with them. Everything that applies to view applies to layout, so I will only explain here what extends and differentiates layout from view.

Layout is a singleton

What we said about views is that you can have as many view instances as you need, and you can include the output of one view in the output of another view. For layout, it's the other way around. As a matter of principle, there has to be just one instance of a layout - a singleton. And this instance is what we work with and, in particular, we pass to the layout the subparts of the output, that is, the content destined for a particular position.

Layout initialization is performed as follows:

MVC_Layout::setCurrentLayout(
    
Factory_MVC::getLayoutInstance(
        
scripts_dir'/some/dir/',
        
script_name'layout-script-name'
    
)
);

And now a few factual things:

  • It is recommended to create an instance via factory.
  • When creating a layout instance, you pass both the name of the directory where the layout scripts are and the name of the layout script (the name of the layout script is the same as the name of the view script). In view, only the directory is passed at initialization, the view script is only determined at render time. The layout needs this information immediately, but even after initialization the name of the view script representing the layout can be changed.
  • If you use Jet MVC, you don't initialize the layout, because the page does that for you.

The layout is then accessed simply as follows:

MVC_Layout::getCurrentLayout()

Layout script

You already know that layout is technically derived from view. So the layout script has the same form and principle as the view script. However, there are a number of extra things in it. Let's take a look at one layout:

<?php
use Jet\MVC_Layout;
use 
Jet\MVC;
use 
Jet\Tr;
use 
Jet\SysConf_URI;

/**
 * @var MVC_Layout $this
 */

require 'parts/init_libs.php';
?>
<!DOCTYPE html>
<html lang="<?= MVC::getLocale()->getLanguage() ?>">
<head>
    <title><?= MVC::base()->getLocalizedDataMVC::getLocale() )->getTitle() ?> : <?= MVC::getPage()->getTitle() ?></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta http-equiv="content-language" content="<?= MVC::getLocale()->getLanguage() ?>"/>
        
    
        <link rel="stylesheet" type="text/css" href="/css/packages/b03da990b885626d8669281b9e41d2f1.css"/>

        <script type="text/javascript" src="/js/packages/e28ddecbd1f561371648b86815345bce.js"></script>


    <link rel="icon" href="<?= SysConf_URI::getImages() ?>favicon.png" type="image/png">
</head>
<body>
<div class="main">
    <div class="header">
        <h1><?= Tr::_'Hello WORLD!' ?></h1>
        <small><?= Tr::_"PHP Jet Example Application" ); ?></small>
    </div>

    <jet_module module="UI.Web" action="breadcrumbNavigation" view="default" is_cacheable="true"/>

    <div class="body">
        <div class="left">
            <jet_module module="UI.Web" action="main_menu" is_cacheable="true"/>

            <jet_layout_position name="left"/>
        </div>
        <div class="center">
            <jet_layout_main_position/>
        </div>
        <div class="right">
            <jet_module module="Test.MVC" action="test_mvc_info" is_cacheable="true"/>

            <jet_layout_position name="right"/>
        </div>
    </div>
</div>
</body>
</html>

Apart from the general style of the familiar view, you will have noticed a few tags starting with jet_*. That's how the determined positions are, but a number of other important things. So let's go over them.

Main positions: jet_layout_main_position

Every layout should have a main position. This position, unlike normal positions, has no defined name.

Position: jet_layout_position

All other positions must already have the name attribute and therefore a specified name. The name of the position can be upper and lower case letters, numbers, spaces, hyphens and underscores.

Position for meta tags: jet_layout_meta_tags

This is a simple position without attributes and specifies the place where the layout will place the generated meta tags determined by the current page.

It is important to note that this operation is performed at the end of processing. Thus, your application models and their controllers can affect the content of meta tags.

Position for required JavaScript files: jet_layout_javascripts

As we'll show later, the way to insert resources into the page from where to pull JavaScript (libraries and your own scripts) is unified, and if the packager is active, CSS and JS packages are created directly. For now, let's just say that this determines where to place the piece of HTML code that JavaScript "pulls in."

Position for required CSS files: jet_layout_css

It's exactly the same as jet_layout_javascripts - only it's CSS related.

Modules: jet_module

And now something interesting. As you already know, the page defines what's on it. But what if you want to put on all pages (with a given layout), for example, breadcrumb navigation, a menu, some general info box, or whatever? Defining that for every page would be really very impractical and annoying. Such content that a given layout should always contain is logically more a matter of the layout than the page. And this is the solution. You can directly define in the layout what application module and what its controller should be in that position.

Let's take a look at one of the tags from the sample layout script:

<jet_module module="UI.Web" action="breadcrumbNavigation" view="default" is_cacheable="true"/>

So it has several attributes some of which are mandatory, others optional and the rest are fully definable by you.

Mandatory attributes are:

The optional attributes are:

  • controller: Defines the name of the controller (not its class, but its name - see the topic page/content). The default value is: 'Main'
  • is_cacheable: Specifies whether the content is static and therefore can be cached. The attribute should be false or true, false is the default value.

All other attributes are optional by you and become content parameters. Thus, they will be available to your controller and you can further specify exactly what you want at that position.

Putting content into positions

Layout may have positions, but logically he doesn't know what should be on them.

If the whole Jet MVC is used, i.e. bases, pages and so on, you don't really even address that (or rather, it's already determined by the definition of the pages and their content).

As mentioned with view, the controller action sends the content back to the page, which passes it directly to the layout. Thus, when you develop an application module, you are exempt from this process.

Even so, you still need to know how to send content directly to the layout. Besides just understanding how things work, you don't always need to use application modules and the whole Jet MVC, because layout can be used on its own without other parts of Jet MVC.

Let's have a look:

MVC_Layout::getCurrentLayout()->addOutputPart(
    
output'<p>Lorem ipsum dolor sit amet</p>',
    
position'left',
    
position_order10
);

You already know that the layout is a singleton, so we get it using the MVC_Layout::getCurrentLayout() method. Let's talk some more about the parameters of the addOutputPart method:

  • output: This is the piece of output (usually a piece of HTML) that you want to place in that position.
  • position: The name of the position you are adding content to. This parameter is optional and if not specified, the default position is the main position.
  • position_order: The order of the content at the target position. This parameter is optional. If the order is not specified, the content is ranked last at that position.

Inserting CSS and JavaScript files

In order to use packagers, you need to unify the insertion of CSS and JavaScript files (such as libraries, but also your CSS and JS). You still have the option to put a script tag in the code with src="//domain/script.js", but then it's impossible to turn many files into one file and speed up page loading.

The correct way to do this is not to insert links directly into the HTML, but to use layout methods that work directly with packagers. Layout will therefore already take care of using the packager and will insert the URLs of the generated packages at the positions defined by the corresponding tags. Or, if the packager is inactive, it will insert the linking of the individual CSS and JS files. Recall that the jet_layout_css and jet_layout_javascripts tags are used to define the positions designated for CSS and JS.

Now let's look at how to tell the layout "I want this CSS and JS files".

The requireJavascriptFile, requireMainJavascriptFile and requireCssFile, requireMainCssFile methods are used for inserting. The common parameter of all methods is the URL of the requested file.

What is the difference between those methods with and without Main in the name? The files are divided into two groups, namely main and regular. The main ones are loaded first (or placed in the package) and then the regular ones. You can thus ensure that, for example, a key library is stretched before a script that depends on it. Further, the order within these two groups is determined by the order in which the URL was inserted. This applies to both CSS and JS

The CSS embedding methods have a media parameter. This corresponds to the media attribute of the link tag. Thus, you can insert CSS for different media and of course the packages will be divided in this way.

And where to do the embedding, i.e. how to directly define what CSS and JS you need? Let's take another look at a small piece of sample layout script. There you will find this: require 'parts/init_libs.php'; This is the way that has worked for me. That is, to make an embeddable layout script and define in it what files I need. The script can then be like this:

<?php

use Jet\MVC_Layout;
use 
Jet\SysConf_URI;

/**
 * @var MVC_Layout $this
 */

$this->requireMainCssFile'https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css' );
$this->requireMainCssFile'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css' );
$this->requireMainCssFileSysConf_URI::getCss() . 'flags.css' );
$this->requireMainCssFileSysConf_URI::getCss() . 'web_main.css?v=1' );

$this->requireMainJavascriptFile'https://code.jquery.com/jquery-3.5.1.js' );

Please note that the layout is again accessed via $this, because the code becomes part of the layout script where it is inserted.

But if, for example, one of your application modules needs some other library, then it is no problem to call at any time:

MVC_Layout::getCurrentLayout()->requireJavascriptFile('//domain/something.js');

Layout Processors

In certain situations, it may be necessary to further modify the final output - i.e. to further modify the already built layout with added content. For example, it is possible to implement a system that adds links to pages based on code. Let's take an example:

namespace JetApplication;

use 
Jet\MVC;
use 
Jet\MVC_Layout;
use 
Jet\MVC_Layout_OutputPostprocessor;

$layout MVC_Layout::getCurrentLayout();

$layout->addOutputPostprocessor( new class($layout) extends MVC_Layout_OutputPostprocessor {
    
    public function 
getId(): string
    
{
        return 
'page_URL_creator';
    }
    
    public function 
processstring $output ): string
    
{
        if(!
preg_match_all('/%URL:([a-z\-0-9A-Z]+)%/'$output$matchesPREG_SET_ORDER)) {
            return 
$output;
        }
        
        foreach( 
$matches as $m ) {
            
$orig_str $m[0];
            
$page_id $m[1];
            
            
$page MVC::getPage$page_id );
            
$URL $page?->getURL()??'';
            
            
$output str_replace($orig_str$URL$output);
        }
        
        return 
$output;

    }
});

This example finds the following code in the output: %URL:about-us% and replaces it with a link to a page with the about-us ID (if page exists).

The system is very simple - the output postprocessor is a simple class extending the abstract class Jet\MVC_Layout_OutputPostprocessor and passing an instance of this class to the layout.

You can further work with postprocessors - see the list of methods.

Overview of the Jet\MVC_Layout class method

General

Method Meaning of
public static getCurrentLayout(
): MVC_Layout|null
Returns the current instance of the layout.
public static setCurrentLayout(
MVC_Layout $current_layout
): void
Sets the current layout instance.
public __construct(
string $scripts_dir,
string $script_name
)
Constructor parameters:
  • scripts_dir: Full path to the directory containing the layout scripts.
  • script_name: The name of the layout script.
public addOutputPart(
string $output,
string|null $position = null,
int|null $position_order = null
): void
Adds an output to the specified position. Parameters:
  • output: This is the piece of output (usually a piece of HTML) that you want to place in that position.
  • position: The name of the position to which you are adding content. This parameter is optional and if not specified, the default position is the main position.
  • position: Content ranking on the target position. This parameter is optional. If the order is not specified, the content is ranked as the last content at the given position.
public render(
): string
Generates the final output including the placement of all content at its positions and returns the result as a return value. The result is not automatically sent to the output.
public parseContent(
string $result
): MVC_Page_Content_Interface[]
Finds all jet_module tags in the layout script and returns prepared instances of content.

Methods for inserting CSS and JavaScript files

Method Meaning of
public requireJavascriptFile(
string $URI
): void
Requirement to insert JavaScript file with normal priority.
public requireMainJavascriptFile(
string $URI
): void
Request to insert a JavaScript file with high priority.
public requireCssFile(
string $URI,
string $media = ''
): void
Requirement to insert CSS file with normal priority.
public requireMainCssFile(
string $URI,
string $media = ''
): void
Requirement to insert high priority CSS file.

Output postprocessor methods

Method Meaning of
public addOutputPostprocessor(
MVC_Layout_OutputPostprocessor $postprocessor
) : void
Adds a postprocessor to the layout.
public getPostprocessor(
string $id
) : null|MVC_Layout_OutputPostprocessor
Returns an instance of a specific postprocessor based on id, or null if no such postprocessor is defined.
public removePostprocessor(
string $id
) : void
Removes the postprocessor based on id.
public performPostprocess(
string $output
) : string
Performs the operation of the preprocessors. This method is called automatically by the layout and it is not necessary to ensure that it is called.
Previous chapter
View - Jet\MVC_View
Next chapter
Jet\MVC_Layout_OutputPostprocessor