YiiFramework Контроллеры, макеты и виджеты - идеология структуры проекта

xintrea

Новичок
Вопрос достаточно общий, но мне нужно разобраться, потому что я только начинаю осваивать Yii.

* * *

Я сделал следующую структуру приложения. Для начала - термины, в которых мыслю.

- Область страницы (pageAreaName) - это "именованная" точка в основном макете сайта. Например title, menu, info, text, bottom.

- Область сайта (siteAreaName) - это название раздела сайта. По сути, это Id контроллера. Например main, guestBook, newsArchive.

Есть основной макет страницы. Это чистый HTML, в нем только "отмечены" области страницы (pageAreaName) в виде вызова специального виджета CDrawPageArea:

PHP:
<!-- Виджет меню -->
<?php $this->widget('CDrawPageArea', array('params'=>array(
  'pageAreaName'=>'menu',
  'siteAreaName'=>Yii::app()->getController()->getId() ))); ?>
Виджет CDrawPageArea (назовем его "промежуточным"), основываясь на информации в каком месте сайта и в каком месте страницы произошел вызов (параметры pageAreaName и siteAreaName), вызывает (по таблицам) другие "конечные" виджеты, передавая им так же информацию о pageAreaName и siteAreaName. Таким образом, конечные виджеты знают, в каком месте сайта и в каком месте страницы они вызваны. И могут соответственно менять своё поведение, если это необходимо. Это очень удобно. Так же удобно то, что таблица вызовов "конечных" виджетов сосредоточена в одном месте, и сразу видна вся структура сайта.

Последовательность выполнения приложения следующая. Вызывается действие контроллера, в нем всего одна команда - отрисовка пустого представления. В момент отрисовки пустого представления вызывается отрисовка основного мекета. И из него уже вызываются отрисовки виджетов описанным выше способом.

* * *

Однако, у такого подхода вылезли проблемы:

1. На сайте должны быть ЧПУ (контроллер/действие/имяПараметра1/значение1/имяПараметра2/значение2). Никаких _GET параметров. Тогда в контроллере параметры действия можно получить как параметры функции. Это красиво и правильно. Но вот вопрос. Как эти параметры передавать в "конечные" виджеты? К тому же параметры действия должны попадать не во все "конечные" виджеты, а только в те, которым они предназначены. Например, если нужно отобразить статью articles/show/articreId/123, то articleId, равное 123, должно попасть только в "конечные" виджеты titleAreaDraw и textAreaDraw, но не в виджеты menuAreaDraw, bottomAreaDraw и т. д.

Прописывать в коде действия (в каком-то формате) информацию о том, в какие виджеты должны попасть данные - идеологически неправильно, ведь вся карта сайта сосредоточена в одном месте - в "промежуточном" виджете CDrawPageArea. А прописывать куда должны попасть данные в самом CDrawPageArea - тоже неправильно, потому что данные с одним и тем же именем (например, pageNum) могут прилетать из разных контроллеров, непонятно как их различать.

2. Непонятно, как при такой структуре пользоваться достоинствами Yii. Например, пользовательским входом-выходом и стандартным контролем доступа Yii на уровне контроллеров, ведь контроллеры в описанной мной структуре по сути ничего не делают (вся логика в виджетах). У меня складывается впечатление, что я как-то не так использую Yii. В примитивном блоге, который идет в виде примера, структура сайта размазана по контроллерам, и ее невидно, нет точки управления (как у меня в CDrawPageArea). Но люди почему-то пользуются Yii именно "в лоб". Но ведь так большой проект не удержишь. Где-то я свернул не туда, но не пойму, где. Ведь фреймверк не должен навязывать структуру построения сайта, это всего лишь инструмент со своими стандартами оформления кода и каталогов, не более. Но что я делаю не так - не пойму.


Вот в этих двух проблемах я и хочу разобраться.
 
Последнее редактирование:

Redjik

Джедай-мастер
ох и каша у тебя... давай с самого начала начнем.
зачем тебе вообще вот эта охинея нужна
Область страницы (pageAreaName) - это "именованная" точка в основном макете сайта. Например title, menu, info, text, bottom.
почему нельзя сделать отдельные виджеты title, menu, info ... , а не пытаться делать швейцарский нож?
 

xintrea

Новичок
почему нельзя сделать отдельные виджеты title, menu, info ... , а не пытаться делать швейцарский нож?
Ну будут у меня виджеты title, menu, info. Как результат их работы всовывать в основной макет?

