Показать сообщение отдельно

  #4  
Старый 10.07.2009, 10:00
Kuzya
Участник форума
Регистрация: 27.04.2008
Сообщений: 224
Провел на форуме:
140661

Репутация: 313
По умолчанию

Записи
Теперь займёмся основной частью нашего сайта - ведением записей. Всю нужную работу будет выполнять контроллер с именем Records. Создайте его с
помощью утилиты yiic командой "crud Records" ( http://www.yiiframework.com/doc/guide/ru/quickstart.first-app и
http://www.yiiframework.com/doc/guide/ru/topics.console ). Не забудьте предварительно создать и соответствующую модель,
иначе контроллер просто не сможет быть создан и в командную строку выплеснется множество ошибок.
Откроем файл /protected/controllers/RecordsController.php и обратим внимание на его методы. Первый из интересующих нас -
"accessRules" (http://www.yiiframework.com/doc/guide/ru/topics.auth#access-control-filter). Здесь содержатся правила доступа
к действиям контроллера. Сразу после создания они следующие:
1. Любым пользователям разрешено просматривать записи в виде списка и по отдельности (методы list и show)
2. Авторизированным пользователям разрешено добавлять записи и изменять их (методы create, update)
3. Администраторам разрешены функции администрирования и удаления (методы admin, delete)
4. Всё остальное всем запрещено
В принципе, нам ничего тут менять не нужно. Только лишь передать права удаления авторизированным пользователям и стереть из
прав действие admin. Его функционал будет заменён, поэтому можете удалить и само действие. Мы сделаем так, чтоб каждый смог
управлять своими записями, в том числе и удалять их.
Действие create. В самом начале происходит проверка наличия отправленных пользователем данных. Если таковые присутствуют то
они вносятся в базу, в ином случае - обрабатывается шаблон "create". Он содержит в себе пару ссылок и вызов шаблона "_form".
Отображению "_form", в свою очередь, передаётся параметр update. Почти такой-же код есть и в шаблоне обновления сообщения. Вообщем,
здесь для двух действий используется одна и та же форма, только передаются ей, при обработке, разные значения параметра "update".
Код кнопок можете спокойно затирать, оставьте лишь вызов формы. А код отображаемого шаблона _form заменим на следующий.

Код HTML:
<?if($update):?>
<h2>Обновление записи</h2>
<?else:?>
<h2>Создание новой записи</h2>
<?endif;?>
<div class="actionBar">
</div>
<?if($update):?>
<form action="/records/update/" method="POST">
<?else:?>
<form action="/records/create/" method="POST">
<?endif;?>
<table width="100%">
<tr>
<td width="20%"><span class="text">Название записи</span></td>
<td>
<input type="text" name="Records[title]" class="long_field" value="<?=$model->title?>"/>
</td>
</tr>
<tr>
<td colspan="2"><span class="text">Текст записи:</span></td>
</tr>
<tr>
<td colspan="2"><textarea name="Records[text]" class="long_textarea" rows="20"><?=$model->text?></textarea></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="createRecord" value="Добавить запись">
</td>
</tr>
</table>
</form>
Здесь мы отправляем 2 параметра - название записи и её содержимое. Но в базе есть ещё 3 поля - время публикации, номер автора и
количество просмотров. Добавим их в POST-массив перед помещением данных в модель, и уже всё вместе занесём в БД. Вот код который должен
быть вызван в том случае если массив $_POST["Records"] обнаружен.

PHP код:
$_POST['Records']['pub_date'] = time();
$_POST['Records']['author_id'] = Yii::app()->user->getId();
$_POST['Records']['views'] = 0;
// Вносим все данные в модель
$model->attributes=$_POST['Records'];
// Сохраняем
if($model->save()) $this->redirect(array('show','id'=>$model->id)); 
После сохранения пользователь будет перенесён на действие show, контроллера Records, передав в качестве параметра id номер новоиспечённой
записи (ссылка типа /records/show/id/5/).
Метод actionShow. Он отображает запрошенную запись, в зависимости от указанного id. Его код состоит из одной строки. В ней вызывается
соответствующий шаблон, принимая результат работы метода loadRecords. В отображении этот результат обрабатывается, и полученные данные
выводятся пользователю. Всё идеально, но есть небольшая загвоздка. В данных сообщения нет одного - имени автора. Есть только его идентификатор
(поле author_id). Давайте настроим связь между моделями Records и Users так, чтоб при запросе одной или нескольких записей автоматически запрашивались
имена их авторов ( http://www.yiiframework.com/doc/guide/ru/database.arr#declaring-relationship ). Для этого в первой модели найдите метод "relations"
и в возвращаемый им массив внесите ячейку "author", куда будет помещена связь со второй моделью.

PHP код:
'author' => array(self::BELONGS_TO,'Users','author_id'), 
Теперь в каждой записи кроме основных полей будет добавлено поле author, содержащее результат работы запроса "SELECT * FROM users WHERE id=$author_id".
В отображение все эти данные поступят вместе с остальными, так что нам нужно всего лишь напечатать свойство "$model->author->login". Вот полный код
шаблона show.

Код HTML:
<?if($model === null):?>
Запрашиваемой записи не существует.
<?else:?>
<h2><?php echo $model->title; ?>(<?=date("d-m-Y",$model->pub_date)?>/<?=$model->author->login;?>)</h2><br />
<p><?=$model->text;?></p>
<!-- Если текущий пользователь является автором этого сообщения то выводим ссылки "Редактировать" и "Удалить" -->
<?if($model->author_id == Yii::app()->user->getId()):?>
<a href="/records/edit/id/<?=$model->id?>/">Редактировать</a>
&amp;amp;amp;amp;amp;amp;nbsp;|&amp;amp;amp;amp;amp;amp;nbsp;
<a href="/records/delete/id/<?=$model->id?>/">Удалить</a>
<?endif;?>
<?endif;?>
Ничего сверхъестественного. Попробуйте добавить запись и просмотреть её.
Метод actionList - просмотр списка записей. Разберём всё что в нём делается. Сначала создаётся объект класса CDbCriteria
( http://www.yiiframework.com/doc/api/CDbCriteria ). С помощью него, при выборке данных из базы, указывается сколько записей нужно выбрать
( количество указано в константе PAGE_SIZE нашего контроллера ). Затем эти данные извлекаются. Они передаются в отображение и выводятся на экран.
В конце создаётся объект класса CPagination ( http://www.yiiframework.com/doc/api/CPagination ), отвечающий за разбиение материала на страницы.
В самом действии почти ничего не нужно менять. Надо внести лишь 2 небольших изменения. Во-первых, значение константы PAGE_SIZE измените на 3.
Во-вторых изменим код самого контроллера так, чтобы он показывал список не всех сообщений, а того пользователя, чей идентификатор передан в параметре
"user_id" ( ссылка будет выглядеть вот так - http://yii/records/list/user_id/3 ). В самое начало кода добавим обработку поступившего номера пользователя.

PHP код:
$user_id = (int) $_GET['user_id']; 
А сразу после создания объекта класса CDbCriteria заполним его свойство "condition", содержащее условия для выборки.

PHP код:
$criteria->condition "author_id={$user_id}"
Обратите внимание вот на что. В начале шаблона ( то есть до обработки полученных данных ) мы должны вывести надпись "Список сообщений пользователя...".
Для того чтоб получить имя автора можно написать в модели соответствующую функцию, вызвать её, передав в качестве исходных данных номер пользователя в
базе, и результат отдать в отображение. А можно взять его сразу из массива сообщений ещё до их обработки. Например, из нулевой записи

PHP код:
$models[0]->author->login 
Возможно первый вариант в чём-то лучше, но мы будем использовать второй - он проще и быстрее.
На очереди шаблон "list". Сотрите из него весь код и внесите туда вот что.

Код HTML:
<h2>Список сообщений пользователя <?=$models[0]->author->login;?></h2><br />
<?php foreach($models as $n=>$model): ?>
<h2><?php echo $model->title; ?>(<?=date("d-m-Y",$model->pub_date)?>)</h2>
<p><a class="text" href="/records/show/id/<?=$model->id;?>"><?=substr($model->text,0,200);?>...</a></p>
<br />
<?endforeach;?>
<!--шаблон страниц-->
<?php $this->widget('CLinkPager',array('pages'=>$pages)); ?>
Здесь мы сначала сообщаем чей список записей в данный момент просматривает пользователь, а затем выводим и сами сообщения, отображая в виде анонса 200
первых символов текста. Можете сейчас пройти по ссылке http://yii/records/list/user_id/5 и Вы увидите блог пользователя Николай.
В правом нижнем углу страницы можно заметить перекорёженое изображение постраничной навигации. Оно выводится виджетом CLinkPager. Здесь мы напоролись
на один из минусов Yii. По каким-то причинам отображений у CLinkPager нет. HTML-код, который Вы видите, находится прямо в PHP-коде виджета. Исправим
это. Откройте файл ClinkPager.php, находящийся в директории /base/framework/web/widgets/pagers/. Нас интересует методы run и createPageButton. Первый
собирает общий html-код и выводит его на экран. Второй - формирует код кнопок. Переделаем код под более-менее стандартный вид. Для этого мы создадим
дерикторию views в папке с виджетом, и поместим туда 2 шаблона. Первый - pageButton.php. Он содержит код одной ссылки на страницу.

Код HTML:
<?if(!$hidden):?>
<?if($selected):?>
<b><?=$label?></b>&amp;amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;amp;nbsp;
<?else:?>
<a class="text" href="/records/list/user_id/<?=(int)$_GET['user_id']?>/page/<?=$page+1?>"><?=$label?></a>&nbsp;&nbsp;
<?endif;?>
<?endif;?>
Второй - pagesLayout, содержащий код отбражения страниц.

Код HTML:
<br />
<br />
<br />
<br />
<center>
<p>
<?foreach($buttons as $button):?>
<?=$button?>
<?endforeach;?>
</p>
</center><br /><br />
Теперь обратимся к методу createPageButton. Вначале, в зависимости от переменных $hidden и $selected формируется класс тега <li>,
затем сам тег собирается и возвращается в виде результата. Заменим всё это одной строкой. Возвратим обработанный шаблон нашей кнопки, куда
передадим все поступившие в функцию данные. В этом нам поможет метод renderPartitional (
http://www.yiiframework.com/doc/api/CController#renderPartial-detail ), класса CController - он не выводит обработанное отображение, а возвращает его.

PHP код:
return Ccontroller::renderPartial("pageButton",Array('page'=>$page,'label'=>$label,'hidden'=>$hidden'selected'=>$selected), true); 
В методе run мы просто сотрём весь код начиная с объявления переменной $htmlOptions. За место него вызовем всё тот-же renderPartitional, передав
ему код кнопок.

PHP код:
echo CController::renderPartial("pagesLayout",Array("buttons"=>$buttons)); 
Всё, после этих манипуляций виджет работает с шаблонами и отображает нормальную по виду строку с выбором страниц. Осталось только осуществить
перевод слов типа Next, Last и т.д. Для этого откроем наш языковой файл и внесём туда следующие ячейки.

PHP код:
"Next &amp;amp;amp;amp;amp;gt;" => "Следующая &amp;amp;amp;amp;amp;gt;",
"&amp;amp;amp;amp;amp;lt; Previous" => "&amp;amp;amp;amp;amp;lt; Предыдущая",
"&amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;lt; First" => "&amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;lt; Первая",
"Last &amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;gt;" => "Последняя &amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;gt;",
"Go to page:" => "Страницы:"
А в методе run, виджета, в самом начале, изменим категории в вызовах метода t, с "yii" на "common"

PHP код:
if($this->nextPageLabel===null)
$this->nextPageLabel=Yii::t('common','Next &amp;amp;amp;amp;amp;gt;');
if(
$this->prevPageLabel===null)
$this->prevPageLabel=Yii::t('common','&amp;amp;amp;amp;amp;lt; Previous');
if(
$this->firstPageLabel===null)
$this->firstPageLabel=Yii::t('common','&amp;amp;amp;amp;amp;lt;&amp;amp;amp;amp;amp;lt; First');
if(
$this->lastPageLabel===null)
$this->lastPageLabel=Yii::t('common','Last &amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;gt;');
if(
$this->header===null)
$this->header=Yii::t('common','Go to page: '); 
Теперь постраничная навигация отображается по-русски.
Изменение записей. Редактирование сообщений осуществляется через метод actionUpdate. Как Вы помните из описания метода actionCreate, он использует
форму "_form". Единственное, что здесь требует редактирования, это шаблон update. Удалите там всё кроме вызова формы.

PHP код:
<?php echo $this->renderPartial('_form', array('model'=>$model,    'update'=>true,)); ?>
Для того чтобы пользователи не могли редактировать чужие сообщения можно сразу после объявления переменной $model, в методе actionUpdate, вставить
одну строку.

PHP код:
if($model->author_id != Yii::app()->user->getId()) $this->redirect("/"); 
Она сверит номер автора сообщения с номером текущего пользователя и в случае их несовпадения завершит работу, перебросив пользователя на главную страницу.
Действие delete. В нём требуется только вставить проверку авторства пользователя и исправить редирект после удаления. Для этого мы в начало метода
внесём следующий код.

PHP код:
$record=$this->loadRecords();
if(
$record->author_id != Yii::app()->user->getId()) $this->redirect("/"); 
А код редиректа заменим на такой.

PHP код:
$this->redirect(array('list','user_id'=>$record->author_id)); 
Вот и всё. Можно приступить к следующему этапу.