Не так давно был написан хаб о «подводных камнях и ракушках» habrahabr.ru/post/254179 в котором было «домашнее задание» — ответ на которое так никто и не прислал, но думаю многие задавались вопросом — как все таки связывать модели из разных модулей. Хочу вам предложить вариант решения данной задачи.

«Наша банда», в составе 3 человек, пытается решать интересные задачки на данном фреймворке, в очередной раз, в свободное время — сели мы значит за кофе с печеньками и стали проверять «смысловую нагрузку» поговорки —
Одна голова — хорошо, но две лучше

В итоге у нас нарисовалась следующая «концепция по разработке зависимых модулей»:
1. Регистрировать релейшены для моделей должны модули;
2. Релейшены должны храниться в статическом свойстве модели;
3. Возможность добавления релейшенов в обе стороны (как на модульную модель, так и в модульной модели — обратная);




Добавление и хранение связей


protected static $relations = [];

public static function addRelation($name, $targetClass, $link, $multiple = false)
{
    self::$relations[$name] = [$targetClass, $link, $multiple];
}

...
Конечно же код не идеален (отсутствие php-doc), ну и многие задались сразу же вопросом — «а как быть с доп условиями (andWhere, orderBy, indexBy, ...)»? Но я, к сожалению не буду затрагивать в данной статье этот вопрос, т.к. это всего лишь наброски мыслей.
Трейт решит проблему со статическим свойством — уникальным для класса модели.

Создание связей


public function getRelation($name, $throwException = true)
{
    if (isset(self::$relations[$name])) {
        return $this->createRelation($name, $throwException);
    }
    return parent::getRelation($name, $throwException);
}

public function __get($name)
{
    if (isset(self::$relations[$name]) && !$this->isRelationPopulated($name)) {
        $related = $this->getRelation($name)->findFor($name, $this);
        $this->populateRelation($name, $related);
        return $related;
    }
    return parent::__get($name);
}

protected function createRelation($name, $throwException = true)
{
    $relation = self::$relations;
    if (!isset($relation[$name])) {
        if (!$throwException) {
            return null;
        }
        throw new InvalidCallException('Relation ' . $name . ' not find in class ' . get_class($this));
    }
    $relation = $relation[$name];
    $class = array_shift($relation);
    $query = $class::find();
    $query->link = array_shift($relation);
    $query->multiple = array_shift($relation);
    $query->primaryModel = $this;
    return $query;
}

подробнее смотрите [[yii\db\BaseActiveRecord::hasOne|hasMany]]

Объявление связей и их регистрация


в данном коде, рассмотрим пример из «домашки» — у пользователя несколько банковских аккаунтов (неймспейсы опустим для облегчения восприятия контента).
public $relations = [
    'User' => [
        'bankAccounts' => ['BankAccount', ['user_id' => 'id'], true]
    ],
    'BankAccount' => [
        'user' => ['User', ['id' => 'user_id'], false]
    ],
];

protected function registerRelations()
{
    foreach ($this->relations as $owner => $relations) {
        foreach ($relations as $name => $relation) {
            list ($targetClass, $link, $multiple) = $relation;
            $owner::addRelation($name, $targetClass, $link, $multiple);
        }
    }
}


Здесь мог быть «вывод» или «итог», но данный хаб думаю в этом не нуждается, прошу прощение за малую информативность о том, как это работает — думаю тут все прозрачно.
Жду критики, вопросов, предложений.
В скором времени будет создано и опубликовано расширение в публичном доступе, об этом будет сообщено в данном хабе и на русскоязычном yii форуме

п.с. родители в школу не пойдут — можете не вызывать

Комментарии ()