Сделал в первую очередь для себя, но получилось достаточно универсально, многоуровневый обработчик массивов для вывода многоуровневого меню/категорий/и всего к чему душа лежит.
Класс достаточно прост, достаточно много комментариев написал. Суть в том что мы имеем в базе (например будем использовать многоуровневое меню с таблицей menu) поля под названием parent_id - указатель на родительский пункт меню, и level- текущий уровень, где находится непосредственно текущий пункт меню.
Название этому алгоритму - Adjacency List (http://doc.prototypes.ru/database/trees ... heory/use/)
Структура таблицы выглядит у меня примерно так:
Код: Выделить всё
CREATE TABLE IF NOT EXISTS `menu` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`parent_id` tinyint(3) NOT NULL,
`level` tinyint(3) NOT NULL,
`position` int(3) NOT NULL,
`published` tinyint(1) NOT NULL,
`title` text NOT NULL,
`alias` text NOT NULL,
`default` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0;
Код: Выделить всё
private static function iterateForMenu();
найти генерацию УРЛов через
Код: Выделить всё
Yii::app()->createUrl('Controller/action')
Многоуровень мы будем рендерить через виджет CMenu.
Вызываем виджет и кладем в него наши массивы:
Код: Выделить всё
<?php
$this->widget('zii.widgets.CMenu',array(
'activateItems'=>true,
'activeCssClass'=>'active',
'items'=>ESIterator::getForMenu('Menu')
));
Код: Выделить всё
CMenu::$items
Код: Выделить всё
ESIterator::getForMenu('Menu')
Чтобы решить проблему как установить соответствующий уровень у пункта меню, имеется так же метод
Код: Выделить всё
ESIterator::setNewLevel(CActiveRecord $this)
Поместить его надо в класс модели например в CActiveRecord::beforeSave()
Код: Выделить всё
public function beforeSave()
{
$this->level = ESIterator::setNewLevel($this);
return parent::beforeSave();
}
Далее...
Чтобы в той же админке, в том же
Код: Выделить всё
CHtml::dropDownList()
| Главная
| -- Под главная
| -- -- Под под главная
| -- -- Под под главная 2
Можем сделать так:
Код: Выделить всё
echo CHtml::dropDownList($model,'parent_id',ESIterator::getLevels('Menu'),array('empty'=>'Корневой пункт'));
Код: Выделить всё
ESIterator::getLevels('Menu')
Дабы установить верного родителя пункта меню если мы НЕ выбрали какой-то пункт (этому поспособствовал массив параметров в dropDownList, с первым пукнтом
array('empty'=>'Корневой пункт меню')), мы в CActiveRecord::beforeSave() внедрим:
Код: Выделить всё
if(empty($this->parent_id)) $this->parent_id = 0;
Вот так вот все просто.
Если вы хотите видеть такой же многоуровень например в админке и если вы используете для вывода данных CGridView, то для него можно подготовить провайдер данных:
Код: Выделить всё
CArrayDataProvider(array $rawData)
Код: Выделить всё
ESIterator::getForDataProvider( string $className )
где вновь указываем имя AR класса модели.
Вот так.
Summary:
Мы имеем 4 метода:
выводим меню в CMenu
public static getForMenu(string $className);
выводим список пунктов в CHtml::dropDownList()
public static getLevels(string $className);
устанавливаем новый (соответствующий) уровень вложенности
public static setNewLevel(ActiveRecord $this);
выводим данные через CArrayDataProvider(array $rawData)
public static getForDataProvider(string $className);
и 2 публичных статических свойства
какая-то загогулина для первого уровня (нулевого быть точнее)
public static $startLine
какая-то загогулина для всех остальных уровней
public static $continousLine
Еще раз повторюсь, что есть что доделывать (улучшать), на моем проекте все работает, выложил потому что может кому пригодится.
Обо всех багах писать мне, буду дебагать. (плюс надеюсь к выходным дописать и проделать тесты)
Собственно сама ссылка на GIT - https://github.com/edwardstock/ESIterator
Cheers