Skip over navigation

Join Stripe & Oro, As We Share Scalable Payment Integration Patterns for Enterprise eCommerce on March 6, 9 am CST/4 pm CET!

Book Your Spot
HomeBlogDebugging frontend layouts

Developers' Digest

Debugging frontend layouts

February 14, 2017 | msarandi

Changing any visual HTML part of a complex application like OroCommerce may easily become a challenge for designer or developer. When similar elements and blocks are reused throughout the system, you need a tool to visualize their mutual location without a distraction of the actual content.

In OroCommerce, Layouts translate the structure of user interface into the adapted and simplified tree structure of the page blocks.

In this post, you will learn about:

  • the layout profiler that enables viewing layout structure and context
  • using layouts for editing, adding, rearranging and removing the content of the web application.

Layout profiler

For layout context investigation – to better understand the relationship between the layout blocks – use Layout profiler.

Note: Layout profiler is available only in the development environment with `debug` mode enabled.

Before you begin, enable all Layout Profiler options in System > Configuration > Development Settings:

Debugging frontend layouts

The options are:

  • Include Block Debug Info Into HTML – adds a unique identifier of the current block (data-layout-debug-block-id) and a rendered template of the current block (data-layout-debug-block-template) to the block HTML attributes.
    Example of the html source code after the option is enabled:
    LayoutSourceCode
  • Generate Layout Tree Dump For The Developer Toolbar – enables rendering of Layout Tree.

Now you can view web page layout structure and discover layout context items.

View layout context items

Open any page in your web application and hover over the Layout:

Layout

What you get is the list of layout context items (action, route_name, theme, etc.) and their values.

LayoutSymfonyProfiler

View layout structure

When you click on the Layout, a layout blocks tree pops up and you can analyze the blocks mutual location and nesting. The data-layout-debug-block-id attribute of the block is used as a tree node name.

LayoutStructure

Customize a web application

Modify a web page using a TWIG template

Layouts come handy when you customize a web application. Following the steps below, you can add a custom link into the unified web application footer.

Start with creating a new html page (e.g. the one you are reading now) and a controller to render it:

namespace OroBundleDemoLayoutBundleController;

    use SymfonyBundleFrameworkBundleControllerController;
    use SensioBundleFrameworkExtraBundleConfigurationRoute;
    use SensioBundleFrameworkExtraBundleConfigurationTemplate;

    class DefaultController extends Controller
    {
        /**
         * @Route("/about-layouts")
         * @Template()
         */
        public function showAboutLayoutsAction()
        {
            return [];
        }
    }

Next, in Layout tree, find an appropriate location for the new block with the link to About layouts page.
For example, create a new about_layouts_block inside the footer_menu_container block:

  1. In the /DemoLayoutBundle/Resources/views create a layouts folder and sub-folder named by your active layout theme.Note: If you use a default theme, create a default sub-folder. You can verify the active theme in the layout context items by hovering over the Layout.Let us use the following path: /DemoLayoutBundle/Resources/views/layouts/default, which means that we modify a layout for the `default` theme.
  2. At the newly created location, create `layout.yml` that defines a block with `link` blockType and adds this new block into the footer_menu_container:
    layout:
        actions:
            - '@addTree':
                items:
                    about_layouts_block:
                        blockType: link
                        options:
                            route_name: oro_demolayout_default_showaboutlayouts
                            text: 'About layouts'
                tree:
                    footer_menu_container:
                        about_layouts_block: ~

This will result in the following layout tree:
footer
The web application footer has temporarily changed to the following:
simpleUpdatedFooter

Now, apply a block theme for the new block by creating a dedicated TWIG template. You probably would like your new link to blend smoothly with the other footer elements.

In your web application, inspect the source code in the footer block to get the general idea of the template you need:
inspectUpdatedFooter

In the actions in DemoLayoutBundle/Resources/views/layouts/default/layout.yml, add @setBlockTheme command with the similar theme:

    actions:
        - '@setBlockTheme':
            themes: 'OroBundleDemoLayoutBundle:layouts:default/layout.html.twig'

and create a new block theme file – DemoLayoutBundle/Resources/views/layouts/default/layout.html.twig:

{% block _about_layouts_block_widget %}

{% endblock %}

After these changes, the link in the footer has moved to the How-to’s section to the left from the original footer content:
finalUpdatedFooter

Modify a web page using layout actions

Prerequisites

