Шаг 5 : Контроллер
Рубрика: Первые шаги
28 Авг. 2010
Давайте сегодня поговорим что же такое “контроллер” и для чего он используется в приложениях на базе MVC
В уроке номер три вы должны были познакомится с кратким описанием что такое контроллер и какие основные функции он выполняет. Для тех кто уже забыл - повторим в более расширенной форме. В Yii фреймворке контроллером считается класс наследуемый от CController или дочернего от него класса.
В этом уроке у нас будет много примеров работы с контроллером, поэтому давайте договоримся что вы не только читаете урок, но и параллельно выполняете его примеры у себя на хостинге (или локалхосте). Самое удобное будет если вы установите yii-blog из официальной документации (http://www.yiiframework.com/doc/blog/ru/start.overview).
Вот как делаю это я:
- Зайди на страницу “Download” (http://www.yiiframework.com/download/) и выкачай последнюю версию фреймворка
- Распакуй. Залей к себе на хостинг
- Войди в папку demo/blog. Скопируй все что там внутри и перемести в корневую папку что бы получилось что папка protected и yii-1.1.2.rN теперь на одном уровне.
- Открой index.php и измени пути к папке фреймворка и конфиг файла
- Заходи к себе через www.mysite.ru и проверь работает ли блог
Больше не какой настройкой заниматься не надо, работает и хорошо. Идем дальше.
Что бы разобраться для чего в приложении использовать контроллер давайте разберем совсем маленький пример приложения на Yii.
Пример 1
В Yii есть отличный класс CUrlManager который отвечает за обработку url по которому обратились к нашему приложению. Зайдя по ссылке www.mysite.ru/hello/world сразу начнет работу CUrlManager и определит куда дальше передать запрос на обработку.
Правила обработки url задаются в главном конфиг файле приложения (обычно protected/config/main.php) и могут быть дополнены или изменены вами в любой момент. По умолчанию забито самое главное правило вашего приложения:
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',Которое говорит о том что первый параметр в url это название контроллера <controller>, а второй это один из его методов <action>. Поэтому при обращении по ссылке www.mysite.ru/hello/world за отображение страницы на экране будет отвечать контроллер Hello и его метод actionWorld. Смелее, пробуйте проделать это у себя в приложении. Увидели ошибку? это говорит об отсутствии класса (контроллера) к которому мы пробуем обратится. Создадим?
Открываем папку protected/controllers и создаем там файлик HelloController.php:
<?php
class HelloController extends CController
{
public function actionWorld()
{
echo 'Привет от HelloController->actionWorld()';
}
}Теперь набрав в браузере www.mysite.ru/hello/world мы получим надпись “Привет от HelloController->actionWorld()”. По аналогии вы можете добавить в этом контроллере еще пару экшинов и убедится что все работает как надо:
<?php
class HelloController extends CController
{
public function actionWorld()
{
echo 'Привет от HelloController->actionWorld()';
}
public function actionStepan()
{
echo 'Привет Степан!';
}
public function actionDima()
{
echo 'Привет Дима!';
}
public function actionIvan()
{
echo 'Привет Иван!';
}
}Пробуй теперь: www.mysite.ru/hello/dima, www.mysite.ru/hello/ivan, www.mysite.ru/hello/stepan. Работает? Еще бы!
Идем дальше...
В каждом приложении существует такое понятие как контроллер “по умолчанию”. Он используется для обработки url когда параметр контроллера опущен совсем. К примеру, при попытке пользователя открыть www.mysite.ru/ какой контроллер будет отвечать за отображение страницы? Правильно, контроллер по умолчанию.
прим. Контроллер по умолчанию задается в файле конфигурации приложения (обычно config/main.php) в виде парраметра defaultController, например: 'defaultController'=>'index'. Если параметр не указан, контроллер по умолчанию - site
Существует еще понятие как экшин “по умолчанию”. Он выполняется в том случае когда не указанно явно к какому экшину идет обращение. К примеру, при попытке открыть www.mysite.ru/hello/ как скрипту узнать к какому экшину контроллера Hello происходит обращение? В этом случае и будет вызван экшин по умолчанию!
прим. Экшин по умолчанию задается прямо в файле контроллера в виде переменной defaultAction, например $defaultAction = ‘test’. Если параметр не указан, экшин по умолчанию - index
Пример 2
Посмотрели вы что я написал и подумали : “Если для каждой страницы создавать свой экшин - тогда как быть с новостями на сайте? Забивать их в ручную и создавать для каждой свой экшин? Бреееддд”. Совершенно верно подумали :) Совсем не обязательно создавать новый экшин для каждой страницы ваших новостей.
Давайте предположим что мы хотим организовать специальные новости для своего сайта. Для начала откроем контроллер с которым мы уже работали (HelloController) и добавим туда новый экшин:
public function actionNews()
{
echo “Вы открыли новость: ” , $_GET['news_name'];
}Все достаточно просто, обычный экшин который выводит на экран значение переменной ‘news_name’. Попробуйте, должно работать - www.mysite.ru/hello/news/?news_name=Тестовая_новость. Работает? Отлично!
На этом можно было бы и остановится, да только не красивый у нас адрес новости получается. Давайте откроем конфигурационный файл (config/main.php) и добавим туда специальное правило для новостей! Найдите блок urlManager, он должен выглядеть у вас следующим образом:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'post/<id:\d+>/<title:.*?>'=>'post/view',
'posts/<tag:.*?>'=>'post/index',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),Первые два правила - используются нашим блог движком, нам их рассматривать не интересно. Третье правило мы рассмотрели выше и знаем для чего оно используется.
Давайте добавим свое правило по типу тех что мы видим:
'news/<news_name:.*?>'=>'hello/news
- 'news/<news_name:.*?>'=>'hello/news' - правило применяется только к url адресам которые начинаются на словое news, например: www.mysite.ru/news/123123 и www.mysite.ru/news/555555
- 'news/<news_name:.*?>'=>'hello/news' - говорит о том, что после слова “news” обязательно должен идти следующий параметр. В экшин этот параметр будет доступен как $_GET[‘news_name’ ]
- 'news/<news_name:.*?>'=>'hello/news' - проверка на тип переменной. В данном случае мы используем “*” (звездочку) говоря о том что тип не имеет значения. Если бы требовалось что б правило срабатывало только когда news_name - число, тогда мы написали бы <news_name:.\d+>
- 'news/<news_name:.*?>'=>'hello/news' - указываем какой контроллер и экшин будет заниматся обработкой этого правила. В данном случае все запросы будут отправлены в контроллер Hello и его экшин News.
Надеюсь это всем стало понятно т.к. использование собственных правил в приложении очень важный момент.
Попробуйте теперь открыть страницу www.mysite.ru/news/Вторая_тестовая_новость. Надеюсь для вас не было неожиданностью что вы увидели на экране надпись “Вы открыли новость: Вторая тестовая новость”? Если все еще не понятно откуда это взялось - просмотрите ваш HelloController.
Идем дальше...
Контроллер сам по себе это сердце логики вашего приложения. В нем вы должны выполнять всякие проверки, обращение к данным из модели и тп. Я мог бы растянуть урок рассказами о том как использовать собственные фильтры в контроллерах, как наследовать их и тп - но все это только запудрит вам голову. Если вы действительно поймете для чего нужен контроллер - вы сможете дойти до всяких “фильтров” сами, прочитав маленький мануальчик.
Поэтому не будем распылятся, а продолжим дальше осознавать для чего нам контроллер и как его использовать.
Из примера 2 у нас уже есть некий набросок для страницы новостей. У нас работают ссылки www.mysite.ru/news/my_test_news и мы можем перейти к следующему этапу разработки.
Пример 3
Давайте немного модернизируем наши новости. Задание достаточное простое: новости у нас находятся в тестовых файлах, а параметр news_name является этим самым именем файла. Открыв страницу www.mysite.ru/news/numb1 на странице должно выводится содержимое файла numb1.txt соответственно.
Модернизируем немного наш экшин:
function actionNews()
{
if (!empty($_GET['news_name'])) {
// если значение не пустое...
// выводим содержимое переданного файла с расширением txt
echo file_get_contents($_GET['news_name'] . ".txt");
}
}Создаем фалик test.txt в главной директории (на уровне с index.php) и заполняем его текстом “привет это файл test.txt”. Теперь пробуем открыть www.mysite.ru/test и если получаем на экран наш текст - значит все работает.
прим. если ругается что файл не найден - скорее всего следует указать полный путь где его искать. такая ошибка может возникнуть из-за специфической настройки php/apache
Для большей красоты можно сделать проверку на существование файла и если его нет - выводить ошибку что новость уже удалена:
function actionNews()
{
if (!empty($_GET['news_name'])) {
// если значение не пустое...
// загоняем в переменную имя файла который надо открыть
$file_name = $_GET['news_name'] . ".txt";
if (file_exists($file_name)) {
// если файл существует
// выводим на экран его содержимое
echo file_get_contents($file_name);
} else {
// если файл не найден
echo 'Новость уже удалили :(';
}
}
}На самом деле использовать такой подход у себя на сайте я настоятельно не рекомендую. Если злоумышленник допрет что вы загружаете новости из файлов, то сразу воспользуется уязвимостью и попробует манипулируя переменной news_name открыть файлы совершенно не относящиеся к новостям (к примеру, www.mysite.ru/news/\/..\/etc/htpasswd и тп). Так что данный пример я привел только что бы показать вам как работать с контроллером. Идеальным вариантом было бы загружать новости из базы данных при помощи модели!
Идем дальше...
Я надеюсь все эти примеры помогли вам еще лучше понять как можно использовать контроллеры у вас в приложениях. Уроки у нас для новичков и нет смысли пихать в вас полностью все что я знаю за один раз. Иначе моя писанина с трудом вмещалась бы на рулоне туалетной бумаги :)
Для написания следующей части урока мне потребуется немного времени и пока я буду этим занят вы выполните мое маленькое задание. Нет, писать сайты вместо меня и заниматься другой грязной работой не потребуется :)) Я предлагаю вам модернизировать наш “Пример 3” и вместо открытия новостей из файлов - постараться получить данные из БД. Помните, в прошлом уроке мы уже научились получать данные из базы методами find и findAll? Постарайтесь выполнить это самостоятельно для закрепления материала по контроллерам.
Вот маленькая подсказка от меня:
- Создаем таблицу в базе данных для наших новостей (через phpmyadmin). Для начала всего три поля: id, url_name,text
- Создаем файл модели
- Изменяем наш экшин таким образом что б он брал данные из нашей модели где url_name = news_name
- Делаем что б если новость найдена - поле text выводилось на экран
От себя
Спасибо что прочитали все это! Желаю вам сил и терпения в изучении Yii Framework.
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Вот один из способов создания динамических вкладок на основе CTabView/CClipWidget:
next
<?php $this->beginWidget('system.web.widgets.CClipWidget', array('id'=>'My tab 1')); ?> My tab 1 ...<?php ... "Создаем вкладки на сайте (используя CTabView и CClipWidget)"
- Сегодня обнаружил что Sam Dark как и обещал перевел интерисующюю практически всех часть документации про RBAC.
next
Благодаря таким людям - ... "Перевод : Аутентификация и авторизация (RBAC)"
- Поздравляю Всех своих читателей и случайных посетителей с уже наступившим 2011 годом. Желаю Вам в новом году чтобы все ... "Всех с новым годом!"

