Урок 4 : Создаем отдельную страницу для постов (тем)
Рубрика: Создаем блог вместе
4 Июн. 2009Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут возникнуть ошибки из-за несовместимости. Обычно ответы на все вопросы работы на 1.1.х были описаны в комментариях ниже статьи.
Как и обещал сегодня мы поговорим как для постов (тем) создать свою отдельную страницу. Тема этого урока как вы понимаете очень проста, и для человека немного знакомого с Yii - не актуальна. В любом случае выбрасывать момент создания отдельной страницы я не могу, поэтому кому интересно - читаем дальше...
---Я надеюсь вы уже знакомы с уроком "Первый раз, первый контроллер.." и имеете на компьютере (хостинге) рабочую копию нашего проекта.
Начнем
Для создания отдельной страницы для поста я могу выбрать несколько путей:
- Расширить actionIndex дописав в нем проверку не висит ли в GET-е где то Id.
- Создать отдельный экшинс
Давайте выберем второй путь чтобы не засорять actionIndex.
Откройте файл PostController.php и добавьте в него два новых экшинса:
public function actionView ()
{
$this->pageTitle = "";
if (!empty($_GET['url'])) {
// На всякий случай удаляю пробелы и устанавливаю
// максимальную длину для url в 100 символов.
$url = substr(trim($_GET['url']), 0, 100);
// Только англ. буквы и цифры в url
if(preg_match("/^[a-zA-Z0-9\-\_]+$/", $url)) {
$post = Posts::model()->find("url = :url", array(
':url' => $url,
));
if (!empty($post)) {
// Тема есть в базе
$this->render('view', array(
'post' => $post,
));
} else {
// Такой темы в базе нет. 404?
Yii::app()->runController('post/error404');
}
} else {
Yii::app()->runController('post/error403');
}
} else {
// $_GET['url'] пустое. Выводим главную страницу
Yii::app()->runController('post/index');
}
}
public function actionError404 ()
{
$this->render('404');
}
Разберем что у нас и для чего:
- actionView - проверяет есть ли в $_GET['url'] какое то значение. Если есть - шуршым в базе и находим нужную тему. Если поста с таким url нету - выполняем экшинс actionError404.
- actionError404 - просто рендерим на экран файл 404.php в котором будет описано мол "Такой страницы у нас нет"
Теперь давайте в views/post и создадим там два файла:
404.php:
<h1>Такой страницы у нас нет</h1>view.php:
<table border="1" width="100%">
<?php if (!empty($post)) : ?>
<tr>
<td><h1><?=$post->name;?></h1>
<tr>
<td><?=$post->created;?>
<tr>
<td><?=$post->text;?>
<?php endif; ?>
</table>
Вот теперь в принципе всё готово.Так как у вас в базе уже есть пару постов (http://yii.dbhelp.ru/step/?n=6), то мы можем попробовать обратиться к одному из них. Для этого в браузере загружаем localhost/post/view/?url=test и смотрим на результат. Если вы всё делали правильно, у вас должно было получиться что то вроде этого http://yii.dbhelp.ru/post/view/?url=hello-yii
Как мне кажется передавать в URL-е таким образом название поста - не красиво, поэтому надо написать простой маршрут. Подробнее почитать как организовать ЧПУ (человеко понятные урлы) на вашем сайте смотрите вот тут
Заходим в protected/config/main.php и добавляем:
'components'=>array(
...
'urlManager'=>array(
'showScriptName' => false, // что бы не цеплялся index.php к ссылкам
'urlFormat'=>'path',
'rules'=>array(
'<url>/post/'=>'post/view',
),
),
...
Обратите внимательно куда именно я добавил правила, не надо лепить просто в любое место!
Теперь наше правило позволяет нам вместо localhost/post/view/?url=test писать localhost/test/post/
Можете по тестировать, всё должно работать.
Если вдруг не работает и к адресу добавляется "index.php" тогда,
открываем .htaccess (в корне сайта) и пишем туда:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
Еще одна мелочь которую мы с вами не написали - это ссылку с главной страницы на страницу поста. Т.е. на странице localhost/post/index/ надо сделать редирект на страницу поста, при нажатии на название.Открываем файл views/post/_list.php
<tr><td>
<table border="1" width="100%">
<tr><td><h2>Название : <?=$post->name;?></h2>
<tr><td><?php echo $post->created;?>
<tr><td><?php echo mb_substr($post->text, 0, 500), "...";?>
</table>
и немного его меняем:
<tr><td>
<table border="1" width="100%">
<tr>
<td><h2>
<?php echo CHtml::link($post->name,array('post/view/', 'url'=>$post->url)); ?>
</h2>
<tr><td><?php echo $post->created;?>
<tr><td><?php echo mb_substr($post->text, 0, 500), "...";?>
</table>
п.с. такой перенос строчек сделан специально что б они не вылезли за блок code моего блога.
Теперь заходим на главную страницу (localhost/ или localhost/post) и нажимаем на название поста.
Барабанная дробь, тун-турурун-турурурурн, все работает :)
Вот такая вот простенькая статья описала вам процесс создания отдельных страниц для постов.
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Сегодня обнаружил что мой бывший сокурсник написал свой некий мод на Yii Blog. Исходные коды я не смотрел, но ... "Yii blog new [update]"
- Разговор сегодня пойдет про SwiftMailer. Это удобный инструмент для отправки почты из php скрипта. Вы сможете:
очень легко и ... "Подключаем SwiftMailer к Yii"
- Рано или позно каждый программист подходит к моменту написания собственной CMS или системы управления сайтами. Несмотря на обильное кол-во опенсорс ... "Идея проекта [OzYii]"

[guest] xoma
Было сказано: Четверг, 04 Июнь 2009
А почему вместо Yii::app()->runController('post/error404');
не использовать код типа:
throw new CHttpException('404','message')???

[adm] zolter
Было сказано: Четверг, 04 Июнь 2009
Когда я пробовал выводить ошибку исключениями как вы показали - у меня не загружаеться общий лаяут. В связи с этим использую дедовский способ, т.е. вызов для этого отдельного экшинса со своим отображением.

[guest] Ponf
Было сказано: Среда, 10 Июнь 2009
При клике на заголовок:
CException
Description
Property "Posts.date" is not defined.

n1ck
Было сказано: Четверг, 11 Июнь 2009
если не секрет, по какому принципу урл вида
/post_url/post а не скажем: /posts/post_url
От общего к частному так сказать

[adm] zolter
Было сказано: Четверг, 11 Июнь 2009
Насколько я заметил поисковики больше любят когда ключевые слова по которым может быть найден сайт находятся ближе к доменному имени. хотя конечно это может быть моё личное заблуждение :)