Modify /DemoLayoutBundle/Controller/DefaultController.php to use OroBundleLayoutBundleAnnotationLayout instead of the SensioBundleFrameworkExtraBundleConfigurationTemplate, and replace the @Template() with the @Layout annotation, like in the following example:

namespace OroBundleDemoLayoutBundleController;

use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use OroBundleLayoutBundleAnnotationLayout;

class DefaultController extends Controller
{
    /**
     * @Route("/about-layouts")
     * @Layout
     */
    public function showAboutLayoutsAction()
    {
        return [];
    }
}

After these changes, the controller switches to the layout-based page rendering.

@add custom layout block

In the layout tree, identify the parent block for your new custom bock (e.g. content).

In the DemoLayoutBundle/Resources/views/layouts/default folder, create an oro_demolayout_default_showaboutlayouts folder to define a page route name.

Inside that folder, create a page.yml file with an @add action that defines a new layout element and nests it inside the block with the specified parentId (use the block name from the layout tree as parentId, for example, content):

layout:
    actions:
        - '@add':
            id:        demo_page_content
            parentId:  content
            blockType: text
            options:
                text: 'Detailed information about using layouts...'

Refresh the web page to view the changes.

@remove existing layout block

To eliminate an unwanted element of the page layout, use @remove action in the page.yml with an id of the block to be removed. For example, you can drop the main menu:

        - '@remove':
            id: main_menu_container

Rearrange a layout with @move action

Use @move action in the page.yml to change the parent of the layout block and append it to the bottom of the children list.

For example, take the following layout tree:

content
|--section-a
|--section-b
footer

If in the page.yml you move the footer node into the content node using the following action:

        - '@move':
            id: footer
            parentId: content

The layout will change to the following one:

content
|--section-a
|--section-b
|--footer

Note: When moved, the block gets to the bottom of the list of its siblings. To rearrange siblings, use series of @move actions. For example, to put the footer before the section-b use the following actions:

        - '@move':
            id: footer
            parentId: content
       - '@move':
            id: section-b
            parentId: content

Updated layout tree is:

header
content
|--section-a
|--footer
|--section-b

Using context variables in layout

Eventually, you may need multiple pages with a shared layout template to access the same static information, for example, a system-wide sub-menu configuration and maintenance announcement text.

Note: For an easier debug process, use oro:layout:debug –context command that shows how the context data-resolver is configured.

To enable sharing context information, pass the Layout context as a parameter in layoutBuilder->getLayout method, like in the following example:

    $layoutContext = new LayoutContext();
    $layoutBuilder = $layoutManager->getLayoutBuilder();
    $layoutBuilder->getLayout($layoutContext);

and add the context variables (e.g. static_content) and an algorythm that returns their value to the @Layout annotation in the controller:

    /**
     * @Route("/about-layouts")
     * @Layout(vars={"static_content"})
     */
    public function showAboutLayoutsAction()
    {
        return [
            'static_content' => 'The selected product category is temporarily under maintenance. We apologize for any inconvenience. Please come back later.'
        ];
    }

As we have added a new static_content variable to the layout context, let’s use its value instead of the static text in the block we created earlier. In the page.yml file located in the DemoLayoutBundle/Resources/views/layouts/default/oro_demolayout_default_showaboutlayouts/ folder, parameterize the text value with the static_content value in the layout context ($context):

        - '@add':
            id:        demo_page_content
            parentId:  content
            blockType: text
            options:
                text: { '@value': $context.static_content }

Now, if you hover over the Layout profiler, you can see a static_content context item and its value.

Using data providers in layout

Dynamic data may be bound to layout elements either via data collection in layout context or via dedicated data provider.
For a quick illustration, let us use an existing category_tree service that returns an array of categories to populate the page title for our maintenance announcement.
The category_tree service is defined in the Oro/Bundle/CatalogBundle/Resources/config/layout.yml in a following manner:

    orob2b_catalog.layout.data_provider.category_tree:
        class: OroB2BBundleCatalogBundleLayoutDataProviderCategoryTreeProvider
        arguments:
            - '@orob2b_catalog.provider.category_tree_provider'
            - '@doctrine'
        tags:
            - { name: layout.data_provider, alias: category_tree }

In the page.yml file, use @setOption action to populate the page title text with the first element of the category tree:

        - '@setOption':
            id: page_title
            optionName: text
            optionValue: {'@value': $data.category_tree[0] }

Additional resources

To help you master the layout management actions, we’ve prepared a `DemoLayoutBundle` with most of the examples provided above. To add this bundle to your application please extract the content of the zip archive into a source code directory.
See also:

Back to top