Работающий экстеншен NestedSet 2

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

Полностью переосмысленная и совместимая с Yii 2 release версия: https://github.com/creocoder/yii2-nested-sets
yujin1st
Сообщения: 192
Зарегистрирован: 2012.03.26, 12:03

Re: Работающий экстеншен NestedSet 2

Сообщение yujin1st »

Плодотворное 1е января! =)
Думаю вам в комментарий нужно написать, чтобы люди добавили строку в composer.lock иначе их ждет веселое обновление...
И пока у вас описания нету - не совсем понятно - схема работы в целом же не поменялась, верно? Только hasManyAttributes ушло в treeAttribute, так ведь?

И вопрос касательно поля parentId: хоть его использование и является отклонением от классических nested sets, но все же это поле удобно использовать в ситуациях ссылки на родительский элемент избегая лишних запросов - вы будете можете сделать его поддержку?
Я для прошлой вашей версии делал свой вариант - https://github.com/yujin1st/yii2-nested-set-behavior, и если вы сами делать не будете - примите pull request?
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

Предыдущая версия расширения была всего лишь прототипом будущего расширения и была написана для Yii 2 beta version. Текущая версия полностью переписана с нуля с учетом всей специфики Yii 2 release version. Схема работы поменялась значительно. Поведение теперь совместимо с любыми другими, например SluggableBehavior. Доработан API, теперь нет методов `move*`. Перемещение узлов теперь происходят теми же методами, что и вставка, что намного логичнее. Также теперь возможно использование стандартных ActiveRecord методов. Т.е. нет больше saveNode() и deleteNode(). Все теперь делается обычными save() и delete(). Сделан отдельный метод makeRoot(). В ближайшее время планируется полная и сильно доработанная документация с хорошими примерами. Что касается parent_id. То естественно, что в поведение этот функционал добавлен не будет. Но планируется отдельное поведение для adjacency list. Таким образом цели можно будет достигнуть просто подключив 2 поведения.
yujin1st
Сообщения: 192
Зарегистрирован: 2012.03.26, 12:03

Re: Работающий экстеншен NestedSet 2

Сообщение yujin1st »

Вижу вы все значительно упростили - это замечательно!
Почему еще спрашиваю про parentId - помимо изменения самого свойства в зависимости от перемещения по дереву возможна же обратная ситуация - перемещение в нужное место в зависимости от свойства. То есть в форме стоит select или autocomplete, выбирается нужный родитель (если поменялся), при валидации проверяется его легитимность, и после совершается перемещение.
Где и каким образом можно это будет реализовать самому, кроме совершения всех операций в контроллере?
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

как по мне там все надо было в скоупы засунуть, ибо ancestor вдруг мне надо с доп условием
yujin1st
Сообщения: 192
Зарегистрирован: 2012.03.26, 12:03

Re: Работающий экстеншен NestedSet 2

Сообщение yujin1st »

lynicidn писал(а):как по мне там все надо было в скоупы засунуть, ибо ancestor вдруг мне надо с доп условием
Так там же и возвращается query - делайте с ним, что хотите.
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

lynicidn писал(а):как по мне там все надо было в скоупы засунуть, ибо ancestor вдруг мне надо с доп условием
Это было сделано ещё несколько лет назад и само собой это есть в complete overhaul версии. Пример:

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

$tree->ancestors()->andWhere(...)->all();
 
yujin1st писал(а): Где и каким образом можно это будет реализовать самому, кроме совершения всех операций в контроллере?
Это достаточно специфическая задача, к поведению имеющая мало отношения. В любом случае конечно же реализовывать нужно всё в рамках толстой модели. Нужно больше деталей чтобы ответить.
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

creocoder писал(а):
lynicidn писал(а):как по мне там все надо было в скоупы засунуть, ибо ancestor вдруг мне надо с доп условием
Это было сделано ещё несколько лет назад и само собой это есть в complete overhaul версии. Пример:

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

$tree->ancestors()->andWhere(...)->all();
yujin1st писал(а): Где и каким образом можно это будет реализовать самому, кроме совершения всех операций в контроллере?
Это достаточно специфическая задача, к поведению имеющая мало отношения. В любом случае конечно же реализовывать нужно всё в рамках толстой модели. Нужно больше деталей чтобы ответить.
ну вы же согласны что выбор предков или дочерних это скоуп а не квери
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