[adm] zolter
Было сказано: Четверг, 11 Июнь 2009
to Ponf
Скорее всего вы в коде вместо "created" написали date. отсюда и ошибка, мол поля такого не найдено. Проверьте базу и проверьте код, где-то точно в этом ошибка

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
Спасибо, заработало =)
Ещё небольшая просьба: комментируйте свой код поподробнее, пожалуйста. В предыдущем уроке некоторые действия больше похожи на колдунство, чем на понятный алгоритм)

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
И ещё: Вашему блогу катострофически не хватает поиска)

[adm] zolter
Было сказано: Пятница, 12 Июнь 2009
Скоро будет)
Просто когда было мало статей - в нем не было смысла :)

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
Вот ещё проблема: попытался сделать отображение типа localhost/post/test
в main.php
'post/в _list.php
$this->createUrl('post/view/', array('url' => $post->url))В итоге адресс стал выглядеть так: /post/view/url/test6
не понятно, откуда /url/ взялось и почему /view/ осталось)
И ещё перестал работать CLinkPager, на вторую страницу норм переходит, а когда пытаешься перейти на первую -
Unable to resolve the request "view/post/".
вобщем запутался я с этими ЧПУ %)

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
пофиксил (перепутал местами post и view)) ), осталась проблема с линкпэйджером
при запросе http://blog.ru/post/index выводится 404.php

[adm] zolter
Было сказано: Пятница, 12 Июнь 2009
Вы всё перемешали :) Если вы хотите что б урл был вида : localhost/post/название_темы(url)/ тогда в main.php:
'post/
т.е. просто местами надо было поменять
сделайте:
'post2/И сразу все проблемы пропадут. :) Просто из-за одинаковых имен такая фигня.

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
это я понял :)
Только с Pager'ом проблема осталась.
линк вида:
localhost/post/index
По идее работает так-контроллер PostController, действие actionIndex. там проверяем наш адресс, и почему-то переходим на 404.php
Не понимаю, в чём косяк(
и то, что title страницы не выводится, это нормально?)