[adm] zolter
Было сказано: Суббота, 28 Август 2010
Продолжим цикл статей для новичков "Первые шаги".
По результатам голосов и статистики посещения этот цикл интересует посетителей моего блога больше всего

[guest] hyzhak
Было сказано: Суббота, 28 Август 2010
Почему-то все забывают рассказать про метод missingAction

[adm] zolter
Было сказано: Суббота, 28 Август 2010
Эта часть статьи не включает в себя рассмотрение методов контроллера такие как render, missingAction, redirect и тп. Материал чисто для понимания для чего используется контроллер

hyzhak
Было сказано: Суббота, 28 Август 2010
Про render согласен, но вот про defaultAction рассказано, а про missingAction нет. IMHO єто напрямую касается понимания как работает контроллер.
Вы рассказали о поведении контроллера, когда:
- єкшн есть;
- єкшн не указан;
а вот что будет, когда указанный єкшн отсутствует?
P.S. я понимаю, что не мне решать о чем писать в статье, а что опустить и что обо всем не расскажешь, но хотелось бы общими усилиями сделать материал более полезным...

[guest] Гость
Было сказано: Суббота, 28 Август 2010
добавь блок рекламных объявлений от яндекса или гугла. буду щелкать когда статья понравится

[guest] zolter
Было сказано: Воскресенье, 29 Август 2010
to hyzhak
все верно, добавлю завтра! спасиб

