Для проекта выбрал модуль https://github.com/evopix/orm-mptt
Описанный в readme функционал – работает без проблем, а вот с остальным пришлось помучиться.
Но обо всем – по порядку.
Многие проекты имеют сложную структуру категорий товаров либо информации.
Часто требуется не фиксированная глубина вложенности.
Для этих целей, как нельзя лучше, подойдет модель хранения данных Nested set и ее реализация посредством модуля для Kohana MPTT.
Более детально о nested set
Есть несколько различных реализаций, но я остановился на модуле от evopix.
Ниже приведу описание основных методов модуля.
Нам потребуется таблица БД, вот ее упрощенная структура:

  • id – уникальный идентификатор-счетчик
  • parent_id – родительский id (для рутов – значение = 0)
  • lvl – уровень (для рутовых категорий он равен 1)
  • lft
  • rgt
  • scope – № ветви
  • name – наименование категории
  • url – url категории

Модель наследуем от ORM_MPTT

 class Model_Category extends ORM_MPTT { }

Создание корневого узла:

$cat = ORM::factory('category');
$cat->name = 'Каталог';
$cat->insert_as_new_root();

Создание последнего дочернего узла:

$parent_cat = ORM::factory('category')->where('name', '=', 'Каталог')->find();
$cat->name = 'Мобильные телефоны';
$cat->insert_as_last_child($parent_cat);
$cat->name = 'Планшеты';
$cat->insert_as_last_child($parent_cat);
$cat->name = 'Аксессуары для планшетов';
$cat->insert_as_last_child($parent_cat);
$cat->name = 'Литература о планшетах';
$cat->insert_as_last_child($parent_cat);

По аналогии создается дочерний узел, но вставляется перед всеми существующими:

$parent_cat = ORM::factory('category')->where('name', '=', 'Каталог')->find();

$cat->name = 'Бытовая техника';

$cat->insert_as_first_child($parent_cat);

Создание братского узла (узла того же уровня вложенности) перед указанным узлом:

$sibling_cat = ORM::factory('category')->where('name', '=', 'Планшеты')->find();

$cat->name = 'Комплектующие';

$cat->insert_as_prev_sibling($sibling_cat);

Создание братского узла (узла того же уровня вложенности) после указанного узла:

$sibling_cat = ORM::factory('category')->where('name', '=', 'Планшеты')->find();
$cat->name = 'Ноутбуки';
$cat->insert_as_next_sibling($sibling_cat);

Перемещение произвольного узла с подузлами в выбранный узел (в нашем случае перемещаем категорию "Аксессуары для планшетов" в категорию "Планшеты" перед всеми имеющимися узлами):

$source_cat = ORM::factory('category')
->where('name', '=', 'Аксессуары для планшетов')->find();

$destination_cat = ORM::factory('category')
->where('name', '=', 'Планшеты')->find();

$cat->move_to_first_child($destination_cat); 
//Перемещаем на место первого дочернего узла

То же самое, но теперь вставляем категорию после всех имеющихся узлов:

$source_cat = ORM::factory('category')
->where('name', '=', 'Литература о планшетах')->find();

$destination_cat = ORM::factory('category')
->where('name', '=', 'Планшеты')->find();

$cat->move_to_last_child($destination_cat); 
//Вставляем последним дочерним узлом

По аналогии возможны перемещения братских узлов:

$sibling_cat = ORM::factory('category')
->where('name', '=', 'Мобильные телефоны')->find();

$cat = ORM::factory('category')
->where('name', '=', 'Планшеты')->find();

$cat->move_to_prev_sibling($sibling_cat); 
//Перемещаем на позицию перед братским узлом
$sibling_cat = ORM::factory('category')
->where('name', '=', 'Мобильные телефоны')->find();

$cat = ORM::factory('category')
->where('name', '=', 'Планшеты')->find();

$cat->move_to_next_sibling($sibling_cat);
//Перемещаем на позицию после братского узла

В этом месте лично меня ожидал неприятный сюрприз.
Никаким образом категории не желали перемещаться.
Для решения данной проблемы необходимо изменить код в модуле modules\orm-mptt\classes\kohana\orm\mptt.php в методе lock(), на следующий:

protected function lock()
{
    $q = 'LOCK TABLE '.$this->_db->quote_table($this->_table_name).' WRITE';

    if ($this->_object_name)
    {
        $q.= ', '.$this->_db->quote_table($this->_table_name);
        $q.= ' AS '.$this->_db->quote_column($this->_object_name).' WRITE';
    }

    $this->_db->query(NULL, $q, TRUE);
}

Перемещение узлов без ошибок возможно только в пределах одного корневого узла, перемещение узлов между деревьями необходимо реализовывать путем удаления узла из одного дерева и создания нового узла в другом дереве.

Некоторые задачи для сайта необходимо выполнять при помощи планировщика crontab
Например, рассылки писем, формирование и отправка отчетов администраторам и т.д.
В этом случае нам необходимо запускать наши скрипты из командной строки.
Для этого подготовим контроллер, например, stat.php,
разместим его в /kohana/application/classes/controller/
Continue reading