Netgen Layouts Documentation

Netgen Layouts is an easy and flexible way of managing all page layouts on your website. Use it to manage and maintain the website layout structure in a slick and agile but at the same time extensible way.

Note

This documentation assumes you have a working knowledge of the Symfony Framework. If you’re not familiar with Symfony, please start with reading the Quick Tour from the Symfony documentation.

Note

Netgen Layouts does not force or otherwise define where your configuration, code, or templates live. Examples in this documentation will use YAML as a configuration format, but you can ofcourse use any format available in Symfony and you can place templates in folder structure that fits your needs best.

Reference

Reference

Install instructions

To install Netgen Layouts, you need to have an existing Symfony full stack installation. For example, Netgen Layouts can be installed on eZ Platform, Sylius or Symfony Demo app.

Use Composer

Add the following to your composer.json. During installation, you will be asked for username and password to packagist.netgen.biz, make sure you have them ready:

{
    "repositories": [
        { "type": "composer", "url": "https://packagist.netgen.biz" }
    ]
}

Execute the following from your installation root:

$ composer require netgen/block-manager

Note

If you’re installing Netgen Layouts on eZ Platform, execute the following instead:

$ composer require netgen/block-manager-ezpublish netgen/platform-ui-layouts-bundle

Note

If you use Netgen Admin UI, you can also install netgen/admin-ui-layouts-bundle package and activate NetgenBundleAdminUILayoutsBundleNetgenAdminUILayoutsBundle bundle in your AppKernel to enable integration of Netgen Layouts into Netgen Admin UI.

Activate the bundles

Add the following bundles to your kernel:

new Knp\Bundle\MenuBundle\KnpMenuBundle(),
new FOS\HttpCacheBundle\FOSHttpCacheBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Netgen\Bundle\CoreUIBundle\NetgenCoreUIBundle(),
new Netgen\Bundle\ContentBrowserBundle\NetgenContentBrowserBundle(),
new Netgen\Bundle\ContentBrowserUIBundle\NetgenContentBrowserUIBundle(),
new Netgen\Bundle\BlockManagerBundle\NetgenBlockManagerBundle(),
new Netgen\Bundle\BlockManagerUIBundle\NetgenBlockManagerUIBundle(),
new Netgen\Bundle\BlockManagerAdminBundle\NetgenBlockManagerAdminBundle(),

Note

If you’re installing Netgen Layouts on eZ Platform, activate the following bundles too:

new Netgen\Bundle\EzPublishBlockManagerBundle\NetgenEzPublishBlockManagerBundle(),
new Netgen\Bundle\PlatformUILayoutsBundle\NetgenPlatformUILayoutsBundle(),

Add the following bundle to your kernel only for dev environment:

new Netgen\Bundle\BlockManagerDebugBundle\NetgenBlockManagerDebugBundle(),
Import database tables

Execute the following from your installation root to import Netgen Layouts database tables:

$ php app/console doctrine:migrations:migrate --configuration=vendor/netgen/block-manager/migrations/doctrine.yml
Routing and assets

Add the following routes to your main routing config file:

netgen_block_manager:
    resource: "@NetgenBlockManagerBundle/Resources/config/routing.yml"
    prefix: "%netgen_block_manager.route_prefix%"

netgen_content_browser:
    resource: "@NetgenContentBrowserBundle/Resources/config/routing.yml"
    prefix: "%netgen_content_browser.route_prefix%"

Run the following from your installation root to symlink assets:

$ php app/console assets:install --symlink --relative

Note

If you’re installing Netgen Layouts on eZ Platform, you also need to dump Assetic assets:

$ php app/console assetic:dump
Adjusting your full views

All of your full views need to extend ngbm.layoutTemplate variable (see below for example). If layout was resolved, this variable will hold the name of the template belonging to the resolved layout. In case when layout was not resolved, it will hold the name of your main pagelayout template (the one your full views previously extended). This makes it possible for your full view templates to be fully generic, that is, not depend whether there is a resolved layout or not:

{% extends ngbm.layoutTemplate %}