[adm] zolter
Было сказано: Пятница, 12 Июнь 2009
Читай пост выше :)
Объясняю почему 404.
1. мы написали правило что всё что post/текст - это ссылка на тему.
2. следовательно post/index это у нас не ссылка на actionIndex теперь, а ссылка на тему с url = "index" которой в базе у нас нету. отсюда и 404 ошибка.
поэтому я и говорю что префикс в урле (в данном случае post) надо поменять на что то другое, что б не совпадало с названием контроллера.

[guest] Ponf
Было сказано: Пятница, 12 Июнь 2009
Жесть, магия)
Спасибо за помощь, жду новых уроков :)

[guest] Гость
Было сказано: Суббота, 20 Июнь 2009
А есть ли в Yii каких нить хелперов ? что б не делать вот этого
preg_match("/^[a-zA-Z0-9\-\_]+$/", $url)
Я имею ввиду что б написать к примеу так
$a = getVar('aaa');
и в а мы получили чистую переменную от всяких XSS... etc

[guest] Макс
Было сказано: Суббота, 20 Июнь 2009
Можно проверять через модель. Т.е. в rules написать правило.
Но это кому как удобнее.

[adm] zolter
Было сказано: Суббота, 20 Июнь 2009
Регулярные выражения это вечная фишка поэтому не вижу ничего такого что б их не использовать :) В Yii нет встроенной фишки каких то таких проверок насколько я знаю (ну разве что как встроенные правила в моделе). Вы такое можете подключить хелперы из Kohana и использовать их (как это сделать я уже описывал в статьях)

[guest] Гость
Было сказано: Воскресенье, 28 Июнь 2009
А как заменить localhost/post/index/page/3 на localhost/page/3 ?

[guest] Maxx
Было сказано: Воскресенье, 28 Июнь 2009
Вот почитайте - http://www.dbhelp.ru/how-to-remove-indexphp/page/

[guest] Trainer
Было сказано: Пятница, 07 Август 2009
Не, тут проблемка, 2-я страница блога имеет адрес
http://localhost/post/index/page/2
т.е. post - это модель, а "/index/page/2" добавляет уже модуль разбиения по страницам...
как убрать???

[guest] Maxx
Было сказано: Пятница, 07 Август 2009
А вы как хотите что б было на 2,3,4й странице без page/номер?

[guest] Trainer
Было сказано: Понедельник, 10 Август 2009
если основная страница блока http://localhost/
то хотелось бы чтобы страницы были http://localhost/page/2 и т.п.
а сейчас получается http://localhost/post/index/page/2
т.е. проставляется имя контроллера и экшена. как бы от них избавиться без?

[guest] snn
Было сказано: Понедельник, 31 Август 2009
приписать в rules
'page/<page>' => 'post/index'

[guest] Гость
Было сказано: Пятница, 04 Сентябрь 2009
А как сделать что бы в pageTitle выводилось названия поста??

[guest] Гость
Было сказано: Пятница, 04 Сентябрь 2009
Я разобрался но не уверен что это правильно)) в view.php прописал
<?php $this->pageTitle = $post->name;?>p.s. Спасибо за уроки))

[adm] zolter
Было сказано: Пятница, 04 Сентябрь 2009
Идея верна :) молодец.
Тока это надо в экшинсе прописать который отвечает за вывод постов, а не в отображении. Ну просто по идеологии, а так будет работать и там и там :)

[guest] Гость
Было сказано: Пятница, 04 Сентябрь 2009
если делать в действие у меня почему-то выводит title
My Web Application - View Post

[adm] zolter
Было сказано: Пятница, 04 Сентябрь 2009
В том месте где у меня комментарий написан:
"// Тема есть в базе"
вставьте на его место $this->pageTitle = $post->name
и должно будет заработать

