Виртуальные атрибуты и ленивая загрузка

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

Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

Привет, коллеги! Возник такой вопрос:
Допустим выбираем данные из таблички, в методе with добавляем выборку связанных данных. Это 2 связи из возможных 4х.

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

$model = RetailerDB::find()
            ->with('priceLevels', 'cities')
            ->where('url=:url', [':url'=>$url])
            ->one();
Потом модели перебрасываем в обычные объекты допустим через метод fill($model): DTO.
В этом методе проверяем, если есть данные в связанных таблицах, то заполняем их, если нет, то оставляем null.
Так вот если использовать метод fill для разных выборок, то при проверке существования данных по связям если они есть, то всё гуд, перебрасываем.
А если в выборке данных нет, а проверяем мы это допустим так if(isset($model->categories)), то вместо возврата нам пустоты срабатывает ленивая загрузка и подгружает нам данные, не нужные в этом случае.
Может как то можно отключить ленивую подгрузку или я что то не так делаю?
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение unknownby »

lexbond писал(а): 2021.04.09, 17:26 Привет, коллеги! Возник такой вопрос:
Допустим выбираем данные из таблички, в методе with добавляем выборку связанных данных. Это 2 связи из возможных 4х.

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

$model = RetailerDB::find()
            ->with('priceLevels', 'cities')
            ->where('url=:url', [':url'=>$url])
            ->one();
Потом модели перебрасываем в обычные объекты допустим через метод fill($model): DTO.
В этом методе проверяем, если есть данные в связанных таблицах, то заполняем их, если нет, то оставляем null.
Так вот если использовать метод fill для разных выборок, то при проверке существования данных по связям если они есть, то всё гуд, перебрасываем.
А если в выборке данных нет, а проверяем мы это допустим так if(isset($model->categories)), то вместо возврата нам пустоты срабатывает ленивая загрузка и подгружает нам данные, не нужные в этом случае.
Может как то можно отключить ленивую подгрузку или я что то не так делаю?
Во-1 есть два варианта использования связанных данных.

Первый вариант как у вас, используя with.
Тогда обращаясь к модели можно взять наименование по связанным данным

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

$model->priceLevels->level_name;
$model->cities->city_name;
Второй вариант подходит, если данные выводятся в табличном виде и идёт поиск по связанным данным.

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

$model = RetailerDB::find()
            ->joinWith('priceLevels', false, 'LEFT JOIN')
            ->joinWith('cities', true, 'LEFT JOIN')
            ->where('url=:url', [':url'=>$url])
            ->one();
В первом случае он не будет прописывать запросы, если нет обращения к данным из связанной таблицы

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

$model->priceLevels->level_name;
Однако, если строк в таблице будет 100, то при таком выводе будет 100 дополнительных запросов в БД.
Чтобы избежать дополнительных запросов используется жадная загрузка и вторым параметром передаётся TRUE
Для городов уже будут загружены все данные, которые выведены в таблице и будет сделан один запрос в БД.
Такого плана будет запрос:
SELECT * FROM cities WHERE city_id IN (тут 100 идентификаторов)

В своем ответе я объяснил как можно использовать данные, если что-то не то, то опишите проблему чуть более яснее.
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

Тут $model->priceLevels это связь hasMany, то есть получить уровни. В основном запросе эти данные не запрашиваются так как не нужны, но потом когда начинаешь делать проверку этого поля на пустоту (а оно должно быть пустое, так как эти данные не нужны), то выполняется ленивая загрузка, то есть вместо ответа "пустота" или null - подтягиваются данные из базы) А вот как этого не допустить если они не нужны?)
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение rak »

lexbond писал(а): 2021.04.12, 14:38 Тут $model->priceLevels это связь hasMany, то есть получить уровни. В основном запросе эти данные не запрашиваются так как не нужны, но потом когда начинаешь делать проверку этого поля на пустоту (а оно должно быть пустое, так как эти данные не нужны), то выполняется ленивая загрузка, то есть вместо ответа "пустота" или null - подтягиваются данные из базы) А вот как этого не допустить если они не нужны?)
вы задачу подробнее объясните, с примерами. потому как сложно понять, что именно делаете и что должно получиться
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение unknownby »