lynicidn писал(а):это скоуп а не квери
http://www.yiiframework.com/doc-2.0/gui ... tml#scopes
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

creocoder писал(а):
lynicidn писал(а):это скоуп а не квери
http://www.yiiframework.com/doc-2.0/gui ... tml#scopes
с юморком :)
я про то что мне надо выбрать всех у кого предок есть содержащий в имени 'asdsad' и дочек у которых статус ENABLED

вы предлагаете вместо скоупов создать 2 квери и смержить?
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

Model::acestors()->orWhere(Model::participant())
п.с. методы вымышлены, но суть думаю ясна
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

я понимаю если бы была нужда в перекрытии этого метода, но у вас все статические методы возвращают 1 объект квери, не вижу смысла, повторюсь опять это мое имхо, дело ваше как делать - вы автор расширения
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

lynicidn писал(а): я про то что мне надо выбрать всех у кого предок есть содержащий в имени 'asdsad' и дочек у которых статус ENABLED
Да, это была бы интересная возможность, будем реализовывать.
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

lynicidn писал(а):я понимаю если бы была нужда в перекрытии этого метода, но у вас все статические методы возвращают 1 объект квери, не вижу смысла, повторюсь опять это мое имхо, дело ваше как делать - вы автор расширения
В поведении нет статических методов. Я думаю что эта возможность будет выглядеть как:

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

Tree::find()->ancestors()->...->all();
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Работающий экстеншен NestedSet 2

Сообщение lynicidn »

creocoder писал(а):
lynicidn писал(а):я понимаю если бы была нужда в перекрытии этого метода, но у вас все статические методы возвращают 1 объект квери, не вижу смысла, повторюсь опять это мое имхо, дело ваше как делать - вы автор расширения
В поведении нет статических методов. Я думаю что эта возможность будет выглядеть как:

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

Tree::find()->ancestors()->...->all();
да, соррри со статическими не досмотрел :roll: я вел лишь к гибкости, чтобы можно было объединять условия(скоупы)
johnluxor
Сообщения: 82
Зарегистрирован: 2010.09.10, 19:39

Re: Работающий экстеншен NestedSet 2

Сообщение johnluxor »

Не посмотрел, что расширение кардинально изменилось и обновил через композер. Подскажите коммит, на который можно вернуться, чтобы был старый код?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Работающий экстеншен NestedSet 2

Сообщение zelenin »

johnluxor писал(а):Не посмотрел, что расширение кардинально изменилось и обновил через композер. Подскажите коммит, на который можно вернуться, чтобы был старый код?
https://github.com/creocoder/yii2-neste ... its/master
yujin1st
Сообщения: 192
Зарегистрирован: 2012.03.26, 12:03

Re: Работающий экстеншен NestedSet 2

Сообщение yujin1st »

creocoder писал(а):
yujin1st писал(а): Почему еще спрашиваю про parentId - помимо изменения самого свойства в зависимости от перемещения по дереву возможна же обратная ситуация - перемещение в нужное место в зависимости от свойства. То есть в форме стоит select или autocomplete, выбирается нужный родитель (если поменялся), при валидации проверяется его легитимность, и после совершается перемещение.
Где и каким образом можно это будет реализовать самому, кроме совершения всех операций в контроллере?

yujin1st писал(а): Где и каким образом можно это будет реализовать самому, кроме совершения всех операций в контроллере?
Это достаточно специфическая задача, к поведению имеющая мало отношения. В любом случае конечно же реализовывать нужно всё в рамках толстой модели. Нужно больше деталей чтобы ответить.
Все же parentId относиться именно к самому дереву, а мы его и строим именно с вашим расширением. И задача простая - перемещение записи в нужное положение в случае изменения id родителя, с сопутствующей валидацией.

Да, возможна ситуация, когда есть, только добавление элементов к уже существующим (node/add-child?id=1) и отдельные действия для перемещения - может быть тот же fancyTree - а тот при drag'n'drop'e будет отправлять запросы на, скажем, node/move-to?id=1&targetId=2 и там будет разумно делать все именно в контролере, но это же неполное решение проблемы... К тому же, тот же d'n'd не удобен при большом дереве.

