Как верно использовать метод связанный с несколькими моделями в 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
201 +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 Апр 2024 в 13:07
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир