FindOrFail

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

FindOrFail

Сообщение Brainfuck »

Давно интересует вопрос: как справляться без findOrFail? Я имею ввиду то что разработчики Yii упорно отказываются добавлять в ORM методы которые кидают исключение при не найденной модели (якобы это нарушает какие-то паттерны, хотя AR сама по себе является антипаттерном). Писать повсюду сотни проверок на null и вручную кидать 404 - нереально (будет просто охренительное захламление кода, превращая его в нечитабельное гавно). Так что у меня например это вообще игнорируется и падает с NullReferenceException в местах использования модели. Знаю что это ненормально, но не знаю как быть по другому...
andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: FindOrFail

Сообщение andku83 »

сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

andku83 писал(а): 2018.05.31, 14:20 сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.
Сложно. Там ведь не только статичный findOneOrFail у AR нужен, но и у Query/ActiveQuery oneOrFail.
Аватара пользователя
Alexum
Сообщения: 683
Зарегистрирован: 2016.09.26, 10:00

Re: FindOrFail

Сообщение Alexum »

Brainfuck писал(а): 2018.05.31, 14:35
andku83 писал(а): 2018.05.31, 14:20 сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.
Сложно. Там ведь не только статичный findOneOrFail у AR нужен, но и у Query/ActiveQuery oneOrFail.
1) Создаёте класс ActiveQueryExtended, отнаследованный от ActiveQuery;
2) переопределяете в нём все поисковые методы (->one(), ->all() и т.п., чтобы результат выполнения проверялся и кидалось исключение);
3) создаёте класс ActiveRecordExtended отнаследованный от ActiveRecord;
4) добавляете в нём метод findOrException() (можно конечно и родной find() переопределить, но чревато, ибо не во всех 100% случаев нужны исключения). В этом методе создаёте и возвращаете экземпляр ActiveQueryExtended на основе класса модели;
5) наследуете свои модели от ActiveRecordExtended.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

Alexum писал(а): 2018.05.31, 15:29
Brainfuck писал(а): 2018.05.31, 14:35
andku83 писал(а): 2018.05.31, 14:20 сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.
Сложно. Там ведь не только статичный findOneOrFail у AR нужен, но и у Query/ActiveQuery oneOrFail.
1) Создаёте класс ActiveQueryExtended, отнаследованный от ActiveQuery;
2) переопределяете в нём все поисковые методы (->one(), ->all() и т.п., чтобы результат выполнения проверялся и кидалось исключение);
3) создаёте класс ActiveRecordExtended отнаследованный от ActiveRecord;
4) добавляете в нём метод findOrException() (можно конечно и родной find() переопределить, но чревато, ибо не во всех 100% случаев нужны исключения). В этом методе создаёте и возвращаете экземпляр ActiveQueryExtended на основе класса модели;
5) наследуете свои модели от ActiveRecordExtended.
Черт. Вот почему я должен делать столько лишних действий если в других фреймворках это есть из коробки? :cry: :? Кстати может есть уже какие-нибудь готовые врапперы над Yii такого типа? Чтоб композером подцепить и все.
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Другие фреймворки - это Laravel, по-видимому.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

mkramer писал(а): 2018.05.31, 15:51 Другие фреймворки - это Laravel, по-видимому.
И в Django тоже есть. А в ASP.NET этого и не требуется, т.к. там Linq предоставляет аналогичную возможность, если конечно говорить об EF.
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Если только findOrFail необходим, то можно даже трейтом обойтись

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

trait ExceptionOnFindFail {
     public static function findOrFail($param) {
         if (($res = static::findOne($param)) === null) {
                throw new NotFoundHttpException();
         }
         
         return $res;
     }
}
И примешивайте к своим моделям.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

mkramer писал(а): 2018.05.31, 16:20 Если только findOrFail необходим, то можно даже трейтом обойтись

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

