/**
* Establishes the relationship between two models.
*
* The relationship is established by setting the foreign key value(s) in one model
* to be the corresponding primary key value(s) in the other model.
* The model with the foreign key will be saved into database without performing validation.
*
* If the relationship involves a junction table, a new row will be inserted into the
* junction table which contains the primary key values from both models.
*
* Note that this method requires that the primary key value is not null.
*
* @param string $name the case sensitive name of the relationship
* @param ActiveRecordInterface $model the model to be linked with the current one.
* @param array $extraColumns additional column values to be saved into the junction table.
* This parameter is only meaningful for a relationship involving a junction table
* (i.e., a relation set with [[ActiveRelationTrait::via()]] or `[[ActiveQuery::viaTable()]]`.)
* @throws InvalidCallException if the method is unable to link two models.
*/
public function link($name, $model, $extraColumns = [])
{
$relation = $this->getRelation($name);
if ($relation->via !== null) {
if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
throw new InvalidCallException('Unable to link models: the models being linked cannot be newly created.');
}
if (is_array($relation->via)) {
/* @var $viaRelation ActiveQuery */
list($viaName, $viaRelation) = $relation->via;
$viaClass = $viaRelation->modelClass;
// unset $viaName so that it can be reloaded to reflect the change
unset($this->_related[$viaName]);
} else {
$viaRelation = $relation->via;
$viaTable = reset($relation->via->from);
}
$columns = [];
foreach ($viaRelation->link as $a => $b) {
$columns[$a] = $this->{$b};
}
foreach ($relation->link as $a => $b) {
$columns[$b] = $model->{$a};
}
foreach ($extraColumns as $k => $v) {
$columns[$k] = $v;
}
if (is_array($relation->via)) {
/* @var $viaClass ActiveRecordInterface */
/* @var $record ActiveRecordInterface */
$record = new $viaClass();
foreach ($columns as $column => $value) {
$record->{$column} = $value;
}
$record->insert(false);
} else {
/* @var $viaTable string */
static::getDb()->createCommand()->insert($viaTable, $columns)->execute();
}
} else {
$p1 = $model->isPrimaryKey(array_keys($relation->link));
$p2 = $this->isPrimaryKey(array_values($relation->link));
if ($p1 && $p2) {
if ($this->getIsNewRecord() && $model->getIsNewRecord()) {
throw new InvalidCallException('Unable to link models: at most one model can be newly created.');
} elseif ($this->getIsNewRecord()) {
$this->bindModels(array_flip($relation->link), $this, $model);
} else {
$this->bindModels($relation->link, $model, $this);
}
} elseif ($p1) {
$this->bindModels(array_flip($relation->link), $this, $model);
} elseif ($p2) {
$this->bindModels($relation->link, $model, $this);
} else {
throw new InvalidCallException('Unable to link models: the link defining the relation does not involve any primary key.');
}
}
// update lazily loaded related objects
if (!$relation->multiple) {
$this->_related[$name] = $model;
} elseif (isset($this->_related[$name])) {
if ($relation->indexBy !== null) {
$indexBy = $relation->indexBy;
$this->_related[$name][$model->{$indexBy}] = $model;
} else {
$this->_related[$name][] = $model;
}
}
}