Вызов виджета происходит из макета, насколько я понял из докуметнации. У меня вызов виджетов как раз и сделан из макета, что есть правильно (ведь для этого виджеты и существуют). Вы предлагаете как-то по-другому вызывать виджеты? Как?
 

Redjik

Джедай-мастер
На какой вопрос отвечать? =)
Вызывай как хочешь.

не привязывайся к контроллеру, данные - ну в крайнем случае из $_GET возьми или через обертку Yii::app()->request ...
 

xintrea

Новичок
На какой вопрос отвечать? =)
Повторюсь: Ну будут у меня виджеты title, menu, info. Как результат их работы всовывать в основной макет?


данные - ну в крайнем случае из $_GET возьми или через обертку Yii::app()->request
Я же в самом начале написал, что никаких _GET у меня нет. В обертке Yii::app()->request есть информация о преданных данных через URL? Я нашел только getPathinfo(), но это не то. Не буду же я самостоятельно парсить URL на предмет обнаружения параметров действия.


а не пытаться делать швейцарский нож?
Вообще, "усложнение" только в том, что вместо прямого вызва виджетов title, menu, info... из макета (как ты предлагаешь), я вызываю "промежуточный" виджет CDrawPageArea. Вот и всё. Например, если построить приложение как ты сказал без наворотов, виджет text (выводящий основной текст страницы), должен показывать разные данные. В новостях - новости, в статьях - статьи, в гостевой - записи гостевой и обрабатывать форму гостевой. Но не буду же я в одном виджете text писать код для отображения всех этих разнородных данных. Я буду вызывать из text другие виджеты - drawNews, drawArticles, drawGuestbook. Вот мы и пришли к тому, что виджет text становится "промежуточным" виджетом, таким же как и CDrawPageArea.

Только в приложении "без наворотов" вся логика размазывается по таким вот "промежуточным" виджетам text, а в случае приложеня с виджетом CDrawPageArea логика "что где рисовать" сосредоточена в одной точке управления. Вот и вся разница.

Получается, что независимо от того, как будет построено приложение, нужно знать ответ на вопрос: как в виджете быстро и просто узнать параметры действия, если они передаются через URL (но не через _GET).
 

MiksIr

miksir@home:~$
Я же в самом начале написал, что никаких _GET у меня нет. В обертке Yii::app()->request есть информация о преданных данных через URL? Я нашел только getPathinfo(), но это не то. Не буду же я самостоятельно парсить URL на предмет обнаружения параметров действия.
Насколько я помню Yii при разборе /имяПараметра1/значение1/имяПараметра2/значение2 создает соответствующие ключи в _GET, соответственно все это доступно через request->getParam.
Никаких _GET параметров. Тогда в контроллере параметры действия можно получить как параметры функции. Это красиво и правильно.
Совершенно не связанные вещи. Если у вас в контроллере экшн с параметрами, то их значения можно и в query string передать.
Я буду вызывать из text другие виджеты - drawNews, drawArticles, drawGuestbook. Вот мы и пришли к тому, что виджет textстановится "промежуточным" виджетом, таким же как и CDrawPageArea.
Это задача контроллера - определить, что выводить, какой шаблон. На новости, статьи и прочее - свой шаблон, или несколько (список новостей/одна новость). Никакие виджеты тут не нужны.
Для простых сайтов всякие базовые вещи типа тайтла и т.п. можно вынести как свойства/методы контроллера (родительского) и обращаться к ним из шаблонов (шаблоны живут в контексте контроллера).
 

MiksIr

miksir@home:~$
Так как Yii1 разрабатывался без неймспейсов, то он использовал префикс названия класса, по-этому принято, что "С" - признак внутреннего класса фреймворка. Свой называйте просто DrawPageArea
 

MiksIr

miksir@home:~$
Но люди почему-то пользуются Yii именно "в лоб". Но ведь так большой проект не удержишь. Где-то я свернул не туда, но не пойму, где. Ведь фреймверк не должен навязывать структуру построения сайта, это всего лишь инструмент со своими стандартами оформления кода и каталогов, не более. Но что я делаю не так - не пойму.
На самом деле фреймворк в какой-то мере должен навязывать структуру. Задачи фреймворка не только дать вам пару готовых библиотек, но и облегчить жизнь людям, которые будут ковыряться в вашем коде после вас.
Контроллеры ничего не размазывают, они структурируют. А вот у вас в случае "большого" проекта будет огромный толстый класс, содержащий в себе логику, в которой придется долго ковыряться, что бы понять - что же будет рендерится в итоге.
В стандартной схеме достаточно проанализировать url правила, что бы понять - в какой контроллер или модуль+контроллер стоит перейти для получения всей информации и какой экшн там смотреть.
 

