вторник, 24 июля 2012 г.

Yii и дерево

Последнее время активно изучаю Yii Framework, которым весьма доволен. Однако, как всегда это бывает, чего-нибудь да не хватает в готовом решении. В моем случае, возникла потребность в оперировании иерархически упорядоченными данными, иначе - деревом. В Yii есть дерево, но мне оно по некоторым причинам не подошло и пришлось сделать свое.

Критерии:
  1. Источник данных - база данных mysql.
  2. Данные должны располагаться в одной таблице (можно и больше, но мне и одной хватило).
  3. Иерархичность должна обеспечиваться одним полем записи - ParentID (в моем случае - pid).
  4. Таблица данных должна формироваться на стороне mysql (хранимая процедура).
  5. Результат должен быть представлен в виде дерева похожего на "аккордеон".
  6. У каждой записи должен быть доступен набор команд.

Внешний вид, который планировалось получить и который получился в итоге:
Widget получил название  AlxdDataTree. Позволяет отобразить упорядоченный иерархический набор данных в виде раскрываемого как "аккордеон" дерева.
Входные данные для AlxdDataTree:
  1. Данные должны передаваться в AlxdDataTree в виде массива записей упорядоченных так, как они будут отображаться в дереве.
  2. Обязательно должны присутствовать поля: id - идентификатор; title - наименование, которое будет отображаться в дереве; level - уровень записи в дереве; count - количество записей в родителе.
  3. Перечень доступных команд для записей должен описываться в виде массива.
В путь...
Прежде всего, мне потребовалось создать таблицу и написать хранимую процедуру для получения данных, которые будут переданы контроллеру, а позже -  AlxdDataTree. 
Таблица получила название classificator. Вот ее перечень полей:

Описание полей:
id - уникальный идентификатор каждой записи
pid - идентификатор родителя
oid - идентификатор владельца классификатора
code - системное имя классификатора
name - наименование классификатора
createtime - дата создания записи
modifytime - дата изменения записи
Пример хранимых данных:

Следующим шагом стало написание хранимой процедуры. Мне понадобилось две хранимые процедуры. Первая используется рекурсией, вторая вызывается из контроллера и возвращает конечный результат.
Первая процедура получает на входе id родителя и его текущий level. Вторая процедура получает на входе oid владельца классификатора. Если владелец как таковой не нужен, можно удалить соотв. поле oid из таблицы и убрать входной параметр oid. Разумеется, придется поправить и сами процедуры.
Результат выполнения процедуры Call getClassificators(1):

Затем создал модель classificator с помощью Gii и контроллер с CRUD операциями. В коде действия Admin добавил следующее:
$classificators = Yii::app()->db->createCommand('call getClassificators('.$oid.');')->queryAll();

$this->render('admin',array(
'classificators'=>$classificators,
));
где, $oid - идентификатор владельца классификатора.

Как следствие, надо пришлось поправить и файл вида admin.php. Удалил в нем widget создающий CGridView. Добавил следующее:
$this->widget('ext.AlxdDataTree.AlxdDataTree', array(
'data'=>$classificators,
'idField'=>'id',
'titleField'=>'name',
'levelField'=>'lvl',
'countField'=>'cnt',
)
);

Как видно, AlxdDataTree расположен в каталоге extension в папке AlxdDataTree. Вот так:

Результат работы приложения:


Вот и все!

А вот и AlxdDataTree со всеми стилями, скриптами, картинками и sql-запросами. Никакой регистрации приложения в Yii не требуется, достаточно просто распаковать архив в папке extension. Файлы sql исключительно для примера и на работу самого widget'а не влияют. Каждый может написать свою процедуру для получения конечной таблицы данных.