Легкая сортировка данных из БД
Рубрика: Мелкие заметки на тему Yii
21 Авг. 2010Для одного из проектов потребовалась сортировка по полям таблицы. Задача как понимаю типичная и многим приходиться её решать от проекта к проекту. В Yii для этого существует класс CSort, но к сожалению нормального описания в примерах я так и не нашел.
---Пошуршав немного на форуме была обнаружена тема « my gripes with CSort (and solution) » где автор делится своим маленьким, но очень интересным классом на основе CSort.
Мне показалась работа автора очень интересной и я решил описать её в этой статье.
Сразу даю ссылку на результат - смотреть демо
Настройка
Первым делом создадим файл «protected/components/CActiveSort.php»
<?php
class CActiveSort extends CComponent {
public $separator = '-';
public $paramName = 'sort';
public $route = '';
protected $aliases;
protected $model;
protected $current, $current_ascend = true;
public function __construct($modelName) {
$this->model = CActiveRecord::model($modelName);
$this->aliases = is_callable(array($this->model, 'sorts')) ? $this->model->sorts() : array();
if (isset($_GET['sort'])) {
$bits = explode($this->separator, $_GET['sort']);
if ($this->getAlias($bits[0])) $this->current = $bits[0];
if (isset($bits[1]) && $bits[1] == 'desc') $this->current_ascend = false;
}
}
protected function getAlias($alias) {
if (!isset($this->aliases[$alias])) {
$attributes = $this->model->attributeNames();
if (in_array($alias, $attributes)) {
$this->aliases[$alias] = $alias;
} else {
return null;
}
}
$config = $this->aliases[$alias];
if (is_string($config)) {
$this->aliases[$alias] = array(
'asc'=>$config.' asc',
'desc'=>$config.' desc'
);
}
return $config;
}
public function applyOrderTo($criteria) {
$config = $this->getAlias($this->current);
if (!$config) return;
$criteria->order = $config[$this->current_ascend ? 'asc' : 'desc'];
}
public function link($alias, $label=null, $htmlOptions=array()) {
if (is_null($label))
$label = $this->model->getAttributeLabel($alias);
$config = $this->getAlias($alias);
if (!$config)
return CHtml::encode($label); # non-sortable
$controller = Yii::app()->getController();
$ascend = ( $alias != $this->current ? false : $this->current_ascend );
$params = $_GET;
$params[$this->paramName] = $alias . $this->separator . ($ascend ? 'desc' : 'asc');
$url = $controller->createUrl($this->route, $params);
$class = $alias == $this->current ? ($ascend ? 'sort-asc' : 'sort-desc') : 'sort-none';
$htmlOptions['class'] = (isset($htmlOptions['class']) ? $htmlOptions['class'].' ' : '') . $class;
return CHtml::link($label, $url, $htmlOptions);
}
}
Для подключения в контроллере понадобится от силы 5 секунд т.к. вся логи у нас будет в модели. Вот так происходит подключение сортировки для контроллера:
<?php
class UserlogController extends CController
{
const PAGE_SIZE=50;
public $defaultAction='browse';
private $_model;
public function actionBrowse()
{
$criteria=new CDbCriteria;
// обычная постраничка, с ней вы должны уже быть знакомы
$pages=new CPagination(UserLog::model()->count($criteria));
$pages->pageSize=self::PAGE_SIZE;
$pages->applyLimit($criteria);
// а вот и работа с нашим классом сортировки
// всего две строчки!
$sort = new CActiveSort('UserLog');
$sort->applyOrderTo($criteria);
$models = UserLog::model()->withNames()->findAll($criteria);
$this->render('browse',array(
'models'=>$models,
'pages'=>$pages,
'sort'=>$sort, // обязательно передаем $sort в отображение
));
}
}
В view файле мы создаем заголовок таблицы с полями по которым будем сортировать (аналогично CSort)
<h1>Логи</h1>
<table class="dataGrid">
<thead>
<tr>
<th><?php echo $sort->link('id', 'ID'); ?></th>
<th><?php echo $sort->link('entry_type'); ?></th>
<th><?php echo $sort->link('client','Client'); ?></th>
<th><?php echo $sort->link('user','User Name'); ?></th>
<th><?php echo $sort->link('text'); ?></th>
<th><?php echo $sort->link('logged'); ?></th>
</tr>
</thead>
<tbody>
...
Ну и самое интересное — это настройка сортировки в вашей модели:
<?php
class UserLog extends CActiveRecord
{
...
/**
* @return array sorting aliases for use with CActiveSort
*/
public function sorts() {
return array(
'user' => array('asc'=>'user.first_name asc, user.last_name asc', 'desc'=>'user.first_name desc, user.last_name desc'),
'client' => 'client.name',
'id' => 'userlog.id'
);
}
}
Этот пример показывает как создать сложные сортировки (user) и простые по полям в базе (client, id). Простые сортировки автоматически добавляют «asc» и «desc», для сложных — требуется описывать самостоятельно (как в примере с user) что бы иметь возможность создавать более сложные и уникальные виды сортировок.
Реальный пример
Вот пример того как сортировка работает в одном из моих проектов.
protected/models/User.php:
<?php
class User extends CActiveRecord
{
public static function model($className=__CLASS__) {
return parent::model($className);
}
public function tableName() {
return '{{users}}';
}
public function rules() {
return array(
array('login, hash', 'safe'),
);
}
public function attributeLabels() {
return array(
'id' => 'Номер',
'login' => 'Логин',
'hash' => 'Хеш'
);
}
public function sorts() {
return array(
'id' => 'id',
'hash' => 'hash',
'login' => 'login',
);
}
}
protected/controllers/IndexController.php:
<?php
class IndexController extends Controller
{
public function actionSort() {
$criteria=new CDbCriteria;
$criteria->order = 'id DESC';
$sort = new CActiveSort('User');
$sort->applyOrderTo($criteria);
$users = User::model()->findAll($criteria);
$this->render('sort', array(
'users' => $users,
'sort' => $sort,
));
}
}
protected/views/index/sort.php:
<table border="1" class="list dataGrid">
<thead>
<tr>
<th width="50"><?php echo $sort->link('id', '#'); ?></th>
<th width="50"><?php echo $sort->link('hash', 'hash'); ?></th>
<th width="50"><?php echo $sort->link('login', 'login'); ?></th>
</tr>
</thead>
<?php
if (!empty($users)) {
foreach ($users as $key => $val) {
?>
<tr>
<td><?=$val->id?>
<td><?=$val->hash?>
<td><?=$val->login?>
<?
}
}
?>
</table>
Ссылки
Если хотите опубликовать этот материал у себя - пожалуйста, разместите ссылку на страницу откуда вы его взяли.
- Я обычно часто просматриваю гугл сообщества в поисках чего-то нового (статьи, вопросы) и сам являюсь активным участником групп по ... "Сообщество Google"
- Эта статья устарела т.к. была написана для yii версии 1.0.х; Если вы используете более новую версию - у вас могут ... "Урок 6 : Регистрация и авторизация. Часть 2"
- Давайте сегодня поговорим о составлении древовидной структуры средствами встроенного в Yii класса CTreeView.
next
Сегодня просматривал официальный форум фреймворка и в одном ... "Учимся создавать красивые деревья! (CTreeView)"

