eZ Platform Discussions

Page Builder: Blocks Reuse


#1

I really like how the page builder uses zones and blocks and I would like to re-use that functionality elsewhere in the site.

Zones:
I have established 3 types of zones: full, left_sidebar, right_sidebar. It would be great to use those zone layouts for other content_type templates. Currently I have a folder called layouts that stores the different layouts that the site can use. I then have another layouts folder inside my page_builder folder. I tried to set up an inheritance twig structure so I could squash these two folders into one common layouts folder but don’t think it’s going to work. I was close but since my landing_page used the layouts/full.html.twig and then inside it rendered the page field type which was using the page_builder/layouts/left_sidebar.html.twig however the twig blocks were being repeated twice and my design broke. I would have to override the entire content twig block with the same div structure which defeats the purpose. I will work on getting an example on github for a better explanation.

Blocks:
I really want to re-use blocks on templates outside of the page fieldtype. So my landing_page can create a “collection” of content items such as featured articles. I would then like on my product content_type template to reuse that “block” by inserting a collection of featured articles. The goal is to not create a collection of content objects for a pagebuilder field type vs a full template.

My current implementation is the following:

page/blocks/collection/sidebar.html.twig

{% extends '@ezdesign/page/blocks/_block.html.twig' %}

{% set block_description = block_description|default() %}

{% block block_identifier %}collection{% endblock %}
{% block block_class %}{{ block_class }}{% endblock %}

{% block block_title_text %}{{ block_displayName ? block_name }}{% endblock %}
{% block block_description_text %}{{ block_description }}{% endblock %}

{% block block_content %}
    <div class="grid-x grid-margin-x">
        {% for item in locationlist %}
            <div class="cell">
                {{ render(controller('AppBundle:PageBuilder:viewItem', {
                    'item': item,
                    'viewType': 'page_block_collection_sidebar'
                })) }}
            </div>
        {% endfor %}
    </div>
{% endblock %}

full/article.html.twig

{% include '@ezdesign/page/blocks/collection/sidebar.html.twig'
    with {'block_name': 'Related Products',
          'block_displayName': true,
          'locationlist': ez_field_value(content, "related_products").destinationContentIds
 } %}

So this is where things get really tricky. The collection will store a locationList array of id’s while the article template is using a relationMultiple which stores a list of content Id’s. So what I do is send my contentId’s array to my collection template as locationList and then instead of rendering the viewLocation view I render another custom controller that determines if the locationList is a list of location ids or a list of content ids.

page/blocks/collection/sidebar.html.twig

{{ render(controller('AppBundle:PageBuilder:viewItem', {
   'item': item,
   'viewType': 'page_block_collection_sidebar'
})) }}

src/AppBundle/Controller/PageBuilderController.php

<?php

namespace AppBundle\Controller;

use eZ\Bundle\EzPublishCoreBundle\Controller;

class PageBuilderController extends Controller
{

/**
 * Displays a list of content objects
 *
 * @param $item
 * @param $viewType
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function viewItemAction($item, $viewType)
{
    $type = 'content';

    if(is_array($item)){
        $item = $item['locationId'];
        $type = 'location';
    }

    return $this->render(
        '@ezdesign/page/item/' . $type . '.html.twig',
        array(
            'id' => $item,
            'viewType' => $viewType
        )
    );
}
}

page/item/content.html.twig

{{ render(controller('ez_content:viewAction', {
'contentId': id,
'viewType': viewType
})) }}

page/item/location.html.twig

{{ render(controller('ez_content:viewLocation', {
'locationId': id,
'viewType': viewType
})) }}

This doesn’t seem efficient and there has to be a better way. Am I looking at needing to create a custom view? I was hoping I could just render the content or location view in my PageBuilderController such as:

if(is_array($item)){
    return $this->render(
        'ez_content:viewLocation',
        array(
            'locationId' =>  $item['locationId'],
            'viewType' => $viewType
        )
    );
} else {
   return $this->render(
      'ez_content:viewContent',
       array(
           'contentId' =>  $item,
           'viewType' => $viewType
       )
   );
}