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:
- 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:
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:
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:
- 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:
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:
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:
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:
- Using layouts with samples
- Layout context documentation
- Layout data documentation
- Theme definition