Многоуровневое меню/категории

Выкладываем свои наработки
Ответить
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Многоуровневое меню/категории

Сообщение edwardstock »

Всем привет! Я на этом форуме первый раз, но Yii использую уже более полу года. <лирика окончена/>

Сделал в первую очередь для себя, но получилось достаточно универсально, многоуровневый обработчик массивов для вывода многоуровневого меню/категорий/и всего к чему душа лежит.

Класс достаточно прост, достаточно много комментариев написал. Суть в том что мы имеем в базе (например будем использовать многоуровневое меню с таблицей 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')
, Где единственный аргумент - это имя ActiveRecord класса модели пунктов меню (string $className).



Чтобы решить проблему как установить соответствующий уровень у пункта меню, имеется так же метод

Код: Выделить всё

 ESIterator::setNewLevel(CActiveRecord $this) 
.
Поместить его надо в класс модели например в CActiveRecord::beforeSave()

Код: Выделить всё

public function beforeSave()
{
    $this->level = ESIterator::setNewLevel($this);        

    return parent::beforeSave();
}
 
Тут мы устанавливаем полю level соответствующий уровень.

Далее...


Чтобы в той же админке, в том же

Код: Выделить всё

CHtml::dropDownList() 
вывести возможные родительские пункты меню с уже отсортированными уровнями и родителями, например так:

| Главная
| -- Под главная
| -- -- Под под главная
| -- -- Под под главная 2

Можем сделать так:

Код: Выделить всё

echo CHtml::dropDownList($model,'parent_id',ESIterator::getLevels('Menu'),array('empty'=>'Корневой пункт'));
 
Как видно из листинга, мы в качестве аргемента, где должны придти данные в виде одномерного массив ключ=>значение, мы указали

Код: Выделить всё

ESIterator::getLevels('Menu') 
где аргументом опять таки является имя AR класса, а метод возвращает пару $id (идентификатор пункта меню)=>$title - название пункта меню (свойство CActiveRecord::$title);

Дабы установить верного родителя пункта меню если мы НЕ выбрали какой-то пункт (этому поспособствовал массив параметров в 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 :)
Последний раз редактировалось edwardstock 2013.04.23, 11:56, всего редактировалось 2 раза.
Аватара пользователя
S c
Сообщения: 883
Зарегистрирован: 2012.04.11, 14:46

Re: Многоуровневое меню/категории

Сообщение S c »

1) будет ли реализовано управление пунктами (хотя б поменять местами?)

разумнее для такого б использовать nestes sets.
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Re: Многоуровневое меню/категории

Сообщение edwardstock »

S c писал(а):1) будет ли реализовано управление пунктами (хотя б поменять местами?)

разумнее для такого б использовать nestes sets.
Управление позициями будет реализовано думаю в мае, т.к. сейчас завал проектами.

Про вложения таким образом не думал, а в чем их преимущества перед adjacency list?
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Многоуровневое меню/категории

Сообщение rak »

edwardstock писал(а): Про вложения таким образом не думал, а в чем их преимущества перед adjacency list?
http://www.slideshare.net/billkarwin/sq ... ke-back/48
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Re: Многоуровневое меню/категории

Сообщение edwardstock »

rak писал(а):
edwardstock писал(а): Про вложения таким образом не думал, а в чем их преимущества перед adjacency list?
http://www.slideshare.net/billkarwin/sq ... ke-back/48
все понял) спасибо за справку, обязательно займусь переработкой
Аватара пользователя
S c
Сообщения: 883
Зарегистрирован: 2012.04.11, 14:46

Re: Многоуровневое меню/категории

Сообщение S c »

edwardstock писал(а):
S c писал(а):1) будет ли реализовано управление пунктами (хотя б поменять местами?)

разумнее для такого б использовать nestes sets.
Управление позициями будет реализовано думаю в мае, т.к. сейчас завал проектами.

Про вложения таким образом не думал, а в чем их преимущества перед adjacency list?
без nested sets не сможете менять местами 2 категории (у которых один и тот же parend_id)
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Многоуровневое меню/категории

Сообщение rak »

S c писал(а): без nested sets не сможете менять местами 2 категории (у которых один и тот же parend_id)
и с чего бы это?
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Re: Многоуровневое меню/категории

Сообщение edwardstock »

Я сортирую узлы при помощи поля position..
Правда так больше пост обработки получается
Аватара пользователя
S c
Сообщения: 883
Зарегистрирован: 2012.04.11, 14:46

Re: Многоуровневое меню/категории

Сообщение S c »

велосипеды изобретать. добавляя position - вы уже сами того не желая потихоньку приближаетесь к вложенным множествам. Все равно что всему дереву lft rgt пересчитывать, что position
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Re: Многоуровневое меню/категории

Сообщение edwardstock »

S c писал(а):велосипеды изобретать. добавляя position - вы уже сами того не желая потихоньку приближаетесь к вложенным множествам. Все равно что всему дереву lft rgt пересчитывать, что position
ну я и не спорю) мне нравится идея nested sets, и я реализую её
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Многоуровневое меню/категории

Сообщение rak »

edwardstock писал(а): мне нравится идея nested sets, и я реализую её
уже есть
http://yiiext.github.io/extensions/nest ... index.html
Аватара пользователя
edwardstock
Сообщения: 16
Зарегистрирован: 2013.04.08, 19:14
Откуда: Москва

Re: Многоуровневое меню/категории

Сообщение edwardstock »

rak писал(а):
edwardstock писал(а): мне нравится идея nested sets, и я реализую её
уже есть
http://yiiext.github.io/extensions/nest ... index.html
тем лучше)
Аватара пользователя
S c
Сообщения: 883
Зарегистрирован: 2012.04.11, 14:46

Re: Многоуровневое меню/категории

Сообщение S c »

это направление можно развивать и развивать.
к тому расширение есть "дополнение" - viewtopic.php?f=9&t=2770. Но в нем достаточно недочетов (в том числе js)

может кто решиться сделать что то универсальное и допилинное
Ответить