Skip to content

Commit

Permalink
Merge pull request #4404 from tim-pixeldeluxe/develop
Browse files Browse the repository at this point in the history
Added function to merge Element objects (Elements service)
  • Loading branch information
brandonkelly authored Jun 14, 2019
2 parents 26cd225 + cce6fac commit 02a2122
Showing 1 changed file with 45 additions and 15 deletions.
60 changes: 45 additions & 15 deletions src/services/Elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ public function updateDescendantSlugsAndUris(ElementInterface $element, bool $up
}

/**
* Merges two elements together.
* Merges two elements together by their IDs.
*
* This method will update the following:
* - Any relations involving the merged element
Expand All @@ -805,17 +805,47 @@ public function updateDescendantSlugsAndUris(ElementInterface $element, bool $up
* @param int $mergedElementId The ID of the element that is going away.
* @param int $prevailingElementId The ID of the element that is sticking around.
* @return bool Whether the elements were merged successfully.
* @throws ElementNotFoundException if one of the elemet IDs don’t exist.
* @throws \Throwable if reasons
*/
public function mergeElementsByIds(int $mergedElementId, int $prevailingElementId): bool
{
// Get the elements
$mergedElement = $this->getElementById($mergedElementId);
if (!$mergedElement) {
throw new ElementNotFoundException("No element exists with the ID '{$mergedElementId}'");
}
$prevailingElement = $this->getElementById($prevailingElementId);
if (!$prevailingElement) {
throw new ElementNotFoundException("No element exists with the ID '{$prevailingElementId}'");
}

// Merge them
return $this->mergeElements($mergedElement, $prevailingElement);
}

/**
* Merges two elements together.
*
* This method will update the following:
* - Any relations involving the merged element
* - Any structures that contain the merged element
* - Any reference tags in textual custom fields referencing the merged element
*
* @param ElementInterface $mergedElement The element that is going away.
* @param ElementInterface $prevailingElement The element that is sticking around.
* @return bool Whether the elements were merged successfully.
* @throws \Throwable if reasons
*/
public function mergeElements(ElementInterface $mergedElement, ElementInterface $prevailingElement): bool
{
$transaction = Craft::$app->getDb()->beginTransaction();
try {
// Update any relations that point to the merged element
$relations = (new Query())
->select(['id', 'fieldId', 'sourceId', 'sourceSiteId'])
->from([Table::RELATIONS])
->where(['targetId' => $mergedElementId])
->where(['targetId' => $mergedElement->id])
->all();

foreach ($relations as $relation) {
Expand All @@ -826,7 +856,7 @@ public function mergeElementsByIds(int $mergedElementId, int $prevailingElementI
'fieldId' => $relation['fieldId'],
'sourceId' => $relation['sourceId'],
'sourceSiteId' => $relation['sourceSiteId'],
'targetId' => $prevailingElementId
'targetId' => $prevailingElement->id
])
->exists();

Expand All @@ -835,7 +865,7 @@ public function mergeElementsByIds(int $mergedElementId, int $prevailingElementI
->update(
Table::RELATIONS,
[
'targetId' => $prevailingElementId
'targetId' => $prevailingElement->id
],
[
'id' => $relation['id']
Expand All @@ -848,7 +878,7 @@ public function mergeElementsByIds(int $mergedElementId, int $prevailingElementI
$structureElements = (new Query())
->select(['id', 'structureId'])
->from([Table::STRUCTUREELEMENTS])
->where(['elementId' => $mergedElementId])
->where(['elementId' => $mergedElement->id])
->all();

foreach ($structureElements as $structureElement) {
Expand All @@ -857,15 +887,15 @@ public function mergeElementsByIds(int $mergedElementId, int $prevailingElementI
->from([Table::STRUCTUREELEMENTS])
->where([
'structureId' => $structureElement['structureId'],
'elementId' => $prevailingElementId
'elementId' => $prevailingElement->id
])
->exists();

if (!$persistingElementIsInStructureToo) {
Craft::$app->getDb()->createCommand()
->update(Table::RELATIONS,
[
'elementId' => $prevailingElementId
'elementId' => $prevailingElement->id
],
[
'id' => $structureElement['id']
Expand All @@ -876,35 +906,35 @@ public function mergeElementsByIds(int $mergedElementId, int $prevailingElementI

// Update any reference tags
/** @var ElementInterface|null $elementType */
$elementType = $this->getElementTypeById($prevailingElementId);
$elementType = $this->getElementTypeById($prevailingElement->id);

if ($elementType !== null && ($refHandle = $elementType::refHandle()) !== null) {
$refTagPrefix = "{{$refHandle}:";
$queue = Craft::$app->getQueue();

$queue->push(new FindAndReplace([
'description' => Craft::t('app', 'Updating element references'),
'find' => $refTagPrefix . $mergedElementId . ':',
'replace' => $refTagPrefix . $prevailingElementId . ':',
'find' => $refTagPrefix . $mergedElement->id . ':',
'replace' => $refTagPrefix . $prevailingElement->id . ':',
]));

$queue->push(new FindAndReplace([
'description' => Craft::t('app', 'Updating element references'),
'find' => $refTagPrefix . $mergedElementId . '}',
'replace' => $refTagPrefix . $prevailingElementId . '}',
'find' => $refTagPrefix . $mergedElement->id . '}',
'replace' => $refTagPrefix . $prevailingElement->id . '}',
]));
}

// Fire an 'afterMergeElements' event
if ($this->hasEventHandlers(self::EVENT_AFTER_MERGE_ELEMENTS)) {
$this->trigger(self::EVENT_AFTER_MERGE_ELEMENTS, new MergeElementsEvent([
'mergedElementId' => $mergedElementId,
'prevailingElementId' => $prevailingElementId
'mergedElementId' => $mergedElement->id,
'prevailingElementId' => $prevailingElement->id
]));
}

// Now delete the merged element
$success = $this->deleteElementById($mergedElementId);
$success = $this->deleteElement($mergedElement);

$transaction->commit();

Expand Down

0 comments on commit 02a2122

Please sign in to comment.