|
|
@ -29,13 +29,14 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
private $_transaction; |
|
|
|
private $_transaction; |
|
|
|
|
|
|
|
|
|
|
|
private $_relationsScenario = []; |
|
|
|
private $_relationsScenario = []; |
|
|
|
|
|
|
|
private $_relationsExtraColumns = []; |
|
|
|
|
|
|
|
|
|
|
|
//private $_relationsCascadeDelete = []; //TODO |
|
|
|
//private $_relationsCascadeDelete = []; //TODO |
|
|
|
|
|
|
|
|
|
|
|
public function init() |
|
|
|
public function init() |
|
|
|
{ |
|
|
|
{ |
|
|
|
parent::init(); |
|
|
|
parent::init(); |
|
|
|
$allowedProperties = ['scenario']; |
|
|
|
$allowedProperties = ['scenario', 'extraColumns']; |
|
|
|
foreach ($this->relations as $key => $value) { |
|
|
|
foreach ($this->relations as $key => $value) { |
|
|
|
if (is_int($key)) { |
|
|
|
if (is_int($key)) { |
|
|
|
$this->_relations[] = $value; |
|
|
|
$this->_relations[] = $value; |
|
|
@ -345,8 +346,6 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
$model = $this->owner; |
|
|
|
$model = $this->owner; |
|
|
|
$this->_relationsSaveStarted = true; |
|
|
|
$this->_relationsSaveStarted = true; |
|
|
|
try { |
|
|
|
try { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($this->_relations as $relationName) { |
|
|
|
foreach ($this->_relations as $relationName) { |
|
|
|
if (array_key_exists($relationName, $this->_oldRelationValue)) { // Relation was not set, do nothing... |
|
|
|
if (array_key_exists($relationName, $this->_oldRelationValue)) { // Relation was not set, do nothing... |
|
|
|
Yii::trace("Linking {$relationName} relation", __METHOD__); |
|
|
|
Yii::trace("Linking {$relationName} relation", __METHOD__); |
|
|
@ -354,7 +353,6 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
if ($relation->multiple === true) { // Has many relation |
|
|
|
if ($relation->multiple === true) { // Has many relation |
|
|
|
// Process new relations |
|
|
|
// Process new relations |
|
|
|
$existingRecords = []; |
|
|
|
$existingRecords = []; |
|
|
|
|
|
|
|
|
|
|
|
/** @var BaseActiveRecord $relationModel */ |
|
|
|
/** @var BaseActiveRecord $relationModel */ |
|
|
|
foreach ($model->{$relationName} as $i => $relationModel) { |
|
|
|
foreach ($model->{$relationName} as $i => $relationModel) { |
|
|
|
if ($relationModel->isNewRecord) { |
|
|
|
if ($relationModel->isNewRecord) { |
|
|
@ -367,7 +365,8 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
throw new DbException("Related record {$pettyRelationName} could not be saved."); |
|
|
|
throw new DbException("Related record {$pettyRelationName} could not be saved."); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
$model->link($relationName, $relationModel); |
|
|
|
$junctionTableColumns = $this->_getJunctionTableColumns($relationName, $relationModel); |
|
|
|
|
|
|
|
$model->link($relationName, $relationModel, $junctionTableColumns); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
$existingRecords[] = $relationModel; |
|
|
|
$existingRecords[] = $relationModel; |
|
|
|
} |
|
|
|
} |
|
|
@ -381,21 +380,31 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
$junctionTablePropertiesUsed = array_key_exists($relationName, $this->_relationsExtraColumns); |
|
|
|
// Process existing added and deleted relations |
|
|
|
// Process existing added and deleted relations |
|
|
|
list($addedPks, $deletedPks) = $this->_computePkDiff($this->_oldRelationValue[$relationName], $existingRecords); |
|
|
|
list($addedPks, $deletedPks) = $this->_computePkDiff( |
|
|
|
|
|
|
|
$this->_oldRelationValue[$relationName], |
|
|
|
|
|
|
|
$existingRecords, |
|
|
|
|
|
|
|
$junctionTablePropertiesUsed |
|
|
|
|
|
|
|
); |
|
|
|
// Deleted relations |
|
|
|
// Deleted relations |
|
|
|
$initialModels = ArrayHelper::index($this->_oldRelationValue[$relationName], function (BaseActiveRecord $model) { |
|
|
|
$initialModels = ArrayHelper::index($this->_oldRelationValue[$relationName], function (BaseActiveRecord $model) { |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
$initialRelations = $model->{$relationName}; |
|
|
|
foreach ($deletedPks as $key) { |
|
|
|
foreach ($deletedPks as $key) { |
|
|
|
$model->unlink($relationName, $initialModels[$key], true); |
|
|
|
$model->unlink($relationName, $initialModels[$key], true); |
|
|
|
} |
|
|
|
} |
|
|
|
// Added relations |
|
|
|
// Added relations |
|
|
|
$actualModels = ArrayHelper::index($model->{$relationName}, function (BaseActiveRecord $model) { |
|
|
|
$actualModels = ArrayHelper::index( |
|
|
|
|
|
|
|
$junctionTablePropertiesUsed ? $initialRelations : $model->{$relationName}, |
|
|
|
|
|
|
|
function (BaseActiveRecord $model) { |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
); |
|
|
|
foreach ($addedPks as $key) { |
|
|
|
foreach ($addedPks as $key) { |
|
|
|
$model->link($relationName, $actualModels[$key]); |
|
|
|
$junctionTableColumns = $this->_getJunctionTableColumns($relationName, $actualModels[$key]); |
|
|
|
|
|
|
|
$model->link($relationName, $actualModels[$key], $junctionTableColumns); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { // Has one relation |
|
|
|
} else { // Has one relation |
|
|
|
if ($this->_oldRelationValue[$relationName] !== $model->{$relationName}) { |
|
|
|
if ($this->_oldRelationValue[$relationName] !== $model->{$relationName}) { |
|
|
@ -449,12 +458,39 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
|
|
|
|
* Return array of columns to save to the junction table for a related model having a many-to-many relation. |
|
|
|
|
|
|
|
* @param string $relationName |
|
|
|
|
|
|
|
* @param BaseActiveRecord $model |
|
|
|
|
|
|
|
* @return array |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private function _getJunctionTableColumns($relationName, $model) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
$junctionTableColumns = []; |
|
|
|
|
|
|
|
if (array_key_exists($relationName, $this->_relationsExtraColumns)) { |
|
|
|
|
|
|
|
if (is_callable($this->_relationsExtraColumns[$relationName])) { |
|
|
|
|
|
|
|
$junctionTableColumns = $this->_relationsExtraColumns[$relationName]($model); |
|
|
|
|
|
|
|
} elseif (is_array($this->_relationsExtraColumns[$relationName])) { |
|
|
|
|
|
|
|
$junctionTableColumns = $this->_relationsExtraColumns[$relationName]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!is_array($junctionTableColumns)) { |
|
|
|
|
|
|
|
throw new RuntimeException( |
|
|
|
|
|
|
|
'Junction table columns definition must return an array, got ' . gettype($junctionTableColumns) |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return $junctionTableColumns; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Compute the difference between two set of records using primary keys "tokens" |
|
|
|
* Compute the difference between two set of records using primary keys "tokens" |
|
|
|
|
|
|
|
* If third parameter is set to true all initial related records will be marked for removal even if their |
|
|
|
|
|
|
|
* properties did not change. This can be handy in a many-to-many relation involving a junction table. |
|
|
|
* @param BaseActiveRecord[] $initialRelations |
|
|
|
* @param BaseActiveRecord[] $initialRelations |
|
|
|
* @param BaseActiveRecord[] $updatedRelations |
|
|
|
* @param BaseActiveRecord[] $updatedRelations |
|
|
|
|
|
|
|
* @param bool $forceSave |
|
|
|
* @return array |
|
|
|
* @return array |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private function _computePkDiff($initialRelations, $updatedRelations) |
|
|
|
private function _computePkDiff($initialRelations, $updatedRelations, $forceSave = false) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Compute differences between initial relations and the current ones |
|
|
|
// Compute differences between initial relations and the current ones |
|
|
|
$oldPks = ArrayHelper::getColumn($initialRelations, function (BaseActiveRecord $model) { |
|
|
|
$oldPks = ArrayHelper::getColumn($initialRelations, function (BaseActiveRecord $model) { |
|
|
@ -463,9 +499,14 @@ class SaveRelationsBehavior extends Behavior |
|
|
|
$newPks = ArrayHelper::getColumn($updatedRelations, function (BaseActiveRecord $model) { |
|
|
|
$newPks = ArrayHelper::getColumn($updatedRelations, function (BaseActiveRecord $model) { |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
return implode("-", $model->getPrimaryKey(true)); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
if ($forceSave) { |
|
|
|
|
|
|
|
$addedPks = $newPks; |
|
|
|
|
|
|
|
$deletedPks = $oldPks; |
|
|
|
|
|
|
|
} else { |
|
|
|
$identicalPks = array_intersect($oldPks, $newPks); |
|
|
|
$identicalPks = array_intersect($oldPks, $newPks); |
|
|
|
$addedPks = array_values(array_diff($newPks, $identicalPks)); |
|
|
|
$addedPks = array_values(array_diff($newPks, $identicalPks)); |
|
|
|
$deletedPks = array_values(array_diff($oldPks, $identicalPks)); |
|
|
|
$deletedPks = array_values(array_diff($oldPks, $identicalPks)); |
|
|
|
|
|
|
|
} |
|
|
|
return [$addedPks, $deletedPks]; |
|
|
|
return [$addedPks, $deletedPks]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|