Архитектура модели. ActiveRecord, обертка и их взаимодействие.

WMix

герр M:)ller
Партнер клуба
Метод abstractItem::delete() просто удаляет запись из таблицы;
это и ожидается, для каждой выбранной ветки

Код:
abstract class abstractCollection{}
class Collection extends abstractCollection {}
хочется предположить что "это представление некоего множества записей"
PHP:
class NodeExt  /*extends abstractCollection*/ {}
и в нем не хватает метода getTree($root_id) и deleteTree($root_id)
которые итерируют наборы и вызывают к примеру delete для каждой записи

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

riff

Новичок
Cellard, в твоих вопросах так много умных слов, что я на всякий случай не влезал.
Я и сейчас не влезаю, просто для себя понять, правильно ли я понял:
Класс1 - удаляет строку из таблицы
Класс2 extended Класс1 - удаляет целую ветку
Если программист1 унаследует Класс2, то всё в порядке,
а если Класс1, то проблема.

Ответь просто "да" или "нет" (правильно ли я понял или нет).
 

yanis

Новичок
PHP:
А как архитектура заставит программиста переопределить метод delete_item?
abstract
 

Cellard

Новичок
Давайте сделаем эти методы. Не вопрос. Но как сделать так, чтобы они автоматически вызывались, когда я вызываю delete() на Item с интерфейсом iNode?
 

yanis

Новичок
Давайте сделаем эти методы. Не вопрос. Но как сделать так, чтобы они автоматически вызывались, когда я вызываю delete() на Item с интерфейсом iNode?
Не то?

PHP:
abstract class Foo {
 
    final public function delete()
    {
        $this->beforeDelete();     
        $this->deleteItem();
        $this->afterDelete();
    }
 
    abstract protected function deleteItem();   
}

class MyFoo extends Foo {
 
    protected function deleteItem()
    {
        //
    }
}
 
Последнее редактирование:
  • Like
Реакции: AmdY

Cellard

Новичок
yanis, вы забываете о том, что Item должен быть работоспособен и без каких либо дополнительных интерфейсов.

Пусть есть User, и есть Category. Оба они наследуют вашему Foo, в котором реализованы методы работы с БД.

И оба вынуждены реализовать метод deleteItem, с пустым телом.

В один прекрасный день мы повесим на Category интерфейс iNode (навесим на него обертку для работы с деревом). И все. Архитектура уже не напомнит мне переопределить deleteItem.

Так мы ничего не добъемся :(
 

fixxxer

К.О.
Партнер клуба
либо наследование, либо евенты, других способов, не нарушающих LSP, нет
 

fixxxer

К.О.
Партнер клуба
Item должен быть работоспособен и без каких либо дополнительных интерфейсов.
Он никому ничего не должен. Не надо пытаться сделать один класс на все случаи жизни. Это ни к чему хорошему никогда не приводило. Не нравится наследование - евенты, yii-like поведения через __call, трейты и так далее - но в этом случае неизбежно появляется ItemFactory<type>
 

riff

Новичок
(пока я думал над ответом fixxxer уже всё написал)

Автор вопроса ответил в личку:
Есть класс Item, он умеет удалять строку из таблицы
Есть класс Node, он содержит алгоритм удаления узла дерева из таблицы

Если мы вызовем Item::delete(), то будет удалена одна запись.
Если мы в конструктор Node передадим Item (который будет implements iNode), и вызовем Node::delete(), то Node, эксплуатируя возможности Item, сможет удалить узел дерева согласно своему алгоритму.

Задача: сделать так, чтобы Item::delete() сам вызывал Node::delete(), если Item implements iNode
По-моему, тут "горе от ума".
а. Сделал базовый класс, который мало что умеет.
б. Сделал "нормальный" класс, который наследуется от базового и умеет уже много.
Ну и хорош, сказал всем используйте его для работы, а кто возьмёт базовый, тот или знает что делает или сам дурак.
 

WMix

герр M:)ller
Партнер клуба
Если правильно понял
он правильно думает, если Node это AR что мешает программисту вызвать метод $node->delete(); и тут наступила Ж. тк. нужно было вызвать $tree->delete().
но тут нужно думать проще и добавить метод $node->deleteNode(); там написать чтото типа getService('Tree')->delete($this->id)
те. не путай delete от AR с delete от твоей логики
 

Cellard

Новичок
fixxxer
Не надо пытаться сделать один класс на все случаи жизни
Так я как раз и не пытаюсь. Базовый класс без проблем решает 90% задач, возникающих на практике. А на разные случаи жизни придумываются различные обертки вокруг базового класса, которые потом успешно используются впоследствии на других проектах.

За events спасибо. Это работает, я уже проверил :)

riff, в. Сделал «продвинутый» класс, который умеет еще больше.
г. Сделал «чудесный» класс, который умеет еще больше, и еще сверху.
д. .....

Фотошоп выпускается в одной версии, с сотней плагинов, а не в тысяче версий, с разным набором предустановленных и скомпилированных плагинов. Так и тут. Неправильно, что класс будет уметь вообще все, что ему нужно и не нужно. Ну некошерно как-то...

WMix. Примерно так сейчас все и работает. Перед тем как вызвать Item::delete() программист должен вызвать все необходимые методы. А если забыл, то на диске остаются не удаленные файлы (такая обертка у нас тоже есть) :)
А дерево... У MP-дерева удалил узел, а InnoDB таблица каскадно удалит потомков, и знать-не-знаю ни про какие индексы... Только с NS-деревьями такая засада, неправильно удалил, индекс не перестроил — убил дерево. Вот я и задумался, как застраховаться от таких фатальных «ой, я забыл метод вызвать».
 

WMix

герр M:)ller
Партнер клуба
100% все одно не защитишся, но можно возвращать НЕ AR а заглушку с внутренней ссылкой
PHP:
class Tree{

   function getNode( $id ){
     return new Node( $this, new Entity($id) );
   }
   function delete( $id ){
     // тут сам!
   }
}

class Node{

    private $tree; // обьект для работы с деревьями
    private $item; // АР

    public function __call($name, $arguments) {
        if(method_exists( $item , $name)){
            return call_user_func_array(array($item, $name), $arguments);
        }
        throw ...
    }

    public function delete(){
        $this->tree->delete($this->item->getId());
    }
}
$node = $tree->getNode(45);
$node->delete();
Примерно так сейчас все и работает. Перед тем как вызвать Item::delete() программист должен вызвать все необходимые методы.
я такое не советовал
 
Последнее редактирование:

fixxxer

К.О.
Партнер клуба
ну вообще напрашивается strategy pattern ;) но это вариация на тему евентов по сути
 

WMix

герр M:)ller
Партнер клуба
а все одно нужно заглушку делать хоть с евентами хоть без.
event только отвяжет AR класс от сервиса
 

fixxxer

К.О.
Партнер клуба
мы тут все забыли, что это все должно быть в одной транзакции;)
 

WMix

герр M:)ller
Партнер клуба
в Tree::delete($id) первой строчкой можно написать begin();
fixxxer, а кстати как ты придумал стратегией решить?
 

Welcome

Новичок
Защиты от дураков нет, дураки очень изобретательны.
Вот весь смысл топика.
 
  • Like
Реакции: AmdY

fixxxer

К.О.
Партнер клуба
fixxxer, а кстати как ты придумал стратегией решить?
делегированием. то есть делаем не совсем AR, а разбиваем на два класса - модель и персистилка, персистилка имплементит интерфейс с методами типа insert/update/delete, дефолтную можно переопределить
 
Сверху