Как стать автором
Обновить

Комментарии 32

В принципе мне кажется, что выполнять проверку параметров стоит на этапе валидации, можно использовать метод find вместо getElementById (указать primary в классе таблицы), а в общем про создание моделей есть очень хорошая презентация www.slideshare.net/weierophinney/playdoh-modelling-your-objects-176600.
А данное расширение класса мало подойдет для крупного проекта
Допустим проект, где есть «Новости», «Статьи», «Товары», «Фирмы», «Города», «Статичные страницы», не считается большим, но данный подход позволил мне сэкономить прилично времени. Если брать разработку крупной CMS, то я пока не вижу причин, почему расширение не подойдёт для неё.

Ещё плюс данного подхода — не будут плодиться методы с одинаковым функционалом но с разными названиями. Например:
getNewsById(), deleteArticleById, addFirm(), updateCityById()
Абсолютно согласен по поводу того, что намного удобнее, когда методы имеют одно и тоже название при условии, что выполняют одинаковые функции. Но на этом можно и остановиться. Основная проблема всех методов, предложенных выше заключается в том, что модель = таблица, а это зачастую не так. Поэтому лучше создать интерфейс для модели, в котором описать все основные методы (при этом я подразумеваю, что одна модель работает сразу с несколькми таблицами), и работать с таблицами через защищённые свойства, например.

Приведу небольшой пример: есть процесс, параметры и связь процесса с параметрами. В этом случае логичнее создать одну модель процесса, которая будет работать с данными трёх таблиц и реализовать в ней методы, описанные в статье. Разница будет в том, что в этих методах будут использоваться зачастую уникальные конструкции (в этом и будет заключаться моделирование процесса).
В данном случае модель как раз равна таблице, т.к. Zend_Db_Table это реализация паттерна «шлюз таблицы». В остальных случаях предпочтительно использовать модель сохраняемую в базу с помощью маппера, который в свою очередь может использовать шлюзы.
Я это понимаю. Поэтому и говорю, что не имеет особого смысла расширять функциональность шлюза до логики модели.
То что описано в статье не является бизнес логикой, и если автору удобно что его шлюзы делают это так, то почему бы и нет? Ведь это и есть задача шлюза — взаимодействовать с таблицой в базе.
Согласен, что это не бизнес логика. И всё же это некоторое ужесточение правил работы с таблицами, которые лучше закладывать в модель, на мой взгляд. Но вы правы в одном — если это делает кому-то работу с данными удобнее, то имеет право на жизнь. Мне же хватает доступных методов для работы с картой базы.
Я не заморачивался и поставил Doctrine, правда жрёт памяти прилично.
А я не пользуюсь Db_Table, перенял опыт у Magento, у них гораздо удобнее все реализовано, да и нет явной привязки к 1 таблице.
И что же вы у Magento переняли? Поделитесь пожалуйста.
По-моему, автор просто открыл для себя «наследование», причем, используя его, он все равно продолжает программировать в процедурном стиле.
Дело не в «открыл для себя наследование». Я заметил, что есть люди, которые всё ещё не используют преимущества ООП или просто отказываются это делать их не видя в этом смысла. Статья описывает простой пример, без очень универсальных и навороченных методов именно для моделей таблицы базы данных.
Я заметил, что есть люди, которые всё ещё не используют преимущества ООП

Например, отдавая данные из слоя бд в виде массива, а не объекта вы тоже не используете эти преимущества. Представьте что вам понадобилось в модель товара добавить метод вычисления НДС исходя из стоимости, в нормальной ситуации это просто решалось наследованием от Zend_Db_Table_Row (с добавлением нужного метода getVat()) и указанием нового класса в поле protected $_rowClass в классе таблицы. Для всего кода, который уже работает с товаром ничего не изменилось бы, просто добавилась бы возможность использования нового метода. С вашим же подходом вы прилепили бы процедурный костыль и вместо использования $table->getItemById($id)->getVat() использовали бы $item = $table->getItemById($id); $table->getVatByPrice($item['price']);
Чувствуете разницу?
Я же не говорю использовать это везде.
Пожалуйста, опишите эти методы здесь

class Model_DbTable_Test extends App_Db_Table
{
protected $_name = 'test';
}

Да и создайте модель, наследуясь от Zend_Db_Table_Row, и решите свою не совсем тривиальную задачу удобным для вас способом. Почему вы пытаетесь применить то, что я написал к тому, для чего это не предназначено?
Я лишь указал вам на несовершенство передачи данных в виде массива и на то, что при правильном оо-проектировании данные должны передаваться в виде объектов. Так же я вам показал к чему приводит на практике использование такого подхода.
Методы работающие с наборами в случае если ничего не найдено должны возвращать пустой набор, но никак не бросать исключение. Если нормальная логика программы построена через try… catch, то это повод задуматься)
А где на ваш взгляд грань между использованием исключений и возвращаемых значений? С наборами всё ясно, но в случае если входные данные указаны неправильно — логичнее использовать исключения, тогда результирующее значение получено всё равно не будет.
Логично использовать исключения в исключительных ситуациях.
Если значение не найдено и скрипт должен продолжать работать дальше с учетом этого обстоятельства, то это не исключительная ситуация. Если элемент не найден (например это была статья на которую мы попытались зайти), то это ошибка 404, а значит исключительная ситуация, которую, в случае с зендом, обработает плагин ошибок и перенаправит на ErrorController.
> throw new Exception(«Необходимо указать id обновляемого элемента»);
Это будет лишнее?