[guest] zolter
Было сказано: Воскресенье, 29 Август 2010
to Гость
спасибо, но лучше не буду загружать блог рекламой. много не заработаю, а посетителей будет бесить :)

[guest] Гость
Было сказано: Понедельник, 30 Август 2010
"Контроллер сам по себе это сердце логики вашего приложения"
Сердце логики любого MVC приложения это модель.
echo file_get_contents($_GET['news_name'] . ".txt");Уязвимость?

[guest] zolter
Было сказано: Понедельник, 30 Август 2010
Модель это всего лишь логика работы с базой. Приложения без базы, которые используют модель как CForm для вывода табличек - я сердцем не считаю. Тогда как раз контроллер связывает модель и отображения + содержит в себе всю логику приложения.
По поводу примера - да, я же написал что это уязвимость :)

[guest] den
Было сказано: Четверг, 02 Сентябрь 2010
Ух ты! между этой статьёй и прошлой год разницы. Я как-то прочитал все статьи одним махом и теперь придётся ждать следующих. Или наверное сам разберусь :)
Приятно пишете, как раз хотел получить информацию в таком виде, в других местах чего-то не хватало.
Пишите больше и чаще (по крайней мере на эту тему). Удачи вам и спасибо.

[guest] zolter
Было сказано: Четверг, 02 Сентябрь 2010
Спасибо за хороший отзыв. Постараюсь уделять на статьи больше времени, особенно на обучающие