xintrea

Новичок
Если у вас в контроллере экшн с параметрами, то их значения можно и в query string передать.
Поясните, что вы имеете в виду под query string.


Это задача контроллера - определить, что выводить, какой шаблон. На новости, статьи и прочее - свой шаблон, или несколько (список новостей/одна новость). Никакие виджеты тут не нужны.
Вот тут ничего не понял. Вы не путаете шаблон с представлением? Вы предлагаете иметь несколько шаблонов страницы сайта, отдельно для каждого вида сообщений - шаблон страницы для вывода списка новостей, шаблон страницы для вывода одной новости, шаблон страницы для вывода гостевой книги? Но это же дублирование HTML кода, зачем так делать?


Для простых сайтов всякие базовые вещи типа тайтла и т.п. можно вынести как свойства/методы контроллера (родительского) и обращаться к ним из шаблонов (шаблоны живут в контексте контроллера).
То есть, в контроллере нам нужно иметь методы типа getTitle(), getBottom(), контроллер вызывает отрисовку вида, автоматически отрисовывается шаблон, а в шаблоне прописано:

Код:
<title> <?php echo $this->getTitle(); ?> </title>
Вы это имеете в виду?
 

MiksIr

miksir@home:~$
Поясните, что вы имеете в виду под query string.
В http протоколе query - это то, что идет в URL после вопросительного знака
Вот тут ничего не понял. Вы не путаете шаблон с представлением? Вы предлагаете иметь несколько шаблонов страницы сайта, отдельно для каждого вида сообщений - шаблон страницы для вывода списка новостей, шаблон страницы для вывода одной новости, шаблон страницы для вывода гостевой книги? Но это же дублирование HTML кода, зачем так делать?
Я не знаю, что вы подразумеваете под "представлением", по-этому не отвечу. Никакого дублирования нет, так как в Yii система лейаутов (макетов). Макет - это HTML оболочка со всякими тайтлами, основными менюшками и т.п. Внутри лейаута есть точка, куда вставляется контент шаблона. Т.е. когда вы говорите в контроллере render('news'), то результат работы шаблона news вставляется в нужную точку лейаута и потом это уже все выводится. Имя лейаута прописано в свойстве контроллера. Если у вас разные макеты - это чаще всего разные контроллеры (например, личный кабинет, страница сайта) - просто меняете в контроллере, на какой лейаут он ссылается. Ну и инклуды и виджеты никто не отменял, что бы вынести общие части из макета.
Другое дело, что бывают сложные страницы, типа главной, где очень много разных данных (5 новостей, 3 записи блога и т.п.). Тут два варианта: 1) Все эти данные вынимаем в контроллере и из него рендерим главную страницу передав эти данные в шаблон, не используя лейаут, либо 2) Пишем виджеты под эти блоки данных, а контроллер пустой, делает только render. Второй способ в общем-то более правильный, но и в первом есть свои плюсы. Можно миксовать способы.
То есть, в контроллере нам нужно иметь методы типа getTitle(), getBottom(), контроллер вызывает отрисовку вида, автоматически отрисовывается шаблон, а в шаблоне прописано:
Да. Но в общем тут может быть много решений. Например, в одном проекте у меня отдельная таблица в базе данных, в которой хранится сопоставление полного URL и всяких title, keywords и т.п. - что бы SEO-шники могли легко править это. Там отдельный компонент, к которому идет обращение из шаблона.
 

fixxxer

К.О.
Партнер клуба
Задача контроллера - инициализация моделей в соответствии с полученными аргументами запроса, создание view, связывание view и моделей и вызов рендеринга. Ничего больше контроллер делать не должен.

Если вывод чего-то зависит от view-логики, во view никто не мешает передать, например, какую-то фабрику, которую вьюха будет дергать.

"Виджеты-хелперы" в стиле CHtml - это жесть и ад. Для таких вещей есть HMVC.
 

xintrea

Новичок
Тут два варианта: 1) Все эти данные вынимаем в контроллере и из него рендерим главную страницу передав эти данные в шаблон, не используя лейаут
Блин, я вообще запутался. В терминологии Yii шаблонов нет. Шаблон - это имеете в виду представление (вид)?

То есть, нужно читать "передав эти данные в представление (т. е. в вид), не используя лейаут" ?

Но тогда у нас получится дублирование HTML-кода:

- представление (вид) страницы сайта - для отрисовки главной
- макет (лайаут) - для отрисовки прочих "простых" страниц сайта

Оба файла, по сути, одно и тоже. Но в первый передается набор переменных, содежимое которых вставляются в нужные места. А во второй файл передается только $content центральной части страницы (а заполнение прочих частей происходит через вызовы типа $this->getTitle()).

Чтобы избежать дублирования HTML, можно, конечно, воспользоваться инклюдами HTML кода, но тогда целостный HTML-код верстки сайта придется нарезать на ~10 частей ("до первой вставки", "после первой перед второй вставкой", "после второй перед третьей" и т. д.). Но это же жутко неудобно.

Или вы имеете в виду, что один и тот же файл можно использовать и как представление, и как лайаут?


либо 2) Пишем виджеты под эти блоки данных, а контроллер пустой, делает только render.
Впринципе, это решение и описано в начальной мессаге. У меня действительно сейчас контроллеры пустые, и делают только render пустой строки, а виджеты вызываются из макета. Но тогда вообще теряется смысл контроллера. И остается пока не до конца решенной проблема - как в виджете получить параметры экшена. Я попробую через _GET, если параметры из ЧПУ-шного URL действительно добавляются в _GET. Но как-то все это коряво.
 

xintrea

Новичок
Задача контроллера - инициализация моделей в соответствии с полученными аргументами запроса, создание view, связывание view и моделей и вызов рендеринга. Ничего больше контроллер делать не должен.

Если вывод чего-то зависит от view-логики, во view никто не мешает передать, например, какую-то фабрику, которую вьюха будет дергать.

"Виджеты-хелперы" в стиле CHtml - это жесть и ад. Для таких вещей есть HMVC.
Я наверно попробую сделать так.

Сделаю дефаулт-контроллер, в котором будут прописаны методы типа getTitle(), getMenu() и т. д. От этого контроллера буду наследовать все контроллеры, отвечающие за отдельные разделы сайта. Тогда и на главной, и на прочих страницах сайта в контроллерах нужно будет переопределить только некоторые методы типа getText() - получение основного текста страницы, getRelationMaterials() - получение ссылок в разделе на похожие материалы, и этого будет достаточно. Ну а вызов этих методов будет происходить из макета по принципу $this->getTitle(). Тогда и макет будет один, и его не надо нарезать, и назначение контроллера остается правильным.
 

hell0w0rd

Продвинутый новичок
xintrea, ничего подобного. Тогда у тебя контроллер через наследование будет хранить информацию о view, а это не верно.
Возьми норм шаблонизатор, где то, что ты хочешь уже есть - Twig.
 

MiksIr

miksir@home:~$
Макет в терминологии Yii = лейаут http://www.yiiframework.com/doc/guide/1.1/ru/basics.view

Способ использования сильно зависит от дизайна и верстки. Если на странице мы можем выделить основную группу данных (список новостей в разделе Новости), то пусть это делает контроллер, и рендерит свою вью, где будет выведен этот список новостей. Результат этой вью будет вставлен в лейаут, который содержит по сути весь дизайн.

Просто может быть такая ситуация, что на странице нет "главной" части - чаще всего это случается на главной странице сайта.

Если в лейауте есть дополнительные динамические элементы - они реализуются через виджеты. Виджеты - один из вариантов реализации HMVC. Входные данные могут быть как получены в виджете через Yii::app()->request, так и инжектированы туда через параметры виджета.
 

MiksIr

miksir@home:~$
xintrea, ничего подобного. Тогда у тебя контроллер через наследование будет хранить информацию о view, а это не верно.
А вас не смущает, что контроллер название вью хранит? ;) Допустим, что title страницы мы ставим во view. Как передать его из этого вью в лейаут? Компонент/хелпер городить? Или сразу за твигом побежим? ;)
 

fixxxer

К.О.
Партнер клуба
В симфони есть native php-шаблонизатор практически со всеми возможностями твига. Посмотри, как там сделано
 

hell0w0rd

Продвинутый новичок
А вас не смущает, что контроллер название вью хранит? ;) Допустим, что title страницы мы ставим во view. Как передать его из этого вью в лейаут? Компонент/хелпер городить? Или сразу за твигом побежим? ;)
название - нет. Ровно как и название модели контроллер хранит.
А вот title - да, в шаблоне должен быть. <title>{% block title %}{% endblock %} - sitename.ru</title>
Переопределяешь блок - и все ок;)
 
Сверху