lexbond писал(а): 2021.04.12, 14:38 Тут $model->priceLevels это связь hasMany, то есть получить уровни. В основном запросе эти данные не запрашиваются так как не нужны, но потом когда начинаешь делать проверку этого поля на пустоту (а оно должно быть пустое, так как эти данные не нужны), то выполняется ленивая загрузка, то есть вместо ответа "пустота" или null - подтягиваются данные из базы) А вот как этого не допустить если они не нужны?)
Если связи в основном запросе не нужны, то и не нужно прописывать их там.
Когда вдруг появится необходимость, вызова связи $model->priceLevels то она сработает хорошо, отличие в том, что при основном запросе не будут запросы к БД, а когда произойдет вызов связи, тогда появятся дополнительные запросы к БД и вытянутся сведения. Я думаю у вас там не 500 запросов, чтобы беспокоиться об этом. :D

Когда будет под 500+ запросов к БД и пользователей будет больше 100, тогда и можно думать об ускорении ;)
masson
Сообщения: 545
Зарегистрирован: 2012.07.03, 15:59

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение masson »

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

$models = RetailerDB::find()
            ->with('priceLevels', 'cities')
            ->joinWith('categories')
            ->where('url=:url', [':url'=>$url])
            ->andWhere(['category.id' => null])
            ->all();
И проверять ничего не надо. Выберутся только те записи, у которых categories = null
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

Спасибо за ответы. Тут есть доля правды, вероятно я не описал полностью вопрос, сейчас постараюсь дополнить.
Есть метод FILL(retailerModel): retailerDTO, он универсален внутри класса, то есть перебрасывает данные с модели AR в обычный объект DTO.
Этот метод используется другими методами в этом же классе, которые достают данные из БД и потом эти данные перебрасывают в обычный объект.
В методе FILL есть что то вроде этого:

$obj->name = $model->name;
$obj->priceLevels = $model->priceLevels;
$obj->cities = $model->cities;

В разных выборках могут быть разные данные, отсюда $obj->name будет всегда, а вот $obj->priceLevels может быть не всегда. Допустим его нету в модели, но когда в методе FILL ты его присваиваешь ($obj->priceLevels = $model->priceLevels;), то вместо null или пустоты у тебя идёт подтягивание данных для этого поля, что тут не нужно. Получилось объяснить суть мысли?) Если нет, распишу ещё подробнее)
masson
Сообщения: 545
Зарегистрирован: 2012.07.03, 15:59

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение masson »

lexbond писал(а): 2021.04.12, 18:33 $obj->priceLevels может быть не всегда. Допустим его нету в модели, но когда в методе FILL ты его присваиваешь ($obj->priceLevels = $model->priceLevels;), то вместо null или пустоты у тебя идёт подтягивание данных для этого поля ....
Не понимаю подтягивание каких данных идет если изначально их нет?
Если $model->priceLevels = null и если запрос сделан как with('priceLevels') - то ничего кроме null подтягиваться не будет

Метод fill() надо смотреть
Последний раз редактировалось masson 2021.04.12, 18:49, всего редактировалось 2 раза.
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение rak »

Так немного понятнее.
Тут либо убирать универсальность, либо делать настройки для метода по какому-то признаку, т.е. грубо говоря

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

public function fill($data, $withPriceLevels = false) {
//...
    if($withPriceLevels) {
    	$obj->priceLevels = $model->priceLevels;
    }
//...
}
или же

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

public function fill($data) {
//...
    if($model instanceof MyModel1) {
    	$obj->priceLevels = $model->priceLevels;
    }
//...
}
Возможно этот метод следует выделить в отдельный класс
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

rak писал(а): 2021.04.12, 18:45 Так немного понятнее.
Тут либо убирать универсальность, либо делать настройки для метода по какому-то признаку, т.е. грубо говоря

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

public function fill($data, $withPriceLevels = false) {
//...
    if($withPriceLevels) {
    	$obj->priceLevels = $model->priceLevels;
    }
//...
}
или же

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

public function fill($data) {
//...
    if($model instanceof MyModel1) {
    	$obj->priceLevels = $model->priceLevels;
    }
//...
}
Возможно этот метод следует выделить в отдельный класс
Варианты хорошие, однако таких параметров может быть больше и аргументов будет туча. Выходит красиво и быстро всё же сделать не получится. Последним вариантом делать это asArray(), там точно связь не подтянется)
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