[adm] zolter
Было сказано: Пятница, 04 Сентябрь 2009
Я ссылку получил. Вобщем как я и думал :)
Ты пытаешься использовать $post->name; до того как обьявлена и заполнена переменная $post.
Т.е. в тот момент когда ты делаешь:
$this->pageTitle = "$this->pageTitle = $post->name;";
у тебя в переменной $post - еще ничего нету. Поэтому это дело надо сделать чуть ниже, вот так вот:
public function actionView ()
{
if (!empty($_GET['url'])) {
// На всякий случай удаляю пробелы и устанавливаю
// максимальную длину для url в 100 символов.
$url = substr(trim($_GET['url']), 0, 100);
// Только англ. буквы и цифры в url
if(preg_match("/^[a-zA-Z0-9\-\_]+$/", $url)) {
$post = Posts::model()->find("url = :url", array(
':url' => $url,
));
if (!empty($post)) {
// ВОТ ТУТ!!
$this->pageTitle = $post->name;;
// Тема есть в базе
$this->render('view', array(
'post' => $post,
));
} else {
// Такой темы в базе нет. 404?
Yii::app()->runController('post/error404');
}
} else {
Yii::app()->runController('post/error403');
}
} else {
// $_GET['url'] пустое. Выводим главную страницу
Yii::app()->runController('post/index');
}
}

[guest] Гость
Было сказано: Суббота, 26 Сентябрь 2009
Кто подскажет а какое правило написать, чтобы страница /post/show/id/1.html стала faq.html?

[guest] zolter
Было сказано: Суббота, 26 Сентябрь 2009
ну типа
'rules'=>array(
'/post/show/id/1'=>''faq,
),
плюс еще поле суфикс надо что б .html в конце было

[guest] Гость
Было сказано: Суббота, 26 Сентябрь 2009
наоборот?
...
'faq'=>'post/show/id/1'
...
но так не работает

[guest] Maxx
Было сказано: Суббота, 26 Сентябрь 2009
Да, наоборот. золт ошибся походу.
А что пишет? ты указываешь youdomain.ru/post/show/id/1 и дает что происходит?

[guest] Гость
Было сказано: Суббота, 26 Сентябрь 2009
Да в том и дело что ничего не дает!
сбивает работу всех правил и вообще переходит на index.php?r=post.....

[guest] Maxx
Было сказано: Суббота, 26 Сентябрь 2009
Стоп. а ты что index.php не убрал с урла?
Попробуй тогда написать index.php?r=faq
если переведет - тогда читай следующую статью про то как убрать index.php с урла.

[guest] Гость
Было сказано: Воскресенье, 27 Сентябрь 2009
это у меня работало!
у меня не получается с правой стороны указать id :-)

[guest] Гость
Было сказано: Воскресенье, 27 Сентябрь 2009
т.е. если убрать строку
'faq'=>'post/show/id/1'
то /post/show/id/1.html прекрасно работает, но вот как длбилься чтобы эта же вещь отзывалась на /faq.html вот это у меня не получается

[guest] Maxx
Было сказано: Воскресенье, 27 Сентябрь 2009
Вобще очень странно, должно работать...
Сделай модреврайтом тогда в htaccess-е если срочно надо. Хотя это наверно не вариант..

[guest] дед Захар
Было сказано: Вторник, 27 Октябрь 2009
Всем здравия!
Такое маленькое уточнение
использование Yii::app()->runController('post/error404');
действительно "ложится в тему", но при 404 ошибке нужно еще и заголовок соответствующий послать
использование же throw new CHttpException('404','message'); как в стандартном примере проще в том плане, что заголовок формируется самим Yii.
Это я просто к тому, что не нужно забывать про заголовки, кто будет пользоваться методом зольтера.
Спасибо.

[guest] дед Захар
Было сказано: Четверг, 29 Октябрь 2009
Не за что. Огромное спасибо за статьи и переводы.

[guest] Антошка
Было сказано: Пятница, 30 Октябрь 2009
привет! спасибо за хороший цикл статей. вопрос такой: я хочу из CPagination убрать отображение index.php, т.е. сделать так, чтобы в ссылке вместо index.php/bla/bla/1 было просто /bla/bla/1 (в .htaccess все необходимые настройки сделал). как это сделать?

[guest] Гость
Было сказано: Пятница, 30 Октябрь 2009
ой, прошу прощения за невнимательность. вопрос отменяется :)

