Debugging frontend layouts

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:

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:

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:

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

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.

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:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

namespaceOroBundleDemoLayoutBundleController;

useSymfonyBundleFrameworkBundleControllerController;

useSensioBundleFrameworkExtraBundleConfigurationRoute;

useSensioBundleFrameworkExtraBundleConfigurationTemplate;

classDefaultControllerextendsController

{

/**

* @Route("/about-layouts")

* @Template()

*/

publicfunctionshowAboutLayoutsAction()

{

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:

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.

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:

YAML

1

2

3

4

5

6

7

8

9

10

11

12

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:
The web application footer has temporarily changed to the following:

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:

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

PHP

1

2

3

actions:

-'@setBlockTheme':

themes:'OroBundleDemoLayoutBundle:layouts:default/layout.html.twig'

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

PHP

1

{%block _about_layouts_block_widget%}

How-to’s

{{ block_widget(block) }}

{% 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:

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:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

namespaceOroBundleDemoLayoutBundleController;

useSymfonyBundleFrameworkBundleControllerController;

useSensioBundleFrameworkExtraBundleConfigurationRoute;

useOroBundleLayoutBundleAnnotationLayout;

classDefaultControllerextendsController

{

/**

* @Route("/about-layouts")

* @Layout

*/

publicfunctionshowAboutLayoutsAction()

{

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):

PHP

1

2

3

4

5

6

7

8

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:

PHP

1

2

-'@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:

PHP

1

2

3

4

content

|--section-a

|--section-b

footer

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

PHP

1

2

3

-'@move':

id:footer

parentId:content

The layout will change to the following one:

PHP

1

2

3

4

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:

PHP

1

2

3

4

5

6

-'@move':

id:footer

parentId:content

-'@move':

id:section-b

parentId:content

Updated layout tree is:

PHP

1

2

3

4

5

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:

PHP

1

2

3

$layoutContext=newLayoutContext();

$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:

PHP

1

2

3

4

5

6

7

8

9

10

/**

* @Route("/about-layouts")

* @Layout(vars={"static_content"})

*/

publicfunctionshowAboutLayoutsAction()

{

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):

PHP

1

2

3

4

5

6

-'@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:

PHP

1

2

3

4

5

6

7

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:

PHP

1

2

3

4

-'@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:

another thing, i saw in the example, there are files named layout.yml and page.yml, if my customized module define a new theme which inherited from default. in this case?
1) the layout.yml is already defined in default, so it will merge the definition, we can not for example define two block with the same name?
2) the page.yml is new file so we should have the full definition?