Трейт для ActiveRecord. FirstOrCreate

Выкладываем свои наработки
Ответить
olegtmb
Сообщения: 18
Зарегистрирован: 2013.08.06, 20:27

Трейт для ActiveRecord. FirstOrCreate

Сообщение olegtmb » 2018.06.14, 21:35

Здравствуйте!
Поиск модели ActiveRecord по атрибутам FindOne() может завершится неудачей и вернёт null.
Эту ситуацию необходимо обрабатывать и возможно создавать новую модель с атрибутами поиска для продолжения работы с ней.
Эту задачу упрощает данный трейт, идея, которого заимствованна из Laravel.
Пример:

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

...
$model = ModelTable::firstOrNew(['id' => 50]);
В данном случае, если не найдётся запись с id равным 50, будет создан новый экземпляр класса ActiveRecord с id=50.
Затем можно изменить другие свойства модели и сохранить её.

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

...
$model = ModelTable::firstOrCreate(['id' => 50]);
Этот метод создаст новый экземпляр модели и сразу сохранит его

ссылки на трейт: https://www.yiiframework.com/extension/ ... -or-create, https://github.com/oleg-p/first-or-create

Аватара пользователя
SiZE
Сообщения: 2581
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение SiZE » 2018.06.14, 22:45

Я бы назвал findOneOrNew и findOneOrCreate :)

А там точно только self, а не static? https://github.com/oleg-p/first-or-crea ... te.php#L26

P.S. PSR-2 не завезли? )

olegtmb
Сообщения: 18
Зарегистрирован: 2013.08.06, 20:27

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение olegtmb » 2018.06.14, 23:38

А там точно только self, а не static?
Исправил self на static. Спасибо SiZE!
Я бы назвал findOneOrNew и findOneOrCreat
Насчёт наименования мне кажется, что firstOrNew более лаконично и смысл не теряется.

Аватара пользователя
SiZE
Сообщения: 2581
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение SiZE » 2018.06.15, 10:13

olegtmb писал(а):
2018.06.14, 23:38
Я бы назвал findOneOrNew и findOneOrCreat
Насчёт наименования мне кажется, что firstOrNew более лаконично и смысл не теряется.
Интуитивно понятней для юиста findOne, раз он внутри вызывается. И без лимита findOne выберет все записи, а потом вернет первую, что может негативно сказаться на производительности, для полей с уникальным индексом это пофиг конечно.

olegtmb
Сообщения: 18
Зарегистрирован: 2013.08.06, 20:27

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение olegtmb » 2018.06.15, 19:48

SiZE писал(а):
2018.06.15, 10:13
И без лимита findOne выберет все записи, а потом вернет первую, что может негативно сказаться на производительности, для полей с уникальным индексом это пофиг конечно.
Реализовано добавление limit(1) в запрос.

Модифицированы и добавлены методы:

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

    //public static function firstOrNew($attributes, $values = [])
    $model = ModelTable::firstOrNew(['id' => 50]);
    $model = ModelTable::firstOrNew(['id' => 50], ['sort' => 10]);
    //Возвращает один экземпляр модели ActiveRecord, соответствующий ограничениям значений массива атрибутов 
    //или возвращает новый экземпляр модели ActiveRecord со свойствами, 
    //соответствующими значениям массива $attributes + значениям массива $values 
    
    //public static function firstOrCreate($attributes, $values = [])
    $model = ModelTable::firstOrCreate(['id' => 50]);
    $model = ModelTable::firstOrCreate(['id' => 50], ['sort' => 10]);
    //Возвращает один экземпляр модели ActiveRecord, соответствующий ограничениям значений массива атрибутов
    //или возвращает новый экземпляр модели ActiveRecord со свойствами,
    //соответствующими значениям массива $attributes + значениям массива $values и сохраняет его
    
    //public static function updateOrCreate($attributes, $values = [])
    $model = ModelTable::updateOrCreate(['id' => 50]);
    $model = ModelTable::updateOrCreate(['id' => 50], ['sort' => 10]);
    //Находит модель по переданным атрибутам,
    //если модель найдена, то присваиваем свойствам модели значения $values и сохраняем её
    //если модель не найдена, то создаём её со значениями $attributes + $value и сохраняем её
    
    //public static function firstOrFail($attributes)
    $models = ModelTable::firstOrFail(['id' => 50]);
    //Возвращает модель по переданным атрибутам,
    //если модель не найдена, то будет выброшено исключение HTTP 404
    
    //public static function findOrFail($attributes)
    $models = ModelTable::findOrFail(['id' => 50]);
    //Возвращает массив моделей по переданным атрибутам,
    //если ни одна модель не найдена, то будет выброшено исключение HTTP 404

Аватара пользователя
SiZE
Сообщения: 2581
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение SiZE » 2018.06.16, 22:04

Модели нельзя кидать NotFoundHttpException. Последнее даже выделено в подпространство web. Модель может быть вызвана в консольном приложении, там нет http протокола. Можно кинуть \RuntimeException

Аватара пользователя
SiZE
Сообщения: 2581
Зарегистрирован: 2011.09.21, 12:39
Откуда: Perm
Контактная информация:

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение SiZE » 2018.06.16, 22:06

Как тебе такое? Раз уж ты все равно SOLID нарушил =)

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

// @return ModelTable|null
$model = ModelTable::firstOrNew(['id' => 50]);
// @return ModelTable
$model = ModelTable::firstOrNew(['id' => 50], ['sort' => 10]); 
// @return ModelTable
// @thrown Exception
$model = ModelTable::firstOrNew(['id' => 50], new RuntimeException());

olegtmb
Сообщения: 18
Зарегистрирован: 2013.08.06, 20:27

Re: Трейт для ActiveRecord. FirstOrCreate

Сообщение olegtmb » 2018.06.16, 23:45

SiZE писал(а):
2018.06.16, 22:06
Как тебе такое? Раз уж ты все равно SOLID нарушил =)

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

// @return ModelTable
// @thrown Exception
$model = ModelTable::firstOrNew(['id' => 50], new RuntimeException());
Да, согласен, можно добавить тип исключения в параметр (Только в методы ...OrFail). Но, надо что-то сделать по умолчанию. Стандартный контроллер Gii генерит такое:

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

    protected function findModel($id)
    {
        if (($model = ChangePrice1c::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
        }
    }

Ответить