Лінива темізація в Yii підключення тем у стилі Drupal

Використання тем у Yii
Відомо, що для включення теми оформлення необхідно вказати її ім'я у параметрі theme:
Робота з темами описується у посібнику, але ми розглянемо кілька моментів і тут.
Отже, якщо ми маємо контролер ShopController
то ми можемо покласти уявлення index.php або вихідну директорію protected/views :
або в директорію уявлень теми protected/views:
Сподіваюся, Ви вже перейшли на використання модулів у своїх проектах, тому далі говоритимемо про них.
Нехай у нас є модуль shop, що містить три контролери:
Уявлення для модуля можна також поміщати або в папку views самого модуля, або в папку views/shop теми.
Якщо у модулі є файл подання
то для його заміни потрібно створити однойменний файл у папці теми:
замість подання модуля підставиться уявлення з теми.
Тут "/shop/" – ім'я модуля, а "/default/" – ім'я контролера.
Тобто якщо нам потрібно зробити кілька тем для одного сайту (наприклад, повна та мобільна версія) або декілька сайтів на одному движку, то достатньо створити кілька тем і перекривати у них уявлення будь-яких модулів.
Які нюанси нам потрібно знати
Використовуючи $this->render(. ) або $this->renderPartial(. ), можна часом піти на деякі хитрощі.
Стандартне включення – вказівка імені файлу подання:
Якщо уявлень багато, то їх можна розкласти по папках і викликати відносною дорогою, використовуючи точку або слеш:
Краще використовувати відразу слеши, так як інакше Yii спробує по точках розібрати псевдонім (а саме пробуватиме різні варіанти: шукати модуль formsі т.д.).
Аналогічно можна «гуляти» ієрархіями папок:
Можна в папці protected/views або themes/classic/views натворювати папок і накидати туди загальних шаблонів, і включати їх викликом від кореня, використовуючи два слеша:
Наприклад, там зручно зберігати лейаути та сайдбари для них. Ми, до речі, вже розглядали для організації різних сайдбарів.
У найпростішому випадку можна просто скопіювати контролери та подання у кожен модуль. Але замість цього можна використовувати один загальний контролер та загальні уявлення з модуля comment.
Тепер у модулях блогу, новин та товарів помістимо порожні контолери-спадкоємці:
Потрібно мати на увазі той факт, що при доступі до уявлень модулів за жорсткими псевдонімами
Від цього тепер можна перейти безпосередньо до суті проблеми.
Де вказувати layout у Yii
Від окремих уявлень настав час перейти до роботи з шаблонами. На різних сайтах та й часом у різних розділах одного сайту можуть бути різні шаблони. Стандартної пари column1.php та column2.php тут явно не вистачить. Де зберігати шаблони? У спільній папці layouts або всередині модулів? Де саме вказувати, який шаблон не потрібен?
Розглянемо кілька підходів до завдання та оберемо найбільш зручний.
Вказівка шаблону в контролері
Демо-блог та керівництво вчить нас вказувати layout у контролері. Це природно, оскільки це насправді і є громадське поле контролера. Стандартне підключення шаблону column2.php з папки protected/views/layouts або themes/
Всі наші контролери успадковуються від Controller, тому ми можемо легко змінити шаблон оформлення будь-якого контролера. Наприклад, магазин ми виведемо у шаблоні views/layouts/shop.php :
І навіть більше: для кошика, замовлення, пошуку та сторінкитовару зробимо свої шаблони:
А ще ми використовуємо модулі, тому можемо запросто створити папку protected/modules/shop/views/layouts усередині модуля, накидати туди наші лейаути та брати прямо звідти:
Тепер для іншої теми можна перевизначити необхідні файли в папці теми, а саме themes/ /views/shop/layouts .
Але якщо в іншій темі для іншого сайту потрібно вказати специфічний шаблон для виведення списку виробників (дія actionBrand)?
Прийде помістити рядок
у метод DefaultController::actionBrand і додати заглушку brand.php до всіх інших сайтів. щоб на них не вискакувала помилка, що уявлення brand.php не знайдено.
Ось ми й наблизилися до проблеми. Одному сайту потрібно «прикрасити» десять дій у трьох контролерах, другому лише кошик магазину, а третьому та одного шаблону вистачить. Але все одно до кожної теми потрібно додати десять. Якщо нова тема «захоче» використовувати одинадцятий лейаут, то контролер знову зміниться, і всі інші теми знову доведеться додавати одинадцяту заглушку.
Вказівка шаблонів у поданні
Контролери у нас спільні для всіх сайтів, тому чіпати їх не варто. А якщо вказувати шаблон у самих уявленнях?
Справді, у поданні themes/
Тепер замість themes/
Таке звільнення від жорстко вписаних літералів у контролері дозволяє нам використовувати той самий контролер у різних сайтах без зміни його коду.
Але ось у чому парадокс:
Ми домовилися раніше, що у нас є три контролери
Насправді у модулі інтернет-магазину їх може бути набагато більше. Але так як присвоєння значення полю $this->layout проводиться в уявленнях, то щоб змінити шаблон всього модуля магазину в нашій теміНеобхідно перевизначити всі уявлення всіх наших контролерів. Тобто не один-два, а кілька десятків! І так за потреби для кожного модуля.
Недоліки статичної темізації
Отже, ми розглянули надання імені шаблону в контролері та у поданні.
Перший спосіб надто «хардкорний», тому що літерали (імена шаблонів) вписані прямо в код нашої програми. А це сама по собі не дуже хороша практика у розробці системи, яку очікується використовувати у більш ніж одному проекті.
Другий спосіб більш гнучкий, оскільки уявлення легко перевизначаються у темі (на відміну контролерів). Але уявлень дуже багато, і часом їх треба перевизначати великими пачками, оскільки, щоб змінити тему будь-якого розділу сайту, потрібно перевизначити всі уявлення цього модуля.
Який може бути вихід?
Вихід – наявність можливості вказувати шаблон поза конкретним контролером і поза уявленням.
Це може бути таблиця відповідності шаблонів конкретним модулям, контролерам та діям, оформлена у вигляді хеш-масиву в конфігураційному файлі:
Відповідно, ця таблиця повинна зберігатися у файлі, поміщеному в папку теми, а базовий контролер у події beforeRender повинен вибрати по цій таблиці потрібний шаблон.
Автозавантаження layout'ів
В ідеалі система темизації має все робити автоматично. З однойменними уявленнями та шаблонами це так і відбувається. Достатньо закинути в папку з темою перевизначене уявлення, так воно одразу використовується замість оригінального.
Таким шляхом «автопідхоплення» ми й підемо.
Для початку приберемо привласнення $this->layout з контролерів та з уявлень.
У найпростішому випадку наш модуль магазину матиме такуструктуру:
І в поточній темі ми перевизначили, наприклад, уявлення дії DefaultController::actionIndex:
А тепер було б непогано створити папку layouts для шаблонів та помістити їх туди:
Для зручності ми назвали файли за принципом
Така нотація дозволяє з одного погляду зрозуміти, до чого повинен застосовуватися кожен шаблон: до всього модуля, конкретного контролера або певної дії.
Тепер напишемо систему автопідвантаження шаблону:
Ця поведінка додає до контролера метод initLayout, який, як ми бачимо, по черзі шукає відповідний шаблон у темі та в оригінальному модулі і надає перший знайдений. Останні два рядки списку повинні вказувати на шаблон за промовчанням. Також, щоб не шукати щоразу заново, історія знайдених файлів кешується.
Поведінку ми повинні підключити до базового контролера і викликати його метод перед генерацією сторінки, тобто в події передпочатком контролера:
Зауважимо, що метод спрацює лише коли CController::layout порожній. Так що Ви, як і раніше, зможете привласнити $this->layout будь-яке значення в контролері або в поданні вручну і воно не перезапишеться.
Якщо Ви не хочете возитися з поведінкою, то скопіюйте код методу initLayout прямо в контролер, замінивши $onwer на $this .
Отже, щоб замінити шаблон магазину на сайті з двоколонного на триколонок, просто додайте в тему один файл:
А щоб зробити сторінку кошика на всю ширину додайте один шаблон безпосередньо для відповідної дії:
У результаті тепер шаблони підхоплюються з папки layuots самого модуля та з папки layuots теми цього модуля автоматично, і майже все оформлення будь-якого з ваших сайтів можна налаштувати прямо в темі.
Не пропускайтенові статті, бонуси та майстер-класи з Yii, Laravel та Symfony: