Плотно работаю с ORM битрикса, все замечательно, но вот понадобилось работать с иерархическими структурами. Единственным методом работы с деревьями являлся старый добрый класс CIBlockSection, но он меня не устраивает по многим причинам:
Все деревья находятся в одной таблице
Деревья привязаны к инфоблокам
Собственные поля можно добавить только через UF_
Ну и т.д.
Решение вроде бы лежит на поверхности. Решено было создать свой класс - потомок от Bitrix\Main\Entity\DataManager и реализовать в нем логику работы с деревьями из CIBlockSection. Способы реализации виделись следующие:
Полностью переписать методы add, update, delete. Сей способ показался мне довольно геморройным. Неизвестно, что изменится в родителе при очередном обновлении и не будет ли из-за этого глючить потомок
Реализовать логику в обработчиках событий onBefore… onAfter… Но тогда пришлось бы жестко контролировать, чтобы в обработчиках событий потомков нашего класса вызывался parent::
Вызывать собственные обработчики событий, а после из них вызывать стандартные методы onBefore… onAfter… Не вышло, т.к. константы с именами обработчиков в DataManager вызываются как self::, а не static::, поэтому переписывать их в наследнике бесполезно
Регистрировать дополнительные обработчики событий, отрабатывающие независимо от того, используется обработка событий в потомках или нет. Вот на этом способе я и остановился
От выбора типа зависит порядок выполнения обработчиков. Собственные методы onBefore… onAfter… реагируют на modern, а classic посылается раньше. Я выбрал classic, поэтому сначала выполнится мой обработчик, а затем собственный, унаследованный от DataManager
Чтобы наш обработчик не регистрировался повторно, добавим свойство класса
protected static $eventHandlers = array();
и выставим флаг
static::$eventHandlers[$eventType]=true;
Теперь переписываем методы add, update, delete следующим образом:
public static function add(array $data)
{
static::handleEvent(self::EVENT_ON_BEFORE_ADD, 'treeOnBeforeAdd');
static::handleEvent(self::EVENT_ON_AFTER_ADD, 'treeOnAfterAdd');
return parent::add($data);
}
public static function upd ate($primary, array $data)
{
static::handleEvent(self::EVENT_ON_BEFORE_UPDATE, 'treeOnBeforeUpdate');
static::handleEvent(self::EVENT_ON_AFTER_UPDATE, 'treeOnAfterUpdate');
return parent::update($primary, $data);
}
public static function delete($primary)
{
static::handleEvent(self::EVENT_ON_DELETE, 'treeOnDelete');
return parent::delete($primary);
}
Далее пишем наши обработчики, в которых уже и реализуем логику работы с деревом
public static function treeOnBeforeAdd(Entity\Event $event){}
public static function treeOnAfterAdd(Entity\Event $event){}
public static function treeOnBeforeUpdate(Entity\Event $event){}
public static function treeOnAfterUpdate(Entity\Event $event){}
public static function treeOnDelete(Entity\Event $event){}
Примеры работы
Пример ORM-класса находится в файле lib/nstest.php все поля, кроме NAME являются обязательными
Во избежание разрушения структуры дерева при совместном доступе желательно блокировать таблицу на запись и откатывать изменения при возникновении ошибок. Для этого создаем методы lockTable() и unlockTable(). В mySQL нельзя использовать одновременно LOCK TABLE и START TRANSACTION (https://dev.mysql.com/doc/refman/5.7/e...tions.html) поэтому метод lockTable() выполняет:
SET AUTOCOMMIT = 0;
LOCK TABLES…
А unlockTable()
UNLOCK TABLES
SE T AUTOCOMMIT = 1
Алгоритм работы с NSDataManager при использовании транзакций примерно такой:
Короче через сутки 58000 добавлено. глубина вложенности 27. Дальше обрываю, поскольку надо работать с мускулем. Параметры php 7, mysql 5.7, Windows 10, включенный Каспер, за весь эксперимент php не превысил 9.7 Мб Вся нагрузка легла на mysql
kopoBko, для скорости вставки и изменения надо смотреть в сторону closure tables. Но в них свой геморрой: квадратичный рост количества записей, проблема с выводом отсортированного дерева. В моих задачах от силы пару тысяч записей будет и глубина не более 3. Зато скорость получения нужной ветки или проверка на вхождение в ветку замечательная.
Есть еще технология nested intervals, но там усложнена реализация переноса ветки, зато повышается скорость. Но опять же, поскольку скорость достигается за счет выделения заранее смежных интервалов, то все равно может возникнуть ситуация, когда необходимого интервала не будет и придется апдейтить всю таблицу
Группы на сайте создаются не только сотрудниками «1С-Битрикс», но и партнерами компании. Поэтому мнения участников групп могут не совпадать с позицией компании «1С-Битрикс».