Skip to content

Commit c721b6e

Browse files
authored
fix: sort recycle bin before restore (#1795)
1 parent 134c838 commit c721b6e

9 files changed

Lines changed: 472 additions & 22 deletions

File tree

src/ExecutionEngine/Util/StepConfig.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,5 @@ enum StepConfig: string
4242
case CONFIG_TYPE_INT = 'int';
4343
case CONFIG_TYPE_STRING = 'string';
4444
case CONFIG_TYPE_BOOL = 'bool';
45+
case ITEMS_TO_RESTORE = 'items_to_restore';
4546
}

src/RecycleBin/ExecutionEngine/Handler/RestoreItemsHandler.php

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
use Exception;
1717
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler;
1818
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config;
19+
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig;
1920
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait;
2021
use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface;
2122
use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\UserTopicServiceInterface;
2223
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\RestoreItemsMessage;
2324
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\Service\RecycleBinServiceInterface;
2425
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
26+
use function count;
2527

2628
/**
2729
* @internal
@@ -35,7 +37,6 @@ public function __construct(
3537
private readonly RecycleBinServiceInterface $recycleBinService,
3638
private readonly PublishServiceInterface $publishService,
3739
private readonly UserTopicServiceInterface $userTopicService,
38-
3940
) {
4041
parent::__construct();
4142
}
@@ -46,26 +47,44 @@ public function __construct(
4647
public function __invoke(RestoreItemsMessage $message): void
4748
{
4849
$jobRun = $this->getJobRun($message);
49-
if (!$this->shouldBeExecuted($jobRun) || !$message->getElement()) {
50+
if (!$this->shouldBeExecuted($jobRun)) {
5051
return;
5152
}
5253

53-
$id = $message->getElement()->getId();
54+
$items = $this->extractConfigFieldFromJobStepConfig(
55+
$message,
56+
StepConfig::ITEMS_TO_RESTORE->value
57+
);
58+
$totalItems = count($items);
59+
$stepName = $this->getJobStep($message)->getName();
60+
61+
foreach ($items as $id) {
62+
try {
63+
$this->recycleBinService->restoreItem($id);
64+
} catch (Exception $e) {
65+
$this->abort($this->getAbortData(
66+
Config::RECYCLE_BIN_RESTORE_FAILED->value,
67+
['id' => $id, 'message' => $e->getMessage()]
68+
));
69+
}
5470

55-
try {
56-
$this->recycleBinService->restoreItem($id);
57-
} catch (Exception $e) {
58-
$this->abort($this->getAbortData(
59-
Config::RECYCLE_BIN_RESTORE_FAILED->value,
60-
['id' => $id, 'message' => $e->getMessage()]
61-
));
71+
$this->updateProgress(
72+
$this->publishService,
73+
$this->userTopicService,
74+
$jobRun,
75+
$stepName,
76+
$totalItems,
77+
$totalItems,
78+
);
6279
}
80+
}
6381

64-
$this->updateProgress(
65-
$this->publishService,
66-
$this->userTopicService,
67-
$jobRun,
68-
$this->getJobStep($message)->getName()
82+
protected function configureStep(): void
83+
{
84+
$this->stepConfiguration->setRequired(StepConfig::ITEMS_TO_RESTORE->value);
85+
$this->stepConfiguration->setAllowedTypes(
86+
StepConfig::ITEMS_TO_RESTORE->value,
87+
StepConfig::CONFIG_TYPE_ARRAY->value
6988
);
7089
}
7190
}

src/RecycleBin/Repository/RecycleBinRepository.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,25 @@ public function getListing(FilterParameter $parameters): Listing
3434

3535
return $listing;
3636
}
37+
38+
public function getItemIdsSortedByPath(array $ids): array
39+
{
40+
if (empty($ids)) {
41+
return [];
42+
}
43+
44+
$listing = new Listing();
45+
$listing->setCondition(
46+
'id IN (' . implode(',', array_map('intval', $ids)) . ')'
47+
);
48+
$listing->setOrderKey('path');
49+
$listing->setOrder('ASC');
50+
51+
$sortedIds = [];
52+
foreach ($listing->load() as $item) {
53+
$sortedIds[] = $item->getId();
54+
}
55+
56+
return $sortedIds;
57+
}
3758
}

src/RecycleBin/Repository/RecycleBinRepositoryInterface.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,11 @@
2222
interface RecycleBinRepositoryInterface
2323
{
2424
public function getListing(FilterParameter $parameters): Listing;
25+
26+
/**
27+
* @param int[] $ids
28+
*
29+
* @return int[]
30+
*/
31+
public function getItemIdsSortedByPath(array $ids): array;
2532
}

