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

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

Добавлено: 2021.04.09, 17:26
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)), то вместо возврата нам пустоты срабатывает ленивая загрузка и подгружает нам данные, не нужные в этом случае.
Может как то можно отключить ленивую подгрузку или я что то не так делаю?

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

Добавлено: 2021.04.12, 13:24
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 идентификаторов)

В своем ответе я объяснил как можно использовать данные, если что-то не то, то опишите проблему чуть более яснее.

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

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

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

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

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

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

Когда будет под 500+ запросов к БД и пользователей будет больше 100, тогда и можно думать об ускорении ;)

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

Добавлено: 2021.04.12, 17:33
masson

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

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

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

Добавлено: 2021.04.12, 18:33
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 или пустоты у тебя идёт подтягивание данных для этого поля, что тут не нужно. Получилось объяснить суть мысли?) Если нет, распишу ещё подробнее)

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

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

Метод fill() надо смотреть

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

Добавлено: 2021.04.12, 18:45
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;
    }
//...
}
Возможно этот метод следует выделить в отдельный класс

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

Добавлено: 2021.04.12, 18:56
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(), там точно связь не подтянется)

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

Добавлено: 2021.04.12, 18:57
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, следовательно виртуальное поле. Поэтому даже если ты его объявляешь/проверяешь, то данные тебе подтянутся по этой связи)

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

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

Тогда попробуй проверять у модели св-во relatedRecords
https://www.yiiframework.com/doc/api/2. ... rds-detail

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

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


Или у вас там динамические контроллеры и представления, которые перемешиваются между собой в хаотичном порядке :D

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

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


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

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

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

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

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

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

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

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

Добавлено: 2021.04.13, 15:11
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.
Спасибо всем)