/** * Merges data and relations from another object of same class, * without conflict resolution. Allows to specify which * dataset takes priority in case its not empty. * has_one-relations are just transferred with priority 'right'. * has_many and many_many-relations are added regardless of priority. * * Caution: has_many/many_many relations are moved rather than duplicated, * meaning they are not connected to the merged object any longer. * Caution: Just saves updated has_many/many_many relations to the database, * doesn't write the updated object itself (just writes the object-properties). * Caution: Does not delete the merged object. * Caution: Does now overwrite Created date on the original object. * * @param DataObject $rightObj * @param string $priority left|right Determines who wins in case of a conflict (optional) * @param bool $includeRelations Merge any existing relations (optional) * @param bool $overwriteWithEmpty Overwrite existing left values with empty right values. * Only applicable with $priority='right'. (optional) * @return Boolean */ public function merge($rightObj, $priority = 'right', $includeRelations = true, $overwriteWithEmpty = false) { $leftObj = $this; if ($leftObj->ClassName != $rightObj->ClassName) { // we can't merge similiar subclasses because they might have additional relations user_error("DataObject->merge(): Invalid object class '{$rightObj->ClassName}'\n\t\t\t(expected '{$leftObj->ClassName}').", E_USER_WARNING); return false; } if (!$rightObj->ID) { user_error("DataObject->merge(): Please write your merged-in object to the database before merging,\n\t\t\t\tto make sure all relations are transferred properly.').", E_USER_WARNING); return false; } // makes sure we don't merge data like ID or ClassName $leftData = $leftObj->db(); $rightData = $rightObj->db(); foreach ($rightData as $key => $rightSpec) { // Don't merge ID if ($key === 'ID') { continue; } // Only merge relations if allowed if ($rightSpec === 'ForeignKey' && !$includeRelations) { continue; } // don't merge conflicting values if priority is 'left' if ($priority == 'left' && $leftObj->{$key} !== $rightObj->{$key}) { continue; } // don't overwrite existing left values with empty right values (if $overwriteWithEmpty is set) if ($priority == 'right' && !$overwriteWithEmpty && empty($rightObj->{$key})) { continue; } // TODO remove redundant merge of has_one fields $leftObj->{$key} = $rightObj->{$key}; } // merge relations if ($includeRelations) { if ($manyMany = $this->manyMany()) { foreach ($manyMany as $relationship => $class) { $leftComponents = $leftObj->getManyManyComponents($relationship); $rightComponents = $rightObj->getManyManyComponents($relationship); if ($rightComponents && $rightComponents->exists()) { $leftComponents->addMany($rightComponents->column('ID')); } $leftComponents->write(); } } if ($hasMany = $this->hasMany()) { foreach ($hasMany as $relationship => $class) { $leftComponents = $leftObj->getComponents($relationship); $rightComponents = $rightObj->getComponents($relationship); if ($rightComponents && $rightComponents->exists()) { $leftComponents->addMany($rightComponents->column('ID')); } $leftComponents->write(); } } } return true; }