src/RecycleBin/Service/JobService.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,28 @@
1313

1414
namespace Pimcore\Bundle\StudioBackendBundle\RecycleBin\Service;
1515

16+
use Generator;
1617
use Pimcore\Bundle\GenericExecutionEngineBundle\Agent\JobExecutionAgentInterface;
1718
use Pimcore\Bundle\GenericExecutionEngineBundle\Model\Job;
1819
use Pimcore\Bundle\GenericExecutionEngineBundle\Model\JobStep;
1920
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config;
21+
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Jobs;
22+
use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig;
23+
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\RestoreItemsMessage;
24+
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Util\JobSteps;
2025
use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface;
2126
use Pimcore\Model\Element\ElementDescriptor;
27+
use function array_map;
28+
use function array_slice;
29+
use function count;
2230

2331
/**
2432
* @internal
2533
*/
2634
final readonly class JobService implements JobServiceInterface
2735
{
36+
private const int RESTORE_BATCH_SIZE = 500;
37+
2838
public function __construct(
2939
private JobExecutionAgentInterface $jobExecutionAgent,
3040
private SecurityServiceInterface $securityService,
@@ -55,4 +65,39 @@ public function createJob(string $jobName, string $jobStepName, string $messageF
5565

5666
return $jobRun->getId();
5767
}
68+
69+
public function createRestoreJob(array $sortedItemIds): int
70+
{
71+
$steps = [];
72+
foreach ($this->chunkGenerator($sortedItemIds, self::RESTORE_BATCH_SIZE) as $batch) {
73+
$steps[] = new JobStep(
74+
JobSteps::RESTORE_ITEMS->value,
75+
RestoreItemsMessage::class,
76+
'',
77+
[StepConfig::ITEMS_TO_RESTORE->value => $batch],
78+
);
79+
}
80+
81+
$job = new Job(
82+
name: Jobs::RECYCLE_BIN_RESTORE->value,
83+
steps: $steps,
84+
);
85+
86+
$jobRun = $this->jobExecutionAgent->startJobExecution(
87+
$job,
88+
$this->securityService->getCurrentUser()->getId(),
89+
Config::CONTEXT_STOP_ON_ERROR->value
90+
);
91+
92+
return $jobRun->getId();
93+
}
94+
95+
private function chunkGenerator(array $items, int $size): Generator
96+
{
97+
$total = count($items);
98+
99+
for ($i = 0; $i < $total; $i += $size) {
100+
yield array_slice($items, $i, $size);
101+
}
102+
}
58103
}

src/RecycleBin/Service/JobServiceInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@
1919
interface JobServiceInterface
2020
{
2121
public function createJob(string $jobName, string $jobStepName, string $messageFQCN, array $items): int;
22+
23+
/**
24+
* @param int[] $sortedItemIds
25+
*/
26+
public function createRestoreJob(array $sortedItemIds): int;
2227
}

src/RecycleBin/Service/RecycleBinService.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
use Pimcore\Bundle\StudioBackendBundle\MappedParameter\ItemsParameter;
2626
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\Event\PreResponse\RecycleBinEvent;
2727
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\DeleteItemsMessage;
28-
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\RestoreItemsMessage;
2928
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Util\JobSteps;
3029
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\Hydrator\RecycleBinHydratorInterface;
3130
use Pimcore\Bundle\StudioBackendBundle\RecycleBin\Repository\RecycleBinRepositoryInterface;
@@ -80,12 +79,9 @@ public function restore(ItemsParameter $parameter): ?int
8079
return null;
8180
}
8281

83-
return $this->jobService->createJob(
84-
Jobs::RECYCLE_BIN_RESTORE->value,
85-
JobSteps::RESTORE_ITEMS->value,
86-
RestoreItemsMessage::class,
87-
$items
88-
);
82+
$sortedIds = $this->recycleBinRepository->getItemIdsSortedByPath($items);
83+
84+
return $this->jobService->createRestoreJob($sortedIds);
8985
}
9086

9187
/**

0 commit comments

Comments
 (0)