Страница 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.
Черт. Вот почему я должен делать столько лишних действий если в других фреймворках это есть из коробки?
Кстати может есть уже какие-нибудь готовые врапперы над 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 проверок и вручную кидаете исключения? Игнорите это как я?
Или как-то еще?
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, чтоб добавить функции для нужных мне выборок