masson писал(а): 2021.04.12, 18:43
lexbond писал(а): 2021.04.12, 18:33 $obj->priceLevels может быть не всегда. Допустим его нету в модели, но когда в методе FILL ты его присваиваешь ($obj->priceLevels = $model->priceLevels;), то вместо null или пустоты у тебя идёт подтягивание данных для этого поля ....
Не понимаю подтягивание каких данных идет если изначально их нет?
Если $model->priceLevels = null и если запрос сделан как with('priceLevels') - то ничего кроме null подтягиваться не будет

Метод fill() надо смотреть
$model->priceLevels - это как раз связь getPriceLevels, следовательно виртуальное поле. Поэтому даже если ты его объявляешь/проверяешь, то данные тебе подтянутся по этой связи)
masson
Сообщения: 545
Зарегистрирован: 2012.07.03, 15:59

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение masson »

Понял. Запрос модели сделан БЕЗ with('priceLevels') и поэтому в obj->priceLevels должен попасть null несмотря на то что в БД фактически данные есть. Так?

Тогда попробуй проверять у модели св-во relatedRecords
https://www.yiiframework.com/doc/api/2. ... rds-detail
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение unknownby »

lexbond писал(а): 2021.04.12, 18:57 $model->priceLevels - это как раз связь getPriceLevels, следовательно виртуальное поле. Поэтому даже если ты его объявляешь/проверяешь, то данные тебе подтянутся по этой связи)
Во-1 данной связи может не оказаться, если вдруг используется другая модель для данного метода, т.е. метод не универсален.
Во-2 для чего вообще этот метод? Для чего промежуточное звено? "Получили объект" -> "переместили данные в другой объект" -> "используем другой объект". Когда можно "Получили объект" -> "используем его". :?
В нужных местах представления будет тот объект, который получили через контроллер.


Или у вас там динамические контроллеры и представления, которые перемешиваются между собой в хаотичном порядке :D
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

unknownby писал(а): 2021.04.13, 08:49
lexbond писал(а): 2021.04.12, 18:57 $model->priceLevels - это как раз связь getPriceLevels, следовательно виртуальное поле. Поэтому даже если ты его объявляешь/проверяешь, то данные тебе подтянутся по этой связи)
Во-1 данной связи может не оказаться, если вдруг используется другая модель для данного метода, т.е. метод не универсален.
Во-2 для чего вообще этот метод? Для чего промежуточное звено? "Получили объект" -> "переместили данные в другой объект" -> "используем другой объект". Когда можно "Получили объект" -> "используем его". :?
В нужных местах представления будет тот объект, который получили через контроллер.


Или у вас там динамические контроллеры и представления, которые перемешиваются между собой в хаотичном порядке :D
Если делать получили объект AR, используем объект AR, то в итоге ваша архитектура превратится в зависимый монолит и при внесении изменений будет полный фаршмак) Я использую в проекте DDD, поэтому тут так)
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

masson писал(а): 2021.04.12, 19:24 Понял. Запрос модели сделан БЕЗ with('priceLevels') и поэтому в obj->priceLevels должен попасть null несмотря на то что в БД фактически данные есть. Так?

Тогда попробуй проверять у модели св-во relatedRecords
https://www.yiiframework.com/doc/api/2. ... rds-detail
Все верно!
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

masson писал(а): 2021.04.12, 19:24 Понял. Запрос модели сделан БЕЗ with('priceLevels') и поэтому в obj->priceLevels должен попасть null несмотря на то что в БД фактически данные есть. Так?

Тогда попробуй проверять у модели св-во relatedRecords
https://www.yiiframework.com/doc/api/2. ... rds-detail
Хорошая идея! Попробую, отпишусь!
lexbond
Сообщения: 11
Зарегистрирован: 2021.04.09, 17:17

Re: Виртуальные атрибуты и ленивая загрузка

Сообщение lexbond »

lexbond писал(а): 2021.04.13, 09:21
masson писал(а): 2021.04.12, 19:24 Понял. Запрос модели сделан БЕЗ with('priceLevels') и поэтому в obj->priceLevels должен попасть null несмотря на то что в БД фактически данные есть. Так?

Тогда попробуй проверять у модели св-во relatedRecords
https://www.yiiframework.com/doc/api/2. ... rds-detail
Хорошая идея! Попробую, отпишусь!
Вопрос решен! Спасибо masson. Всё решается проверкой наличия ключа (имени свойства) в массиве relatedRecords.
Спасибо всем)
Закрыто