[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Начальный практикум в YII
Kuzya
Официальный сайт фреймворка: http://yiiframework.com/
Русскоязычный форум: http://www.yiiframework.com/forum/index.php/board,19.0.html
Версия фреймворка на момент написания статьи: 1.0.6

Автор: Кузьмин Антон (Kuzya)
Сайт: http://kuzya.name
Дата: 05-07-2009

К сожалению редактор форума не позволяет создавать структурированный текст, из-за чего большие части кода порой трудно читаются.
В этом случае рекомендую Вам скачать PDF-вариант статьи: practical_intro_yii.pdf (17 страниц, шрифт Times New Roman, 12pt).


Прикреплённые файлы: design.zip, ready.zip, database.zip


Введение
Здравствуйте. В этой статье мы будем практиковаться в изготовлении простейшего приложения с помощью фреймворка Yii. Нашей целью будет создание
сайта на котором люди могут вести свои блоги и комментировать чужие.
Скажу сразу что я являюсь ярым противником различных классов и хэлперов для работы с HTML. Поэтому, в отличие от демонстрационных приложений
производителей, я не использую классы типа CHtml. Хотя возможно кто-то сочтёт это неправильным. Вообщем, все шаблоны в этой статье содержат
только HTML и альтернативный PHP-синтаксис.
Начните с создания "чистого" хоста для нашего сайта. Я назвал его "yii".В корневой директории создайте папку base. Скопируйте туда содержимое
архива фреймворка (директории framework, requirements и т.д.). После этого, так же в корне, создайте новое приложение с помощью утилиты "yiic"
и её команды webapp ( http://www.yiiframework.com/doc/guide/ru/q...start.first-app ).

Дизайн
Возьмите архив с дизайном (приложен к статье). В нём есть директории fonts, images и файл style.css. Скопируйте их в корень сайта. Теперь пройдите
в директорию /protected/views/layouts и откройте файл main.php. Здесь содержится глобальный внешний вид для всех без исключения страниц. Сначала
просто скопируйте в этот шаблон HTML-содержимое нашего дизайна ( из архивного index.html ). После этого, при обращении к корню хоста, Вы должны
увидеть совершенно другую картину.

Главная переменная в layout`e это $content. Туда помещается содержимое текущей страницы. Давайте затрём приветствие

HTML
<h2>Здравствуйте уважаемый гость!</h2>
<p>
Почему мы призываем вас завести блог именно у нас?
Блог - это то место, где можно делиться мыслями, публиковать статьи, фотографии, общаться с друзьями или просто рассказывать о своей жизни. Создай
личный дневник только для друзей или заведи блог на тему, которая тебя интересует.
На нашем сайте Вы найдёте новых друзей, сможете бесконечно общаться с ними.
</p>


и за место него вставим отображение этой переменной

PHP
<?=


У нас в приложении главным контроллером является Site. В нём есть действие indexAction, которое отображает соответствующий шаблон. Откроем его
( /protected/views/site/index.php ) и за место содержимого поместим ранее вырезанную часть layout`a. Обновите страницу. Внешне никаких изменений
произойти недолжно.

Локализация
Локализацией ( http://www.yiiframework.com/doc/guide/ru/topics.i18n ) мы займёмся именно сейчас. Почему, Вы поймёте по ходу чтения этого раздела.
Для начала настроим исходный язык приложения (sourceLanguage) и пользовательский (просто language). Для этого откройте файл конфигураций
/protected/config/main.php и после параметров "basePath" и "name" добавьте соответствующие ячейки:

PHP
'sourceLanguage' => 'en_US',
'language' => 'ru_RU',


Так мы указываем приложению что в качестве исходного языка используется английский, а требуется русский. Обратите внимание на то что если эти
языки будут совпадать то никакой перевод задействован не будет.
Непосредственно преобразование английских фраз в русские мы будем осуществлять с помощью метода t, класса Yii, которому нужно передать категорию
фразы, и саму фразу ( http://www.yiiframework.com/doc/api/YiiBase#t-detail ). Метод покопается в языковых файлах и вернёт текст перевода. Сами
фразы мы будем хранить в виде массива, в php-файлах ( хотя возможны варианты, например хранение фраз в БД). Пройдите в директорию /protected/messages/
и создайте там папку ru_RU. В ней должны находиться файлы категорий с русскоязычными текстами. Мы будем пользоваться всего одной категорией ( естественно
в настоящем приложении их несколько ) - common. Создайте для неё файл common.php со следующим содержимым:

PHP
return Array(

);


Поместим в него фразы из нашего приветствия с главной страницы. Отдельно фразу "Здравствуйте уважаемый гость!" и текст приветствия.

PHP
<?php
return Array(
"hello_guest" => "Здравствуйте уважаемый гость!",
"welcome" => "Почему мы призываем вас завести блог именно у нас? Блог - это то место, где можно делиться мыслями, публиковать статьи, фотографии,
 общаться с друзьями или просто рассказывать о своей жизни. Создай личный дневник только для друзей или заведи блог на тему, которая тебя интересует.
 На нашем сайте Вы найдёте новых друзей, сможете бесконечно общаться с ними. "
);


Теперь снова обратимся к отображению действия indexAction, и фразу приветствия заменим на

PHP
<?=Yii::t("common","hello_guest")


а текст приветствия на

PHP
<?=Yii::t("common","welcome")


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

.htaccess

Сейчас мы сделаем так чтоб URL у нас были не такие "index.php?r=path/path", а такие - "/path/path". Для этого откройте конфигурационный файл и в
ячейку components добавьте

PHP
'urlManager'=>array(
'urlFormat'=>'path',
),


А в корень сайта положите файл .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


Более подробную информацию по изменению URL Вы можете найти здесь - http://www.yiiframework.com/doc/guide/ru/topics.url

База данных
Займёмся базой данных. Её SQL-код лежит в архиве database.zip. Создайте базу yii и выполните этот код в ней. Теперь следует указать фреймворку
с какой базой ему нужно работать и как с ней соединяться. Снова обратимся к файлу /protected/config/main.php и раскомментируем в нём строки

PHP
/*
'db'=>array(
'connectionString'=>'Your DSN',
),
*/


Далее, за место "Your DSN" вставим строку соединения с БД. У меня это "mysql:host=localhost;dbname=yii". Добавим в этот массив ещё несколько строк:

PHP
'class' => 'CDbConnection'// Выбираем класс для работы с БД
'username' => 'root'// Имя пользователя
'password' => ''// Пароль
'charset' => 'utf8' // Кодировка


Измените их значения под свой сервер.

Регистрация
Самое первое что мы должны сделать на нашем сайте - соорудить регистрацию и авторизацию. Для этого мы модифицируем уже существующий у нас контроллер
Site и создадим модель Users. Создание модели лучше всего производить с помощью утилиты yiic. Для этого запустите yiic с параметром shell и указанием
индексного файла нашего приложения. После этого выполните команду "model Users". Создастся модель которую мы используем для работы с соответствующей
таблицей.
Сначала сделаем регистрацию. Объявите для этого метод actionRegister в контроллере Site. Впишем туда отображение формы регистрации. Она будет лежать
в шаблоне /protected/views/site/register.php и иметь следующее содержимое.

HTML
<h2>Регистрация</h2>
<form action="/site/register/" method="POST">
<table width="100%">
<tr>
<td>Имя пользователя</td>
<td><input type="text" name="Users[login]" class="long_field" ></td>
</tr>
<tr>
<td>E-mail</td>
<td><input type="text" name="Users[email]" class="long_field" ></td>
</tr>
<tr>
<td>Пароль</td>
<td><input type="password" name="Users[password]" class="long_field"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="&nbsp;Зарегистрироваться&nbsp;" />
</td>
</tr>
</table>
</form>


Код обработки этого шаблона будет простой.

PHP
$this->render("register");


Теперь пройдите по ссылке http://yii/site/register/. Вы должны увидеть форму регистрации из трёх полей. Как видно из шаблона,
форма отсылает данные методом POST на этот же адрес. То есть при обращении к действию register контроллер должен как-то проверить
- пришла форма, или это простое обращение. Такую проверку мы будем осуществлять проверяя существование массива $_POST['Users']. Если он есть
- нам пришли данные из формы. В случае регистрации мы просто создадим новый объект модели Users, укажем необходимые поля и сохраним изменения.

PHP
$users = new Users;
// Заполняем поля
$users->login    $_POST['Users']['login'];
$users->password md5($_POST['Users']['password']);
$users->email    $_POST['Users']['email'];
// Сохраняем данные
$users->save();


После регистрации пользователю нужно сообщить об успешности этой операции. Мы воспользуемся Flash-сообщениями. Сначала внесём соответствующий
текст в языковой файл, в ячейку "registered"

PHP
"registered"  => "Поздравляем! Вы зарегистрированы!"


А затем объявим его в контроллере, вызывав метод setFlash ( http://www.yiiframework.com/doc/api/CWebUser#setFlash-detail ).

PHP
Yii::app()->user->setFlash("registered",Yii::t("common","registered"));


Здесь мы создали у текущего пользователя сообщение об успешной регистрации с идентификатором "registered". Осталось только шаблон формы изменить
так чтоб она не отображалась если есть Flash-сообщение. В этом нам помогут методы hasFlash и getFlash этого же объекта. Первый определяет есть
ли нужное нам сообщение, а второй это сообщение возвращает.

HTML
<h2>Регистрация</h2>
<?if(Yii::app()->user->hasFlash("registered")):?>
<p>
<?=Yii::app()->user->getFlash("registered")?>
</p>
<?else:?>
<form action="/site/register/" method="POST">
<table width="100%">
<tr>
<td>Имя пользователя</td>
<td><input type="text" name="Users[login]" class="long_field" ></td>
</tr>
<tr>
<td>E-mail</td>
<td><input type="text" name="Users[email]" class="long_field" ></td>
</tr>
<tr>
<td>Пароль</td>
<td><input type="password" name="Users[password]" class="long_field"></td>
</tr>
<tr>
<td>Подтверждение пароля</td>
<td><input type="password" name="Users[password_confirm]" class="long_field"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="&nbsp;&nbsp;Зарегистрироваться&nbsp;&nbsp;" />
</td>
</tr>
</table>
</form>
<?endif;?>


А вот код который должен у нас получится в действии actionRegister.

PHP
if(isset($_POST['Users']))
{
$users = new Users;
// Сохраняем поля
$users->login    $_POST['Users']['login'];
$users->password md5($_POST['Users']['password']);
$users->email    $_POST['Users']['email'];
// Сохраняем данные
$users->save();
// Устанавливаем сообщение
Yii::app()->user->setFlash("registered",Yii::t("common","registered"));
}

$this->render("register");


Следующим шагом мы добавим валидацию ( http://www.yiiframework.com/doc/guide/form...alidation-rules ).
Нам нужно проверить отсутсвие логина и указанного почтового адреса в базе. Для этого откроем файл /protected/models/User.php и
посмотрим метод "rules". Там сейчас 3 правила - максимальная длинна логина 30 символов, пароля - 32, email`a - 50. Мы внесём сюда
5 правил - поля почтового адреса и логина не должны быть пустыми, содержимого этих полей в БД быть не должно, почтовый адрес должен
иметь правильный формат.

PHP
return array(
// логин и почта должны быть введены
array('login','required','on'=>'register','message'=>Yii::t('common','login_empty')),
array(
'email','required','on'=>'register','message'=>Yii::t('common','email_empty')),
// Они должны быть уникальными
array('login','unique','on'=>'register','message'=>Yii::t('common','login_in_base')),
array(
'email','unique','on'=>'register','message'=>Yii::t('common','email_in_base!')),
// Почтовый адрес должен иметь соответствующий формат
array('email','email','on'=>'register','message'=>Yii::t('common','email_no_right_format'))
);


Внесём в языковой файл соответствующие переводы.

PHP
"login_empty"   => "Поле логина не должно быть пустым",
"email_empty"   => "Поле почтового адреса не должно быть пустым",
"login_in_base" => "Введёный логин присутсвует среди зарегистрированных пользователей",
"email_in_base" => "Введёный e-mail присутсвует среди зарегистрированных пользователей",
"email_no_right_format" => "Вы ввели не правильный email"


Теперь модифицируем контроллер. Во-первых, объявим массив куда будут складываться все ошибки валидации. Назовём его $errors. Во-вторых,
перед сохранением данных мы вызовем метод "validate". И в третьих, мы осуществим обработку метода getErrors, модели, так чтоб в $errors
поместились все имеющиеся ошибки. Затем мы передадим массив с ошибками в обработчик шаблона.

PHP
$errors = Array();
if(isset(
$_POST['Users']))
{
$users = new Users;
// Сохраняем поля
$users->login    $_POST['Users']['login'];
$users->password md5($_POST['Users']['password']);
$users->email    $_POST['Users']['email'];
// Проверяем данные
$users->validate('register');
// Если ошибки есть
if( $users->hasErrors() )
{
// Помещаем их текст в массив $errors
foreach($users->getErrors() as $field$errors[] = $field[0];
}
// Если массив ошибок пуст
if(!count($errors)) {
// Сохраняем данные
$users->save();
// Устанавливаем сообщение
Yii::app()->user->setFlash("registered",Yii::t("common","registered"));
// Обновляем страницу
$this->refresh();
// Если ошибки есть то показываем форму регистрации и передаём их в шаблон
}
}
$this->render('register',Array('errors' => $errors));


В шаблон же мы добавим код который высветит все ошибки, если таковые есть. Его следует поместить прямо перед начальным тегом <form>.

HTML
<?if(count($errors)):?>
Ошибки: <br />
<?foreach($errors as $error):?>
&amp;amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;amp;nbsp;<b><?=$error;?></b><br />
<?endforeach;?>
<?endif;?>
<br />


Теперь, если Вы всё сделали правильно, при отправке пустой формы покажутся соответствующие сообщения.



Спустя 9 минут, 7 секунд (7.07.2009 - 09:09) Kuzya написал(а):
Авторизация
Для авторизации мы будем использовать метод actionLogin, контроллера Site и шаблон /protected/views/site/login.php, содержимое которого мы поменяем на

HTML
<div id="bodyPanel">
<h2>Войдите</h2>
<form action="/site/login/" method="POST">
<table width="100%">
<tr>
<td width="20%">Имя пользователя</td>
<td><input type="text" name="Users[login]" class="long_field"></td>
</tr>
<tr>
<td width="20%">Пароль</td>
<td><input type="password" name="Users[password]" class="long_field"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="&nbsp;&nbsp;Войти&nbsp;&nbsp;" />
</td>
</tr>
</table>
</form>
</div>


Так же придётся сменить содержимое метода "authenticate" компонента UserIdentity (/protected/components/UserIdentity.php).
Этот метод содержит код благодаря которому пользователи могут входить на сайт. Работает он сейчас с двумя аккаунтами, информация о которых
внесена непосредственно внутрь метода. Заменим его на код который будет брать информацию из базы данных. Сначала объявим два новых свойства:

PHP
private $_id;
private 
$_name;


Затем поменяем полностью код метода "authenticate":

PHP
// Получаем имя пользователя и пароль
$username =$this->username;
$password md5($this->password);
// Ищем их в БД
$user Users::model()->find("login=? AND password=?", Array($username,$password));
// Если пользователь не найден возвращаем false
if($user === null) return false;
// Если найден то заполняем свойства класса
$this->_id $user->id;
$this->_name $user->login;
// и возвращаем true
return true;


И опишем ( а точнее перегрузим уже существующие ) два дополнительных общедоступных метода, возвращающих вышеуказанные свойства.

PHP
public function getId()
{
return 
$this->_id;
}

public function 
getName()
{
return 
$this->_name;
}


Вернёмся к контроллеру. Затрите код его метода actionLogin и поместите туда следующее

PHP
if(isset($_POST['Users']))
{
// Авторизируем пользователя
$identity = new UserIdentity($_POST['Users']['login'],$_POST['Users']['password']);
// Если он не авторизирован
if( $identity->authenticate() )
{
// Если всё хорошо то заносим в данные
// пользователя информацию об авторизации
Yii::app()->user->login($identity);
// И перемещаем его на главную страницу
$this->redirect("/");
}
// Если форма не отправлена то просто отображаем её
} else $this->render('login');


Всё, теперь в "Yii::app()->user" ( http://www.yiiframework.com/doc/api/CWebUser ) буде содержаться информация о вошедшем пользователе. А его имя
и номер мы получим с помощью "Yii::app()->user->getName()" и "Yii::app()->user->getId()" соответственно.
Помните надпись "Здравствуйте гость!" на главной? Давайте сделаем так, чтобы она менялась в зависимости от того авторизирован пользователь или нет.
Добавим в языковой файл соответствующую фразу.

PHP
"hello_user"   => "Здравствуйте {login}!",


А индексную страницу изменим вот так.

HTML
<h2>
<?if(Yii::app()->user->isGuest):?>
<?=Yii::t("common","hello_guest")?>
<?else:?>
<?=Yii::t("common","hello_user",Array("{login}" => Yii::app()->user->getName()))?>
<?endif;?>
</h2>
<p>
<?=Yii::t("common","welcome")?>
</p>


Здесь мы проверяем - если пользователь является гостем то выводим сообщения для гостей, а если нет - выводим фразу приветствия, указывая в качестве
параметра "login" (который содержится в фразе) логин пользователя. И немного модифицируем главное меню. Пусть авторизированный пользователь видит
ссылки на главную, на выход и на создание новой записи. Для этого, в layout`e, заменим код

HTML
<ul>
<li><a href="/site/login">Вход</a></li>
<li><a href="/site/register/">Регистрация</a></li>
<li><a href="/">Главная</a></li>
</ul>


на

HTML
<ul>
<?if(Yii::app()->user->isGuest):?>
<li><a href="/site/login/">Вход</a></li>
<li><a href="/site/register/">Регистрация</a></li>
<?else:?>
<li><a href="/site/logout/">Выход</a></li>
<li><a href="/records/create/">Написать</a></li>
<?endif;?>
<li><a href="/">Главная</a></li>
</ul>


Регистрируемся, входим и проверяем как изменяется главная страница.

Спустя 37 секунд (7.07.2009 - 09:10) Kuzya написал(а):
Записи
Теперь займёмся основной частью нашего сайта - ведением записей. Всю нужную работу будет выполнять контроллер с именем Records. Создайте его с
помощью утилиты yiic командой "crud Records" ( http://www.yiiframework.com/doc/guide/ru/q...start.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/d...ng-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->


Возможно первый вариант в чём-то лучше, но мы будем использовать второй - он проще и быстрее.
На очереди шаблон "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/CContr...rPartial-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));


Вот и всё. Можно приступить к следующему этапу.

Спустя 1 минута, 42 секунды (7.07.2009 - 09:12) Kuzya написал(а):

Комментарии.
На нашем сайте пользователи (как авторизированные так и неизвестные) должны иметь возможность комментировать каждую запись. Для этого мы создадим
контроллер Comments. По аналогии с контролером записей нужно воспользоваться утилитой yiic и командами model и crud.
После создания контроллера откройте его и подредактируйте правила доступа следующим образом. В разрешённые всем действия добавьте create. Действия
update, delete и admin удалите. Представим что всех их будет выполнять либо администратор ресурса, либо владелец блога.
Затем откройте отображение show, контроллера Records. После кода отображения записи поместите код формы, в которую посетитель будет вносить текст
комментария.

HTML
<br />
<br />
<br />
<h3>Оставьте свой отзыв!</h3>
<form action="/comments/create/" method="post">
<input type="hidden" name="Comments[post_id]" value="<?=$model->id?>" />
<table width="100%">
<tr>
<td>Текст комментария:</td>
</tr>
<tr>
<td><textarea name="Comments[text]" class="long_field"></textarea></td>
</tr>
<tr>
<td><input type="submit" value="Сохранить" /></td>
</tr>
</table>
</form>


Далее нужно изменить сам контроллер. А именно - метод actionCreate. Изначально он выполняет сохранение тех данных которые приходят к нему из формы.
Но в форме у нас всего два поля - комментарий и номер записи. А в таблице должны быть заполнены ещё столько же - дата публикации и идентификатор автора.
Поэтому, прямо перед передачей POST-параметров в модель

PHP
$model->attributes $_POST['Comments'];


мы создадим в POST-массиве две дополнительные ячейки. В одной будет номер автора (или 0 в случае неавторизированного гостя), а во второй - текущее
время в формате UNIX TIMESTAMP.

PHP
$_POST['Comments']['author_id'] = (Yii::app()->user->getId()) ? Yii::app()->user->getId() : 0;
$_POST['Comments']['pub_date']  = time();


Как всегда, изменим и код редиректа. Сейчас он перенаправляет пользователя на метод show, дабы тот увидел то что он опубликовал. Но нам нужно
показать посетителю не именно то что он оставил, а комментируемую запись и все сообщения комментаторов. Для этого мы перебросим его на метод show
контроллера create, поместив в ссылку номер комментируемой записи.

PHP
$this->redirect("/records/show/id/{$model->post_id}");


Следующим шагом нужно организовать отображение всех комментариев которые есть у записи. Сделать это можно двумя путями - в контроллере Records,
в методе actionShow, получить список отзывов о записи (например, описать для этого соответствующую функцию в модели Comments). А можно настроить ещё
одну связь в модели Records с моделью комментариев. Мы пойдём вторым путём и в массив, возвращаемый методом relations поместим вот такую ячейку.

PHP
'comments' => array(self::HAS_MANY,'Comments','post_id')


Так мы укажем приложению что у каждой записи имеется ( возможно ) множество комментариев, которые должны отбираться в модели Comments, по полю
post_id. Все они будут помещаться в итоговую модель, в соответствующую ячейку. Но в самих комментариях нам нужно отображать ещё и имя их автора.
Для этого применим связь которую уже объявляли при работе с записями - свяжем модель комментариев и модель пользователей по полю author_id.

PHP
'author' => array(self::BELONGS_TO,'Users','author_id'),


Осталось лишь отображение. Для этого в шаблон show поместите следующий код, прямо между формой отзыва и текстом записи.

HTML
<br /><br /><br />
<h3>Комментарии:</h3>
<?foreach($model->comments as $comment):?>
<b>
<?if(isset($comment->author->login)):?>
<?=$comment->author->login?>
<?else:?>
Гость
<?endif;?>
(<?=date('d-m-Y',$comment->pub_date)?>)
</b><br />
<?=$comment->text?><br /><br />
<?endforeach;?>


Теперь у наших блогов есть ещё и комментарии.

Кэширование.
Ну и под конец коснёмся темы кэширования. Оно в Yii ( http://www.yiiframework.com/doc/guide/ru/caching.overview ) может быть реализовано с
помощью пяти компонентов - memCache, ACP, XCache, EAcceleratior и CDbCache. Для всех них создан один уникальный класс-оболочка - CCache
( http://www.yiiframework.com/doc/api/CCache#get-detail ). Через него можно работать со всеми пятью компонентами через один и тот же интерфейс.
Мы воспользуемся кэшированием с использованием базы данных ( CDbCache ). Для активизации компонента требуется открыть главный конфигурационный
файл, и в массив ячейки "components", добавить ячейку "cache" со следующим содержимым

PHP
'cache' => Array('class' => 'system.caching.CDbCache'),


Более ничего делать не нужно. Давайте применим кэширование к показу сообщений в блоге. Кэшировать мы будем данные получаемые из БД ( работа с данными

- http://www.yiiframework.com/doc/guide/ru/caching.data ). Задействовать нам нужно всего 3 метода - get, set и delete. Они принадлежат объекту
возвращаемому Yii::app()->cache. В качестве основного параметра им нужно передать специальный идентификатор ( обычное имя под которым кусок информации
будет записан, удалён или получен из кэша ). Из названий этих методов понятно что первый проверяет наличие определённых данных, второй эти данные
устанавливает, а третий - удаляет.
Откроем контроллер ответственный за записи в блогах и обратимся к методу actionShow. В нём мы будем кэшировать данные поступающие из базы. Изменим
код следующим образом. В начале мы получим номер запрашиваемой записи - он находится в параметре id. Далее, мы проверим - если кэш записи уже есть,
то достанем его и передадим в отображение. Если нет - вызовем метод loadRecords, сохраним в кэше его результат и передадим в отображение. Ничего сложного.

PHP
$id = (int) $_GET['id'];

if(
Yii::app()->cache->get("show_{$id}") === false)
{
$model $this->loadRecords();
Yii::app()->cache->set("show_{$id}",$model);
} else
$model Yii::app()->cache->get("show_{$id}");

$this->render('show',array('model'=>$model));


Но ведь содержимое сообщения может изменяться. При этом старый кэш нужно будет удалять. В таких случаях следует описать вызов метода delete.
У нас они находятся в действиях редактирования и удаления. В "update" мы заменим код

PHP
if($model->save())
$this->redirect(array('show','id'=>$model->id));


на

PHP
if($model->save())
{
Yii::app()->cache->delete("show_{$model->id}");
$this->redirect(array('show','id'=>$model->id));
}


А в "delete", сразу перед редиректом мы вставим следующую строку:

PHP
Yii::app()->cache->delete("show_{$record->id}");


Всё, эта часть закончена.

Теперь рассмотрим работу с фрагментом страницы ( http://www.yiiframework.com/doc/guide/ru/caching.fragment ). Это ещё проще. Здесь задействуются
методы beginCache и endCache класса CController (то есть можно будет к нему обращаться в отображении через $this). Мы должны вызвать их в начале
и в конце того куска страницы который должен быть записан. При обращениях пользователей, если фрагмент уже находится в кэше он будет извлечён и
показан. Если же нет, то он выполнится и запишется.
Этим фрагментом будет код отвечающий за показ комментариев в отображении records/show. В качестве кэш-идентификатора у нас будет фраза
"comments_page_номер_страницы". Прямо перед заголовком "Комментарии" поместите следующий код:

PHP
<?if($this->beginCache("comments_page_{$model->id}")):


А после цикла foreach, и трёх переносов строк, вызов окончания кэширования.

PHP
<?$this->endCache("comments_page_{$model->id}");endif;


Ну и удаление кэша мы вызовем в контроллере Comments, в действии create, сразу перед редиректом.

PHP
Yii::app()->cache->delete("comments_page_{$_POST['Comments']['post_id']}");


Всё, теперь у нас кэшируются и записи, и комментарии. Как видите, всё очень легко.

Заключение.
На мой взгляд Yii, в отличие от многих других фреймворков, является инструментом с высоким порогом вхождения. Понять его основы, логику, принципы
и зависимости в начале довольно трудно. Так же мне кажется что утверждение разработчиков о том, что Yii можно использовать для любых приложений,
не верно. Всё-таки он больше подходит именно для веб-2.0-приложений ( как его позиционируют например на сайте журнала phpInside ). Если разобраться
то оказывается что по функционалу фреймворк очень хорош, и является одновременно мощным и простым.

Если в процессе чтения статьи у Вас что-то не получилось то Вы можете взять готовый код экспериментального приложения в архиве ready.zip. Удачи
Вам в Ваших проектах!


Спустя 7 месяцев, 25 дней, 4 часа, 47 минут, 40 секунд (2.03.2010 - 14:59) Г написал(а):
при регистрации валидация пишет ошибку

Спустя 1 месяц, 15 дней, 20 часов, 27 минут, 52 секунды (18.04.2010 - 10:27) La5 написал(а):
Спасибо, как раз сейчас начал с ним разбираться. Ваша статья помогает smile.gif

Спустя 3 месяца, 18 дней, 6 часов, 8 минут, 16 секунд (6.08.2010 - 16:36) rusanger написал(а):
а почему ничего скачать нельзя?

Спустя 1 месяц, 5 дней, 3 часа, 48 минут, 11 секунд (11.09.2010 - 20:24) Guest написал(а):
ава

Спустя 2 минуты, 34 секунды (11.09.2010 - 20:26) Guest написал(а):
Yii::app()->cache->delete("comments_page_{$_POST['Comments']['post_id']}");

Данный код не будет очищать кэш, потому что для ключей кешированных фрагментов используется специальный алгоритм
http://www.yiiframework.com/forum/index.ph...agment-caching/

Спустя 9 месяцев, 23 дня, 16 часов, 57 минут, 13 секунд (5.07.2011 - 13:24) volodya81 написал(а):
Не работает сайт автора поста. Может у кого нибудь есть файлы на которые указывают ссылки в первом посте?

Спустя 22 минуты, 17 секунд (5.07.2011 - 13:46) Krevedko написал(а):
ниче се автор дает. сколько фреймворков знаете всего ?

Спустя 11 часов, 58 минут, 56 секунд (6.07.2011 - 01:45) KonstantinK написал(а):
volodya81 Посту то уже завтра будет 2 года вот и не работают ссылки smile.gif

Спустя 22 часа, 32 минуты, 51 секунда (7.07.2011 - 00:18) volodya81 написал(а):
Цитата (KonstantinK @ 5.07.2011 - 22:45)
volodya81 Посту то уже завтра будет 2 года вот и не работают ссылки smile.gif


Прикол в том, что автор пропал! На письма не отвечает. Может с ним что то случилось?
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.