Что делать в ситуации если поле выбора родителя присутствует в форме редактирования элемента? Это означает, что в форме обязательно будет выбор родительского элемента (dropdown, autocomplete или прямой ввод id) - и именно здесь появляется задача.

Ясно, что после отправки формы нужно проверить существование родителя (и может еще дополнительные проверки на возможность перемещения в соответствии с логикой приложения) и непосредственно переместить/сохранить элемент. И если проверки можно сделать в правилах самой модели, то как быть именно с сохранением - где его размещать?
В контролере неверно, поскольку сохранение много где будет использоваться. Создать свой метод который и будет определять всю логику? Но и тут подводные камни...

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

public function mySave($runValidation = true) {
    $oldParentId = ???; //  где его взять? $changedAttributes, доступны только в afterSave, $this->oldAttributes  устанавливается только updateInternal, а значит тоже не доступен.
    if ($this->parentId == $oldParentId) {
      return $this->save($runValidation);
    } else if (!$oldParentId) {
      return $this->makeRoot($runValidation);
    } else {
      $target = self::findOne($this->parentId);
      return $this->appendTo($target, $runValidation);
    }
  }
Да можно сделать отдельное действие (node/move?id=1), где будет форма с одним полем - выбор родителя и для этой формы будет использоваться именно этот метод, но это лишнее действие, и к тому же опять идет ограничение, что перемещаться можно только в одном месте в приложении.
Я не забываю и про то, что первичный ключ может быть составным, но с другой стороны насколько часто это может быть, а даже если и так, то это вполне решаемо.

------------

И подскажите, как теперь сделать безопасное удаление (поле deleted = 1)? Я обычно заменяю deleteInternal / delete (для элементов дерева) на код ниже, тем самым остаются и события и проверки до и после операции. Проблема в том, как отключить afterSafe вашего поведения (переиндексацию элементов)? Пометку "удалено" дочерних элементов можно легко установить в afterSave самой модели, поэтому ее не касаюсь.

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

public function delete($fromDb = false) {
    if ($fromDb) return parent::delete();
    if ($this->beforeDelete()) {
      $this->deleted = 1;
      $this->saveNode(false, ['deleted']); это для старого варианта nestedSets, обычно  просто $this->save();
      $this->afterDelete();
      return 1;
    }
    return false;
  } 
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: Работающий экстеншен NestedSet 2

Сообщение creocoder »

yujin1st писал(а):Что делать в ситуации если поле выбора родителя присутствует в форме редактирования элемента? Это означает, что в форме обязательно будет выбор родительского элемента (dropdown, autocomplete или прямой ввод id) - и именно здесь появляется задача.
Как я и сказал, контроллеры должны быть тонкими. В данном случае существует ровно 2 правильных решения этой задачи с точки зрения проектирования в Yii. Это отдельная модель формы для перемещения или основная модель + сценарий. Заводится атрибут target_id, для которого описываются правила валидации, в данном случае проверка существования target. Контроллер при обоих ситуациях тонкий. Вариант с моделью формы:

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

public function actionMove($id)
{
    $model = new TreeMoveForm(['source_id' => $id]);
    
    if ($model->load(Yii::$app->requrest->post()) && $model->move()) {
        ...
    } else {
        ...
    }
}
 
Вариант основная модель + сценарий:

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

public function actionMove($id)
{
    $model = $this->loadModel($id);
    $model->scenario = Tree::SCENARIO_MOVE;
    
    if ($model->load(Yii::$app->requrest->post()) && $model->save()) {
        ...
    } else {
        ...
    }
}
 
В обоих случаях target_id передается строго посредством POST.
yujin1st писал(а):И подскажите, как теперь сделать безопасное удаление (поле deleted = 1)? Я обычно заменяю deleteInternal / delete (для элементов дерева) на код ниже, тем самым остаются и события и проверки до и после операции. Проблема в том, как отключить afterSafe вашего поведения (переиндексацию элементов)? Пометку "удалено" дочерних элементов можно легко установить в afterSave самой модели, поэтому ее не касаюсь.
Безопасное удаление как в случае данного поведения, так и без него правильно делать через подавление самого удаления на уровне beforeDelete() который должен возвращать false.
Ответить