Как верно использовать метод связанный с несколькими моделями в Yii2? Возник вопрос - как верно использовать метод связанный с несколькими моделями. Есть несколько вариантов, но не знаю, какой вернее. Хотелось бы научиться правильно использовать ООП в разрезе Yii2. Если кому-то не лень во все это вникать, то большое спасибо =). Итак, у нас есть:
AR модели One, Two, Threeclass One extends ActiveRecord
{
// Есть атрибуты - поля в БД: id, category_id

public function getTwo()
{
return $this->hasMany(Two::className(), ['one_id' => 'id']);
}
...
}
class Two extends ActiveRecord
{
// Есть атрибуты - поля в БД: id, one_id

public function getOne()
{
return $this->hasOne(One::className(), ['id' => 'one_id']);
}
public function getThree()
{
return $this->hasMany(Three::className(), ['id' => 'three_id'])
->viaTable('two_three', ['two_id' => 'id']);
}
...
}
class Three extends ActiveRecord
{
// Есть атрибуты - поля в БД: id, category_id, name, group

public function getThree()
{
return $this->hasMany(Three::className(), ['id' => 'two_id'])
->viaTable('two_three', ['three_id' => 'id']);
}
...
}
Для класса One необходимо сделать следующий функционал: выбор всех связанных с с текущим объектом моделей Three у которых атрибут category_id равен атрибуту category_id текущего объекта One. Итоговый массив сгруппировать по group - атрибуту модели Three. Результат временно сохранить. Т.е. все это можно добиться добавив в класс One следующие свойство и метод:private $threeByGroup = [];

public function threeArrayForGroup($group)
{
if (empty($this->threeByGroup)) {
$this->threeByGroup = ArrayHelper::map(
Three::find()->where(['category_id' => $this->category_id])->all(),
'id', 'name', 'group'
);
}
return isset($this->threeByGroup[$group]) ? $this->threeByGroup[$group] : [];
}
Далее появляется задача для класса Two реализовать подобный функционал, которого можно добиться добавив в класс Two свойство и метод:private $threeByGroup = [];

public function threeArrayForGroup($group)
{
if (empty($this->threeByGroup)) {
$this->threeByGroup = ArrayHelper::map(
$this->getThree()->all(),
'id', 'name', 'group'
);
}
return isset($this->threeByGroup[$group]) ? $this->threeByGroup[$group] : [];
}
Эти методы можно получать непосредственно через объекты One и Two соответственно. Т.е. везде, где есть доступ к готовым объектам. Это плюс (наверное).
Собственно, после этого и начинаются проблемы. Как видно, два метода, добавленные в классы One и Two отличаются только источником данных для метода ArrayHelper::map.
Как это лучше сделать? Можно оставить как есть, но ведь можно удалить данные методы и добавить уже в класс Three свойство и метод:public static $threeByGroup = [];
...
public static function threeArrayForGroup($group, $category_id = false, $two_id = false)
{
if (! $category_id && ! $two_id) {
throw new InvalidParamException('Хотя бы один из двух аргументов должен быть не пустым: $category_id $two_id ');
}
if ($category_id) {
$data = Three::find()->where(['category_id' => $category_id])->all();
} elseif ($two_id) {
$data = Three::find()->joinWith('two')->where(['two.id' => $two_id])->all();
}
$key = $category_id .’_’.$two_id;
if (empty($this->threeByGroup[$key])) {
$this->threeByGroup[$key] = ArrayHelper::map(
$data,
'id', 'name', 'group'
);
}
return isset($this->threeByGroup[$key][$group]) ? $this->threeByGroup[$key][$group] : [];
}
Данный метод придется вызывать отдельно в контролере и присваивать его результат переменной, которую уже передавать вид, но это и логично. Можно пойти по другому пути в классах One и Two оставить методы:public function threeArrayForGroup($group)
{
$data = Three::find()->where(['category_id' => $this->category_id])->all();
$key = ‘category’;
return Three:: threeArrayForGroup($group, $data, $key);
}
иpublic function threeArrayForGroup($group)
{
$data = $this->getThree()->all();
$key = ‘two_’ . $this->id;
return Three:: threeArrayForGroup($group, $data, $key);
}
Соответственно, а в классе Three внести соответствующие изменения в метод.public static $threeByGroup = [];
...
public static function threeArrayForGroup($group, $data, $key)
{
if (empty($this->threeByGroup[$key])) {
$this->threeByGroup[$key] = ArrayHelper::map(
$data,
'id', 'name', 'group'
);
}
return isset($this->threeByGroup[$key][$group]) ? $this->threeByGroup[$key][$group] : [];
}
Тогда методы можно будет вызывать через объекты One и Two. Есть и еще варианты, но не хочу больше нагружать бедного читателя. Если кто-то выскажет свои мысли, как это лучше делать, буду очень признателен.

21 Авг 2019 в 07:06
176 +1
0
Ответы
1

Не совсем понятно, почему вы добавляете свойство $threeByGroup как приватное для каждого объекта класса One и Two. Вместо этого, вы можете использовать статическое свойство в классе Three для хранения кэша данных. Таким образом, данные будут доступны для всех объектов классов One и Two.

Также, вместо вызова метода threeArrayForGroup через класс Three, вы можете добавить статический метод в классы One и Two, который будет обращаться к методу в классе Three.

Например, вы можете изменить методы в классах One и Two следующим образом:

class One extends ActiveRecord {
public function threeArrayForGroup($group) {
return Three::threeArrayForGroupByCategory($group, $this->category_id);
}
}
class Two extends ActiveRecord {
public function threeArrayForGroup($group) {
return Three::threeArrayForGroupByTwo($group, $this->id);
}
}

И соответственно, измените статический метод в классе Three:

class Three extends ActiveRecord {
public static $threeByGroup = [];
public static function threeArrayForGroupByCategory($group, $category_id) {
$key = $category_id;
if (empty(self::$threeByGroup[$key])) {
$data = self::find()->where(['category_id' => $category_id])->all();
self::$threeByGroup[$key] = ArrayHelper::map(
$data,
'id', 'name', 'group'
);
}
return isset(self::$threeByGroup[$key][$group]) ? self::$threeByGroup[$key][$group] : [];
}
public static function threeArrayForGroupByTwo($group, $two_id) {
$key = $two_id;
if (empty(self::$threeByGroup[$key])) {
$data = self::find()->joinWith('two')->where(['two.id' => $two_id])->all();
self::$threeByGroup[$key] = ArrayHelper::map(
$data,
'id', 'name', 'group'
);
}
return isset(self::$threeByGroup[$key][$group]) ? self::$threeByGroup[$key][$group] : [];
}
}

Таким образом, вы можете вызывать метод threeArrayForGroup через объекты классов One и Two, который будет делегировать запрос к статическому методу класса Three, хранящему кэш данных для всех объектов.

20 Апр в 13:07
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Название заказа не должно быть пустым
Введите email
Бесплатные доработки
Гарантированные бесплатные доработки
Быстрое выполнение
Быстрое выполнение от 2 часов
Проверка работы
Проверка работы на плагиат
Интересные статьи из справочника
Поможем написать учебную работу
Название заказа не должно быть пустым
Введите email
Доверьте свою работу экспертам
Разместите заказ
Наша система отправит ваш заказ на оценку 92 436 авторам
Первые отклики появятся уже в течение 10 минут
Прямой эфир