[adm] zolter
Было сказано: Суббота, 21 Август 2010
На днях использовал в своем проекте, не мог не поделится :)

[guest] Гость
Было сказано: Воскресенье, 22 Август 2010
А почему бы не использовать решения на js (sorter.js к примеру). Великолепно сортируются таблицы размером 250х20 полей. А исходную выборку мы можем закэшировать.

[guest] zolter
Было сказано: Воскресенье, 22 Август 2010
Данное решение отлично работает вместе с постраничным листанием данных. В случае js вариантов придется делать выборку сразу всех элементов, кешировать их и только потом с ними работать. А что если это 1 млн записей? :)

[guest] junqed
Было сказано: Воскресенье, 22 Август 2010
Хоть в документации мало примеров, зато есть сам исходник - можно посмотреть как этот класс работает. Я так и сделал :)

[guest] zolter
Было сказано: Воскресенье, 22 Август 2010
Иногда на это нет времени :)
Обычно тоже так и капаю когда есть время пошуршать исходниками

[guest] Гость
Было сказано: Воскресенье, 22 Август 2010
вроде всё сделал правильно но почемуто выдаёт ошибку
Не определено свойство "CActiveSort.modelClass".
стоит Yii 1.1.3

[guest] Гость
Было сказано: Понедельник, 23 Август 2010
сейчас переделал, сделал без CActiveDataProvider ошибка пропала

[guest] Ekstazi
Было сказано: Понедельник, 23 Август 2010
Привет, состыковаться можем ? Спишемся ? Моя аська - 7306857. Есть пару вопросов.