{% block content %}
    {# My full view code #}
{% endblock %}
Adjusting your base pagelayout template

To actually display the resolved layout template in your page, you need to modify your main pagelayout template to include a Twig block named layout which wraps everything between your opening and closing <body> tag:

<body>
    {% block layout %}
        {# Other Twig code #}

        {% block content %}{% endblock %}

        {# Other Twig code #}
    {% endblock %}
</body>

There are two goals to achieve with the above Twig block:

  • If no layout could be resolved for current page, your full view templates will just keep on working as before
  • If layout is resolved, it will use the layout block, in which case content Twig block and other Twig code will not be used. You will of course need to make sure that in this case, all your layouts have a full view block in one of the zones which will display your content Twig block from full view templates
Configuring the pagelayout

As written before, Netgen Layouts replaces the pagelayout in your full views with its dynamic variable called ngbm.layoutTemplate. It basically injects itself between rendering of your full view and your pagelayout. Since your full views do not extend from your main pagelayout any more, Netgen Layouts needs to know what was your original full view to fallback to it. You can configure your pagelayout in Netgen Layouts config like this:

netgen_block_manager:
    pagelayout: '@App/pagelayout.html.twig'

Note

If you’re installing Netgen Layouts on eZ Platform, your main pagelayout is taken from existing eZ Platform configuration, so you can skip this step.

Update Varnish VCL configuration

To enable caching and later cache clearing of block and layout HTTP caches, you will need to use Varnish. To make the cache clearing work, you need to modify your Varnish VCL and add the following rules somewhere in your vcl_recv function.

Note

If you’re using eZ Platform and the VCL supplied by it, the best place to put this is in ez_purge function (which is called from vcl_recv), right after if (req.http.X-Location-Id) { ... } block.

For Varnish 3:

if (req.http.X-Layout-Id) {
    ban( "obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
    }
    error 200 "Banned";
}

if (req.http.X-Block-Id) {
    ban( "obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
    }
    error 200 "Banned";
}

For Varnish 4 and later:

if (req.http.X-Layout-Id) {
    ban("obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
    }
    return (synth(200, "Banned"));
}

if (req.http.X-Block-Id) {
    ban("obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
    }
    return (synth(200, "Banned"));
}

Configuration reference

Layout type configuration

The following lists all available layout type configuration options:

netgen_block_manager:
    layout_types:
        # Layout type identifier
        my_layout:
            # The switch to enable or disable showing of the layout type in the interface
            enabled: true
            # Layout type name, required
            name: 'My layout'

            # A collection of zones available in the layout type, required
            zones:
                # Zone identifier
                left:
                    # Zone name, required
                    name: 'Left'

                    # List of block definitions which are allowed in the zone
                    allowed_block_definitions: []
                right:
                    name: 'Right'
                    allowed_block_definitions: [title]
Block definition configuration

The following lists all available block definition configuration options:

netgen_block_manager:
    block_definitions:
        # Block definition identifier
        my_block:
            # The switch to enable or disable showing of all block types
            # related to block definition in the interface
            enabled: true

            # Identifier of a handler which the block definition will use.
            # The value used here needs to be the same as the identifier
            # specified in handler tag in Symfony DIC.
            # If undefined, the handler with the same identifier as the
            # block definition itself will be used.
            handler: ~

            # Block definition name, required
            name: 'My block'

            # The list of collections the block has. Only one collection named
            # "default" is supported for now. Omit the config to disable the collection.

            collections:
                default:
                    # The list of valid items for the collection. Use null to
                    # allow all items, an empty array to disable adding manual
                    # items, and a list of items to limit the collection to
                    # to only those items.
                    valid_item_types: null

                    # The list of valid query types for the collection. Use null to
                    # allow all query types, an empty array to disable dynamic collections,
                    # and a list of query types to limit the collection to
                    # only those query types.
                    valid_query_types: null

            # This controls which forms will be available to the block.
            # You can either enable the full form, or content and design forms.
            # If full form is enabled, all block options in the right sidebar
            # in the Block Manager app will be shown at once, otherwise,
            # Content and Design tabs will be created in the right sidebar
            forms:
                full:
                    enabled: true
                    type: Netgen\BlockManager\Block\Form\FullEditType
                design:
                    enabled: false
                    type: Netgen\BlockManager\Block\Form\DesignEditType
                content:
                    enabled: false
                    type: Netgen\BlockManager\Block\Form\ContentEditType

            # The list of all view types in a block definition, required
            view_types:

                # View type identifier
                my_view_type:
                    # Switch to control if the view type is shown in the interface or not
                    enabled: true

                    # View type name, required
                    name: 'My view type'

                    # The list of allowed item view types for this block view type
                    item_view_types:

                        # Item view type identifier
                        my_item_view_type:
                            # Switch to control if the item view type is shown in the interface or not
                            enabled: true

                            # Item view type name, required
                            name: 'My item view type'

                    # Use this configuration to control which block parameters will be displayed
                    # when editing a block in specified view type. Use null to display all
                    # parameters, an empty array to hide all parameters and a list of parameter
                    # names to list specific parameters to show. You can also prefix the parameter
                    # with exclamation mark to exclude it.
                    valid_parameters: null
Block type and block type group configuration

The following lists all available block type and block type group configuration options:

netgen_block_manager:
    block_types:
        # Block type identifier
        my_block_type:
            # The switch to enable or disable showing the block type in the interface
            enabled: true

            # Block type name, if undefined, will use the name of a block definition
            # with the same identifier as the block type itself.
            name: ~

            # Block definition identifier of the block type, if undefined, will use the
            # block definition with the same identifier as the block type itself.
            definition_identifier: ~

            # Default values for the block
            defaults:

                # Default name (label) of the block
                name: ''

                # Default view type of the block. If empty, will use the first available view type.
                view_type: ''

                # Default item view type of items inside the block. If empty, will use the first
                # available item view type in regards to chosen block view type.
                item_view_type: ''

                # Default values for block parameters
                parameters:
                    param1: value1
                    param2: value2

    block_type_groups:

        # Block type group identifier
        my_group:

            # The switch to enable or disable showing the block type group in the interface
            enabled: true

            # Block type group name, required
            name: 'My group'

            # List of block types to show inside the group
            block_types: [my_type_1, my_type_2]

View layer

All entities in Netgen Layouts (layouts, blocks, mapping rules, forms) are rendered through the same API called the view layer. View layer revolves around three concepts:

  • View
  • Match configuration
  • View context
View

All entities in Netgen Layouts specify their own view, which defines what variables will be available in the Twig templates when the entity is rendered. For example, layout view (identified by layout_view identifier), specifies that all layout templates will have a variable called layout, which will hold the currently rendered template, while item view (identified by item_view identifier), specifies that all block item templates will have two variables: item, which holds the currently rendered block item, and view_type, which will hold the item view type with which the item is rendered.

There are a handful predefined views available in Netgen Layouts, but most interesting ones are ofcourse views for rendering layouts, blocks and block items. The other ones are used by administration interface of Netgen Layouts (list of mappings, layouts and shared layouts) and are rarely needed, e.g. when developing custom mapping targets and conditions.

Match configuration

Match configuration is a single rule which specifies which template will be rendered for a specific view when all configured conditions are met. Those conditions are called matches, and services which perform the matching process are called matchers.

View context

View context is a set of match configuration rules and each view context is used in different parts of Netgen Layouts. View contexts are what makes it possible for example to use different templates for frontend and backend for your layout types and blocks. Match configurations in a view context are processed sequentially and template from first configuration that matches the rules will be used to render the value.

Netgen Layouts uses four view contexts to render its’ templates: default for rendering the frontend templates, api for rendering the Block Manager app templates and admin and value view contexts for rendering the administration interface.

Configuring the view layer

The following configuration shows an example on how to configure layout_view and block_view views, specifying some match rules in two different view contexts (default and api).

netgen_block_manager:
    view:
        # The identifier of the view
        layout_view:
            # The name of the view context
            default:
                # The identifier of a match configuration (unused, but needs to specified in YAML)
                # The first match configuration for which all conditions match will be picked up and used to render the view
                layout_1:
                    # The template that will be used when conditions specified below match
                    template: "@App/layout/layout_1.html.twig"

                    # The list of custom parameters that will be injected in the template if the conditions specified below match
                    parameters:
                        param1: value1
                        param2: value2

                    # The list of conditions that will need to match for this rule to be used
                    match:
                        layout\type: layout_1
                layout_2:
                    template: "@App/api/layout/layout_2.html.twig"
                    match:
                        layout\type: layout_2
        block_view:
            api:
                title:
                    template: "@App/block/title.html.twig"
                    match:
                        block\definition: title
                text:
                    template: "@App/api/block/text.html.twig"
                    match:
                        block\definition: text

Tip

If you use eZ Platform, view configuration is siteaccess aware. This means you can have different templates for different siteaccesses or siteaccess groups for the same block views or layout types.

For example, you can use the following config to use two different templates for my_layout layout type for eng and cro siteaccesses:

netgen_block_manager:
    system:
        eng:
            view:
                layout_view:
                    default:
                        my_layout:
                            template: "@App/layouts/my_layout_eng.html.twig"
                            match:
                                layout\type: my_layout
        cro:
            view:
                layout_view:
                    default:
                        my_layout:
                            template: "@App/layouts/my_layout_cro.html.twig"
                            match:
                                layout\type: my_layout
Custom view contexts

If, for some reason, you would want to render some layouts or blocks by hand in your PHP code, you can create custom view contexts (and custom templates, ofcourse) on the fly and use them directly when rendering the layouts or blocks, without touching and compromising the existing frontend templates.

So for example, to render a block view with your custom view context called my_context, you would define a configuration similar to this:

netgen_block_manager:
    view:
        block_view:
            my_context:
                title:
                    template: "@App/block/my_context/title.html.twig"
                    match:
                        block\definition: title

and then somewhere in your controller:

$block = $this->get('netgen_block_manager.api.service.block')->loadBlock(42);
return $this->get('netgen_block_manager.view.builder')->buildView($block, 'my_context');

Note

You don’t need to return Symfony Response object from your controllers, because Netgen Layouts will create and render it on the fly with a built in event listener.

List of built in views

The following lists all built in views with their identifiers, supported interfaces and the list of variables available in the rendered template.

block_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\Block\Block interface.

Available variables
Variable name Description
block The block which is being rendered
layout_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\Layout\Layout interface.

Available variables
Variable name Description
layout The layout which is being rendered

Warning

Frontend templates for layouts (default context) are an exception and are not rendered through the Netgen Layouts view layer. Instead, they are rendered by extending from a special ngbm.layoutTemplate variable, available in your full view templates. Because of that, in frontend layout templates, layout variable is not available. Instead, the rendered layout is accessed by using ngbm.layout variable.

item_view

This view is used to render entities implementing Netgen\BlockManager\Item\ItemInterface interface.

Available variables
Variable name Description
item The item which is being rendered
view_type Item view type that the item is being rendered with
parameter_view

This view is used to render entities implementing Netgen\BlockManager\Parameters\ParameterValue interface.

Available variables
Variable name Description
parameter The parameter which is being rendered

Note

While rendering other views will throw an exception if there is no template match in requested view context, this view will fallback to the default view context. This is due to the fact that in most of the cases, rendering a block parameter will look exactly the same in the backend and in the frontend.

This makes it possible to only specify one match configuration rule (in the default context) and one template to render the parameter in any view context.

placeholder_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\Block\Placeholder interface.

Available variables
Variable name Description
placeholder The placeholder which is being rendered
block The block to which the rendered placeholder belongs
rule_condition_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\LayoutResolver\Condition interface.

Available variables
Variable name Description
condition The condition which is being rendered
rule_target_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\LayoutResolver\Target interface.

Available variables
Variable name Description
target The target which is being rendered
rule_view

This view is used to render entities implementing Netgen\BlockManager\API\Values\LayoutResolver\Rule interface.

Available variables
Variable name Description
rule The rule which is being rendered
form_view

This view is used to render entities implementing Symfony\Component\Form\FormView interface.

Available variables
Variable name Description
form The Symfony form view which is being rendered
form_object The underlying Symfony form from which the view was built
List of built in matchers

The following lists all built in view matchers. As a rule of thumb, all matchers accept either a scalar or an array of scalars as their value. If an array is provided, the matcher will match if any of the values in the provided array is matched.

Note

While you can use all matchers in all views, most of the combinations do not make sense and will simply not match. For example, using layout\type matcher in block_view view will never match since block_view renders a block, while layout\type matches on layout type of a rendered layout.

block\definition

Matches on block definition of the rendered block. Used in block_view view.

Example
netgen_block_manager:
    view:
        block_view:
            default:
                title:
                    template: '@App/block/title.html.twig'
                    match:
                        block\definition: title
block\view_type

Matches on view type of the rendered block. Used in block_view view.

Tip

This matcher is usually used with block\definition matcher to specify for which exactly block and view type will the template be used.

Example
netgen_block_manager:
    view:
        block_view:
            default:
                title\title:
                    template: '@App/block/title.html.twig'
                    match:
                        block\definition: title
                        block\view_type: title
layout\type

Matches on layout type of the rendered layout. Used in layout_view view.

Example
netgen_block_manager:
    view:
        layout_view:
            default:
                layout_3:
                    template: '@App/layout/layout_3.html.twig'
                    match:
                        layout\type: layout_3
layout\shared

Matches on the shared flag of the rendered layout. Used in layout_view view.

Note

While this matcher accepts an array as its value as all other matchers do, it will discard any other value in the array except the first one. This makes sense, since the only valid value for this matcher is a boolean.

Example
netgen_block_manager:
    view:
        layout_view:
            default:
                all_shared_layouts:
                    template: '@App/layout/shared_layout.html.twig'
                    match:
                        layout\shared: true
item\value_type

Matches on the type of rendered item. Used in item_view view.

Example
netgen_block_manager:
    view:
        item_view:
            default:
                my_item:
                    template: '@App/item/my_item.html.twig'
                    match:
                        item\value_type: my_item
item\view_type

Matches on item view type of the rendered item. Used in item_view view.

Tip

This matcher is usually used with item\value_type matcher to specify for which exactly item and item view type will the template be used.

Example
netgen_block_manager:
    view:
        item_view:
            default:
                my_item\standard_with_intro:
                    template: '@App/item/my_item.html.twig'
                    match:
                        item\value_type: my_item
                        item\view_type: standard_with_intro
parameter\type

Matches on type of the rendered parameter. Used in parameter_view view.

Example
netgen_block_manager:
    view:
        parameter_view:
            default:
                link:
                    template: '@App/parameter/link.html.twig'
                    match:
                        parameter\type: link
rule_condition\type

Matches on type of the rendered condition. Used in rule_condition_view view.

Example
netgen_block_manager:
    view:
        rule_condition_view:
            default:
                query_parameter:
                    template: '@App/condition/query_parameter.html.twig'
                    match:
                        rule\condition_type: query_parameter
rule_target\type

Matches on type of the rendered target. Used in rule_target_view view.

Example
netgen_block_manager:
    view:
        rule_target_view:
            default:
                route:
                    template: '@App/target/route.html.twig'
                    match:
                        rule\target_type: route
form\type

Matches on type of the Symfony form which is rendered. Used in form_view view.

Tip

This matcher is usually used with other form\* matchers if you wish, for example, to separate templates for rendering block create and block edit forms.

Example
netgen_block_manager:
    view:
        form_view:
            default:
                layout\edit:
                    template: '@App/layout/edit.html.twig'
                    match:
                        form\type: Netgen\BlockManager\Layout\Form\EditType
form\block\definition

Matches on block definition of a block which is edited through the Symfony form. Used in form_view view.

Example
netgen_block_manager:
    view:
        form_view:
            default:
                block\title\edit:
                    template: '@App/block/title/edit.html.twig'
                    match:
                        form\type: Netgen\BlockManager\Block\Form\FullEditType
                        form\block\definition: title
form\query\type

Matches on query type of a query which is edited through the Symfony form. Used in form_view view.

Example
netgen_block_manager:
    view:
        form_view:
            default:
                query\my_query\edit:
                    template: '@App/query/my_query/edit.html.twig'
                    match:
                        form\type: Netgen\BlockManager\Collection\Query\Form\FullEditType
                        form\query\type: my_query
form\config\config_key

Matches on the config key of a config which is edited through the Symfony form. Used in form_view view.

Tip

This matcher is usually used with form\config\value_type matcher because most of the time, forms for rendering different aspects of value configuration will be different.

Example
netgen_block_manager:
    view:
        form_view:
            default:
                config\block\http_cache\edit:
                    template: '@App/config/block/http_cache/edit.html.twig'
                    match:
                        form\type: Netgen\BlockManager\Config\Form\EditType
                        form\config\value_type: Netgen\BlockManager\API\Values\Block\Block
                        form\config\config_key: http_cache
form\config\value_type

Matches on the type of value for which the config is edited through the Symfony form. Used in form_view view.

Tip

This matcher is usually used with form\config\config_key matcher because most of the time, forms for rendering different aspects of value configuration will be different.

Example
netgen_block_manager:
    view:
        form_view:
            default:
                config\block\http_cache\edit:
                    template: '@App/config/block/http_cache/edit.html.twig'
                    match:
                        form\type: Netgen\BlockManager\Config\Form\EditType
                        form\config\value_type: Netgen\BlockManager\API\Values\Block\Block
                        form\config\config_key: http_cache

Parameter types

When adding a parameter to the block or a query in your block definition or a query type, you can specify the parameter name, the type of the parameter and parameter options. This is achieved through an object called parameter builder which is provided to you in your block definition or a query type.

Parameter builder has methods for working with the parameters: adding, removing and fetching the parameters as well as setting parameter options.

When adding parameters to block definitions or queries, referencing the parameter type is done by specifying FQCN of the parameter type.

Common parameter options

In addition to the list of options specific to each parameter type, all types have four common options: required, default_value, group and label.

required

type: bool, required: No, default value: false

Specifies if the parameter value is required or not.

default_value

type: mixed, required: No, default value: null

Specifies the default value of the parameter.

group

type: array of string values, required: No, default value: array()

Specifies the parameter group. Groups are a generic concept when working with parameters. Netgen Layouts does not enforce or otherwise define how you use parameter groups. Internally, Netgen Layouts uses groups in block definitions to specify to which form (content or design) the parameter belongs and in query types to specify which parameters are advanced and hidden by default.

label

type: string, false or null, required: No, default value: null

Specifies the human readable name of the parameter. In Netgen Layouts this label is for example used in Symfony forms which edit blocks or queries.

Tip

Use false to disable parameter label in Symfony forms. If null is specified, the label will be generated by using Symfony translation component from the parameter name.

List of built in parameter types

The following lists all parameter types built into Netgen Layouts together with the options available in each parameter type.

BooleanType
Identifier boolean
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\BooleanType
Valid value A boolean
Overriden options
default_value

If the parameter is required, the default parameter value will be set to false, if not specified otherwise.

Compound\BooleanType
Identifier compound_boolean
Available options
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\Compound\BooleanType
Valid value A boolean

This parameter is a special one in a manner that it can hold other parameters.

The main purpose of the parameter is not functional, but presentational. That is, it allows building Symfony forms that have parameters which can be shown and hidden by checking and un-checking the checkbox input.

The value of the parameter is preserved with all other parameters and its sub-parameters in a flat list, without hierarchy.

Available options
reverse

type: bool, required: No, default value: false

Specifies that the sub-parameters will be shown in the Symfony form when the parameter is unchecked, rather than when it’s checked.

Overriden options
default_value

If the parameter is required, the default parameter value will be set to false, if not specified otherwise.

ChoiceType
Identifier choice
Available options
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\ChoiceType
Valid value One (or more) of the values specified in options option
Available options
multiple

type: bool, required: No, default value: false

Specifies if the parameter type will accept multiple values.

options

type: non empty array or callable, required: Yes

Specifies the list of values allowed in the parameter. The list of values needs to be a hash where keys represent option label and value represents option value.

If callback is used, the callback needs to return the array in the same format.

Overriden options
default_value

If the parameter is required, the default parameter value will be equal to the first value in the list of allowed values, if not specified otherwise.

EmailType
Identifier email
Class Netgen\BlockManager\Parameters\ParameterType\EmailType
Valid value A valid e-mail address
HtmlType
Identifier html
Class Netgen\BlockManager\Parameters\ParameterType\HtmlType
Valid value A valid HTML markup

This parameter type represents a valid HTML markup. The reason why there’s a custom parameter type is HTML filtering. This parameter type will filter all input to get a valid HTML markup which is free of potential security issues, like XSS.

IdentifierType
Identifier identifier
Class Netgen\BlockManager\Parameters\ParameterType\IdentifierType
Valid value A string matching the ^[A-Za-z]([A-Za-z0-9_])*$ regex
IntegerType
Identifier integer
Available options
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\IntegerType
Valid value An integer
Available options
min

type: int, required: No, default value: null

Specifies the minimum value of the parameter.

max

type: int, required: No, default value: null

Specifies the maximum value of the parameter.

Overriden options
default_value

If the parameter is required, the default parameter value will be equal to the min option, if not specified otherwise.

ItemLinkType
Identifier item_link
Available options
Class Netgen\BlockManager\Parameters\ParameterType\ItemLinkType
Valid value The identifier of an existing value in the form value_type://value, for example ezlocation://42
Available options
value_types

type: array of string values, required: No, default value: All enabled value types

The list of accepted value types.

LinkType
Identifier link
Available options
Class Netgen\BlockManager\Parameters\ParameterType\LinkType
Valid value A structure containing valid URL, e-mail address, phone number or internal link, together with link suffix and target. This structure is represented by Netgen\BlockManager\Parameters\Value\LinkValue object.

This parameter type represents a link. Multiple link types are supported:

  • URL
  • E-mail address
  • Phone number
  • Internal link (link to valid value of an item)
Available options
value_types

type: array of string values, required: No, default value: All enabled value types

The list of accepted value types.

NumberType
Identifier number
Available options
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\NumberType
Valid value Any number (integer or float)
Available options
min

type: number, required: No, default value: null

Specifies the minimum value of the parameter.

max

type: number, required: No, default value: null

Specifies the maximum value of the parameter.

scale

type: int, required: No, default value: 3

The number of digits after the decimal point.

Overriden options
default_value

If the parameter is required, the default parameter value will be equal to the min option, if not specified otherwise.

RangeType
Identifier range
Available options
Overridden options
Class Netgen\BlockManager\Parameters\ParameterType\RangeType
Valid value An integer

This parameter type represents a range. While in IntegerType, min and max options are optional, here, they are required.

Available options
min

type: int, required: Yes

Specifies the minimum value of the parameter.

max

type: int, required: Yes

Specifies the maximum value of the parameter.

Overriden options
default_value

If the parameter is required, the default parameter value will be equal to the min option, if not specified otherwise.

TextLineType
Identifier text_line
Class Netgen\BlockManager\Parameters\ParameterType\TextLineType
Valid value A string without line breaks
TextType
Identifier text
Class Netgen\BlockManager\Parameters\ParameterType\TextType
Valid value A multi-line string
UrlType
Identifier url
Class Netgen\BlockManager\Parameters\ParameterType\UrlType
Valid value A valid URL
ContentTypeType
Identifier ez_content_type
Available options
Class Netgen\BlockManager\Ez\Parameters\ParameterType\ContentTypeType
Valid value One (or more) of the valid eZ Platform content types

This parameter allows to input one or more existing eZ Platform content types as its value. The parameter automatically shows the list of all content types in eZ Platform.

This parameter type is available only if using Netgen Layouts on top of eZ Platform.

Available options
multiple

type: bool, required: No, default value: false

Specifies if the parameter type will accept multiple values.

LocationType
Identifier ezlocation
Class Netgen\BlockManager\Ez\Parameters\ParameterType\LocationType
Valid value ID of an existing eZ Platform location

This parameter allows to input the ID of an existing eZ Platform location as its value.

This parameter type is available only if using Netgen Layouts on top of eZ Platform.

TagsType
Identifier eztags
Available options
Class Netgen\BlockManager\Ez\Parameters\ParameterType\TagsType
Valid value Array of IDs of existing tags in Netgen Tags bundle

This parameter allows to input the list of existing IDs of tags available in Netgen Tags.

Requires Netgen Tags Bundle to be activated work.

Available options
min

type: int, required: No, default value: null

Specifies the minimum number of tags that can be set as value. Use null to disable the limit.

max

type: int, required: No, default value: null

Specifies the maximum number of tags that can be set as value. Use null to disable the limit.

Twig usage

Netgen Layouts includes a number of Twig functions and tags to make it easier to work with layouts and blocks in your Twig templates.

Some of the functions are used by the frontend and backend layout and block templates, while others are used exclusively in the administration interface of Netgen Layouts.

List of built in Twig functions

The following lists all Twig functions built into Netgen Layouts.

ngbm_render_block

This function is used to render a block:

{{ ngbm_render_block(block) }}

This will render the provided block in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the block template:

{# layout.html.twig #}

{{ ngbm_render_block(block, {'the_answer': 42}) }}

{# block.html.twig #}

{{ the_answer }}

Finally, you can render the block with a view context different from the current one:

{{ ngbm_render_block(block, {}, 'my_context') }}
ngbm_render_layout

Warning

Since all frontend templates for layouts use ngbm.layout variable to access the layout instead of the default layout variable, this function cannot be used by default to render the layouts in the frontend (i.e. in default view context). Instead, it is available for usage in Netgen Layouts administration interface and for rendering the layout with custom view contexts.

This function is used to render a layout:

{{ ngbm_render_layout(layout) }}

This will render the provided layout in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the layout template:

{# my.html.twig #}

{{ ngbm_render_layout(layout, {'the_answer': 42}) }}

{# layout.html.twig #}

{{ the_answer }}

Finally, you can render the layout with a view context different from the current one:

{{ ngbm_render_layout(layout, {}, 'my_context') }}
ngbm_render_item

This function is used to render a block item.

In addition to the item you’re rendering, you need to provide the item view type with which you wish to render the item:

{{ ngbm_render_item(item, 'overlay') }}

This will render the provided item in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the item template:

{# block.html.twig #}

{{ ngbm_render_item(item, 'overlay', {'the_answer': 42}) }}

{# item.html.twig #}

{{ the_answer }}

Tip

Normally, parameters provided here are not transferred to content views in eZ Platform, but only to the item template, which in case of eZ Platform is only a proxy to eZ content view layer. However, you can use a special parameter called ezparams whose contents will be transferred. For example:

{# block.html.twig #}

{{ ngbm_render_item(item, 'overlay', {'ezparams': {'the_answer': 42}}) }}

{# overlay.html.twig from eZ Platform #}

{{ the_answer }}

Finally, you can render the item with a view context different from the current one:

{{ ngbm_render_item(item, 'overlay', {}, 'my_context') }}
ngbm_render_parameter

This function is used to render a parameter:

{{ ngbm_render_parameter(parameter) }}

This will render the provided parameter in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the parameter template:

{# block.html.twig #}

{{ ngbm_render_parameter(parameter, {'the_answer': 42}) }}

{# parameter.html.twig #}

{{ the_answer }}

Finally, you can render the parameter with a view context different from the current one:

{{ ngbm_render_parameter(parameter, {}, 'my_context') }}
ngbm_render_placeholder

This function is used to render a placeholder of a container block.

To render the placeholder, you need to provide the block from which you want to render the placeholder, as well as placeholder identifier:

{{ ngbm_render_placeholder(block, 'left') }}

This will render the provided placeholder in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the placeholder template:

{# block.html.twig #}

{{ ngbm_render_placeholder(block, 'left', {'the_answer': 42}) }}

{# placeholder.html.twig #}

{{ the_answer }}

Finally, you can render the placeholder with a view context different from the current one:

{{ ngbm_render_placeholder(block, 'left', {}, 'my_context') }}
ngbm_render_rule

This function is used to render a rule:

{{ ngbm_render_rule(rule) }}

This will render the provided rule in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the rule template:

{# my.html.twig #}

{{ ngbm_render_rule(rule, {'the_answer': 42}) }}

{# rule.html.twig #}

{{ the_answer }}

Finally, you can render the rule with a view context different from the current one:

{{ ngbm_render_rule(rule, {}, 'my_context') }}
ngbm_render_rule_target

This function is used to render a rule target:

{{ ngbm_render_rule_target(target) }}

This will render the provided target in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the target template:

{# my.html.twig #}

{{ ngbm_render_rule_target(target, {'the_answer': 42}) }}

{# target.html.twig #}

{{ the_answer }}

Finally, you can render the target with a view context different from the current one:

{{ ngbm_render_rule_target(target, {}, 'my_context') }}
ngbm_render_rule_condition

This function is used to render a rule condition:

{{ ngbm_render_rule_condition(condition) }}

This will render the provided condition in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the condition template:

{# my.html.twig #}

{{ ngbm_render_rule_condition(condition, {'the_answer': 42}) }}

{# condition.html.twig #}

{{ the_answer }}

Finally, you can render the condition with a view context different from the current one:

{{ ngbm_render_rule_condition(condition, {}, 'my_context') }}
ngbm_render_value_object

This function is used to render any value object supported by Netgen Layouts view layer. All other rendering extensions reuse this function in one form or another.

To render the value object (for example, a block), just transfer it to the function:

{{ ngbm_render_value_object(block) }}

This will render the provided value object in the view context of the template from which you called the function or in the default view context if the calling template is not rendered by the Netgen Layouts view layer.

You can transfer a list of custom parameters to the function, which will be injected as variables into the template:

{# my.html.twig #}

{{ ngbm_render_value_object(block, {'the_answer': 42}) }}

{# block.html.twig #}

{{ the_answer }}

Finally, you can render the value object with a view context different from the current one:

{{ ngbm_render_value_object(block, {}, 'my_context') }}
ngbm_item_path

This function is used to generate an URL to an item from your CMS.

Note

To be able to generate the URL, you need to implement an instance of Netgen\BlockManager\Item\ValueUrlBuilderInterface for your item.

To generate the URL, you can simply call the function with the item in question:

<a href="{{ ngbm_item_path(item) }}">{{ item.name }}</a>

If you do not have access to the item object, you can generate the URL with ID and value type of the item:

<a href="{{ ngbm_item_path(42, 'ezlocation') }}">{{ 'My item' }}</a>

Alternatively, you can use a special format used by Netgen Layouts in the form of an URI scheme value_type://value_id:

<a href="{{ ngbm_item_path('ezlocation://42') }}">{{ 'My item' }}</a>
ngbm_ezcontent_name

This function is used to retrieve the name of a content from eZ Platform by its ID.

Note

This function exists because eZ Platform does not provide a way to retrieve the content name from its ID. The function is only available if Netgen Layouts is installed on top of eZ Platform.

To retrieve the content name, call the function with the content ID:

{{ ngbm_ezcontent_name(42) }}

If the content with provided ID does not exist, an empty string will be returned.

ngbm_ezlocation_path

This function is used to retrieve the names of all parent locations for eZ Platform location with provided ID.

Note

This function exists because eZ Platform does not provide a way to retrieve the location name from its ID. The function is only available if Netgen Layouts is installed on top of eZ Platform.

To retrieve the names of parent locations, call the function with the location ID:

{% set names = ngbm_ezlocation_path(42) %}

{{ names|join(' / ') }}

This will return the array of parent names, starting from the top most parent.

If the location with provided ID does not exist, null will be returned.

ngbm_ez_content_type_name

This function is used to retrieve the name of a content type from eZ Platform by its identifier.

Note

This function exists because eZ Platform does not provide a way to retrieve the content type name from its identifier. The function is only available if Netgen Layouts is installed on top of eZ Platform.

To retrieve the content type name, call the function with the content type identifier:

{{ ngbm_ez_content_type_name('article') }}

If the content type with provided identifier does not exist, an empty string will be returned.

List of built in Twig tags

The following lists all Twig tags built into Netgen Layouts.

ngbm_render_zone

This tag is used to render an entire layout zone. Since zones do not have their own template, this tag simply renders all blocks one after another.

Note

Examples below show usage as if the tag is used in frontend layout templates which load the zone from ngbm.layout variable available in those templates. This does not mean you can’t transfer an instance of Netgen\BlockManager\API\Values\Layout\Zone object manually.

To render a zone, you can simply call the tag with the zone in question:

{% ngbm_render_zone ngbm.layout.zone('left') %}

This will render the provided zone in the default view context.

You can also render the zone with your own custom view context:

{% ngbm_render_zone ngbm.layout.zone('left') context='my_context' %}

Warning

When rendering a zone with a custom view context, all blocks and block items which do not specify custom view context will be rendered with the view context you provided. You need to make sure all your blocks and block items have the templates for specified view context, otherwise, you will get an exception while rendering the page.

List of built in Twig global variables

The following lists all Twig global variables built into Netgen Layouts.

ngbm

This global variable has a couple of purposes, three main ones being:

  • to provide currently resolved layout to frontend layout templates
  • to provide currently resolved layout template to your full views
  • to provide a way to load Netgen Layouts configuration in Twig templates without having to write custom code

The following is a list of variables available:

ngbm.layout

This variable holds the layout resolved in the current request (instance of Netgen\BlockManager\API\Values\Layout\Layout). It is mostly used in frontend layout templates to access the layout and render the zones.

ngbm.layoutView

This variable holds the layout view of the layout resolved in current request (instance of Netgen\BlockManager\View\View\LayoutViewInterface). You can use it to access any data from the view, like the view context or the name of the layout template. If no layout was resolved, this variable will be set to false and if layout resolving process was not ran, it will be set to null.

ngbm.rule

This variable holds the rule (instance of Netgen\BlockManager\API\Values\LayoutResolver\Rule) that was used to resolve the layout for the current request. You can use it to access targets and conditions that were responsible for resolving the layout.

ngbm.config

This variable is an instance of Netgen\Bundle\BlockManagerBundle\Configuration\ConfigurationInterface. It makes it possible to access configuration of Netgen Layouts (basically, any container parameter which name starts with netgen_block_manager.).

For example, to access a container parameter called netgen_block_manager.some_config, you can use ngbm.config.parameter('some_config').

Tip

In cases when Netgen Layouts is used on top of eZ Platform, you can use this variable to access siteaccess aware container parameters too. For example, accessing parameter called netgen_block_manager.cro.some_value can be done with the same code as before: ngbm.config.parameter('some_config').

In addition to container parameters, this variable makes it possible to access the list of entities provided by Netgen Layouts, like block definitions, query types and so on. You can access them by using the same ngbm.config.parameter() function call as before, with the parameter names specified below:

  • block_definitions - Provides a list of all block definitions
  • block_types - Provides a list of all block types
  • block_type_groups - Provides a list of all block type groups
  • layout_types - Provides a list of all layout types
  • query_types - Provides a list of all query types
  • value_types - Provides a list of all value types
  • target_types - Provides a list of all target types
  • condition_types - Provides a list of all condition types
  • parameter_types - Provides a list of all parameter types

ngbm.layoutTemplate

This variable is a shortcut to access the template name of the layout resolved in current request (available as ngbm.layoutView.template). You will mostly use this variable in your full views to extend from instead of your default pagelayout. Using this variable starts the layout resolving process if it was not ran already. In case when no layout was resolved, this variable holds the name of your default pagelayout, so your full views can fallback to it without need to modify them.

ngbm.pageLayoutTemplate

This variable holds the name of your default pagelayout which you configured inside Netgen Layouts. It is mostly used to extend from in frontend layout templates, so those templates can fallback to it to render the page head, header, footer and so on.
ngbm_admin

This global variable is used by the administration interface of Netgen Layouts. Currently, only one variable is available:

ngbm_admin.pageLayoutTemplate

This variable holds the name of the pagelayout template for the admin interface. The idea behind it is that you can change the pagelayout of the administration interface without having to change the administration templates themselves. This can be achieved by setting this variable to a desired template name before admin interface is rendered (e.g. in an event listener).

Events

Netgen Layouts dispatches some events in a lifecycle of displaying the page with a resolved layout that you can listen to and act upon.

The following lists all available events.

ngbm.view.build_view

Event class: Netgen\BlockManager\Event\CollectViewParametersEvent

This event will be dispatched when the view of a value object is being built. It can be used to inject custom variables into the view before the view is built.

For example, you can use the following to inject a variable into the block view:

public function onBuildView(CollectViewParametersEvent $event)
{
    $view = $event->getView();
    if (!$view instanceof BlockViewInterface) {
        // Do nothing if the view does not belong to a block
        return;
    }

    if ($view->getContext() !== 'default') {
        // Do nothing if the view context is not for the frontend
        return;
    }

    $event->addParameter('the_answer', 42);
}
ngbm.view.render_view

Event class: Netgen\BlockManager\Event\CollectViewParametersEvent

This event will be dispatched when the view of a value object is being rendered. It can be used to inject custom variables into the view before the view is sent to Twig for rendering.

The example for injecting a variable into the view is the same as with build_view event.

ngbm.admin.match

Event class: Netgen\Bundle\BlockManagerAdminBundle\Event\AdminMatchEvent

This event will be dispatched when the request is matched as being a Netgen Layouts admin interface request. It is usually used if you want to override the pagelayout of Netgen Layouts admin interface for integrating it in other admin panels.

Symfony services

Netgen Layouts provides a number of Symfony services that you can use to work in PHP code with layouts, blocks, collections, queries and so on.

API services

The following services are the entry point and central place for all operations with any entity available in Netgen Layouts. It is used by Netgen Layouts itself to render the layouts in the frontend, in REST API used in Block Manager app and in the admin interface. Basically, they provide a CRUD interface to work with layouts, blocks, collections and layout resolver rules.

Service name Description
netgen_block_manager.api.service.layout CRUD operations for layouts
netgen_block_manager.api.service.block CRUD operations for blocks
netgen_block_manager.api.service.collection CRUD operations for collections
netgen_block_manager.api.service.layout_resolver CRUD operations for layout resolver rules
View API

The following services can be used to manually render any entity in Netgen Layouts.

Service name Description
netgen_block_manager.view.view_builder Used to manually build the view for the entity.
netgen_block_manager.view.view_renderer Used to manually render the entity view once it has been built.
netgen_block_manager.view.renderer Shortcut service that uses view builder and view renderer services to render the provided entity.
Block items

The following services can be used to manually load and build block items to inject them into blocks and link to them.

Service name Description
netgen_block_manager.item.item_builder Used to manually build the item from the provided entity.
netgen_block_manager.item.item_loader Used to manually load the item from provided value type and value ID.
netgen_block_manager.item.url_builder Used to build the URL to the item.
Registries

A number of registries is provided so you can access the list of all available block definitions, query types and so on.

Service name Description
netgen_block_manager.block.registry.block_definition Used to access all available block definitions
netgen_block_manager.block.registry.block_type Used to access all available block types
netgen_block_manager.block.registry.block_type_group Used to access all available block type groups
netgen_block_manager.collection.registry.query_type Used to access all available query types
netgen_block_manager.parameters.registry.parameter_type Used to access all available parameter types
netgen_block_manager.item.registry.value_type Used to access all available value types
netgen_block_manager.layout.registry.layout_type Used to access all available layout types
netgen_block_manager.layout.resolver.registry.target_type Used to access all available target types
netgen_block_manager.layout.resolver.registry.condition_type Used to access all available condition types
Shortcut services

Some of the entities that can be accessed through one of the above registries can also be accessed directly by using a unique service name. All of those service names are in the form of <prefix>.<identifier>. Prefix is the same for all entities that implement the same interface, while identifier identifies a single entity.

The following table lists all available service prefixes:

Entity type Service prefix
Block definition netgen_block_manager.block.block_definition.
Block type netgen_block_manager.block.block_type.
Block type group netgen_block_manager.block.block_type_group.
Query type netgen_block_manager.collection.query_type.
Value type netgen_block_manager.item.value_type.
Layout type netgen_block_manager.layout.layout_type.

As an example, if you wish to load the service for title block definition, you would use a service name netgen_block_manager.block.block_definition.title.

Other services

The following lists various other useful services which can be used by client code:

Service name Description
netgen_block_manager.http_cache.client Provides APIs for invalidating layout and block HTTP caches
netgen_block_manager.configuration Provides a way to access Netgen Layouts configuration values
netgen_block_manager.collection.result_loader Generates the collection result (items) from a provided collection
netgen_block_manager.layout.resolver Exposes APIs to manually run the layout resolving process on a request
eZ Publish specific services

The following lists various useful services available when Netgen Layouts is installed on top of eZ Publish.

Service name Description
netgen_block_manager.ezpublish.content_provider Used to extract current content and location for use by contextual blocks and queries

Upgrades

Upgrades

Upgrading from 0.7.0 to 0.8.0

Upgrade composer.json

In your composer.json file, upgrade the version of netgen/block-manager package to ~0.8.0 and run the composer update command.

Note

If you have Netgen Layouts installed on eZ Platform, the package name will be netgen/block-manager-ezpublish.

Note

Integrations into Netgen Admin UI and eZ Platform UI also have separate packages whose versions need to be bumped to ~0.8.0.

Activate required bundles

Version 0.8 requires FOS HTTP Cache Bundle to be activated, so if not already present, activate FOS\HttpCacheBundle\FOSHttpCacheBundle in your kernel.

Database migration

Run the following command from the root of your installation to execute migration to version 0.8 of Netgen Layouts:

$ php app/console doctrine:migrations:migrate --configuration=vendor/netgen/block-manager/migrations/doctrine.yml
Upgrading Netgen Content Browser

Netgen Content Browser version 0.8 was also automatically installed. Be sure to read its upgrade instructions too, to make sure you custom code keeps working.

Updates to Varnish VCL

To enable caching and later cache clearing of block and layout HTTP caches, you will need to use Varnish. To make the cache clearing work, you need to modify your Varnish VCL and add the following rules somewhere in your vcl_recv function. If you’re using eZ Platform and the VCL supplied by it, the best place to put this is in ez_purge function (which is called from vcl_recv), right after if (req.http.X-Location-Id) { ... } block.

For Varnish 3:

if (req.http.X-Layout-Id) {
    ban( "obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
    }
    error 200 "Banned";
}

if (req.http.X-Block-Id) {
    ban( "obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
    }
    error 200 "Banned";
}

For Varnish 4 and later:

if (req.http.X-Layout-Id) {
    ban("obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
    }
    return (synth(200, "Banned"));
}

if (req.http.X-Block-Id) {
    ban("obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
    if (client.ip ~ debuggers) {
        set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
    }
    return (synth(200, "Banned"));
}
Breaking changes

The following breaking changes were made in version 0.8 of Netgen Layouts. Follow the instructions to upgrade your code to this newer version.

  • Some classes and interfaces had their namespaces changed. Update your custom code working with these classes and interfaces. The following table lists the old and the new interface and class names:

    Old FQCN New FQCN
    Netgen\BlockManager\API\Values\Page\Block Netgen\BlockManager\API\Values\Block\Block
    Netgen\BlockManager\API\Values\Page\Placeholder Netgen\BlockManager\API\Values\Block\Placeholder
    Netgen\BlockManager\API\Values\Page\CollectionReference Netgen\BlockManager\API\Values\Block\CollectionReference
    Netgen\BlockManager\API\Values\Page\BlockCreateStruct Netgen\BlockManager\API\Values\Block\BlockCreateStruct
    Netgen\BlockManager\API\Values\Page\BlockUpdateStruct Netgen\BlockManager\API\Values\Block\BlockUpdateStruct
    Netgen\BlockManager\API\Values\Page\Layout Netgen\BlockManager\API\Values\Layout\Layout
    Netgen\BlockManager\API\Values\Page\Zone Netgen\BlockManager\API\Values\Layout\Zone
    Netgen\BlockManager\API\Values\Page\LayoutCreateStruct Netgen\BlockManager\API\Values\Layout\LayoutCreateStruct
    Netgen\BlockManager\API\Values\Page\LayoutUpdateStruct Netgen\BlockManager\API\Values\Layout\LayoutUpdateStruct

    Notable change is the Block interface since it’s used in Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface::getDynamicParameters. method. You will need to modify your custom block definition handlers to use the new interface.

  • In Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface::getDynamicParameters method, a second, unused, parameter called $parameters was removed from the interface. Remove it from your custom block definition handlers.

  • A new method called isContextual was added to Netgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface interface. The purpose of this method is to signal to the system when the query acts as a contextual query, i.e., if it depends on data from current request to run. You need to add this method to your custom query type handlers.

    The following is the method signature as well as an example implementation:

    /**
     * Returns if the provided query is dependent on a context, i.e. current request.
     *
     * @param \Netgen\BlockManager\API\Values\Collection\Query $query
     *
     * @return bool
     */
    public function isContextual(Query $query);
    
    public function isContextual(Query $query)
    {
        return $query->getParameter('use_current_location')->getValue() === true;
    }
    
  • BlockDefinitionHandlerInterface::hasCollection method has been removed. From now on, specifying that the block has a collection is done through configuration. The following shows the old and new way of specifying that the block has a collection:

    // Old way
    
    <?php
    
    namespace MyApp\Block\BlockDefinition\Handler;
    
    use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler;
    
    class MyBlockHandler extends BlockDefinitionHandler
    {
        /**
         * Returns if this block definition should have a collection.
         *
         * @return bool
         */
        public function hasCollection()
        {
            return true;
        }
    }
    
    # New way
    
    netgen_block_manager:
        block_definitions:
            my_block:
                collections: ~
    
  • Netgen\BlockManager\Layout\Resolver\TargetTypeInterface::provideValue method has a changed signature. From now on, Symfony Request object is provided as a parameter to the method, so there’s no need to manually fetch the current request from the request stack. The new interface looks like this:

    /**
     * Provides the value for the target to be used in matching process.
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     *
     * @return mixed
     */
    public function provideValue(Request $request);
    
  • Netgen\BlockManager\Layout\Resolver\ConditionTypeInterface::matches method has a changed signature. From now on, Symfony Request object is provided as a first parameter to the method, so there’s no need to manually fetch the current request from the request stack. The new interface looks like this:

    /**
     * Returns if this request matches the provided value.
     *
     * @param \Symfony\Component\HttpFoundation\Request $request
     * @param mixed $value
     *
     * @return bool
     */
    public function matches(Request $request, $value);
    
  • Netgen\BlockManager\Traits\RequestStackAwareTrait trait has been removed. Inject the request stack service directly into the constructor.

  • If using Netgen Layouts with eZ Publish 5, instead of redefining the alias for the content provider service, you now have to redefine the alias for newly introduced content extractor service.

    # Before
    
    netgen_block_manager.ezpublish.content_provider:
        alias: netgen_block_manager.ezpublish.content_provider.ez5_request
    
    # After
    
    netgen_block_manager.ezpublish.content_extractor:
        alias: netgen_block_manager.ezpublish.content_extractor.ez5_request
    
  • netgen_block_manager.google_maps_api_key configuration was renamed to netgen_block_manager.api_keys.google_maps. The following shows an example of the old and new configs:

    # Old config
    
    netgen_block_manager:
        google_maps_api_key: MY_API_KEY
    
    # New config
    
    netgen_block_manager:
        api_keys:
            google_maps: MY_API_KEY
    
  • standard item view type is always added to all view types automatically. However, this was not true for view types that specified custom item view types. You had to specify standard item view type manually if you wanted to use it. From now on, standard item view type will be added in those cases too. If you wish to disable it, you can do so like this:

    netgen_block_manager:
        block_definitions:
            my_block:
                view_types:
                    my_view_type:
                        item_view_types:
                            standard:
                                enabled: false
    
  • User policies are introduced. To be able to manage user policies in legacy administration interface of eZ Publish, you need to activate the provided nglayouts legacy extension. If you’re using eZ Platform UI, policy management is available automatically.

  • Custom items can now be added to blocks manually, instead of just being able to return them from query types. Make sure to implement the Netgen\BlockManager\Item\ValueLoaderInterface for your custom items, as well as Content Browser backend, and then activate the value type in configuration:

    netgen_block_manager:
        items:
            value_types:
                my_value_type:
                    name: 'My value type'
    

Cookbook

Cookbook

Netgen Layouts provides around 10 officially supported extension points, ranging from simpler ones like creating custom layout types to advanced and rarely used ones like creating custom conditions and targets for layout matching process. The following chapters will go through each of these extension points and detail the process of creating your own layout types, blocks, query types and so on.

Creating a custom layout type

Netgen Layouts ships with some layout types (from simple ones with one zone, to more complicated ones with 8 zones), which are readily available to be used in your own projects without any additional configuration.

If none of those layout types satisfy your needs, you can create your own. Creating your own layout type is split into three parts:

  • Basic configuration
  • Creating frontend and backend Twig templates for rendering the layout
  • Connecting the templates with your layout type

We will demonstrate the process by creating a simple layout type with two zones: left and right.

Basic configuration

To register a new layout type in Netgen Layouts, add the following YAML config:

netgen_block_manager:
    layout_types:
        my_layout:
            name: 'My layout'
            zones:
                left:
                    name: 'Left'
                right:
                    name: 'Right'

This specifies that our new layout type has a my_layout identifier, that its human readable name is My layout and that it has two zones, left and right.

Creating frontend and backend Twig templates

Netgen Layouts uses two separate templates to render the layout on the frontend and in the backend. By default, all frontend templates and backend templates for built-in layout types are based on Bootstrap grid. Backend templates, used by the Block Manager app, will be rendered correctly as long as you provide the valid Bootstrap markup. On the other hand, to render frontend templates correctly, you will need to include CSS for Bootstrap Grid component in your site.

Note

It is possible to switch all frontend templates to use a different grid like Flexbox, however at this time, only Bootstrap implementation of templates is available.

Creating a backend template

Backend templates for layout types usually do not have any logic, apart from HTML markup that specifies where each of the layout type zones goes within Bootstrap grid. If needed, backend templates can access the currently rendered layout with a Twig variable named layout.

The following template shows an example of how would the backend template look for layout type we configured earlier:

{# @App/layouts/api/my_layout.html.twig #}

<div class="row">
    <div class="col-md-8">
        <div data-zone="left"></div>
    </div>

    <div class="col-md-4">
        <div data-zone="right"></div>
    </div>
</div>

It is important for backend templates to have <div data-zone="my_zone"></div> element with correct zone identifier (replacing my_zone) for every zone configured.

Creating a frontend template

Frontend templates are usually more complicated, since they need to provide the code to actually render layout zones by themselves.

All markup in frontend templates for your layout types needs to be inside a Twig block named layout and they always need to extend a special ngbm.pageLayoutTemplate variable available in the template. This variable will always hold the name of the main pagelayout of your app, which is either configured manually through Netgen Layouts configuration, or in some cases picked up automatically from available configuration of your app (if using eZ Platform for example).

This template has access to currently rendered layout via Twig variable named ngbm.layout and each zone is rendered via ngbm_render_zone Twig tag. Since zone does not have a template of its own, this tag simply renders all blocks one after another. Accessing a zone via ngbm.layout.zone('left') function will either return the requested zone, or the zone in a shared layout if one is configured for the specified zone, so no additional code is required to handle zones connected to shared layouts.

The complete frontend template for your custom layout type with two zones might look something like this:

{# @App/layouts/my_layout.html.twig #}

{% extends ngbm.pageLayoutTemplate %}

{% block layout %}
    <div class="container">
        <div class="row">
            <div class="col-lg-8">
                {% if ngbm.layout.zone('left') is not empty %}
                    {% ngbm_render_zone ngbm.layout.zone('left') %}
                {% endif %}
            </div>

            <div class="col-lg-4">
                {% if ngbm.layout.zone('right') is not empty %}
                    {% ngbm_render_zone ngbm.layout.zone('right') %}
                {% endif %}
            </div>
        </div>
    </div>
{% endblock %}
Connecting the templates with your layout type

To activate the frontend and backend templates you defined, you will need to configure them through the view layer configuration. Read up on what a view layer is and the corresponding terminology in documentation specific to view layer itself.

Currently, two matchers are implemented in the view layer for layout view:

  • layout\type - Matches on layout type of a layout
  • layout\shared - Matches on “shared” flag of a layout

Most of the time, you will use layout\type matcher for configuring templates for your custom layout types. The reason for this is that shared layouts are never rendered directly on the frontend so there is no really need for using layout\shared matcher. The reason for its existence is that it is used in the administration interface of Netgen Layouts.

The following is an example config that enables the two templates we created:

netgen_block_manager:
    view:
        layout_view:
            default:
                my_layout:
                    template: "@App/layouts/my_layout.html.twig"
                    match:
                        layout\type: my_layout
            api:
                my_layout:
                    template: "@App/layouts/api/my_layout.html.twig"
                    match:
                        layout\type: my_layout
                        api_version: 1

At this point, your new layout type is ready for usage.

Creating a custom block

Similar to layout types, when creating a custom block, you need a bit of configuration and some templates, but since blocks almost always need some custom logic, you will also need to create a PHP class that will handle custom functionalities of a block. In the following examples, we will show creating a custom block that can render a Markdown document.

When creating a custom block, you will often run into two entities mentioned in code and configuration: a block definition, and a block type. Before you actually create a custom block, it is important to understand a difference between a block definition and a block type.

Difference between block definition & block type

Block definition is the central entity you will be creating when creating a custom block. As the name implies, block definition defines how your custom block behaves. This includes specifying what parameters will the block have and what type are they of and if the block has a collection or not. It also gives you a possibility to write your own custom behaviour for a block, based on block parameters. In case of container blocks, it specifies which placeholders the container block has.

Each block definition can have multiple block types. Block type is nothing more than a starting configuration used when creating a block in a layout. In Block Manager app, block types are what is shown on the left side and what you drag and drop to a zone in a layout. Creating block types for a certain block definition requires only a couple of lines of configuration where you would specify starting values for block label, block view, block item view and block parameters.

Once you create a block in a layout, it doesn’t store the information from which block type it was created, it only stores the block definition. When you think about it, this makes sense. Since block type is a starting configuration for a block you’re adding to a layout, and that configuration can change in the lifecycle of a block, there is no benefit in storing the information which block type was used to create the block. On the other hand, block definition needs to be stored because it defines how block parameters will be validated, what custom behaviour the block has and so on.

Configuring a new block definition

To register a new block definition in Netgen Layouts, you will need the following configuration:

netgen_block_manager:
    block_definitions:
        my_markdown:
            name: 'My markdown block'
            view_types:
                my_markdown:
                    name: 'My markdown block'

This configuration example adds a new block definition with my_markdown identifier, which as a human readable name My markdown block and has one view type, also called my_markdown.

View type is nothing more than an identifier of a template which will be used to render the block. Every block definition needs at least one view type.

Note

By convention, in built in blocks, if a block definition has only one view type, like above, that view type will have the same identifier as the block definition itself.

Creating a PHP service for a block definition

Every block definition needs a single PHP class that specifies the entire behaviour of a block. This class needs to implement Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface interface which specifies a number of methods for you to implement. To simplify implementing new block definitions, an abstract class exists (Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler) which has all of those methods implemented with default and empty implementations, reducing the need for writing boilerplate code.

Let’s create a basic block definition handler class:

<?php

namespace AppBundle\Block\BlockDefinition\Handler;

use Netgen\BlockManager\API\Values\Block\Block;
use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;

class MyMarkdownHandler extends BlockDefinitionHandler
{
    /**
     * Builds the parameters by using provided parameter builder.
     *
     * @param \Netgen\BlockManager\Parameters\ParameterBuilderInterface $builder
     */
    public function buildParameters(ParameterBuilderInterface $builder)
    {
    }

    /**
     * Returns the array of dynamic parameters provided by this block definition.
     *
     * @param \Netgen\BlockManager\API\Values\Block\Block $block
     *
     * @return array
     */
    public function getDynamicParameters(Block $block)
    {
    }
}
Specifying block parameters

First method we will look at is buildParameters method. By using an object called parameter builder and adding parameter specifications to it, this method will specify which parameters your custom block will have. Details on how the parameter builder works, what parameter types exist and how to implement custom parameter type are explained in dedicated chapter.

Let’s add a custom parameter to our block which will serve as an input for raw Markdown content:

use Netgen\BlockManager\Parameters\ParameterType;

public function buildParameters(ParameterBuilderInterface $builder)
{
    $builder->add('content', ParameterType\TextType::class);
}

If you open Block Manager app, you will notice that all blocks have at least CSS class, CSS ID and Set container parameters. These three parameters are nothing special, they are exactly the same as any other parameter in any block. They can be added to your block definition handler manually, but in order to reduce the amount of code you need to write, there is a handy method called buildCommonParameters in BlockDefinitionHandler abstract class, which will add those three parameters for you. So finally, our buildParameters method will look like this:

public function buildParameters(ParameterBuilderInterface $builder)
{
    $builder->add('content', ParameterType\TextType::class);

    $this->buildCommonParameters($builder);
}

Notice that we didn’t specify the human readable labels for the parameters. That’s because they are generated automatically via translation system. To create the correct labels for your block parameters, you need to add one string to ngbm translation catalog for every parameter in your block, (excluding the ones provided by buildCommonParameters method, as they already have translation strings) with the format block.<block_definition>.<parameter_name> where block_definition and parameter_name are placeholders that need to be replaced with correct values. So, for our custom Markdown block definition, the translation file would look something like this:

block.my_markdown.content: 'Content'
Custom block behaviour

Second method in our handler example above is called getDynamicParameters. This method is used for your own custom logic. Anything goes in this method. You can inject dependencies into your block definition handler, use them here, do some processing based on provided instance of a block or some other parameters you provide when rendering a block manually and so on.

After all processing is done, this method needs to return the array with key/value pairs which will be injected into template when block is rendered. Each of these values can either be a regular scalar, array, object and so on, or it can be a closure, which will transparently be called to calculate the value at the moment the parameter is used inside the block template.

In case of our Markdown handler, we will need to inject a Markdown parser into our handler, and use it in this method to parse the raw Markdown into HTML. We will be using Michelf\MarkdownInterface, Markdown parser which is already pre-installed with Netgen Layouts:

/**
 * @var \Michelf\MarkdownInterface
 */
protected $markdownParser;

public function __construct(MarkdownInterface $markdownParser)
{
    $this->markdownParser = $markdownParser;
}

public function getDynamicParameters(Block $block)
{
    $rawContent = $block->getParameter('content')->getValue();

    return array(
        'html' => $this->markdownParser->transform($rawContent),
    );
}
Defining the Symfony service for our handler

To connect the created handler with block definition configuration, we need to register the handler in Symfony DIC. We also need to specify a service for Markdown parser we used in the handler:

services:
    app.markdown:
        class: Michelf\MarkdownExtra

    app.block.block_definition.handler.markdown:
        class: AppBundle\Block\BlockDefinition\Handler\MyMarkdownHandler
        arguments:
            - "@app.markdown"
        tags:
            - { name: netgen_block_manager.block.block_definition_handler, identifier: my_markdown }

This configuration is a fairly regular specification of services in Symfony, however, to correctly recognize our PHP class as a block definition handler, we need to tag it with netgen_block_manager.block.block_definition_handler tag and attach to it an identifier key with a value which equals to the identifier of block definition we configured at the beginning (in this case my_markdown).

Specifying block view templates

Every view type in your block definition needs to have two templates, one for frontend and one for backend. If you remember, we specified that our my_markdown block definition has one view type, also called my_markdown.

Frontend block template

Let’s create a template for displaying the block in the frontend with my_markdown view type. Every frontend template for the block needs to extend from @NetgenBlockManager/block/block.html.twig and all content of the template needs to be inside Twig block called content. The currently rendered block is accessible via block variable which you can use to access block parameters specified in the handler as well as any dynamic parameters in the block.

Tip

View type templates for built in block definitions are also a great source of inspiration, so make sure to give them a look.

Our frontend template for the Markdown block definition will simply output the parsed Markdown which is provided by the handler:

{# @App/blocks/my_markdown/my_markdown.html.twig #}

{% extends '@NetgenBlockManager/block/block.html.twig' %}

{% block content %}
    {{ block.dynamicParameter('html')|raw }}
{% endblock %}
Backend block template

As for backend, in this specific case, the template will look almost the same (since all we want is to render the parsed Markdown), save for the different template used to extend from.

In general, all backend templates need to extend from @NetgenBlockManager/api/block/block.html.twig (notice that this template is different from the frontend base template, this one is in an api folder).

In most cases, backend template will be simpler than the frontend one, without any design specific markup and so on. Everything you can use in frontend templates is also available here, meaning that you can use the block variable to access the block and its parameters.

Going back to our example backend template, it will look like this:

{# @App/blocks/api/my_markdown/my_markdown.html.twig #}

{% extends '@NetgenBlockManager/api/block/block.html.twig' %}

{% block content %}
    {{ block.dynamicParameter('html')|raw }}
{% endblock %}
Connecting the templates with your block definition

To activate the frontend and backend templates you defined, you will need to configure them through the view layer configuration. Read up on what a view layer is and the corresponding terminology in documentation specific to view layer itself.

Currently, two matchers are implemented in the view layer for block view:

  • block\definition - Matches on block definition of a block
  • block\view_type - Matches on view type of a block

If you are creating a block which will only have a single view type, you can omit the block\view_type matcher and use only block\definition matcher, which will make sure that templates you defined will be applied to any future view types of your block automatically.

The following is an example config that enables the two templates we created:

netgen_block_manager:
    view:
        block_view:
            default:
                my_markdown:
                    template: "@App/blocks/my_markdown/my_markdown.html.twig"
                    match:
                        block\definition: my_markdown
                        # View type matcher is optional
                        block\view_type: my_markdown
            api:
                my_markdown:
                    template: "@App/blocks/api/my_markdown/my_markdown.html.twig"
                    match:
                        block\definition: my_markdown
                        # View type matcher is optional
                        block\view_type: my_markdown
                        api_version: 1

The following configuration shows how you can specify a fallback template that will be applied to all block view types that do not specify their own template rules:

netgen_block_manager:
    view:
        block_view:
            default:
                my_markdown:
                    template: "@App/block/my_markdown.html.twig"
                    match:
                        block\definition: my_block
            api:
                my_markdown:
                    template: "@App/api/block/my_markdown.html.twig"
                    match:
                        block\definition: my_block

Note

Take care to specify the fallback rule at the bottom of all other rules, since the first rule that matches will be used when searching for templates.

After you have defined the configuration for the view layer, your block is ready for usage.

Defining block types for your block definition

Remember block types and how we said that block types are a starting configuration for a block definition? Remember how we said that block types are the thing that is shown on the left hand side in the Block Manager app?

When you create a custom block definition, Netgen Layouts internally creates for you a single block type with the same name as block definition with empty default configuration, and adds it to a block type group called “Custom blocks”. This is to enable the block definition to be displayed in the interface so you can actually add it to a layout.

If you want to create another starting configuration for your block definition, you can do so by configuring an additional block type which will also be automatically added to a “Custom blocks” group. For example:

netgen_block_manager:
    block_types:
        my_markdown_v2:
            name: 'My Markdown block with default title'
            definition_identifier: my_markdown
            defaults:
                parameters:
                    content: '# Some default title'

This configuration defines a block type with my_markdown_v2 identifier, which sets a default value for content parameter.

If you want to define some other group where your block type should live, you can do so. In that case, the block type will not be shown in the Custom blocks group, but in the group you specified. You can use the configuration similar to this:

netgen_block_manager:
    block_type_groups:
        my_group:
            name: 'My group'
            block_types: [my_markdown_v2, second_block_type, other_block_type]

Tip

Once you start adding more and more block types for your block definition, you might decide that you no longer need the automatically created block type with empty configuration. In that case, you might want to simply disable it:

netgen_block_manager:
    block_types:
        my_markdown:
            enabled: false

Creating a custom container block

Containers are blocks like any other, so everything described in chapter on creating a custom block applies here.

The difference between regular blocks and containers is that containers can contain other blocks. This is achieved by using something called a placeholder, which is nothing more than a label which is applied to every child block in a container. When rendering the container, you can use this label to specify where in the template will all blocks with that label be rendered. As with zones, all blocks with a specified label will be rendered one after another.

Think of a placeholder in the container block as a lightweight variant of a zone in a layout. When you drag and drop a new block to a placeholder in a container, placeholder label is automatically assigned to the new block so it can be rendered when requested.

Note

Currently, it is not possible to add containers within containers.

Container definition handler

When creating a custom block definition handler for a container block, instead of extending from Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler abstract class, you will extend from Netgen\BlockManager\Block\BlockDefinition\ContainerDefinitionHandler which is also an abstract class. What is left for you to do is to define which placeholders your new container block has by implementing getPlaceholderIdentifiers method. For example, Two columns container block provided by Netgen Layouts looks like this:

<?php

namespace Netgen\BlockManager\Block\BlockDefinition\Handler\Container;

use Netgen\BlockManager\Block\BlockDefinition\ContainerDefinitionHandler;

class TwoColumnsHandler extends ContainerDefinitionHandler
{
    /**
     * Returns placeholder identifiers.
     *
     * @return array
     */
    public function getPlaceholderIdentifiers()
    {
        return array('left', 'right');
    }
}
Twig templates

Frontend Twig templates for container blocks have all the same features as Twig templates for regular blocks. The only difference is that in templates for container blocks, you can use ngbm_render_placeholder Twig function to render all blocks which are located in specified placeholder.

For example, frontend Twig template for Two columns block provided by Netgen Layouts looks like this:

{% extends '@NetgenBlockManager/block/block.html.twig' %}

{% block content %}
    <div class="row">
        <div class="col-md-6">
            {{ ngbm_render_placeholder(block, 'left') }}
        </div>
        <div class="col-md-6">
            {{ ngbm_render_placeholder(block, 'right') }}
        </div>
    </div>
{% endblock %}

As for backend Twig templates for container blocks, they are similar to backend Twig templates for layout types. They do not need custom logic to render the blocks, except specific HTML elements used as markers for block placeholders:

{% extends '@NetgenBlockManager/api/block/block.html.twig' %}

{% block content %}
    <div class="row">
        <div class="col-md-6">
            <div data-placeholder="left" data-receiver></div>
        </div>
        <div class="col-md-6">
            <div data-placeholder="right" data-receiver></div>
        </div>
    </div>
{% endblock %}

Basically, for every placeholder in your container block, you need to have a <div data-placeholder="my_placeholder" data-receiver></div> element with a placeholder identifier in data-placeholder attribute.

Adding collections to blocks

As you are probably aware, blocks can have collections attached to them, which provide block items to the block. Block definition can control if the block can have a collection by specifying it in the block definition configuration. By default, blocks do not use collections, so if you want to allow your custom block definitions to use collections, configure it like this:

netgen_block_manager:
    block_definitions:
        my_block:
            collections: ~

Once you specify that your block definition can use collections, all blocks will automatically be created with a manual collection attached to the block.

Note

Currently, every block can have only one collection with an identifier named default.

You can also configure which items and query types are valid for a collection. That way, you can specify, for example, that your collection can only have manual items or only a dynamic collection, or any combination of two.

To specify which item are allowed within the collection, use the config similar to this:

netgen_block_manager:
    block_definitions:
        my_block:
            collections:
                default:
                    # Set to an empty array to disable manual collection
                    valid_item_types: [my_item_type]

To specify which query types are allowed within the collection, use the config similar to this:

netgen_block_manager:
    block_definitions:
        my_block:
            collections:
                default:
                    # Set to an empty array to disable dynamic collection
                    valid_query_types: [my_query]
Accessing the collection and items in the template

The collection provides the block items to the block in form of a collection result set object (Netgen\BlockManager\Collection\Result\ResultSet) which is a collection of result objects (Netgen\BlockManager\Collection\Result\Result) which themselves are a simple wrapper around the single item in a collection.

In the Twig templates for your block views, all collection results are accessible through collections variable, which you can use to iterate over the items in a collection and render them:

{% for result in collections.default %}
    {{ ngbm_render_item(result.item, block.itemViewType) }}
{% endfor %}

When rendering an item, you need to specify with which item view type it will be rendered (block.itemViewType in the above example). As with block view types, item view type is nothing more than an identifier of a template which will be used to render the item. Every block view type can define which item view types it supports with the configuration like this:

netgen_block_manager:
    block_definitions:
        my_block:
            name: 'My block'
            view_types:
                first:
                    name: 'First'
                    item_view_types:
                        item_type_1:
                            name: 'Item type 1'
                        item_type_2:
                            name: 'Item type 2'
                second:
                    name: 'Second'

With this, we specified that first block view type supports item_type_1 and item_type_2 item view types, while second block view type does not specify any specific item view types.

For every view type, an item view type called standard will be added automatically to configuration. This is to make it easier to create item view type templates for simpler blocks. If you wish to disable this standard item view type, you can do so like this:

netgen_block_manager:
    block_definitions:
        my_block:
            view_types:
                my_view_type:
                    item_view_types:
                        standard:
                            enabled: false

Tip

In your Twig templates for block view types, you can ofcourse choose not to use the item view type stored in a block (block.itemViewType), but use a hardcoded one, or mix the hardcoded item view type with the one stored in a block and so on.

Adding custom view types to blocks

Just as you can specify that your own custom blocks have certain view types, you can extend existing blocks to add your own view types to them. Configuration to do so is completely the same as with adding a custom block.

When adding a block view type, you need to specify them in block definition settings, as well as configure the template used for them (both for frontend and backend).

The following configuration shows an example how to add a custom view type to existing list block definition called list_numbered and how to specify the templates that will be used:

netgen_block_manager:
    block_definitions:
        list:
            view_types:
                list_numbered:
                    name: 'List (numbered)'
    view:
        block_view:
            default:
                list\numbered:
                    template: "@App/block/list/list_numbered.html.twig"
                    match:
                        block\definition: list
                        block\view_type: list_numbered
            api:
                list\numbered:
                    template: "@NetgenBlockManager/api/block/list/list.html.twig"
                    match:
                        block\definition: list
                        block\view_type: list_numbered

Note that template rule for backend (api context) reuses the template for list view type already available in the list block. This is to make sure that both list and list_numbered view types are displayed in the same way in the backend. This is of course optional as you can specify your own template for the backend.

Adding a custom item view type

You can also add custom item view types to every existing block and its view types.

The following configuration shows an example how to add a custom item view type called standard_with_intro to already existing block view type in list block definition:

netgen_block_manager:
    block_definitions:
        list:
            view_types:
                list:
                    item_view_types:
                        standard_with_intro:
                            name: 'Standard (with intro)'

You can specified the template to be used like this:

netgen_block_manager:
    view:
        item_view:
            default:
                my_item\standard_with_intro:
                    template: "@App/items/my_item/standard_with_intro.html.twig"
                    match:
                        item\value_type: my_item
                        item\view_type: standard_with_intro
            api:
                my_item\standard_with_intro:
                    template: "@App/items/api/my_item/standard_with_intro.html.twig"
                    match:
                        item\value_type: my_item
                        item\view_type: standard_with_intro

Tip

If using Netgen Layouts in eZ Platform, every content view type is also a valid item view type. Because of that, you don’t need to duplicate templates from eZ Platform in Netgen Layouts to display eZ locations. For example, if you have a listitem content view type for your content, you can use it just by specifying that some of your block view types have a listitem item view type and selecting in the block configuration in the right sidebar.

Note

In case of eZ Platform, it is always a good idea to specify a fallback content view template that will be applied to all content types, since Netgen Layouts does not limit which eZ content types can be added to blocks.

Disabling existing view types in blocks

You can disable any existing block view types or item view types to stop them from showing up in Block Manager app.

The following configuration shows an example how to disable a block view type:

netgen_block_manager:
    block_definitions:
        list:
            view_types:
                some_view_type:
                    enabled: false

The following configuration shows an example how to disable an item view type:

netgen_block_manager:
    block_definitions:
        list:
            view_types:
                list:
                    item_view_types:
                        some_item_view_type:
                            enabled: false

Note that when you disable a block view type or an item view type, they will still be used by the rendering engine. However, you will not be able to save the block configuration any more in Block Manager app until you change the (item) view type to some other enabled one.

Creating custom query types

When installed on pure Symfony, Netgen Layouts comes with no built in query types which means you can only use manual collections in your blocks.

Tip

If you install Netgen Layouts on eZ Platform, you will get a single query type that will be automatically used when switching a collection from manual to dynamic in blocks.

When you implement your own query types, you will be able to use dynamic collections, and if you implement more than one query type, you will be able to select which query type to use when switching to dynamic collection in a block.

To implement a query type, you need a bit of configuration and a PHP class that will handle custom functionalities of a query type. In the following examples, we will show creating a custom query type that will use eZ search engine to search for items by text.

Configuring a new query type

To register a new query type in Netgen Layouts, you will need the following configuration:

netgen_block_manager:
    query_types:
        my_search:
            name: 'My search'

This configuration specifies a new query type with my_search identifier and My search human readable name.

Creating a PHP service for a query type

Every query type needs a single PHP class that specifies the entire behaviour of a query type. This class needs to implement Netgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface interface which specifies a number of methods for you to implement.

Let’s create a basic query type handler class:

<?php

namespace AppBundle\Collection\QueryType\Handler;

use Netgen\BlockManager\API\Values\Collection\Query;
use Netgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;

class MySearchHandler implements QueryTypeHandlerInterface
{
    /**
     * Builds the parameters by using provided parameter builder.
     *
     * @param \Netgen\BlockManager\Parameters\ParameterBuilderInterface $builder
     */
    public function buildParameters(ParameterBuilderInterface $builder)
    {
    }

    /**
     * Returns the values from the query.
     *
     * @param \Netgen\BlockManager\API\Values\Collection\Query $query
     * @param int $offset
     * @param int $limit
     *
     * @return mixed[]
     */
    public function getValues(Query $query, $offset = 0, $limit = null)
    {
    }

    /**
     * Returns the value count from the query.
     *
     * @param \Netgen\BlockManager\API\Values\Collection\Query $query
     *
     * @return int
     */
    public function getCount(Query $query)
    {
    }

    /**
     * Returns the limit internal to this query.
     *
     * @param \Netgen\BlockManager\API\Values\Collection\Query $query
     *
     * @return int
     */
    public function getInternalLimit(Query $query)
    {
    }

    /**
     * Returns if the provided query is dependent on a context, i.e. current request.
     *
     * @param \Netgen\BlockManager\API\Values\Collection\Query $query
     *
     * @return bool
     */
    public function isContextual(Query $query)
    {
    }
}
Specifying query type parameters

First method we will look at is buildParameters method. By using an object called parameter builder and adding parameter specifications to it, this method will specify which parameters your custom query type will have. Details on how the parameter builder works, what parameter types exist and how to implement custom parameter type are explained in dedicated chapter.

Let’s add a couple of custom parameters to our query type which will serve as an input for search text:

use Netgen\BlockManager\Parameters\ParameterType;

public function buildParameters(ParameterBuilderInterface $builder)
{
    $builder->add('search_text', ParameterType\TextType::class);
    $builder->add('limit', ParameterType\IntegerType::class);
}

Notice that we didn’t specify the human readable labels for the parameters. That’s because they are generated automatically via translation system. To create the correct labels for your query type parameters, you need to add one string to ngbm translation catalog for every parameter in your query type with the format query.<query_type>.<parameter_name> where query_type and parameter_name are placeholders that need to be replaced with correct values.

So, for our custom search query type, the translation file would look something like this:

query.my_search.search_text: 'Search text'
query.my_search.limit: 'Limit'
Fetching the items

Second method in our handler example above is called getValues. This method is used for fetching the items from a query.

This method needs to return the array of domain objects that will be automatically converted to block items.

Tip

In case of eZ Platform, query types can return the list of eZ ContentInfo or Location value objects.

/**
 * @const int
 */
const DEFAULT_LIMIT = 25;

/**
 * @var \eZ\Publish\API\Repository\SearchService
 */
protected $searchService;

public function __construct(SearchService $searchService)
{
    $this->searchService = $searchService;
}

public function getValues(Query $query, $offset = 0, $limit = null)
{
    $searchResult = $this->searchService->findLocations(
        $this->buildQuery($query)
    );

    return array_map(
        function (SearchHit $searchHit) {
            return $searchHit->valueObject;
        },
        $searchResult->searchHits
    );
}

public function getInternalLimit(Query $query)
{
    $limit = $query->getParameter('limit')->getValue();
    if (!is_int($limit)) {
        return self::DEFAULT_LIMIT;
    }

    return $limit >= 0 ? $limit : self::DEFAULT_LIMIT;
}

/**
 * Builds the query from current parameters.
 *
 * @param \Netgen\BlockManager\API\Values\Collection\Query $query
 * @param bool $buildCountQuery
 *
 * @return \eZ\Publish\API\Repository\Values\Content\LocationQuery
 */
protected function buildQuery(Query $query, $buildCountQuery = false)
{
    $locationQuery = new LocationQuery();

    $criteria = array(
        new Criterion\FullText($query->getParameter('search_text')->getValue()),
        new Criterion\Visibility(Criterion\Visibility::VISIBLE),
    );

    $locationQuery->filter = new Criterion\LogicalAnd($criteria);

    $locationQuery->limit = 0;
    if (!$buildCountQuery) {
        $locationQuery->limit = $this->getInternalLimit($query);
    }

    return $locationQuery;
}

As you can see, getValues method simply builds a location query for eZ search engine and returns the list of found eZ locations. Conversion to block items is handled automatically by Netgen Layouts.

Note

Notice that we didn’t use $offset and $limit parameters which were provided to getValues method. These parameters are provided as a placeholder for future updates and are currently unused by the system and will always be equal to their default values.

Fetching the item count

To retrieve the item count from the query type, we use the getCount method:

public function getCount(Query $query)
{
    $searchResult = $this->searchService->findLocations(
        $this->buildQuery($query, true)
    );

    return $searchResult->totalCount;
}
Contextual queries

Notice how we implemented above a method called getInternalLimit to calculate the correct limit which will be applied to eZ search query. While it can obviously be used by the query type handler itself, the purpose of this method is to signal to the system how much items the collection would have if it was ran. This information is used when displaying a block in Block Manager app whose collection is a contextual one.

A contextual query is a query which needs the current context (i.e. current request) to run. Think of a situation where you have a layout with a block which shows top 5 items from the category it is applied to. Contextual query removes the need to create five different layouts for five different categories just so you can change the parent category from which to fetch the items. Instead, in a contextual query, you will take the currently displayed item and use it as the parent, making it possible to have only one layout for all five different categories.

In order for the system to work properly with contextual queries, two methods are used:

  • isContextual method, which signals to the system if the query is contextual or not. Most of the time, this method will return a value of a boolean parameter specified inside of the query which decides if a query is contextual or not, for example:

    public function isContextual(Query $query)
    {
        return $query->getParameter('use_current_location')->getValue() === true;
    }
    
  • already described getInternalLimit method, used by the block with a contextual query so it can display a preview of how the block would look like.

In our case, we will simply return false from isContextual method:

public function isContextual(Query $query)
{
    return false;
}
Defining the Symfony service for our handler

To connect the created handler with query type configuration, we need to register the handler in Symfony DIC:

services:
    app.collection.query_type.handler.my_search:
        class: AppBundle\Collection\QueryType\Handler\MySearchHandler
        arguments:
            - "@ezpublish.api.service.search"
        tags:
            - { name: netgen_block_manager.collection.query_type_handler, type: my_search }

This configuration is a fairly regular specification of services in Symfony, however, to correctly recognize our PHP class as a query type handler, we need to tag it with netgen_block_manager.collection.query_type_handler tag and attach to it an type key with a value which equals to the identifier of query type we configured at the beginning (in this case my_search).

After this, our query type is ready for usage.

Creating custom parameter types

Netgen Layouts ships with around 15 parameter types which you can use in your custom blocks and queries and which fulfill most of the everyday usecases.

However, if you need custom validation on some of your parameters, or custom display in Symfony forms, you need to create a custom parameter type.

We will show how to create a custom parameter type on an example of a parameter which stores and validates dates in Google Analytics format.

There are two parts to implementing a parameter type in Netgen Layouts:

  • Implementing a main parameter type service which deals with parameter value conversion and validation
  • Implementing a form mapper which takes care of creating a Symfony form for the parameter type
Implementing the parameter type service

Parameter type service needs to implement Netgen\BlockManager\Parameters\ParameterTypeInterface. Since this interface has a handful of methods, there is a handy abstract class from which you can extend so you don’t have to write boilerplate code in your parameter types.

This cuts down the number of required methods to implement to 2. Let’s create an empty class extending the abstract class:

<?php

namespace AppBundle\Parameters\ParameterType;

use Netgen\BlockManager\Parameters\ParameterInterface;
use Netgen\BlockManager\Parameters\ParameterType;

class GoogleAnalyticsDateType extends ParameterType
{
    /**
     * Returns the parameter type identifier.
     *
     * @return string
     */
    public function getIdentifier()
    {
    }

    /**
     * Returns constraints that will be used to validate the parameter value.
     *
     * @param \Netgen\BlockManager\Parameters\ParameterInterface $parameter
     * @param mixed $value
     *
     * @return \Symfony\Component\Validator\Constraint[]
     */
    protected function getValueConstraints(ParameterInterface $parameter, $value)
    {
    }
}

As you can see, only two methods need to be implemented for basic parameter type to work: getIdentifier, which should return a unique identifier of a parameter type, and getValueConstraints, which should return an array of Symfony validator constraints which validate the value.

Note

Every parameter type needs to allow null as its value, since parameters are by default optional in blocks and queries. Because of that, take care not to include constraints which validate that the value is not null, like NotNull or NotBlank.

To specify constraints when the parameter is required, you can use the getRequiredConstraints method. It already has a default implementation which you can override to suit your needs.

Let’s implement getIdentifier and getValueConstraints methods:

/**
 * Returns the parameter type identifier.
 *
 * @return string
 */
public function getIdentifier()
{
    return 'ga_date';
}

/**
 * Returns constraints that will be used to validate the parameter value.
 *
 * @param \Netgen\BlockManager\Parameters\ParameterInterface $parameter
 * @param mixed $value
 *
 * @return \Symfony\Component\Validator\Constraint[]
 */
protected function getValueConstraints(ParameterInterface $parameter, $value)
{
    return array(
        new Constraints\Type(array('type' => 'string'))
    );
}

With the above implementation, we specified that the unique identifier of our parameter type is ga_date and that the value of the parameter should be a string.

This is a good time to add any custom validations you want, for example, you can implement a validator and a constraint that validates the date as a Google Analytics date.

The purpose of other methods in ParameterTypeInterface is detailed below:

configureOptions

This method uses the Symfony OptionsResolver component to specify any custom options your parameter type might have. Here, you can use the full power of the component to define required and optional options, custom validation and so on. For example, you might specify the minimum year your parameter accepts and then use the option in getValueConstraints method to modify the value constraints accordingly. An example implementation might look like this:

public function configureOptions(OptionsResolver $optionsResolver)
{
    $optionsResolver->setDefault('min_year', null);
    $optionsResolver->setRequired(array('min_year'));
    $optionsResolver->setAllowedTypes('min_year', array('int', 'null'));
}

getConstraints

This method by default takes the constraints from getValueConstraints and getRequiredConstraints method, and merges them together. Notice that this method has public visibility, while getValueConstraints and getRequiredConstraints are protected. This means that this method is the one used by Netgen Layouts when validating the parameter value and if you override it, you will effectively override any constraints implemented in the two protected methods.

toHash

This method is responsible for converting the parameter value to a hash format (scalar or an array of scalars). Since every parameter value is stored in the database encoded into JSON, this method must not return any data that cannot be safely encoded into JSON. Default implementation does not convert the value and simply returns it as is.

fromHash

This method does the opposite of toHash method. That is, it converts the JSON decoded data stored in the database to a value that will be used by the rest of Netgen Layouts code as well as your custom code. This can be anything really: a scalar, an array, on object or a whole object graph. Default implementation does not convert the value and simply returns it as is.

createValueFromInput

This method is used if you wish to provide a couple of ways for your users to specify the value of a parameter when creating a block or a query. For example, this method can be used to allow the users to provide a date as a string as well as an instance of a DateTime class. By default, this method forwards the call to fromHash method, which means users can provide the value of the parameter in the format which is equal to the format stored in the database.

isValueEmpty

This method is used to signal to the system when the value of the parameter is considered empty. For example, a date can be empty if the value of the parameter is null or an empty string. By default, this method uses empty PHP language construct to check emptiness of the value.
Implementing the form mapper

Form mapper object is responsible for specifying how the parameter will look like on a Symfony form. The interface Netgen\BlockManager\Parameters\Form\MapperInterface provides three methods for you to implement. There is also an abstract class which you can extend to ease the implementation, so you need to implement only one method.

Basic form mapper needs to only specify which Symfony form type to use:

<?php

namespace AppBundle\Parameters\FormMapper;

use Netgen\BlockManager\Parameters\Form\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class GoogleAnalyticsDateMapper extends Mapper
{
    /**
     * Returns the form type for the parameter.
     *
     * @return string
     */
    public function getFormType()
    {
        return TextType::class;
    }
}

The purpose of other methods in MapperInterface is detailed below:

mapOptions

If your parameter type has custom options which need to be forwarded to Symfony form type, you can use this method to do so. For example, if you implemented a custom Symfony form type for your Google Analytics date, you could transfer your min_year option to the Symfony form, so it does not allow specifying any year lower than what is defined in your option.

handleForm

This method is a generic method which receives the form built from the information in getFormType and mapOptions methods and makes it possible to do anything you wish with the form, like attaching custom data mappers or data transformers, adding event listeners to the form and so on.

Basically, anything you can do in Symfony form type class with a form field, you can do here too.

Registering the Symfony services

To activate both the parameter type and the form mapper, you need to specify them as Symfony services.

Parameter type service needs to have a netgen_block_manager.parameters.parameter_type tag in its service definition, while the form mapper needs to have a netgen_block_manager.parameters.form.mapper tag, together with the type attribute whose value is equal to the parameter type identifier.

Our parameter type and form mapper service definitions should look like this:

services:
    app.parameters.parameter_type.ga_date:
        class: AppBundle\Parameters\ParameterType\GoogleAnalyticsDateType
        tags:
            - { name: netgen_block_manager.parameters.parameter_type }

    app.parameters.form.mapper.ga_date:
        class: AppBundle\Parameters\FormMapper\GoogleAnalyticsDateMapper
        tags:
            - { name: netgen_block_manager.parameters.form.mapper, type: ga_date }

Creating custom value types

In Netgen Layouts, (block) item is a generic concept in a way that blocks do not (and should not) care what kind of items are put inside the blocks. To achieve this, a block item is a wrapper around a value object which comes from your CMS. For example, in eZ Platform integration, Netgen Layouts supports two types of values: eZ location and eZ content.

To be able to create block items from your own domain objects, you need to create and register your own custom value type. Value type has three purposes:

  • Loading of your domain object from the CMS by its ID by using a value loader
  • Handling the domain objects provided by your custom query types by using a value converter
  • Generating the URL to the domain object by using a value URL builder
Registering a new value type

To be able to use your domain objects inside Netgen Layouts as block items, you need to register a new value type in the configuration. To do so, you need to provide a unique identifier for your value type and a human readable name:

netgen_block_manager:
    items:
        value_types:
            my_value_type:
                name: 'My value type'

This configuration registers a new value type in the system with my_value_type identifier.

Implementing a value loader

A value loader is an object responsible for loading your domain object by its ID. It is an implementation of Netgen\BlockManager\Item\ValueLoaderInterface which provides a single method called load. This method takes the ID of the domain object and should simply return the object once loaded or throw an exception if the object could not be loaded.

For example:

<?php

namespace AppBundle\Item\ValueLoader;

use Netgen\BlockManager\Item\ValueLoaderInterface;

class MyValueTypeLoader implements ValueLoaderInterface
{
    /**
     * Loads the value from provided ID.
     *
     * @param int|string $id
     *
     * @throws \Netgen\BlockManager\Exception\Item\ItemException If value cannot be loaded
     *
     * @return mixed
     */
    public function load($id)
    {
        try {
            return $this->myBackend->loadMyObject($id);
        } catch (Exception $e) {
            throw new ItemException(
                sprintf('Object with ID "%s" could not be loaded.', $id),
                0,
                $e
            );
        }
    }
}

Once implemented, you need to register the loader in Symfony DI container:

app.block_manager.value_loader.my_value_type:
     class: AppBundle\Item\ValueLoader\MyValueTypeLoader
     tags:
         - { name: netgen_block_manager.item.value_loader, value_type: my_value_type }

Notice that the service is tagged with netgen_block_manager.item.value_loader DI tag which has a value_type attribute. This attribute needs to have a value equal to your value type identifier.

Implementing Content Browser support

To be able to actually select the items from the CMS and add them to your blocks, you also need to implement a Netgen Content Browser backend.

To automatically recognize which backend is responsible for which value types, you need to make sure that the identifier of the item in the Netgen Content Browser backend you implemented is the same as the identifier of the value type you configured above.

Implementing a value converter

As you’re probably aware, query types need not worry themselves about returning PHP objects specific to Netgen Layouts to work. Instead, they simply return domain objects which are then converted by Netgen Layouts into block items.

Converting the domain objects to Netgen Layouts items is done through so called value converters and every value type needs to have a value converter implemented. Value converter should implement Netgen\BlockManager\Item\ValueConverterInterface, which provides methods that return the data used by Netgen Layouts to work with block items, like the ID of the object, name and if the object is considered visible in your CMS.

Method supports should return if the value converter supports the given object. Usually, you will check if the provided object is of correct interface. This makes it possible to handle different types of value objects in the same value converter. For example, in eZ Platform, Content and ContentInfo are two different objects that represent the same piece of content in the CMS, but with different usecases in mind.

Method getValueType should simply return the identifier of the value type you choose when activating the value type in the configuration.

An example implementation of a value converter might look something like this:

<?php

namespace AppBundle\Item\ValueConverter;

use App\MyValue;
use Netgen\BlockManager\Item\ValueConverterInterface;

class MyValueTypeConverter implements ValueConverterInterface
{
    /**
     * Returns if the converter supports the object.
     *
     * @param mixed $object
     *
     * @return bool
     */
    public function supports($object)
    {
        return $object instanceof MyValue;
    }

    /**
     * Returns the value type for this object.
     *
     * @param mixed $object
     *
     * @return string
     */
    public function getValueType($object)
    {
        return 'my_value_type';
    }

    /**
     * Returns the object ID.
     *
     * @param \App\MyValue $object
     *
     * @return int|string
     */
    public function getId($object)
    {
        return $object->id;
    }

    /**
     * Returns the object name.
     *
     * @param \App\MyValue $object
     *
     * @return string
     */
    public function getName($object)
    {
        return $object->name;
    }

    /**
     * Returns if the object is visible.
     *
     * @param \App\MyValue $object
     *
     * @return bool
     */
    public function getIsVisible($object)
    {
        return $object->isVisible();
    }
}

Once implemented, you need to register the converter in Symfony DI container and tag it with netgen_block_manager.item.value_converter tag:

app.block_manager.value_converter.my_value_type_content:
     class: AppBundle\Item\ValueConverter\MyValueTypeConverter
     tags:
         - { name: netgen_block_manager.item.value_converter }
Implementing a value URL builder

To generate the links to your domain objects in your blocks, you can use ngbm_item_path Twig function in your Twig templates. This function internally forwards the URL generation to the correct value URL builder based on the value type of the item. To generate the URL for your value type, simply implement the Netgen\BlockManager\Item\ValueUrlBuilderInterface, which provides a single method called getUrl responsible to generate the URL.

Note

getUrl method should return the full path to the item, including the starting slash, not just a slug.

An example implementation might use the Symfony router and generate the URL based on the object ID:

<?php

namespace AppBundle\Item\ValueUrlBuilder;

use Netgen\BlockManager\Item\ValueUrlBuilderInterface;

class MyValueTypeUrlBuilder implements ValueUrlBuilderInterface
{
    /**
     * Returns the object URL. Take note that this is not a slug,
     * but a full path, i.e. starting with /.
     *
     * @param mixed $object
     *
     * @return string
     */
    public function getUrl($object)
    {
        return $this->router->generate(
            'my_custom_route',
            array(
                'id' => $object->id,
            )
        );
    }
}

Once implemented, you need to register the URL builder in Symfony DI container:

app.block_manager.value_url_builder.my_value_type:
     class: AppBundle\Item\ValueUrlBuilder\MyValueTypeUrlBuilder
     tags:
         - { name: netgen_block_manager.item.value_url_builder, value_type: my_value_type }

Notice that the service is tagged with netgen_block_manager.item.value_url_builder DI tag which has a value_type attribute. This attribute needs to have a value equal to your value type identifier.

Implementing item templates

Once a custom value type is implemented, it’s time to implement Twig templates that will be used to render the item that holds the value.

Just like with block templates, for rendering an item, you need to implement two templates, one for backend (Block Manager app) and one for frontend.

Implementing a backend template

A backend template, or rather, template for Block Manager app is simple. It receives the item in question in item variable and can be used to render the item name and item image. The basic structure of the template looks like this:

<div class="image">
    <img src="/path/to/image.jpg" />
</div>

<div class="name">
    <p><a href="{{ ngbm_item_path(item) }}" target="_blank" rel="noopener noreferrer">{{ item.name }}</a></p>
</div>

Rendering an item name and URL works for all items, as long as you implemented proper value URL builders and converters. Rendering an image is left for you, as often it requires additional steps in contrast to just outputting the image path.

Registering the backend template is done via the view config:

netgen_block_manager:
    view:
        item_view:
            api:
                my_value:
                    template: "@App/api/item/view/my_value.html.twig"
                    match:
                        item\value_type: my_value
Implementing a frontend template

Just as with the backend template, frontend template receives the item in question via item variable. Frontend templates depend on your design, so there’s little sense in providing an example implementation, but once you implement your frontend template, you can register it with:

netgen_block_manager:
    view:
        item_view:
            default:
                my_value:
                    template: "@App/item/view/my_value.html.twig"
                    match:
                        item\value_type: my_value

Creating custom target types

Netgen Layouts is shipped with a number of target types to which you can map your layouts to be used in a layout resolving process. These target types are generic, allowing you to attach a layout to a request URI, a Symfony route or their prefixes. In most cases, when working with pure Symfony apps, this is enough.

However, if you wish that your layouts can be mapped to domain objects from your CMS directly, you can create custom target types.

Note

Custom target type can be whatever comes to mind, not only a domain object from your CMS.

Warning

Custom target types are one of the most complicated extension points in Netgen Layouts and creating a custom target type involves creating configuration, templates, translations and quite a bit of code.

Implementing the target type classes

Implementing a target type in PHP requires creating at least three Symfony services:

  • Target type itself
  • Symfony form mapper
  • A target handler for every database engine supported in Netgen Layouts
Creating a target type

A target type is a PHP class implementing Netgen\BlockManager\Layout\Resolver\TargetTypeInterface. The purpose of this class is twofold:

  • Provide Symfony constraints that validate the value of the target when adding it to a mapping
  • Extract the value of the target from the request to be used in layout resolving process

The first point is achieved by implementing getConstraints method, which should return the array of Symfony validator constraints which should validate the value. For example, in eZ location target type, these constraints validate that the ID of the location is a number larger than 0 and that the location with the provided ID actually exists:

public function getConstraints()
{
    return array(
        new Constraints\NotBlank(),
        new Constraints\Type(array('type' => 'numeric')),
        new Constraints\GreaterThan(array('value' => 0)),
        new EzConstraints\Location(),
    );
}

The second point is achieved by implementing the provideValue method. This method takes a request object and should return a value of your target type if it exists in the request or null if it doesn’t. For example, eZ location target type extracts the location from provided request and returns its ID:

public function provideValue(Request $request)
{
    $location = $this->contentExtractor->extractLocation($request);

    return $location instanceof APILocation ? $location->id : null;
}

The one method that remains to be implemented is the getType method, which should return a unique identifier of the target type.

Once this is done, we need to register the target type in the Symfony DIC with the netgen_block_manager.layout.resolver.target_type tag:

app.target_type.my_target:
    class: AppBundle\Layout\Resolver\TargetType\MyTarget
    tags:
        - { name: netgen_block_manager.layout.resolver.target_type }

Tip

You can add a priority attribute to the tag, which allows you to make your target type considered before others when deciding if the current request matches one of the targets.

Creating the form mapper

To be able to add the target to a mapping or edit the value of an existing target, you need to provide a form mapper which provides data for generating Symfony form for your target type. The mapper needs to implement Netgen\BlockManager\Layout\Resolver\Form\TargetType\MapperInterface and there’s also a handy abstract class which you can extend to cut down the number of methods to define to one: getFormType, which returns which Symfony form type should be used to edit the target:

<?php

namespace AppBundle\Layout\Resolver\Form\TargetType\Mapper;

use Netgen\BlockManager\Layout\Resolver\Form\TargetType\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class MyTarget extends Mapper
{
    /**
     * Returns the form type that will be used to edit the value of this condition type.
     *
     * @return string
     */
    public function getFormType()
    {
        return TextType::class;
    }
}

There are two other methods in the interface:

  • mapOptions which makes it possible to provide custom options to the form type
  • handleForm which allows you to customize the form in any way you see fit

Finally, you need to register the mapper in the Symfony container with the correct tag and the identifier of the target type:

app.layout.resolver.form.target_type.mapper.my_target:
    class: AppBundle\Layout\Resolver\Form\TargetType\Mapper\MyTarget
    tags:
        - { name: netgen_block_manager.layout.resolver.form.target_type.mapper, target_type: my_target }
Creating target handlers for the database engine

Matching the target value from the request to the value stored in the database is done in the database itself. This means that you need to provide a so called target handler for every database engine supported in Netgen Layouts.

The only supported database engine is called “doctrine”, since it uses Doctrine library to communicate with the database.

This target handler needs to implement Netgen\BlockManager\Persistence\Doctrine\QueryHandler\LayoutResolver\TargetHandler interface which provides a single method called handleQuery which takes the Doctrine query object and the target value and should modify the query in way to match the provided value.

Stored target value can be accessed in the query with rt.value so to match a simple integer, you would implement it like this:

/**
 * Handles the query by adding the clause that matches the provided target values.
 *
 * @param \Doctrine\DBAL\Query\QueryBuilder $query
 * @param mixed $value
 */
public function handleQuery(QueryBuilder $query, $value)
{
    $query->andWhere(
        $query->expr()->in('rt.value', array(':target_value'))
    )
    ->setParameter('target_value', $value, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
}

Finally, the target handler needs to registered in the Symfony container with the correct tag and target type identifier:

app.layout_resolver.target_handler.doctrine.my_target:
    class: AppBundle\LayoutResolver\TargetHandler\Doctrine\MyTarget
    tags:
        - { name: netgen_block_manager.persistence.doctrine.layout_resolver.query_handler.target_handler, target_type: my_target }
Implementing the target type template

Target type uses a single template in the value view context of the Netgen Layouts view layer to display the value of the target in the admin interface. Since the target itself usually provides only the scalar identifier as its value, this template usually needs some logic to display the name of the target (from your CMS for example). In case of eZ Platform, these templates for example use Twig functions to load the content and location objects and return their names and paths:

{% set content_name = ngbm_ezcontent_name(target.value) %}

{{ content_name != null ? content_name : '(INVALID CONTENT)' }}

To register the template in the system, the following configuration is needed (make sure to use the value view context):

netgen_block_manager:
    view:
        rule_target_view:
            value:
                my_target:
                    template: "@App/layout_resolver/target/value/my_target.html.twig"
                    match:
                        rule_target\type: my_target
Target type translations

Each target type uses two translation strings, one in ngbm and one in ngbm_admin catalog. The first one is a generic string which should provide a human readable name of the target type and should be in the layout_resolver.target.<target_type_identifier> format:

The second one is used as a label in administration of interface which states for which target types is the mapping used and should be in layout_resolver.rule.target_header.<target_type_identifier> format:

Creating custom condition types

Netgen Layouts is shipped with a couple of condition types which you can use to limit your layout mappings to certain conditions. One example condition type which is built into Netgen Layouts is the query parameter condition type which enables the mapping if some query parameter from the request matches the value stored in the condition. In case of eZ Platform integration, an example condition type which is built in is the siteaccess condition type that activates the mapping only if the siteaccess of the current request matches the condition.

If you wish to map your layouts to targets in some other conditions, like time of day, location of the user, IP address, you name it…, you can create your own condition types.

Warning

Custom condition types are one of the most complicated extension points in Netgen Layouts and creating a custom condition type involves creating configuration, templates, translations and quite a bit of code.

Implementing the condition type classes

Implementing a condition type in PHP requires creating two Symfony services:

  • Condition type itself
  • Symfony form mapper
Creating a condition type

A condition type is a PHP class implementing Netgen\BlockManager\Layout\Resolver\ConditionTypeInterface. The purpose of this class is twofold:

  • Provide Symfony constraints that validate the value of the condition when adding it to a mapping
  • Deciding if the current request matches the provided value of the condition

The first point is achieved by implementing getConstraints method, which should return the array of Symfony validator constraints which should validate the value. For example, in eZ siteaccess condition type, these constraints validate that all selected siteaccesses are non empty strings and that they actually exist:

public function getConstraints()
{
    return array(
        new Constraints\NotBlank(),
        new Constraints\Type(array('type' => 'array')),
        new Constraints\All(
            array(
                'constraints' => array(
                    new Constraints\Type(array('type' => 'string')),
                    new EzConstraints\SiteAccess(),
                ),
            )
        ),
    );
}

The second point is achieved by implementing the matches method. This method takes a request object and based on the data from the request decides if it matches the provided value. For example, the matches method of the eZ siteaccess condition type returns true only if the siteaccess is provided in the request and is equal to one of the stored values of the condition:

public function matches(Request $request, $value)
{
    $siteAccess = $request->attributes->get('siteaccess');
    if (!$siteAccess instanceof SiteAccess) {
        return false;
    }

    if (!is_array($value) || empty($value)) {
        return false;
    }

    return in_array($siteAccess->name, $value, true);
}

The one method that remains to be implemented is the getType method, which should return a unique identifier of the condition type.

Once this is done, we need to register the condition type in the Symfony DIC with the netgen_block_manager.layout.resolver.condition_type tag:

app.condition_type.my_condition:
    class: AppBundle\Layout\Resolver\ConditionType\MyCondition
    tags:
        - { name: netgen_block_manager.layout.resolver.condition_type }
Creating the form mapper

To be able to add the condition to a mapping or edit the value of an existing condition, you need to provide a form mapper which provides data for generating Symfony form for your condition type. The mapper needs to implement Netgen\BlockManager\Layout\Resolver\Form\ConditionType\MapperInterface and there’s also a handy abstract class which you can extend to cut down the number of methods to define to one: getFormType, which returns which Symfony form type should be used to edit the condition:

<?php

namespace AppBundle\Layout\Resolver\Form\ConditionType\Mapper;

use Netgen\BlockManager\Layout\Resolver\Form\ConditionType\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class MyCondition extends Mapper
{
    /**
     * Returns the form type that will be used to edit the value of this condition type.
     *
     * @return string
     */
    public function getFormType()
    {
        return TextType::class;
    }
}

There are two other methods in the interface:

  • mapOptions which makes it possible to provide custom options to the form type
  • handleForm which allows you to customize the form in any way you see fit

Finally, you need to register the mapper in the Symfony container with the correct tag and the identifier of the condition type:

app.layout.resolver.form.condition_type.mapper.my_condition:
    class: AppBundle\Layout\Resolver\Form\ConditionType\Mapper\MyCondition
    tags:
        - { name: netgen_block_manager.layout.resolver.form.condition_type.mapper, condition_type: my_condition }
Implementing the condition type template

Condition type uses a single template in the value view context of the Netgen Layouts view layer to display the value of the condition in the admin interface. Since the condition itself usually provides only the scalar identifier as its value, this template usually needs some logic to display the human readable value of the condition. For example, content type condition from eZ Platform uses custom Twig functions to display content type names instead of the identifiers:

{% set content_type_names = [] %}

{% for value in condition.value %}
    {% set content_type_names = content_type_names|merge([ngbm_ez_content_type_name(value)]) %}
{% endfor %}

{{ content_type_names|join(', ') }}

To register the template in the system, the following configuration is needed (make sure to use the value view context):

netgen_block_manager:
    view:
        rule_condition_view:
            value:
                my_condition:
                    template: "@App/layout_resolver/condition/value/my_condition.html.twig"
                    match:
                        rule_condition\type: my_condition
Condition type translations

Each condition type uses one translation string in the ngbm catalog. This is a generic string which should provide a human readable name of the condition type and should be in the layout_resolver.condition.<condition_type_identifier> format:

Adding parameters to existing blocks

Currently, there is no straightforward and official way to add parameters to existing blocks in Netgen Layouts.

However, if really needed, you can always override the class for an existing block definition handler by using a compiler pass in Symfony, or redefining the Symfony service for the handler. Overriden class can then extend from the original handler class and add new parameters or modify the existing ones.

For example, to add a new parameter to a Title block, you would create a new class which extends from the original one:

<?php

namespace AppBundle\Block\BlockDefinition\Handler;

use Netgen\BlockManager\Block\BlockDefinition\Handler\TitleHandler as BaseTitleHandler;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;
use Netgen\BlockManager\Parameters\ParameterType\TextLineType;

class TitleHandler extends BaseTitleHandler
{
    public function buildParameters(ParameterBuilderInterface $builder)
    {
        parent::buildParameters($builder);

        $builder->add('my_param', TextLineType::class);
    }
}

You can ofcourse override the getDynamicParameters method too if needed:

public function getDynamicParameters(Block $block)
{
    $params = parent::getDynamicParameters($block);

    $params['my_dynamic_param'] = 42;

    return $params;
}

After that, you need to create a compiler pass which sets the new class for the handler:

<?php

namespace AppBundle\DependencyInjection\CompilerPass;

use AppBundle\Block\BlockDefinition\Handler\TitleHandler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class TitleHandlerPass implements CompilerPassInterface
{
    const SERVICE_NAME = 'netgen_block_manager.block.block_definition.handler.title';

    public function process(ContainerBuilder $container)
    {
        if (!$container->has(static::SERVICE_NAME)) {
            return;
        }

        $container
            ->findDefinition(static::SERVICE_NAME)
            ->setClass(TitleHandler::class);
    }
}

And finally, you need to register the compiler pass in your bundle class:

<?php

namespace AppBundle;

use AppBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new CompilerPass\TitleHandlerPass());
    }
}

Tips & tricks

Tips & tricks

How to override existing templates

By using the view layer configuration in Netgen Layouts, it is possible to override any template for built in layout types, block view types and block item view types.

Warning

View layer works in a way that first configuration which matches the rules is used for rendering the entity, so take care to properly use Symfony configuration prepending to make sure your rules are matched before the built in ones.

Overriding layout type templates

You can override a template for any layout type (including built in ones) with the following example configuration:

netgen_block_manager:
    view:
        layout_view:
            default:
                layout_4_override:
                    template: "@App/layouts/layout_4.html.twig"
                    match:
                        layout\type: layout_4

This overrides the layout_4 layout type to use @App/layouts/layout_4.html.twig template.

Overriding block view type templates

You can override a template for any block view type (including built in ones) with the following example configuration:

netgen_block_manager:
    view:
        block_view:
            default:
                simple_title_override:
                    template: "@App/blocks/title/simple_title.html.twig"
                    match:
                        block\definition: title
                        block\view_type: simple_title

This overrides the simple_title view type of the title block definition to use @App/blocks/title/simple_title.html.twig template.

Overriding block item view type templates

You can override a template for any block item view type (including built in ones) with the following example configuration:

netgen_block_manager:
    view:
        item_view:
            default:
                my_item_standard_override:
                    template: "@App/items/my_item/standard.html.twig"
                    match:
                        item\value_type: my_item
                        item\view_type: standard

This overrides the standard item view type of the my_item value type to use @App/items/my_item/standard.html.twig template.

Tip

To override item view types for ezcontent and ezlocation items, use the regular eZ Platform content view configuration instead of overriding the templates in Netgen Layouts.

How to add icons for custom layout types

Once you add a custom layout type, you will notice that the icon is missing in Block Manager app and admin interface. This is due to fact that icons are loaded through CSS, rather than through configuration.

In order to add your own icon to Netgen Layouts, you will need to inject a piece of CSS into Netgen Layouts configuration for admin interface and Block Manager app.

Warning

Since CSS classes cannot start with a number, layout type identifiers should not start with a number.

Tip

Since layout type icons are used in several places in admin interface and Block Manager app in different sizes, it is recommended to create your own icons in SVG format, rather than PNG to allow the CSS to resize the icons.

If you wish to add an icon for a layout type with my_layout identifier in admin interface, you can use the following CSS:

.layout-icon.my_layout {
    background-image: url(/path/to/icon/my_layout.svg);
}

If you wish to add an icon for a layout type with my_layout identifier in Block Manager app, you can use the following CSS:

[value="my_layout"] + label:before {
    background-image: url(/path/to/icon/my_layout.svg);
}

After that, you need to register your CSS file in configuration:

netgen_block_manager:
    admin:
        stylesheets:
            - '/path/to/bmadmin/style.css'
    app:
        stylesheets:
            - '/path/to/bmapp/style.css'

How to add icons for custom block types

Once you add a custom block definition and corresponding block type, you will notice that the icon is missing in Block Manager app. This is due to fact that icons are loaded through CSS, rather than through configuration.

In order to add your own icon to Netgen Layouts, you will need to inject a piece of CSS into Netgen Layouts configuration for Block Manager app.

Warning

Since CSS classes cannot start with a number, block definition and block type identifiers should not start with a number.

If you wish to add an icon for a block type with my_block identifier in Block Manager app, you can use the following CSS:

.add-block-btn.icn-my_block .icon {
    background-image: url(/path/to/icon/my_layout.svg);
}

After that, you need to register your CSS file in configuration:

netgen_block_manager:
    app:
        stylesheets:
            - '/path/to/bmapp/style.css'

How to quickly render a layout on a single URL

Usually, if you wish to render a page with a layout in your CMS, you would need to create a controller which renders a template which then extends from ngbm.layoutTemplate variable for layout to be resolved.

Even worse, you would need to create a page (article, blog post, landing page or something else) in the CMS just to make the URL available in the system.

Creating a Symfony controller and a page in your CMS just to render an URL with a layout can be an overkill, and in those cases, you can use a controller built into Symfony to quickly render a layout on a single URL.

First, you need to create a simple Twig template which you can reuse in cases like this:

{# @App/empty_page.html.twig #}

{% extends ngbm.layoutTemplate %}

After that, create a Symfony route which uses a built in Symfony controller to render the template:

my_cool_page:
    path: /my/cool/page
    defaults:
        _controller: 'FrameworkBundle:Template:template'
        template: '@App/empty_page.html.twig'

Finally, you need to add a mapping in Netgen Layouts administration to link one of the layouts to the route you created. Once this is done, you can access the URL you provided in your route config and see the layout rendered in all its glory.

How to inject current eZ location and content in blocks and query types

Note

This tutorial is eZ Platform/eZ Publish 5 specific.

Often, your custom block definitions and query types need the current eZ Platform location or content to work. While you can extract the information about the current location and content from the Request object, the differences between different versions of eZ kernel make it impossible to create generic block definitions and query types that work on any eZ kernel available.

That’s why there is a handy Symfony service called Content Provider in Netgen Layouts that can be used to retrieve the current location or content in a generic way. In background, this service uses different implementations of a service that extracts content and locations from the request for every eZ kernel version available.

Note

If using eZ Publish 5, you need to specify a Symfony service alias somewhere in your configuration which makes sure that an eZ 5 version of the extractor is used:

netgen_block_manager.ezpublish.content_extractor:
    alias: netgen_block_manager.ezpublish.content_extractor.ez5_request

Service identifier of the Content Provider is netgen_block_manager.ezpublish.content_provider and you can inject it into your block definitions and query types like any other service.

Content Provider exposes two methods: provideLocation and provideContent which return the location and content from the current request and null if no location or content exist.

As an example, you can use the Content Provider to inject the location and content objects into Twig templates for your blocks:

<?php

namespace AppBundle\Block\BlockDefinition\Handler;

use Netgen\BlockManager\API\Values\Block\Block;
use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler;
use Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface;

class MyBlockHandler extends BlockDefinitionHandler
{
    /**
     * @var \Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface
     */
    protected $contentProvider;

    /**
     * Constructor.
     *
     * @param \Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface $contentProvider
     */
    public function __construct(ContentProviderInterface $contentProvider)
    {
        $this->contentProvider = $contentProvider;
    }

    /**
     * Returns the array of dynamic parameters provided by this block definition.
     *
     * @param \Netgen\BlockManager\API\Values\Block\Block $block
     *
     * @return array
     */
    public function getDynamicParameters(Block $block)
    {
        return array(
            'content' => $this->contentProvider->provideContent(),
            'location' => $this->contentProvider->provideLocation(),
        );
    }
}

How to override the templates for Grid block columns

Grid block type uses a nifty little trick to specify the templates for its 2, 3, 4 or 6 columns variants which makes it possible to override the template for only one of those variants without the need for overriding the entire grid template.

Basically, it uses a feature from the view layer which makes it possible to inject custom parameters to Twig templates straight from the match configuration rule. By default, if no parameter is provided that specifies the template to use, Grid block uses built in templates for each of the column variants.

As an example, the following configuration would override the template for 4 column variant of a Grid block:

netgen_block_manager:
    view:
        block_view:
            default:
                list\grid\override:
                    template: "@NetgenBlockManager/block/list/grid.html.twig"
                    parameters:
                        column_templates:
                            4: "@App/block/grid/4_columns.html.twig"
                    match:
                        block\definition: list
                        block\view_type: grid

Basically, this rule specifies that we want to use the original grid template available in Netgen Layouts (@NetgenBlockManager/block/list/grid.html.twig) and to also provide a parameter called column_templates which the original grid template uses to extract the template names for column variants from.

We only specified that 4 columns template should be overriden by specifying the key with a value of 4, but we can easily add more overrides by specifying more array keys in the column_templates parameter.

How to specify default value for block parameters

As you probably know, you can create a new block in a layout by drag and dropping it from the left sidebar to the layout. This means that there is no way to specify the starting values for the parameters in the block. Instead, the default values provided in the block definition are used.

This is not a problem per se, but it does mean that in cases when you do need different starting values than those provided by the block definition, you need to add a block and immediately edit it in the right sidebar to change the parameter values to more suitable ones. This only slows down layout creation process.

To overcome this, you can specify default values for any block parameter in the configuration. For example, if you want to change the default number of columns of a Grid block, you would use a configuration like this one:

block_types:
    grid:
        defaults:
            parameters:
                number_of_columns: 6

From now on, every time you add a Grid block to your layout, it will be created with 6 columns.

How to inject items to blocks manually

Usually, block items are provided to blocks with a collection query. But, sometimes it is useful to provide some specific block items to the block by hand (meaning in block definition handler). This way, you could reuse block item templates by calling the ngbm_render_item function on those items, instead of duplicating the block item template logic inside the block itself.

To achieve this, you can use a Symfony service from Netgen Layouts called item builder which is an instance of Netgen\BlockManager\Item\ItemBuilderInterface. After that, you can just use it in your getDynamicParameters method of the block definition handlers to build the items and provide them as dynamic parameters to the block.

Note

This service is used by the collection query result building process too, so you will be basically using the same principles Netgen Layouts uses when building items.

The ID of the item builder Symfony service is netgen_block_manager.item.item_builder and you can just inject it to your block definition handlers as usual.

The service provides a single method called build which receives a single parameter, your domain object, which will then build the block item. For this to work, you need to have value converters (instances of Netgen\BlockManager\Item\ValueConverterInterface) for any domain object you wish to build items from.

For example, to build the item from your domain object, you would write something like this in your getDynamicParameters method:

$myObject = $this->myBackend->loadMyObject(42);

$item = $this->itemBuilder->build($myObject);