Magento для PHP MVC розробників – Макети, Блоки та Шаблони (ч.3/11)
Продовжуючи огляд Magento ми перейдемо до розгляду макетів (Layouts) та блоків (Blocks).
На відміну від багатьох популярних PHP MVC систем, контролер Magento не передає об’єкт даних у View (Відображення) або не задає параметри для об’єкта view. Натомість, компонента View моделі безпосередньо посилається на системні моделі, щоб отримати інформацію, необхідну для відображення.
Одним з наслідків цього дизайнерського рішення є те, що компонента View була розділена на Blocks (блоки) і Templates (шаблони). Blocks – це PHP об’єкти, Template – “сирі” PHP файли, які містять суміш HTML і PHP (де PHP використовується в якості мови програмування для шаблонів). Кожен блок прив’язаний до одного, простого файлу шаблону. Всередині phtml-файлу ключове слово $this
містить посилання на об’єкт блоку шаблона.
Швидкий приклад. Погляньте на шаблон списку товарів за замовчуванням у файлі:
app/design/frontend/base/default/template/catalog/product/list.phtml
Ви побачите наступний код шаблону PHP.
<?php $_productCollection=$this->getLoadedProductCollection(); $_helper = $this->helper('catalog/output'); ?> <?php if(!$_productCollection->count()): ?> <p class="note-msg"><?php echo $this->__('There are no products matching the selection.') ?></p> <?php else: ?>
Метод getLoadedProductCollection
можна знайти в шаблоні блоку,
Mage_Catalog_Block_Product_List
app/code/core/Mage/Catalog/Block/Product/List.php ... public function getLoadedProductCollection(){ return $this->_getProductCollection(); } ...
Метод блоку _getProductCollection
потім створює екземпляри моделі і читає їх дані, повертаючи результат в шаблон.
Вкладені блоки
Реальна сила поєднання Блок/Шаблон (Block/Template) стає явною при використанні getChildHtml
методу. Він дозволяє включати контент другорядного Блок/Шаблон всередину основного Блок/Шаблон.
Блоки викликають Блоки, які викликають Блоки – ось так і буде створено весь HTML-макет для вашої сторінки.
Погляньте на шаблон макета сторінки в одну колонку.
app/design/frontend/base/default/template/page/1column.phtml   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>"> <head> <?php echo $this->getChildHtml('head') ?> </head> <body<?php echo $this->getBodyClass()?' class="'.$this->getBodyClass().'"':'' ?>> <?php echo $this->getChildHtml('after_body_start') ?> <div class="wrapper"> <?php echo $this->getChildHtml('global_notices') ?> <div class="page"> <?php echo $this->getChildHtml('header') ?> <div class="main-container col1-layout"> <div class="main"> <?php echo $this->getChildHtml('breadcrumbs') ?> <div class="col-main"> <?php echo $this->getChildHtml('global_messages') ?> <?php echo $this->getChildHtml('content') ?> </div> </div> </div> <?php echo $this->getChildHtml('footer') ?> <?php echo $this->getChildHtml('global_cookie_notice') ?> <?php echo $this->getChildHtml('before_body_end') ?> </div> </div> <?php echo $this->getAbsoluteFooter() ?> </body> </html>
Сам шаблон містить зовсім не багато коду. Тим не менше, кожен виклик $this->getChildHtml(...)
буде підвантажувати та додавати до шаблону ще один блок (Block). Ці блоки, в свою чергу, також можуть використовувати getChildHtml
для отримання інших блоків. Ось так блоками і будується весь фінальний шаблон сторінки.
Макет (Layout)
Отже, Блоки та Шаблони, це все добре, але вам, ймовірно, цікаво:
- Як я кажу Magento, який блок я хочу використати на цій сторінці?
- Як я кажу Magento, з якого блоку я повинен почати рендеринг?
- Як мені вказати конкретний блок в
getChildHtml (...)
? Ці рядки не схожі на ім’я мого блоку.
На ці питання відповідає Макет. Layout (Макет) – це набір XML рядків, який буде визначати, які блоки на сторінці включені, а які блоки не беруть участі при рендерингові.
У минулій статті серії ми виводили контент на екран прямо із екшен методів. Цього разу давайте створимо простий HTML шаблон для нашого модуля Hello World.
По-перше, створимо файл в
app/design/frontend/ПАКЕТ_ТЕМ/ТЕМА/layout/local.xml
з наступним вмістом
<?xml version="1.0" encoding="UTF-8"?> <layout> <default> <reference name="root"> <block type="page/html" name="root" output="toHtml" template="simple_page.phtml" /> </reference> </default> </layout>
Потім створіть файл в
app/design/frontend/ПАКЕТ_ТЕМ/ТЕМА/template/simple_page.phtml
з наступним вмістом (ми використовуємо специфікацію HTML5)
<!DOCTYPE html> <html lang="<?php echo $this->getLang() ?>"> <head> <title>Тест</title> <style type="text/css"> body { background-color: #f00; } </style> </head> <body> </body> </html>
Для запуску процесу створення макету (макетування) із Контролера нам потрібно додати виклики ще двох методів із екшен методу.
public function indexAction() { $this->loadLayout(); $this->renderLayout(); }
Очистіть кеш Magento і перезавантажте сторінку з контролером Hello World (за адресою VASH-DOMEN/helloworld/index
). Тепер ви повинні побачити веб-сторінку з яскраво-червоним фоновим кольором та HTML кодом, який міститься в шаблоні simple_page.phtml
.
Що відбувається
Отже, тут багато магії вуду та загадкового чаклунства. Давайте поглянемо на те, що відбувається всередині.
При відкритті сторінки в Мадженто, система створює один великий XML-файл, що містить у собі всі налаштування макету теми (всі теги <block/>
, <reference/>
та <remove/>
). Коли викликається метод контролера loadLayout
, то Magento буде:
- Створювати цей XML-макет
- Створювати екземпляри класу Блока для кожного тега
<block/>
і<reference/>
, використовуючи в якості класу ім’я атрибутаtype
тега як шлях до глобальних налаштувань, та зберігати їх у внутрішньому масиві об’єктів макету_blocks
, використовуючи ім’я атрибутаname
в якості ключа до масиву. - Якщо тег
<block/>
містить атрибутoutput
, його значення додається до внутрішнього масиву_output
об’єктів макету.
Потім, коли ви викличете в контролері метод renderLayout
, Magento буде перебирати всі Блоки в масиві _output
, використовуючи значення атрибуту output
в якості методу зворотного виклику. Це завжди toHtml
, і він являє собою відправну точку для виведення цього шаблону блока.
У наступних розділах ми оглянемо те, як створюються екземпляри Блоків, як генеруються їх файли макету, і завершимо на процесі виводу.
Створення екземпляру Блока
Отже, в XML-файлі макету тег <block/>
або <reference/>
має атрибут type
, що насправді є URI згрупованого імені класу. Тобто, коротким записом імені класу, який використовує Блок.
<block type="page/html" ... <block type="page/template_links" ...
Розташовується URI згрупованого імені класу у файлі глобальної конфігурації. Перша частина URI (до слешу; у наведених вище прикладах page
) використовується для запиту до глобальної конфігурації, щоб знайти ім’я класу сторінки (page
). Друга частина URI (після слешу) буде додана до назви базового класу, щоб створити ім’я класу, який Magento повинен буде «екземплярувати».
Як приклад, оглянемо URI для page/html
. Спочатку Magento дивиться файл глобальної конфігурації для цього типу:
app/code/core/Mage/Page/etc/config.xml
і знаходить
<page> <class>Mage_Page_Block</class> </page>
Це дає нам ім’я базового класу Mage_Page_Block
. Потім, друга частина URI (html
) додається до цього імені класу, щоб отримати остаточну назву класу Блоку Mage_Page_Block_Html
. Це і буде класом, екземпляр якого буде створено.
Якщо ми створимо блок з тим самим ім’ям, як у вже існуючого блока, новий екземпляр блоку замінить оригінальний екземпляр. Це те, що ми зробили в нашому файлі local.xml
(зверху в статті).
Використання посилань
<reference name="" />
зачепить всі XML декларації, що містяться у вже існуючому блокові із вказаним ім’ям. Всі вузли <block/>
, що включені до посилання (reference), будуть підписані в якості дочірніх блоків до батьківського блоку посилання.
<layout version="0.1.0"> <default> <block type="page/html" name="root" output="toHtml" template="page/2columns-left.phtml"> <!-- ... дочірні блоки ... --> </block> </default> </layout>
в іншому файлі макета:
<layout version="0.1.0"> <default> <reference name="root"> <!-- ... інший дочірній блок ... --> <block type="page/someothertype" name="some.other.block.name" template="path/to/some/other/template" /> </reference> </default> </layout>
Незважаючи на те, що кореневий блок (root) оголошується в окремому XML-файлі макета, новий блок додається як дочірній блок. Magento спочатку створює Блок page/html
з ім’ям root
. Тоді, коли він пізніше зустріне посилання з тим самим ім’ям (root), він призначить новий блок some.other.block.name
як дочірній кореневому блокові.
Як генеруються Файли Макетів
Отже, на даний час у нас вже дещо покращилось розуміння того, що відбувається з XML макетом. Але звідки цей XML-файл береться? Щоб відповісти на це питання, ми повинні ввести два нових поняття: Handles (зачіпки) і Package Layout (пакет макетів).
Handles (зачіпки)
Кожен запит сторінки в Magento буде генерувати кілька унікальних зачіпок. Наприклад, для головної сторінки сайту це можуть бути такі зачіпки: default
, cms_index_index
, customer_logged_out
.
Зачіпки містяться в різних місцях в системі Magento. Звернемо увагу на дві зачіпки: default
і helloworld_index_index
. Зачіпка default
присутня у кожному запиті в системі Magento. Зачіпка helloworld_index_index
створюється шляхом об’єднання назви маршруту (helloworld), назви дії контролера (index) та назви методу дії контролера (index) у єдиний рядок. Це означає, що кожен можливий метод в контролері має пов’язану з ним зачіпку.
Пакет Макетів
Ви можете уявити Пакет Макетів в якості файлу глобальної конфігурації. Це великий XML-файл, який містить всі можливі конфігурації макетів для конкретної системи Magento. Він створюється шляхом об’єднання вмісту всіх файлів XML-макетів для поточної теми. Для теми Мадженто за замовчуванням це вміст усіх файлів у папці:
app/design/frontend/base/default/layout/
За лаштунками цього залишаються секції <frontend><layout><updates/>
та <adminhtml><layout><updates/>
глобальної конфігурації, які містять вузли з усіма назвами файлів, що повинні завантажуватись у поточну область. Після того, як файли, що перелічені у файлі config
обробились, Magento об’єднує їх усіх і останнім додає універсальний XML-файл – local.xml
. Це той файл, в якому ви зможете додати свої унікальні налаштування до поточної теми Magento.
Комбінація Зачіпок і Пакету Макетів
Якщо ви подивитеся на файл Макетів, ви побачите деякі знайомі теги, наприклад <block/>
і <reference/>
, але всі вони огорнуті іншими тегами, наприклад:
<default /> <catalogsearch_advanced_index /> ...
Це все теги-зачіпки. Макет сторінки при кожному окремому запиті генерується шляхом захоплення всіх секцій пакету макетів, які збігаються з зачіпками у запиті.
Плюс ще один додатковий тег, про який вам слід знати в пакеті макетів. Тег <update/>
, що дозволяє включити інші теги-зачіпки. Наприклад:
<customer_account_index> <!-- ... --> <update handle="customer_account"/> <!-- ... --> </customer_account_index>
Ця конструкція означає, що запити до зачіпки customer_account_index
повинні включати в себе <reference/>
і <blocks/>
з зачіпки <customer_account/>
.
Закріплення вивченого на практиці
Добре, тепер перейдемо від теорії до практики. Давайте повернемося до того, що ми робили раніше. Володіючи новими знаннями, додаємо:
<layout version="0.1.0"> <default> <block type="page/html" name="root" output="toHtml" template="simple_page.phtml" /> </default> </layout>
у файл local.xml
. Це означає, що ми перевизначили тег “root” іншим блоком. Розміщуючи це в зачіпці <default/>
, ми забезпечили перевизначення, що буде спрацьовувати при кожному запиті сторінки в системі. Це, ймовірно, не те, що нам потрібно.
Якщо ви відкриєте будь-яку іншу сторінку на сайті, ви помітите, що вони або порожні або мають той же червоний фоновий колір, що й сторінка модуля “hello world”. Давайте змінимо запис у local.xml так, щоб зміни стосувались лише сторінки “hello world”. Ми зробимо це, змінивши зачіпку default
на зачіпку для контролера нашого модуля (тобто, helloworld_index_index
).
<layout version="0.1.0"> <helloworld_index_index> <block type="page/html" name="root" output="toHtml" template="simple_page.phtml" /> </helloworld_index_index> </layout>
Очистіть кеш в Magento і решта сторінок повинні оновитись.
Зараз це відноситься тільки до нашого index
методу. Давайте також протестуємо і goodbye
метод. У контролері змінюємо екшен метод goodbye
так, щоб це виглядало наступним чином:
public function goodbyeAction() { $this->loadLayout(); $this->renderLayout(); }
Якщо ви завантажите наступну адресу, то ви помітите, що як і раніше отримуєте макет Magento за замовчуванням (default):
VASH-DOMEN/helloworld/index/goodbye
Нам потрібно додати зачіпку з повним іменем дії (helloworld_index_goodbye
) до файлу local.xml. Це дозволить використовувати тег update
для зачіпки helloworld_index_index
замість того, щоб оголошувати новий <block/>
.
<layout version="0.1.0"> <!-- ... --> <helloworld_index_goodbye> <update handle="helloworld_index_index" /> </helloworld_index_goodbye> </layout>
Завантаження наступних сторінок (після очищення кешу) повинно давати ідентичні результати:
VASH-DOMEN/helloworld/index/index VASH-DOMEN/helloworld/index/goodbye
Початок Виводу та getChildHtml
У стандартній конфігурації вивід починається з Блоку root
(оскільки він має атрибут виводу – output
). Ми перевизначаємо кореневий шаблон власним
template="simple_page.phtml"
Шаблони посилаються на кореневу папку поточної або за замовчуванням (default) теми. Для дефолтної теми це шлях:
app/design/frontend/base/default
Для теми custom (наприклад) це буде шлях:
app/design/frontend/default/custom/template
Тобто, повний шлях до файлу шаблону буде таким:
app/design/frontend/default/custom/template/simple_page.phtml
Пакет тем base є кінцевим місцем призначення для будь-якого шаблону. Якщо Magento не знайде шаблон в будь-якій іншій темі, він повернеться до базової (base). Проте, як уже згадувалося, ви не повинні розміщувати свою тему у цій папці (або ж коригувати файли у ній), оскільки після оновлення Magento всі ваші коригування будуть затерті. Натомість, створюйте свій пакет тем, або ж власну тему у пакеті тем default.
Додавання блоків контенту
Пуста червона сторінка досить нудна. Давайте наповнимо її деяким змістом. Змінюємо зачіпку <helloworld_index_index/>
в local.xml
так, як це вказано нижче:
<helloworld_index_index> <block type="page/html" name="root" output="toHtml" template="simple_page.phtml"> <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/> </block> </helloworld_index_index>
Ми додаємо новий Блок, що вкладений у кореневий блок root
. Це блок, який поставляється з Magento – він дозволяє виводити на сторінці форму для реєстрації клієнта. Розмістивши блок у кореневому root блоці, ми зробили його доступним для включення до нашого шаблону simple_page.html
. Отже, це дає нам змогу викликати блок в шаблоні simple_page.phtml
, використовуючи Блоковий метод getChildHtml
. Редагуємо шаблон simple_page.html наступним чином:
<body> <?php echo $this->getChildHtml('customer_form_register'); ?> </body>
Очистіть кеш Magento, перезавантажте сторінку, і ви побачите на сторінці із червоним фоновим кольором форму для реєстрації клієнтів. В Magento також є вбудований Блок з іменем top.links
. Давайте спробуємо підключити до шаблону і його. Редагуємо simple_page.html
наступним чином:
<body> <h1>Links</h1> <?php echo $this->getChildHtml('top.links'); ?> </body>
Коли ви перезавантажите сторінку, то помітите, що заголовок <h1>Links</h1>
рендериться, але нічого не рендериться для блоку top.links
. Це тому, що ми не додали його в local.xml
(або в інший файл XML-макетів). Метод getChildHtml
може викликати тільки ті блоки, які зазначені як дочірні блоки в макеті. Це дозволяє Magento виводити тільки дійсно потрібні блоки, а також дозволяє вам встановлювати різні шаблони для блоків.
Давайте додамо блок top.links
до нашого local.xml
<helloworld_index_index> <block type="page/html" name="root" output="toHtml" template="simple_page.phtml"> <block type="page/template_links" name="top.links"/> <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/> </block> </helloworld_index_index>
Очистіть кеш і перезавантажте сторінку. Тепер ви повинні побачити вивід модуля top.links
(деякі основні посилання системи: наприклад, лінк для логіну на сайт, лінк на кошик та ін.).
Час застосувати action метод
Існує ще одна важлива концепція, яку слід розглянути, перш ніж завершити цей урок, і це тег <action/>
. Використовуючи тег <action/>
, ми можемо викликати публічні методи PHP класів блоків. Тому замість того, щоб змінювати шаблон кореневого root блоку, замінивши екземпляр блоку на свій власний, ми можемо використовувати виклик методу setTemplate
.
<layout version="0.1.0"> <helloworld_index_index> <reference name="root"> <action method="setTemplate"> <template>simple_page.phtml</template> </action> <block type="page/template_links" name="top.links"/> <block type="customer/form_register" name="customer_form_register" template="customer/form/register.phtml"/> </reference> </helloworld_index_index> </layout>
Цей код XML макету спочатку встановить шаблон для кореневого root блоку, а потім додасть два блоки, які ми використовуємо як дочірні. Як тільки ми очистимо кеш, результат має виглядати так само, як і раніше. Перевага використання <action/>
— це той самий екземпляр блоку, який був створений раніше, а всі його асоціації батьки/нащадки збережуться. З цієї причини це буде більш надійний спосіб впровадження наших змін.
Всі аргументи екшен методу повинні бути огорнуті окремим дочірнім нодом тегу <action/>
. Ім’я цього ноду (вузла) не має значення, важливий лише порядок вузлів. Ми могли б написати нод action
з попереднього прикладу наступним чином, що буде мати такий же ефект:
<action method="setTemplate"> <some_new_template>simple_page.phtml</some_new_template> </action>
Цей приклад лише для того, щоб проілюструвати, що імена аргументів вузла action
є довільними.
Підсумок
В цьому уроці ми оглянули основні принципи компонування в Мадженто. Ми оглянули теги <block/>
, <reference/>
, <update/>
та <action/>
, а також зачіпки оновлень макету, такі як <default/>
та <cms_index_index/>
. Вони складають більшу частину конфігурації макета, що використовується в Magento. Якщо це здається вам трохи складним, не хвилюйтеся, PHP-розробнику Мадженто рідко потрібно працювати з макетами на такому фундаментальному рівні. Magento надає ряд попередньо сконструйованих макетів, які можна модифікувати для досягнення основних потреб інтернет-магазину на цьому двигуні. А розуміння того, як працює вся система макетів, може стати відмінною допомогою, коли ви зіткнетесь із проблемами макетування чи захочете додати нові функції до існуючої системи.
Автор: Alan Storm (http://alanstorm.com/layouts_blocks_and_templates/)
Переклад українською: Ненько Сергій
Адаптація перекладу: SebWeo
- Magento для PHP MVC розробників (Alan Storm) – ч.1/11
- Magento для PHP MVC розробників – розбір Контролера (ч.2/11)
- Magento для PHP MVC розробників – Макети, Блоки та Шаблони (ч.3/11)
- Magento для PHP MVC розробників – Моделі та основи ORM (ч.4/11)
- Magento для PHP MVC розробників – Інсталювання Ресурсу (ч.5/11)
- Magento для PHP MVC розробників – Розширений ORM – EAV (ч.6/11)
- Magento для PHP MVC розробників – Особлива конфігурація системи (ч.7/11)
- Magento для PHP MVC розробників – Поглиблене налаштування системи (ч.8/11)
- Magento для PHP MVC розробників – Колекції Varien Data (ч.9/11)
- Magento для PHP MVC розробників – Перевизначення і оновлюваність системи (ч.10/11)
- Magento для PHP MVC розробників – Конфігурація системи за замовчуванням (ч.11/11)