>это ошибка 404, а значит исключительная ситуация
Тогда в этой ошибке можно будет указать только то, что статья не найдена — причина данного события останется скрытой.

Исключения при правильном использовании более информативны.
Да дело ведь не в информативности. Исключения используемые для управления логикой приложения подобны goto.
А в чём будет разница между выходом из функции с null вместо результата или выброса исключения в этом месте? Человек который потом будет использовать эту функцию по тексту поймёт что пошло не так, а получив не то значение потратит ещё уйму времени на определение причины.

Согласен, что не стоит делать проверку if(count($result) > 0) { чтобы бросить исключение — здесь ясно, что ничего не найдено, но вот throw new Exception(«Необходимо указать id для выбора элемента»); что-то вроде этого намного правильнее, чем return null;.

Вы согласны?
Нет). Тем о чем Вы написали занимается валидация, которая конечно может быть частью модели, но никак не частью метода удаляющего данные. А в данном случае метод должен вернуть 0, он ведь не затронул ни одну строку?
Черт я говорил про удаление), а в случае выборки метод опять же должен вернуть пустой набор, если он возвращает наборы, либо null, если он возвращает row.
А уже в контроллере можно делать так if ($row)

В выборках наличие непустого id контролируется, например, через роутинг -> news_id => '\d+'
С таким простым случаем я согласен — если условие задано таким, что ничего не возвращает, то метод должен продолжить своё выполнение и вернуть значение. Но если передан пустой идентификатор, то это тоже нарушает правила использования функции.
PHP язык с динамической типизацией, и во все функции всех библиотек можно передать пустое значение (не считаем интерфейсы и массивы). Представтье если они все начнут выкидывать исключения по этому поводу.
Это и минус. Особенно, когда функция, возвращающая целое внезапно вернула null. Аналогично и при передаче значений. Если метод ожидает целое, а ему приходит пусто, то, на мой взгляд, здесь более правильное поведение языков со статической типизацией и поэтому эксепшен повторяет их логику.

Если пришло пусто, значит где-то шагом или несколькими шагами раньше сформировалось это пустое значение как результат какой-то ошибки. Без использования исключений отловить её будет очень непросто.
> А в данном случае метод должен вернуть 0, он ведь не затронул ни одну строку?
С этим согласен, но я другое имею в виду. Допустим есть функция с 2мя параметрами: id, typeId, sortKey. Я проверяю, что id > 0, typeId в array(1, 2). Если id < 0, то не будет записи и в принципе ничего страшного. В тоже время передавая typeId не в заданном диапазоне я не получу данных, но это не то, т.к. в этом случае функция в принципе неправильно используется.
Вы предлагаете контролировать typeId внутри метода доступа к базе? А если я добавлю новый typeId? Кто мне напомнит что нужно залезть во все методы где стоит такая валидация и починить это?

Методы поиска занимаются только поиском, и если они что то не вернули то значит вы им не то передали. Если ошибка в том что метод не рабочий — правьте тесты.

Если мы говорим про вставку данных, то целостность базы должна поддерживаться на уровне базы и единственный способ сделать это, использовать внешние ключи. Без них любая база превратиться в помойку, даже при самых страшных проверках.
Речь о поиске. Имеется в виду, что typeId в array(1,...,5), но тот метод работает только с 2мя из них (такая у него специфика, такое бывает).

Но как быть если данный метод используется в 10ти местах стразу и везде на входные данные наложены одни теже ограничения? Писать отдельный метод валидации для этого поиска тоже не выглядит очень логично.
Конечно нужно пропускать через валидаторы данные, которые будут отдаваться методу, но если делать простые проверки в самом методах класса — это сделает класс более независимым и даст возможность быстро понять, где была ошибка, особенно программистам с небольшим опытом.
А если часть скрипта не должна продолжать работу, то это тоже исключительная ситуация.

Простой пример — если запись в таблице не удалось добавить, или обновить, то и файл, который к ней относится будет удалён из временной директории.

Имхо проще эту логику описать в try{} как некое подобие «транзакции».
Очень странное у Вас представление о транзакциях.

Все Ваши примеры решаются тем инструментом который для этого предназначен — это условные операторы.
Выдержка из книги «Совершенный код»:

8 ) Кодирование путём исключения
Этот антипаттерн представляет собой реализацию нормальной логики работы программы с помощью механизма исключений. Например, рекурсивный поиск по дереву может в качестве результата поиска кидать исключение. Такая реализация на первый взгляд может выглядеть заманчиво и удобно, но не более чем на первый взгляд.
Исключения должны использоваться с одной единственной целью — проинформировать систему об ошибке. Использование исключений как инструмента для управления логикой программы вносит неоднозначность. Глядя на конструкцию try-throw-catch / try-raise-except, программисту совершенно не очевидно, для чего именно эта конструкция используется. Кроме того, управление логикой через исключения и система оповещения об ошибках, построенная на исключениях, могут попросту мешать друг другу, поскольку они построены на одном механизме.
Для обработки ошибок используйте только исключения, а исключения — только для обработки ошибок.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории