RSS Мои друзья Контакты

Magento конфигурация от А до Я: layout-updates

Думаю почти все начинают свое знакомство с Magento с редактирования стандартной темы, добавления новых блоков или изменения старых. Это часть View из тройки букаф MVC и она достаточно сложная для новичков, по-этому предлагаю разобраться что это за файлы xml в директории layout.

Страница - кирпичик к кирпичику

В Magento страницы строятся посредством блоков с использованием шаблонов, которые имеют расширение *.phtml. Любой phtml файл является валидным html кодом. Конечный результат создается путем декорации, т.е. вставки одного блока в другой. Хороший рисунок для понимания сути был найден в гайде по Symfony1.4

Magento viewЗдесь все работает точно также. В поставке стандартной темы есть 4 layout-a: 1 колонка, 2 колонки с leftbar, 2 колонки с rightbar, 3 колонки.

Почти каждый модуль имеет layout-update - xml файл, который вносит дополнительный функционал в часть View. Конфигурационные файлы layout-ов имеют несколько основных директив: handle, reference, block, action, remove. Рассмотрим каждый из них по отдельности.

Handle

Дословно можно сказать, что это крючок, на который можно повесить несколько блоков. Но я себе это представляю, как событие, на которое вешается блок и при его (события) появлении блок будет обработан и выведен пользователю. Handle-ы генерируются контроллером. Отрыв класс Mage_Core_Controller_Varien_Action увидим, что на любой экшен генерируется 4 стандартных handle-а: default (в методе loadLayout) и еще 3, один из которых отвечает за выбранную тему, второй за текущий модуль, контроллер и экшен и третий за текущий store (в методе addActionLayoutHandles)

// load store handle
$update->addHandle('STORE_'.Mage::app()->getStore()->getCode());

// load theme handle
$package = Mage::getSingleton('core/design_package');
$update->addHandle(
    'THEME_'.$package->getArea().'_'.$package->getPackageName().'_'.$package->getTheme('layout')
);

// load action handle
$update->addHandle(strtolower($this->getFullActionName()));

Например, если у нас 1 store (магазин), стандартная тема и мы на странице просмотра списка продуктов в категории, то сгенерируется: default, STORE_default, THEME_frontend_default_default и catalog_category_view.

Также посмотрев более детально на методы loadLayout и renderLayout можно догадаться, что handle-ы можно генерировать динамически в зависимости от определенных условий, как делается в Mage_Catalog_CategoryController

$update = $this->getLayout()->getUpdate();
$update->addHandle('default');

if (!$category->hasChildren()) {
    $update->addHandle('catalog_category_layered_nochildren');
}

$this->addActionLayoutHandles();
$update->addHandle($category->getLayoutUpdateHandle());
$update->addHandle('CATEGORY_' . $category->getId());
$this->loadLayoutUpdates();

// apply custom layout update once layout is loaded
if ($layoutUpdates = $settings->getLayoutUpdates()) {
    if (is_array($layoutUpdates)) {
        foreach($layoutUpdates as $layoutUpdate) {
            $update->addUpdate($layoutUpdate);
        }
    }
}

$this->generateLayoutXml()->generateLayoutBlocks();

Основное описание всех страниц находится в page.xml. Если открыть его, то увидим наши handle-ы (default, print, page_empty, page_one_column, etc)

<layout version="0.1.0">
    <!-- Default layout, loads most of the pages -->

    <default translate="label" module="page">
        // default block structure
    </default>

    <print translate="label" module="page">
        // block structure
    </print>

     <!-- Custom page layout handles -->
    <page_empty translate="label">
        <label>All Empty Layout Pages</label>
        <reference name="root">
            <action method="setTemplate"><template>page/empty.phtml</template></action>
            <!-- Mark root page block that template is applied -->
            <action method="setIsHandle"><applied>1</applied></action>
        </reference>
    </page_empty>

    <page_one_column translate="label">
        <label>All One-Column Layout Pages</label>
        <reference name="root">
            <action method="setTemplate"><template>page/1column.phtml</template></action>
            <!-- Mark root page block that template is applied -->
            <action method="setIsHandle"><applied>1</applied></action>
        </reference>
    </page_one_column>
    <!-- another hanbles -->
</layout>

Reference

С помощью данной директивы можно обратится к блоку и вызвать у него определенные экшены или добавить дочерние блоки, как например в примере выше, handle = page_one_column. Мы обращаемся к самому верхнему по структуре блоку root и устанавливаем ему другой шаблон для отображения.

Эта директива имеет атрибут name, в котором указываем имя блока, к которому хотим обратится. Почти все блоки имеют свое уникальное имя. При написании своих layout-update-ов давайте имена всем блокам, которые в будущем нужно будет изменять.

Block

Блок - сердце всей структуры. Имеет несколько атрибутов:

  • type - Magento путь, указывающий на класс блока. Например, "cms/block" указывает на класс Mage_Cms_Block_Block, который находится в app/code/core/Mage/Cms/Block/Block.php. Если Вы не нуждаетесь в специфической логики обработки блока, а просто хотите вывести какой-то шаблон, то используйте "core/template"
  • name - уникальное имя блока, нужно для того чтобы потом можно было обращаться к ему посредством директивы reference
  • as - псевдоним для имени блока, нужно если хотим обратится к блоку из кода, при помощи методов родительского блока getChild или getChildHtml.
  • ifconfig - конфигурационный путь, блок будет показан только, если значение настройки равно true
  • after - указывает имя блока после которого нужно вывести данный
  • before - указывает имя блока перед которым нужно вывести данный. Если указать знак минуса ("-"), то блок будет выведен самым первым из всех в своем родителе
  • template - указывает шаблон для блока
  • translate - указывает теги(ноды, узлы), содержимое которых нужно перевести. Теги прописываются через пробел

Например, выведем на главной список всех новинок

<cms_index_index>
    <reference name="content">
        <block type="catalog/product_new" name="products.new" before="-" />
    </reference>
</cms_index_index>

Существует несколько стандартных имен блоков: root, left, right, content.

Action

С помощью этой директивы можно вызывать методы блока, после его инициализации. Имеет атрибут method, в котором указывается имя метода. Аргументы перечисляются в тегах, имена тегов никак не используются. В метод аргументы передаются в такой очередности, в которой они были прописаны в xml файле, например установим другой шаблон руту

<reference name="root">
    <action method="setTemplate"><template>page/1column.phtml</template></action>
    <!-- Mark root page block that template is applied -->
    <action method="setIsHandle"><applied>1</applied></action>
</reference>

Remove

С помощью этой директивы можно удалять блоки. Имеет один атрибут name - имя блока, который нужно удалить. Например, удалим назойливые баннеры из стандартной темы

<layout>
  <default>
     <remove name="right.permanent.callout" />
     <remove name="left.permanent.callout" />
  </default>
</layout>

Ставим точки над і

Так как handle - это крючок (событие), значит он самый главный в иерархии. Все остальное должно обязательно находится внутри его! В нем (handle-е) может находится: block, remove, reference. Reference - это просто ссылка на блок, т.е. способ обновить содержимое блока. Объясню на примере блока с именем content. Если открыть файл page.xml, то там в default handle, можно найти такое

<default translate="label" module="page">
    <label>All Pages</label>
    <block type="page/html" name="root" output="toHtml" template="page/3columns.phtml">

        <block type="page/html_head" name="head" as="head">
        </block>

        <block type="page/html_header" name="header" as="header">
            // header block children
        </block>

        // some blocks
        
        <block type="core/text_list" name="content" as="content" translate="label">
            <label>Main Content Area</label>
        </block>
        
        // another blocks
    </block>
</default>

Что же делать, если нужно добавить дочерний блок в контент, но не исправляя файл page.xml? Нужно использовать reference!

<default>
    <reference name="content">
        <block type="core/template" name="test.block" template="test/some_text.phtml" />
    </reference>
</default>

Все что находится внутри reference можно считать, что оно находится внутри блока с заданным именем.

Block может находится внутри другого блока или reference-а (тогда он является дочерним блок и доступ к нему из родительского можно получить через метод getChild, первый параметр которого - элиас блока, т.е. содержимое атрибута as, по-этому если Вы планируете обратится к блоку из кода прописывайте ему элиас), также он может находится внутри handle (так как блок с именем root, смотреть page.xml).

Action может находится только в reference или block, так как эта директива означает вызов метода из класса блока. Например, блок head имеет методы addJs, addCss, addItem с помощью которых можно добавить на страницу файлы javascript (из директории js обычно) и css (из директории текущего скина).

Метод addItem универсальный, с его помощью можно добавить javascript (как из корневого каталога, так и с каталога скина) и css, а также задавать параметры для conditional comments в IE

<default translate="label" module="page">
    <label>All Pages</label>
    <block type="page/html" name="root" output="toHtml" template="page/3columns.phtml">
        <block type="page/html_head" name="head" as="head">
            <action method="addJs"><script>prototype/prototype.js</script></action>
            // add another files
            
            <action method="addCss"><stylesheet>css/styles.css</stylesheet></action>
            <action method="addItem"><type>skin_css</type><name>css/styles-ie.css</name><params/><if>lt IE 8</if></action>

            <action method="addItem"><type>js</type><name>lib/ds-sleight.js</name><params/><if>lt IE 7</if></action>
            <action method="addItem"><type>skin_js</type><name>js/ie6.js</name><params/><if>lt IE 7</if></action>
        </block>
    </block>
</default>

P.S.: вот и все. Надеюсь материал сильно поможет начинающим, по-скольку все это я сам проходил набивая собственные шишки. Планирую написать серию статей о конфигурационных файлах в Magento. Ждите продолжения

UPD: забыл упомянуть о теге update. Имеет один атрибут handle, да-да это имя именно той самой директивы, о которой говорилось выше. Директива может находится только внутри другого handle-а, переносит все блоки из указанного handle-а в тот, в котором она прописана, например

<customer_account_edit translate="label">
    <label>Customer Account Edit Form</label>

    <update handle="customer_account"/>

    // another directives
</customer_account_edit>

Все директивы из customer_account будут использованы и в customer_account_edit handle-е. Применяется для того, чтобы избежать дублирования одинаковых директив.

Добавить комментарий

Комментариев: 12

  • Роман
    Ответить 28 октября 2011 г., 13:15
    Сереж, с обновлением freaksidea, очень нравится твои статьи. Хотел бы для уточнить ! Как понять где описываются все директивы такие как remove.
    Или где handle default, print, page_empty, page_one_column, etc , атрибуты block? с actions(и) тоже не совсем понятно , вроде как они описываются в Block'e , такие вот как
    insider/insider.js
    в Mage_Core_Page_Block_Html_Head
    а вот где допустим method="setImgSrc"
    с catalog.xml .
    Во общем Сереж откуда черпать , если не трудно.
    • Серега (Администратор)
      Ответить 28 октября 2011 г., 14:45
      директивы могут быть описаны где угодно, в одном из файлов layout-update-ов. Все эти файлы при открытии страницы объединяются и получаем один большой XML.

      Обновил статью надеюсь описание поможет понять
  • Роман
    Ответить 28 октября 2011 г., 13:47
    method="addJs"
    в место insider/insider.js
  • Елена
    Ответить 1 ноября 2011 г., 17:18
    Огромное спасибо за статью!
    С нетерпением ждем продолжения.

    Может есть какие-нить хорошие ссылочки на мануал с описанием стандартных методов мадженто, таких как: getResourceModel(), getModel(), getCollection() и т.д.?
    • Сергей (Администратор)
      Ответить 1 ноября 2011 г., 20:43
      есть, но на английском и на разных сайтах. Можно поискать на:
      - http://www.magentocommerce.com/wiki/
      - http://alanstorm.com/

      Или просто открыть файл app/Mage.php и посмотреть там
      • Роман
        Ответить 1 ноября 2011 г., 23:39
        - http://alanstorm.com/ понравился!
        от себя
        http://inchoo.net/category/ecommerce/magento/
        http://magento-forum.ru/forum/103/
  • Антон
    Ответить 28 ноября 2011 г., 15:22
    Сергей, спасибо за статью.
    Есть вопрос. Для чего нужны page_empty, page_one_column, page_three_columns и т.д.? Я пробовал удалить их из page.xml, но все продолжает работать. Я не нашел ни в коде, ни в админке ни единой ссылки на эти хендлы. Для того чтобы указать структуру, везде она явно указывается, например . В результате смысл тех перечисленных хендлов не понятен.
    • Сергей (Админ)
      Ответить 29 ноября 2011 г., 0:11
      Думаю, что если выключить модуль Mage_Cms, тогда будут использоваться эти хендлы.
  • Вячеслав
    Ответить 22 июня 2012 г., 15:56
    Супер статья Сергей. Я из нее больше узнал чем в 3 книгах,которые есть на русском по мадженто. Респект парень. Жду продолжения ))
    Сложно разбираться самому, а с таким описание упрощается работка... Еще раз спасибо.
  • It
    Ответить 29 августа 2012 г., 20:56
    Спасибо БОЛЬШОЕ автору за статью!
  • Виктор
    Ответить 31 августа 2012 г., 15:56
    Спасибо. А как можно добавить новый блок на страницу просмотра ордера в нужном месте, если там только <reference name="content">
    <block type="adminhtml/sales_order_view" name="sales_order_edit"></block>
    </reference>
    ?
  • Денис
    Ответить 14 ноября 2012 г., 21:43
    Наверное самые доходчивые статьи со всех, что читаю сейчас, изучая магенто. Хотелось бы что бы автор не останавливался