Страница 1 из 2

FindOrFail

Добавлено: 2018.05.31, 12:48
Brainfuck
Давно интересует вопрос: как справляться без findOrFail? Я имею ввиду то что разработчики Yii упорно отказываются добавлять в ORM методы которые кидают исключение при не найденной модели (якобы это нарушает какие-то паттерны, хотя AR сама по себе является антипаттерном). Писать повсюду сотни проверок на null и вручную кидать 404 - нереально (будет просто охренительное захламление кода, превращая его в нечитабельное гавно). Так что у меня например это вообще игнорируется и падает с NullReferenceException в местах использования модели. Знаю что это ненормально, но не знаю как быть по другому...

Re: FindOrFail

Добавлено: 2018.05.31, 14:20
andku83
сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.

Re: FindOrFail

Добавлено: 2018.05.31, 14:35
Brainfuck
andku83 писал(а): 2018.05.31, 14:20 сделайте свой промежуточный SuperActiveRecord и в нем добавьте этот метод, и все все свои классы наследуйте от вашего нового.
Сложно. Там ведь не только статичный findOneOrFail у AR нужен, но и у Query/ActiveQuery oneOrFail.

Re: FindOrFail

Добавлено: 2018.05.31, 15:29
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.

Re: FindOrFail

Добавлено: 2018.05.31, 15:36
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 такого типа? Чтоб композером подцепить и все.

Re: FindOrFail

Добавлено: 2018.05.31, 15:51
mkramer
Другие фреймворки - это Laravel, по-видимому.

Re: FindOrFail

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

Re: FindOrFail

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

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

trait ExceptionOnFindFail {
     public static function findOrFail($param) {
         if (($res = static::findOne($param)) === null) {
                throw new NotFoundHttpException();
         }
         
         return $res;
     }
}
И примешивайте к своим моделям.

Re: FindOrFail

Добавлено: 2018.05.31, 16:23
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 тоже бы не помешал. По сути только эти два и нужны - т.к. для массивов и так нормально когда пустой массив возвращается. Т.е. итерация по нему не вызовет ошибки, а просто ничего не произойдет.

Re: FindOrFail

Добавлено: 2018.05.31, 16:28
mkramer
Ну сделайте ещё свой ActiveQuery или примесь/поведение для него. Всё равно ActiveQuery почти всегда переопределяется даже в простых проектах

Re: FindOrFail

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

Re: FindOrFail

Добавлено: 2018.05.31, 16:32
Brainfuck
Хорошо бы чтобы тут отписался кто-нибудь из разработчиков Yii. Так сказать хочется посмотреть в глаза человеку заставляющему так страдать своих пользователей. :(

Re: FindOrFail

Добавлено: 2018.05.31, 16:39
Brainfuck
Мне так и не ответили по сути: как вы справляетесь без такого метода? Если не говорить о переопределении всего этого барахла (т.к. я слабо верю что кто-то реально этим занимается). Ставите овер 100500 проверок и вручную кидаете исключения? Игнорите это как я? :D Или как-то еще?

Re: FindOrFail

Добавлено: 2018.05.31, 17:11
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

Re: FindOrFail

Добавлено: 2018.05.31, 17:15
mkramer
зачем Alexum посоветовал кидать исключение из all() - не знаю, честно говоря

Re: FindOrFail

Добавлено: 2018.05.31, 17:42
rugabarbo
Нет высокой активности в сторону этой фичи: https://github.com/yiisoft/yii2/issues/15937
Пообсуждали и забили.

Re: FindOrFail

Добавлено: 2018.05.31, 17:48
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;
    }
}
Ладно сойдет. Придется правда это таскать за собой в другие проекты...

Re: FindOrFail

Добавлено: 2018.05.31, 18:08
mkramer
Таскать лучше трейтами. Вдруг не от ActiveRecord надо будет унаследоваться

Re: FindOrFail

Добавлено: 2018.06.01, 09:32
Brainfuck
mkramer писал(а): 2018.05.31, 18:08 Таскать лучше трейтами. Вдруг не от ActiveRecord надо будет унаследоваться
Как ты подмешаешь трейт в ActiveQuery?

Re: FindOrFail

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