- Метод в классе контроллера с префиксом action.
- Класс, наследованный от CAction или его предков.
Начнем с минусов:
- Дополнительные файлы, растяпы могут легко потерять при переносе проекта.
Плюсы:
- Один и тот же код не придётся писать несколько раз.
- Совершенствуя действие не придётся помнить о всех контроллерах.
Ладно, рекламная пауза закончилась, приступим к более интересному. Приведём пример, чтоб закрепить у себя в памяти прочитанное.
Кратко о задаче:
Думаю, 9 из 10 сайтов имеют раздел новости. Конечно же могут быть частные случаи, но, в основном, новости — это список. Список новостей, отсортированных по дате создания, список новостей с определённым тегом, список определённого автора, список определённого статуса или просто новость с определенным ID (я её тоже отнесу к списку, хотя конечно же, список этот из одного элемента).
Приступим
Предположим, у нас есть модель News (не будем её описывать, так как атрибуты модели, правила валидации и всё остальное не так важно).
Так же есть представления: newsItem — для отдельной новости, newsList — для списка новостей.
Наше действие будет искать нужные записи, разбивать записи на страницы и выводить представление.
appliaction.controllers.News.ListAction.php
Код: Выделить всё
class ListAction extends CAction {
/**
* @var string Атрибут модели для фильтра поиска записей.
*/
public $findAttr = NULL;
/**
* @var string Имя параметра $_GET для фильтра поиска записей.
*/
public $findParam = NULL;
/**
* @var int Количество записей на странице.
*/
public $postsPerPage = 10;
/**
* @var string Имя представления.
*/
public $view = 'list';
/**
* @var CDBCriteria Критерий поиска.
*/
public $criteria = NULL;
/**
* @var array Параметры представления.
* Индекс элемента массива будет использован как имя переменной передаваемой в представление,
* а сам элемент либо имя метода, либо callback-функция возвращающая значение переменной.
*/
public $data = array();
/**
* @var CActiveRecord.
*/
public $model = NULL;
/**
* Рендер представления.
*/
public function run() {
// Проверяем обязательный параметр
if (is_null($this->model)) {
throw new CException(Yii::t('yii', 'Property "{class}.{property}" is not defined.',
array('{class}' => get_class($this), '{property}' => 'model')));
}
$data = array();
// Обходим массив параметров представления и получаем значения параметра
foreach ($this->data as $key => $var) {
// Если элемент массива является валидной callback-функцией выполняем функция, получая значение.
if (is_string($key) && is_callable($var)) {
$data[$key] = call_user_func($var, $this);
}
// Если элемент строка, проверяем наличие метода в данном классе.
elseif (is_string($var) && method_exists($this, 'get' . ucfirst($var))) {
$data[is_string($key) ? $key : $var] = call_user_func(array(
$this,
'get' . ucfirst($var)
));
}
}
$this->getController()->render($this->view, $data);
}
/**
* Устанавливаем параметры представления.
*
* @param array $data
*/
public function setData(array $data) {
$this->data = is_array($data) ? $data : array();
}
/**
* Установка имени атрибута модели.
*
* @param string $findAttr
*/
protected function setFindAttr(string $findAttr) {
$findAttr = trim($findAttr);
$this->findAttr = empty($findAttr) ? NULL : $findAttr;
}
/**
* Установка параметра.
*
* @param string $findParam
*/
protected function setFindParam(string $findParam) {
$findParam = trim($findParam);
$this->findParam = empty($findParam) ? NULL : $findParam;
}
/**
* Получаем значение для фильтра записей.
*
* @return string
*/
public function getFindString() {
return Yii::app()->request->getParam($this->findParam, NULL);
}
/**
* Возвращает критерий поиска.
*
* @return CDbCriteria
*/
public function getCriteria() {
if (is_null($this->criteria)) $this->criteria = new CDbCriteria;
return $this->criteria;
}
/**
* Получаем модели.
*
* @return CActiveRecord
*/
/*public function getModel() {
return is_object($this->model) ? $this->model : NULL;
}*/
/**
* Добавляет условие поиска.
*/
public function addCondition() {
$findString = $this->getFindString();
if (!is_null($this->findAttr) && !empty($findString)) {
$this->getCriteria()->addColumnCondition(array($this->findAttr => $findString));
}
}
/**
* Возвращает массив записей.
*
* @return array
*/
public function getRecords() {
$this->addCondition();
return $this->model->findAll($this->getCriteria());
}
/**
* Возвращает искомую запись.
*
* @return CActiveRecord
*/
public function getRecord() {
$this->addCondition();
return $this->model->find($this->getCriteria());
}
/**
* Возвращает количество записей.
*
* @return int
*/
public function getCount() {
return $this->model->count($this->getCriteria());
}
/**
* Возвращает CPagination для списка.
*
* @return CPagination
*/
public function getPages() {
$pages = new CPagination($this->getCount());
$pages->pageSize = $this->postsPerPage;
$pages->applyLimit($this->getCriteria());
return $pages;
}
}
Код: Выделить всё
class NewsController extends CController {
public function actions() {
// Общий критерий, сортируем по дате добавления
$criteria = new CDbCriteria;
$criteria->order = 'сreatedTime DESC';
// Путь до действия
$path = 'appliaction.controllers.News.ListAction';
return array(
// Действия для показа списка новостей
'list' => array(
'class' => $path,
'model' => News::model(),
'postsPerPage' => 5,
'criteria' => $criteria,
'data' => array('pages', 'records'),
),
// Покажем список, но теперь уже с другим представлением — таблицей
'table' => array(
'class' => $path,
'model' => News::model(),
'view' => 'table',
'criteria' => $criteria,
'data' => array('pages', 'records'),
),
// Список новостей с заданным тегом
'tag' => array(
'class' => $path,
'model' => News::model(),
'criteria' => $criteria,
'findParam' => 'tag',
'data' => array(
'pages' => array($this, 'getpagesByTags'),
'records' => array($this, 'getRecordsByTags'),
),
),
// Покажем новость с определенным ID
'id' => array(
'class' => $path,
'model' => News::model(),
'findParam' => 'id',
'findAttr' => 'id',
'criteria' => $criteria,
'view' => 'show',
'data' => array('record'),
),
// Список новостей с заданным алиасом
'show' => array(
'class' => $path,
'model' => News::model(),
'findParam' => 'alias',
'findAttr' => 'alias',
'criteria' => $criteria,
'view' => 'show',
'data' => array('record'),
),
// Действие для отображения списка новостей заданного автора через адресную строку
'author' => array(
'class' => $path,
'model' => News::model(),
'findParam' => 'author',
'findAttr' => 'author',
'postsPerPage' => 20,
'criteria' => $criteria,
'data' => array('pages', 'records'),
),
);
}
// Для списка новостей с заданным тегом нам понадобятся callback-функции
// Теги в модели реализованы с помощью CTagableBehavior (ищите на форуме)
public function getCountByTags(&$action) {
return News::model()
->withTags($action->getFindString())
->count($action->getCriteria());
}
public function getRecordsByTags(&$action) {
return News::model()
->withTags($action->getFindString())
->findAll($action->getCriteria());
}
public function getPagesByTags(&$action) {
$pages = new CPagination($this->getCountByTags($action));
$pages->pageSize = $action->postsPerPage;
$pages->applyLimit($action->getCriteria());
return $pages;
}
}
http://localhost/news/list/
http://localhost/news/tag/tag/yii/
http://localhost/news/id/id/1/
http://localhost/news/author/author/mc-bear/
И, напоследок, бонус, в наше действие я неспроста внёс модель. Если, вдруг, у нас будет другая модель, например User, и в контроллере UserController нам нужно будет вывести список пользователей, можно использовать написанное заранее действие:
Код: Выделить всё
class UserController extends CController {
public function actions() {
// Общий критерий, сортируем по имени пользователя
$criteria = new CDbCriteria;
$criteria->order = 'name DESC';
// Путь до действия
$path = 'appliaction.controllers.News.ListAction';
return array(
// Действие для отображения списка пользователей
'list' => array(
'class' => $path,
'model' => User::model(),
'postsPerPage' => 20,
'criteria' => $criteria,
'data' => array('pages', 'records'),
),
// Действия для отображения списка пользователей определенного статуса
'status' => array(
'class' => $path,
'model' => User::model(),
'postsPerPage' => 20,
'criteria' => $criteria,
'findParam' => 'status',
'findAttr' => 'status',
'data' => array('pages', 'records'),
),
);
}
}
http://localhost/user/list/
http://localhost/user/status/status/admin/