trait ExceptionOnFindFail {
     public static function findOrFail($param) {
         if (($res = static::findOne($param)) === null) {
                throw new NotFoundHttpException();
         }
         
         return $res;
     }
}
И примешивайте к своим моделям.
Не только. ActiveQuery->oneOrFail тоже бы не помешал. По сути только эти два и нужны - т.к. для массивов и так нормально когда пустой массив возвращается. Т.е. итерация по нему не вызовет ошибки, а просто ничего не произойдет.
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Ну сделайте ещё свой ActiveQuery или примесь/поведение для него. Всё равно ActiveQuery почти всегда переопределяется даже в простых проектах
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

mkramer писал(а): 2018.05.31, 16:28 Ну сделайте ещё свой ActiveQuery или примесь/поведение для него. Всё равно ActiveQuery почти всегда переопределяется даже в простых проектах
Как показывали выше - это довольно не простое занятие переопределять ActiveQuery, т.к. тогда придется все методы AR переопределять чтобы они мою реализацию использовали. И я ни разу не видел чтобы кто-то его переопределял. С чего вы такое взяли я без понятия.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

Хорошо бы чтобы тут отписался кто-нибудь из разработчиков Yii. Так сказать хочется посмотреть в глаза человеку заставляющему так страдать своих пользователей. :(
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

Мне так и не ответили по сути: как вы справляетесь без такого метода? Если не говорить о переопределении всего этого барахла (т.к. я слабо верю что кто-то реально этим занимается). Ставите овер 100500 проверок и вручную кидаете исключения? Игнорите это как я? :D Или как-то еще?
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Ну я руками или как gii генерит отдельный метод. Мне три лишние строчки не трудно написать.

Переопределить ActiveQuery, чтоб добавить туда oneOrFail() - очень просто. И я его всегда переопределяю, поскольку scopes вынесены в Yii туда, и я предпочитаю писать Model::find()->newest(10)->all() вместо Model::find()->orderBy(["dt" => SORT_DESC])->limit(10)->all()
т.к. тогда придется все методы AR переопределять чтобы они мою реализацию использовали
Вот ничего подобного. Вcё работает через ActiveRecord::find(), поэтому переопределить достаточно его, и об этом написано в документации, которую я прочёл первым делом, когда перешёл на Yii2 с Kohana.
https://www.yiiframework.com/doc/guide/ ... ry-classes
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

зачем Alexum посоветовал кидать исключение из all() - не знаю, честно говоря
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: FindOrFail

Сообщение rugabarbo »

Нет высокой активности в сторону этой фичи: https://github.com/yiisoft/yii2/issues/15937
Пообсуждали и забили.
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

mkramer оказался прав - переопределить не так уж сложно:

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

class ActiveQuery extends \yii\db\ActiveQuery {
    public function oneOrFail(string $message = null, ?Connection $db = null) {
        $value = $this->one($db);

        if (is_null($value))
            throw new NotFoundHttpException($message);

        return $value;
    }
}

class ActiveRecord extends \yii\db\ActiveRecord {
    public static function find(): ActiveQuery {
        return \Yii::createObject(ActiveQuery::class, [get_called_class()]);
    }

    public static function findOneOrFail($condition, string $message = null) {
        $value = static::findOne($condition);

        if (is_null($value))
            throw new NotFoundHttpException($message);

        return $value;
    }
}
Ладно сойдет. Придется правда это таскать за собой в другие проекты...
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Таскать лучше трейтами. Вдруг не от ActiveRecord надо будет унаследоваться
Brainfuck
Сообщения: 313
Зарегистрирован: 2018.02.19, 14:20

Re: FindOrFail

Сообщение Brainfuck »

mkramer писал(а): 2018.05.31, 18:08 Таскать лучше трейтами. Вдруг не от ActiveRecord надо будет унаследоваться
Как ты подмешаешь трейт в ActiveQuery?
mkramer
Сообщения: 531
Зарегистрирован: 2014.12.14, 13:02

Re: FindOrFail

Сообщение mkramer »

Brainfuck писал(а): 2018.06.01, 09:32 Как ты подмешаешь трейт в ActiveQuery?
Запилишь свой ActiveQuery для класса и к нему подмешаешь. Ну, правда, это я почти всегда перекрываю ActiveQuery, чтоб добавить функции для нужных мне выборок
Ответить