[guest] SergO_Cz
Было сказано: Понедельник, 13 Декабрь 2010
Насчет новостей в GET-параметре, вот эта строчка не работает и ни на что не влияет:
'news/<news_name:.*?>'=>'hello/news',,есть она, или нету - ошибка 500 и сообщение, что нет такого параметра "news_name".
Когда я решил посмотреть, что же там в массиве $_GET:
public function actionNews()
{
echo 'Вы открыли новость: ';
print_r($_GET);
}
, оказалось следующее (адрес >>> вывод на экран):
1) mysite.ru/news/a >>> Вы открыли новость: Array ( [a] => )
2) mysite.ru/news/a/b >>> Вы открыли новость: Array ( [a] => b )
3) mysite.ru/news/a/b/c >>> Вы открыли новость: Array ( [a] => b [c] => )
4) mysite.ru/news/a/b/c/d >>> Вы открыли новость: Array ( [a] => b [c] => d )
и.т.д.,
что это - моя ошибка, или статья немного устарела? (Yii 1.1.5)

[guest] Андрей
Было сказано: Понедельник, 13 Декабрь 2010
Здравствуйте, читал читал, решился всё-таки попробовать, поставил и тут опа! маленький курьёз
The table "{{post}}" for active record class "Post" cannot be found in the database
Или может я пропустил где то донастройку БД?
ЗЫ Присоединюсь к поблагодарившим, приятная манера изложения!

[guest] zolter
Было сказано: Вторник, 14 Декабрь 2010
Добрый вечер,
А таблица post в базе есть? Может забыли sql код запустить на её создание?

[guest] Дмитрий
Было сказано: Пятница, 28 Январь 2011
Спасибо за Ваш блог! Очень понравился фреймворк Yii. Сейчас использую Codeigniter и cakePHP. Но обучение и примеры у Вас заставляют изучить замечательный Yii. Долгих лет жизни!

[guest] Гость
Было сказано: Воскресенье, 06 Февраль 2011
Приветствую!
не мог не написать..
Грамотный подход, ничего лишнего.
Спасибо за доходчивое обучение!

[guest] cruser
Было сказано: Вторник, 08 Февраль 2011
Присоединюсь к вопросу SergO_Cz. Есть ли разница в создании правил в Yii версии 1.1.5(6) и более ранних версиях(1.0.х)?
Спасибо.

[guest] Гость
Было сказано: Среда, 16 Март 2011
Здравствуйте.
Столкнулся со следующей проблемой:
Поставил Yii на виртуальный сервер и делал все по инструкции. И после создания контролера, как описано в этой статье, адрес: http://127.0.0.1/hello/world, упорно не открывался.
Долго мучился и чисто случайно попробовал адрес: http://127.0.0.1/index.php/hello/world, который наконец то открылся.
Кто-нибудь знает с чем это может быть связанно? Может я что-то неправильно настроил?

[guest] Maxx
Было сказано: Четверг, 17 Март 2011
Попробуйте эту статью http://www.dbhelp.ru/how-to-remove-indexphp/page/
Если не поможет - значит у вас отключена работа htaccess в апаче, и её надо включить.

[guest] Гость
Было сказано: Среда, 23 Март 2011
Долго я искал внятного хелпа для начинающих по Yii. Думаю, что нашел. Спасибо Вам, и пожалуйста пишите еще)

[guest] sidan
Было сказано: Пятница, 29 Апрель 2011
Извините, мож я где-нибудь что-нибудь не понял...
Добавил прописал .htaccess, чтобы адрес был без index.php.
Прописал в config/main.php, чтобы были красивые урлы.
Однако для обращения к странице приходиться прописывать https://mysite.ru/?r=news/num
Где я ошибся?

[guest] Гость
Было сказано: Понедельник, 15 Август 2011
Извините, не могу понять в чем проблема..
Ссылка типа www.mysite.ru/news/my_test_news не работает, а если написать www.mysite.ru/news/=my_test_news и в правиле 'news/<news_name:.*?>'=>'hello/news добавить равно перед именем новости 'news/=<news_name:.*?>'=>'hello/news то все работает нормально..Подскажите в чем проблема. Использую Yii_1.1.8
Заранее, спасибо!

[guest] Maxx
Было сказано: Понедельник, 15 Август 2011
Скорее всего конфиликт с другим каким то правилом, покажите весь url конфиг

[guest] Гость
Было сказано: Понедельник, 15 Август 2011
Сделал все как описано выше,в итоге получилось
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'post/<id:\d+>/<title:.*?>'=>'post/view',
'posts/<tag:.*?>'=>'post/index',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
'news/<news_name:.*?>'=>'hello/news',
),
),
Проблему я уже рассказал. Помогите))

[guest] Maxx
Было сказано: Среда, 17 Август 2011
Перенеси правило:
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
под
'news/<news_name:.*?>'=>'hello/news',
и должно заработать.

[guest] MegaRocks
Было сказано: Пятница, 09 Сентябрь 2011
Таже самае беда что и у SergO_Cz.
После того как передаю чрез строку броузера значение:
например:
http://www.yiiapp/hello/news/Hello
получаю массив:
Array ( [Hello] => )
т.е таким способом передаётся не значение, а ключ...
при этом если явно передавать переменную news_name:
http://www.yiiapp/hello/news/?news_name=Answer
получаю такой массив:
Array ( [news_name] => Answer )
и всё работает. В чем же дело?
yii-1.1.8

[guest] Megarocks
Было сказано: Пятница, 09 Сентябрь 2011
Сам разобрался.
Дело в том что правило нужно задавать в формате:
'hello/news/<news_name:.*?>'=>'hello/news',
И тогда есть и контроллер и его экшн и переменная, а вот за ними всеми уже идёт её значение.

[guest] Гость
Было сказано: Воскресенье, 25 Сентябрь 2011
Отличная статья! Спасибо.
Тоже наткнулся на пару грабель, таких как: частные правила в urlManager нужно писать НАД общими (тупил минут 20 =) и что бы убрать index.php из урла необходимо создавать правила для модреврайта в .htaccess. Желательно об этом упомянуть или хотя бы дать ссылку на решение.
ПС. Ещё не сказано, что БД должна быть подключена (делается в том же config/main.php). При установке по умолчанию SQL Lite кажись.

StrikerWolf
Было сказано: Вторник, 24 Январь 2012
zolter Help!!! Я скопировал все содержимое папки blog в корень каталога где лижит и сам Yii (в качестве хоста использую Denwer 3, Yii framework 1.1.9 v.). Потом прохожу по соответствующему адресу и тут же получаю две ошибки:
Warning: require_once(W:\home\site.ts\www/../../framework/yii.php) [function.require-once]: failed to open stream: No such file or directory in W:\home\site.ts\www\index.php on line 10
Fatal error: require_once() [function.require]: Failed opening required 'W:\home\site.ts\www/../../framework/yii.php' (include_path='.;/usr/local/php5/PEAR') in W:\home\site.ts\www\index.php on line 10
Я только начинаю изучать этот frmework. И решил что лучше спросить у знающего чловека.
З.Ы. ничего не менял в коде файлов. тольк скопипастил их и все.

[guest] zolter
Было сказано: Вторник, 24 Январь 2012
Так все просто, у тебя фреймворка найти не может.
Открой файл index.php что в корне у тебя должен быть, найди там:
$yii=dirname(__FILE__).'/../../framework/yii.php';
и замени на:
$yii=dirname(__FILE__).'/yii-1.1.9.r3527/framework/yii.php';

StrikerWolf
Было сказано: Вторник, 24 Январь 2012
Спасибо zolter! Все работает теперь, немного ругается на БД точнее на отсутствие пока оной ))). Поражен столь быстрым ответом, за это отдельный respect тебе!!!!

[guest] zolter
Было сказано: Вторник, 24 Январь 2012
Нет проблем :) По умолчанию ДБ для блога стоит sqlite, поэтому у тебя и ругается. В Денвере по умолчанию оно не включено, надо дополнительно через сайт качать ставить и в php.ini включать. А вот с мускулом будет работать без проблем.
В config/main.php поменяешь что б работал с mysql
в папке date найдешь дамп базы и загрузишь его в базу и все будет гуд. Удачи!

[guest] zolter
Было сказано: Вторник, 24 Январь 2012
Качай http://www.denwer.ru/packages/php5.html
А в php.ini раскоментруй extension=php_pdo_sqlite_external.dll
и заработает отлично с sqlite. (не забудь грузануть денвер)

[guest] StrikerWolf
Было сказано: Среда, 25 Январь 2012
Спс! Сегодня после работы дома попробую обязательно.