[guest] babbik
Было сказано: Четверг, 11 Март 2010
Вопрос про название скрипта в url. zolter, почему на твоем блоге, в некоторым местах в ссылках остался index.php - например в нумерации страниц? там как-то формируются другим способом ссылки, в отличии от остальных?

[guest] zolter
Было сказано: Четверг, 11 Март 2010
Там кажется явно в пагинаторе надо указывать что б index.php не приклеивало. Надо api посмотреть будет.

[guest] like2dev
Было сказано: Суббота, 20 Март 2010
Зачем добавлять проверку в действии , если можно сделать в юрл мэенеджере например так :
/page/test
'/page/<url:\w+>/'=>'post/view',

[guest] like2dev
Было сказано: Суббота, 20 Март 2010
Примечание: Использование правил URL снижает производительность приложения. Это происходит по той причине, что в процессе парсинга запрошенного URL CUrlManager пытается найти соответствие каждому правилу до тех пор, пока какое-нибудь из правил не будет применено. Чем больше правил, тем больший урон производительности. Поэтому в случае высоконагруженных приложений использование правил URL стоит минимизировать.
Понятно, почему =)

[guest] zolter
Было сказано: Суббота, 20 Март 2010
ну и плюс к тому что я писал этот урок когда была версии yii только 1.0.5 кажысь. А там такого еще не было в CUrlManager :)

[guest] Гость
Было сказано: Вторник, 18 Май 2010
по поводу .htaccess прописал, и как было index.php/post/index так и осталось, в чем дело? если забить в урл /post/index то чатес работает страница грузиться, значит нужно в меню в представлении править ссылки по которым кликаем, но там итак нету индекса array('label'=>'Contact', 'url'=>array('site/contact')), где че убрать?

[guest] Гость
Было сказано: Вторник, 18 Май 2010
Спасибо! 'showScriptName' => false, провтыкал сорри

[guest] wlnner
Было сказано: Вторник, 31 Август 2010
При выполнении первой части этого урока возникла ошибка.
Перехожу по ссылке localhost/post/view/?url=test, выдается ошибка CException Description PostController cannot find the requested view "view".
Кодил пошагово, без ошибок.

[guest] zolter
Было сказано: Вторник, 31 Август 2010
Файлик view.php точно создать не забыли в папке view/post ?

[guest] Гость
Было сказано: Воскресенье, 13 Февраль 2011
блин после того как исправил url у меня даже перелистывать на 2-ую страницу не хочет... говарит что нет её

[guest] Гость
Было сказано: Воскресенье, 13 Февраль 2011
убираешь когда из main корректор url тогда листает

[guest] Гость
Было сказано: Четверг, 17 Март 2011
после добавления кода в PostController.php сайт полностью перестает работать, не как не разберусь почему(

[guest] Паша
Было сказано: Вторник, 10 Январь 2012
Разбираюсь по-маленьку. Спасибо, очень интересно.

[guest] Паша
Было сказано: Четверг, 12 Январь 2012
Подскажите, почему может не отображаться Captcha?
Т.е. Она вроде как выводится, но самой картинки нет.
Перепроверил, всё вроде как надо.
Usercontroller.php
class UserController extends CController
{
public function actions()
{
return array(
// Создаем экшинс captcha.
// Он понадобиться нам для формы регистрации (да и авторизации)
'captcha'=>array(
'class'=>'CCaptchaAction',
'backColor'=> 0x003300,
'maxLength'=> 3,
'minLength'=> 3,
'foreColor'=> 0x66FF66,
),
);
}
User.php
class User extends CActiveRecord
{
// для капчи
public $verifyCode;
registration.php
<td><?php $this->widget('CCaptcha', array('buttonLabel' => '<br>[новый код]')); ?></td>
<td><?=CHtml::activeTextField($form,'verifyCode'); ?></td>
Поставил 1.1.9 Там работает (на этом же сервере). Пробывал прописывать как там, результат тот же...
Из=за чего это может быть? Где искать, что попробывать?

[guest] Гость
Было сказано: Четверг, 12 Январь 2012
Скинь код контроллера и модели где идет разговор про качпу в другой урок какой то. Тут как то глюкнул низ страницы


