From 94ed0af8769204148dac59f14b7b454daef265ac Mon Sep 17 00:00:00 2001 From: lukmzig Date: Wed, 26 Nov 2025 15:02:52 +0100 Subject: [PATCH 01/13] add logic to patch assets in more messages --- config/elements.yaml | 1 + config/pimcore/execution_engine.yaml | 1 + .../Request/PatchAssetFolderRequestBody.php | 24 +++----- .../Controller/PatchFolderController.php | 15 +++-- .../PatchDataObjectFolderRequestBody.php | 32 ++++------ .../Controller/PatchFolderController.php | 11 +++- .../Messenger/Handler/PatchFolderHandler.php | 56 +++++------------- .../Messenger/Handler/PatchHandler.php | 19 +++++- .../Messenger/Handler/RefreshJobHandler.php | 59 +++++++++++++++++++ .../Messenger/Messages/RefreshJobMessage.php | 23 ++++++++ src/Element/ExecutionEngine/Util/JobSteps.php | 1 + src/Patcher/Service/PatchService.php | 38 ++++++++---- src/Patcher/Service/PatchServiceInterface.php | 3 +- translations/studio_api_docs.en.yaml | 4 +- 14 files changed, 182 insertions(+), 105 deletions(-) create mode 100644 src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php create mode 100644 src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php diff --git a/config/elements.yaml b/config/elements.yaml index 497b60a42..871698df1 100644 --- a/config/elements.yaml +++ b/config/elements.yaml @@ -62,6 +62,7 @@ services: Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\PatchHandler: ~ Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\PatchFolderHandler: ~ Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\ElementUsageReplaceHandler: ~ + Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Handler\RefreshJobHandler: ~ # # Event Subscriber diff --git a/config/pimcore/execution_engine.yaml b/config/pimcore/execution_engine.yaml index 2e8964bf4..2f70dc9f4 100644 --- a/config/pimcore/execution_engine.yaml +++ b/config/pimcore/execution_engine.yaml @@ -25,6 +25,7 @@ framework: Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\ElementDeleteMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchFolderMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchMessage: pimcore_generic_execution_engine + Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RefreshJobMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RecycleBinMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RewriteRefMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage: pimcore_generic_execution_engine diff --git a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php index 9713a47c0..159f462cf 100644 --- a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php +++ b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php @@ -39,23 +39,13 @@ public function __construct() properties: [ new Property( property: 'data', - type: 'array', - items: new Items( - required: ['folderId'], - properties: [ - new Property( - property: 'folderId', - description: 'Folder ID', - type: 'integer', - example: 83 - ), - new UpdateIntegerProperty('parentId'), - new UpdateStringProperty('key'), - new UpdateStringProperty('locked'), - new CustomMetadata(PatchCustomMetadata::class), - ], - type: 'object', - ), + properties: [ + new UpdateIntegerProperty('parentId'), + new UpdateStringProperty('key'), + new UpdateStringProperty('locked'), + new CustomMetadata(PatchCustomMetadata::class), + ], + type: 'object', ), new Property( property: 'filters', diff --git a/src/Asset/Controller/PatchFolderController.php b/src/Asset/Controller/PatchFolderController.php index e9ad1c9cb..71bff1631 100644 --- a/src/Asset/Controller/PatchFolderController.php +++ b/src/Asset/Controller/PatchFolderController.php @@ -18,6 +18,7 @@ use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException; use Pimcore\Bundle\StudioBackendBundle\MappedParameter\PatchFolderParameter; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; @@ -38,6 +39,8 @@ */ final class PatchFolderController extends AbstractApiController { + private const string ROUTE = '/assets/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly PatchServiceInterface $patchService, @@ -49,15 +52,16 @@ public function __construct( /** * @throws UserNotFoundException */ - #[Route('/assets/folder', name: 'pimcore_studio_api_patch_asset_folder', methods: ['PATCH'])] + #[Route(self::ROUTE, name: 'pimcore_studio_api_patch_asset_folder', methods: ['PATCH'])] #[IsGranted(UserPermissions::ASSETS->value)] #[Patch( - path: self::PREFIX . '/assets/folder', + path: self::PREFIX . self::ROUTE, operationId: 'asset_patch_folder_by_id', description: 'asset_patch_folder_by_id_description', summary: 'asset_patch_folder_by_id_summary', tags: [Tags::Assets->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[PatchAssetFolderRequestBody] #[CreatedResponse( description: 'asset_patch_by_id_created_response', @@ -67,10 +71,13 @@ public function __construct( HttpResponseCodes::NOT_FOUND, HttpResponseCodes::UNAUTHORIZED, ])] - public function assetPatchFolderById(#[MapRequestPayload] PatchFolderParameter $patchFolderParameter): Response + public function assetPatchFolderById ( + int $id, + #[MapRequestPayload] PatchFolderParameter $patchFolderParameter + ): Response { - $jobRunId = $this->patchService->patchFolder( + $id, ElementTypes::TYPE_ASSET, $patchFolderParameter, $this->securityService->getCurrentUser() diff --git a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php index 1c2c41ce4..6df7d814b 100644 --- a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php +++ b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php @@ -39,27 +39,17 @@ public function __construct() properties: [ new Property( property: 'data', - type: 'array', - items: new Items( - required: ['folderId'], - properties: [ - new Property( - property: 'folderId', - description: 'Folder ID', - type: 'integer', - example: 2 - ), - new UpdateIntegerProperty('parentId'), - new UpdateIntegerProperty('index', 0), - new UpdateStringProperty('key'), - new UpdateStringProperty('locked'), - new UpdateStringProperty('childrenSortBy'), - new UpdateStringProperty('childrenSortOrder'), - new UpdateBooleanProperty('published'), - new UpdateObjectProperty('editableData'), - ], - type: 'object', - ), + properties: [ + new UpdateIntegerProperty('parentId'), + new UpdateIntegerProperty('index', 0), + new UpdateStringProperty('key'), + new UpdateStringProperty('locked'), + new UpdateStringProperty('childrenSortBy'), + new UpdateStringProperty('childrenSortOrder'), + new UpdateBooleanProperty('published'), + new UpdateObjectProperty('editableData'), + ], + type: 'object', ), new Property( property: 'filters', diff --git a/src/DataObject/Controller/PatchFolderController.php b/src/DataObject/Controller/PatchFolderController.php index 2533d4770..ca63aabd4 100644 --- a/src/DataObject/Controller/PatchFolderController.php +++ b/src/DataObject/Controller/PatchFolderController.php @@ -19,6 +19,7 @@ use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException; use Pimcore\Bundle\StudioBackendBundle\MappedParameter\PatchFolderParameter; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; @@ -39,6 +40,8 @@ */ final class PatchFolderController extends AbstractApiController { + private const string ROUTE = '/data-objects/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly PatchServiceInterface $patchService, @@ -50,15 +53,16 @@ public function __construct( /** * @throws InvalidArgumentException|UserNotFoundException */ - #[Route('/data-objects/folder', name: 'pimcore_studio_api_patch_data_object_folder', methods: ['PATCH'])] + #[Route(self::ROUTE, name: 'pimcore_studio_api_patch_data_object_folder', methods: ['PATCH'])] #[IsGranted(UserPermissions::DATA_OBJECTS->value)] #[Patch( - path: self::PREFIX . '/data-objects/folder', + path: self::PREFIX . self::ROUTE, operationId: 'data_object_patch_folder_by_id', description: 'data_object_patch_folder_by_id_description', summary: 'data_object_patch_folder_by_id_summary', tags: [Tags::DataObjects->value] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[PatchDataObjectFolderRequestBody] #[CreatedResponse( description: 'data_object_patch_by_id_created_response', @@ -69,10 +73,11 @@ public function __construct( HttpResponseCodes::UNAUTHORIZED, ])] public function dataObjectsPatchFolderById( + int $id, #[MapRequestPayload] PatchFolderParameter $patchFolderParameter ): Response { - $jobRunId = $this->patchService->patchFolder( + $id, ElementTypes::TYPE_OBJECT, $patchFolderParameter, $this->securityService->getCurrentUser() diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php index 27044a851..1d2e9615c 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchFolderHandler.php @@ -17,19 +17,16 @@ use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\DataIndex\Grid\GridSearchInterface; use Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\PatchFolderMessage; -use Pimcore\Bundle\StudioBackendBundle\Element\Service\ElementServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\AbortActionData; -use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait; use Pimcore\Bundle\StudioBackendBundle\Grid\MappedParameter\GridParameter; use Pimcore\Bundle\StudioBackendBundle\Grid\Mapper\FilterParameterMapperInterface; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; -use Pimcore\Bundle\StudioBackendBundle\Patcher\Service\PatchServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait; +use Pimcore\Model\Element\ElementDescriptor; use Symfony\Component\Messenger\Attribute\AsMessageHandler; -use function count; /** * @internal @@ -43,8 +40,6 @@ final class PatchFolderHandler extends AbstractHandler public function __construct( private readonly FilterParameterMapperInterface $filterParameterMapper, private readonly PublishServiceInterface $publishService, - private readonly ElementServiceInterface $elementService, - private readonly PatchServiceInterface $patchService, private readonly UserResolverInterface $userResolver, private readonly GridSearchInterface $gridSearch, ) { @@ -61,6 +56,11 @@ public function __invoke(PatchFolderMessage $message): void return; } + $job = $jobRun->getJob(); + if ($job === null) { + return; + } + $validatedParameters = $this->validateFullParameters( $message, $jobRun, @@ -92,41 +92,15 @@ public function __invoke(PatchFolderMessage $message): void return; } - $jobEnvironmentData = $jobRun->getJob()?->getEnvironmentData(); - - $elementCount = count($elementIds); - foreach ($elementIds as $elementId) { - $element = $this->elementService->getAllowedElementById( - $elementType, - $elementId, - $validatedParameters->getUser() - ); - - try { - $this->patchService->patchElement( - $element, - $elementType, - $jobEnvironmentData[$folderId], - $validatedParameters->getUser() - ); - } catch (Exception $exception) { - $this->abort($this->getAbortData( - Config::ELEMENT_PATCH_FAILED_MESSAGE->value, - [ - 'type' => $elementType, - 'id' => $element->getId(), - 'message' => $exception->getMessage(), - ], - )); - } - - $this->updateProgress( - $this->publishService, - $jobRun, - $this->getJobStep($message)->getName(), - $elementCount - ); - } + $job->setSelectedElements( + array_map(static fn ($id) => new ElementDescriptor($elementType, $id), $elementIds) + ); + + $this->updateProgress( + $this->publishService, + $jobRun, + $this->getJobStep($message)->getName() + ); } protected function configureStep(): void diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php index 8804874a5..8efd2bd22 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/PatchHandler.php @@ -20,6 +20,7 @@ use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Model\AbortActionData; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Trait\HandlerProgressTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Patcher\Service\PatchServiceInterface; @@ -70,9 +71,12 @@ public function __invoke(PatchMessage $message): void $this->elementService ); $elementId = $element->getId(); - $elementType = $this->getElementType($element); + $elementType = $this->getElementType($element, true); + $folderId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::FOLDER_TO_EXPORT->value); $jobEnvironmentData = $jobRun->getJob()?->getEnvironmentData(); - if (!isset($jobEnvironmentData[(string)$elementId])) { + + $patchDataKey = $folderId ?? $elementId; + if (!isset($jobEnvironmentData[$patchDataKey])) { $this->abort($this->getAbortData( Config::ELEMENT_PATCH_FAILED_MESSAGE->value, [ @@ -87,7 +91,7 @@ public function __invoke(PatchMessage $message): void $this->patchService->patchElement( $element, $elementType, - $jobEnvironmentData[$elementId], + $jobEnvironmentData[$patchDataKey], $validatedParameters->getUser() ); } catch (Exception $exception) { @@ -103,4 +107,13 @@ public function __invoke(PatchMessage $message): void $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } + + protected function configureStep(): void + { + $this->stepConfiguration->setDefault(StepConfig::FOLDER_TO_EXPORT->value, null); + $this->stepConfiguration->setAllowedTypes( + StepConfig::FOLDER_TO_EXPORT->value, + StepConfig::CONFIG_TYPE_INT->value + ); + } } diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php new file mode 100644 index 000000000..b8595cf11 --- /dev/null +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php @@ -0,0 +1,59 @@ +getJobRun($message); + if (!$this->shouldBeExecuted($jobRun)) { + return; + } + + $selectedElementCount = count($jobRun->getJob()?->getSelectedElements() ?? []); + $totalElements = $jobRun->getTotalElements(); + if ($selectedElementCount === $totalElements) { + return; + } + + $jobRun->setTotalElements(count($jobRun->getJob()?->getSelectedElements() ?? [])); + + $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); + } +} diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php new file mode 100644 index 000000000..63772fbf0 --- /dev/null +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Messages/RefreshJobMessage.php @@ -0,0 +1,23 @@ +getClassId(); if ($elementType === ElementTypes::TYPE_OBJECT && $classId === null) { throw new InvalidArgumentException('Class ID must be provided for object folder patching'); @@ -100,23 +103,32 @@ public function patchFolder( name: Jobs::PATCH_ELEMENTS->value, steps: [ new JobStep( - JobSteps::ELEMENT_FOLDER_PATCHING->value, - PatchFolderMessage::class, - '', - [ + name: JobSteps::ELEMENT_FOLDER_PATCHING->value, + messageFQCN: PatchFolderMessage::class, + condition: '', + config: [ StepConfig::CONFIG_FILTERS->value => $patchFolderParameter->getFilters(), StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', ] ), - ], - selectedElements: array_map( - static fn (array $data) => new ElementDescriptor( - $elementType, - $data['folderId'] + new JobStep( + name: JobSteps::ELEMENT_REFRESH_COUNT->value, + messageFQCN: RefreshJobMessage::class, + condition: '', + config:[], + selectionProcessingMode: SelectionProcessingMode::ONCE ), - $patchFolderParameter->getData() - ), - environmentData: array_column($patchFolderParameter->getData(), null, 'folderId'), + new JobStep( + name: JobSteps::ELEMENT_PATCHING->value, + messageFQCN: PatchMessage::class, + condition: '', + config:[ + StepConfig::FOLDER_TO_EXPORT->value => $folderId + ] + ), + ], + selectedElements: [new ElementDescriptor($elementType, $folderId)], + environmentData: [$folderId => $patchFolderParameter->getData()], ); $jobRun = $this->jobExecutionAgent->startJobExecution( diff --git a/src/Patcher/Service/PatchServiceInterface.php b/src/Patcher/Service/PatchServiceInterface.php index 4caddd1d0..4014c9b13 100644 --- a/src/Patcher/Service/PatchServiceInterface.php +++ b/src/Patcher/Service/PatchServiceInterface.php @@ -39,10 +39,11 @@ public function patch( * @throws InvalidArgumentException */ public function patchFolder( + int $folderId, string $elementType, PatchFolderParameter $patchFolderParameter, UserInterface $user, - ): ?int; + ): int; /** * @throws ElementSavingFailedException diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml index 852dc5eb6..4b40d6185 100644 --- a/translations/studio_api_docs.en.yaml +++ b/translations/studio_api_docs.en.yaml @@ -163,7 +163,7 @@ asset_patch_by_id_description: | asset_patch_by_id_success_response: Successfully patched asset asset_patch_by_id_summary: Patch assets by ID asset_patch_folder_by_id_description: | - Patching assets based on the given folder ID and data.
Patching on folders will be done asynchronously.
+ Patching assets based on the given folder ID and data.
Patching on folder will be done asynchronously.
You can also use filters and sorting. Filter assets from folder based on the grid filter schema asset_patch_folder_by_id_summary: Patch all assets based on folder ID and filters asset_replace_description: | @@ -321,7 +321,7 @@ data_object_patch_by_id_description: | data_object_patch_by_id_success_response: Successfully patched data object data_object_patch_by_id_summary: Patch data objects by ID data_object_patch_folder_by_id_description: | - Patching data objects based on the given folder ID, filters and data.
Patching on folders will be done asynchronously.
+ Patching data objects based on the given folder ID, filters and data.
Patching on folder will be done asynchronously.
You can also use filters and sorting. Filter data objects from folder based on the grid filter schema data_object_patch_folder_by_id_summary: Patch all data objects based on folder ID and filters data_object_replace_content_description: | From e728717792b62909f0a1266138c2323df2c6bbd5 Mon Sep 17 00:00:00 2001 From: lukmzig <30526586+lukmzig@users.noreply.github.com> Date: Wed, 26 Nov 2025 14:04:43 +0000 Subject: [PATCH 02/13] Apply php-cs-fixer changes --- src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php | 1 - src/Asset/Controller/PatchFolderController.php | 5 ++--- .../Attribute/Request/PatchDataObjectFolderRequestBody.php | 1 - .../AutomationAction/Messenger/Handler/RefreshJobHandler.php | 1 + src/Patcher/Service/PatchService.php | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php index 159f462cf..a78afd138 100644 --- a/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php +++ b/src/Asset/Attribute/Request/PatchAssetFolderRequestBody.php @@ -14,7 +14,6 @@ namespace Pimcore\Bundle\StudioBackendBundle\Asset\Attribute\Request; use Attribute; -use OpenApi\Attributes\Items; use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Property; use OpenApi\Attributes\RequestBody; diff --git a/src/Asset/Controller/PatchFolderController.php b/src/Asset/Controller/PatchFolderController.php index 71bff1631..5bb332097 100644 --- a/src/Asset/Controller/PatchFolderController.php +++ b/src/Asset/Controller/PatchFolderController.php @@ -71,11 +71,10 @@ public function __construct( HttpResponseCodes::NOT_FOUND, HttpResponseCodes::UNAUTHORIZED, ])] - public function assetPatchFolderById ( + public function assetPatchFolderById( int $id, #[MapRequestPayload] PatchFolderParameter $patchFolderParameter - ): Response - { + ): Response { $jobRunId = $this->patchService->patchFolder( $id, ElementTypes::TYPE_ASSET, diff --git a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php index 6df7d814b..8c85a913b 100644 --- a/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php +++ b/src/DataObject/Attribute/Request/PatchDataObjectFolderRequestBody.php @@ -14,7 +14,6 @@ namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Attribute\Request; use Attribute; -use OpenApi\Attributes\Items; use OpenApi\Attributes\JsonContent; use OpenApi\Attributes\Property; use OpenApi\Attributes\RequestBody; diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php index b8595cf11..bef4d913b 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php @@ -20,6 +20,7 @@ use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use function count; /** * @internal diff --git a/src/Patcher/Service/PatchService.php b/src/Patcher/Service/PatchService.php index a17fa42c5..ea6caea81 100644 --- a/src/Patcher/Service/PatchService.php +++ b/src/Patcher/Service/PatchService.php @@ -123,7 +123,7 @@ public function patchFolder( messageFQCN: PatchMessage::class, condition: '', config:[ - StepConfig::FOLDER_TO_EXPORT->value => $folderId + StepConfig::FOLDER_TO_EXPORT->value => $folderId, ] ), ], From e384944dc483a41ed27e999e7363774738df96e5 Mon Sep 17 00:00:00 2001 From: lukmzig Date: Wed, 26 Nov 2025 15:07:59 +0100 Subject: [PATCH 03/13] update refresh handler --- .../AutomationAction/Messenger/Handler/RefreshJobHandler.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php index bef4d913b..8feba1a32 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/RefreshJobHandler.php @@ -48,12 +48,11 @@ public function __invoke(RefreshJobMessage $message): void } $selectedElementCount = count($jobRun->getJob()?->getSelectedElements() ?? []); - $totalElements = $jobRun->getTotalElements(); - if ($selectedElementCount === $totalElements) { + if ($selectedElementCount === $jobRun->getTotalElements()) { return; } - $jobRun->setTotalElements(count($jobRun->getJob()?->getSelectedElements() ?? [])); + $jobRun->setTotalElements($selectedElementCount); $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } From e8f736c704cba05f3798dee8ecb53dcfab9ad81e Mon Sep 17 00:00:00 2001 From: lukmzig Date: Thu, 27 Nov 2025 15:31:48 +0100 Subject: [PATCH 04/13] optimize export processing --- config/assets.yaml | 1 - config/data_objects.yaml | 1 - config/export.yaml | 2 + config/pimcore/execution_engine.yaml | 3 +- .../Handler/ExportDataCollectionHandler.php | 25 +-- .../ExportFolderDataCollectionHandler.php | 158 ---------------- .../Handler/ExportDataCollectionHandler.php | 30 +--- .../ExportFolderDataCollectionHandler.php | 168 ------------------ .../ExportFolderDataCollectionMessage.php | 23 --- src/ExecutionEngine/Util/StepConfig.php | 1 + .../Request/ExportDataRequestBody.php | 6 + .../Request/ExportFolderDataRequestBody.php | 1 - .../Controller/Csv/FolderController.php | 11 +- .../Controller/Xlsx/FolderController.php | 11 +- .../Handler/FolderCollectionHandler.php | 141 +++++++++++++++ .../Messages/FolderCollectionMessage.php} | 4 +- .../MappedParameter/ExportFolderParameter.php | 20 --- .../MappedParameter/ExportParameter.php | 12 +- .../Service/ExecutionEngine/ExportService.php | 128 +++++++------ .../ExportServiceInterface.php | 20 ++- .../Util/Trait/CsvExportHandlerSetupTrait.php | 5 + src/Grid/Service/GridService.php | 8 + src/Patcher/Service/PatchService.php | 34 ++-- translations/studio_api_docs.en.yaml | 9 +- 24 files changed, 317 insertions(+), 505 deletions(-) delete mode 100644 src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php delete mode 100644 src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php delete mode 100644 src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php create mode 100644 src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php rename src/{Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php => Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php} (72%) diff --git a/config/assets.yaml b/config/assets.yaml index 556f63f05..4cd63fca6 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -74,7 +74,6 @@ services: Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\AssetCloneHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\AssetUploadHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ExportDataCollectionHandler: ~ - Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ExportFolderDataCollectionHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ZipDownloadHandler: ~ Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Handler\ZipUploadHandler: ~ diff --git a/config/data_objects.yaml b/config/data_objects.yaml index 22fec0efd..bebb04e96 100644 --- a/config/data_objects.yaml +++ b/config/data_objects.yaml @@ -206,7 +206,6 @@ services: Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\CloneHandler: ~ Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\ExportDataCollectionHandler: ~ - Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Handler\ExportFolderDataCollectionHandler: ~ # # Event Subscriber diff --git a/config/export.yaml b/config/export.yaml index 68f1343c0..93bc96c71 100644 --- a/config/export.yaml +++ b/config/export.yaml @@ -23,6 +23,8 @@ services: arguments: $xlsxExportService: '@Pimcore\Bundle\StudioBackendBundle\Export\Service\XlsxExportService' + Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Handler\FolderCollectionHandler: ~ + # # Services # diff --git a/config/pimcore/execution_engine.yaml b/config/pimcore/execution_engine.yaml index 2f70dc9f4..8fa7a7cb5 100644 --- a/config/pimcore/execution_engine.yaml +++ b/config/pimcore/execution_engine.yaml @@ -14,12 +14,10 @@ framework: Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\AssetCloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\AssetUploadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage: pimcore_generic_execution_engine - Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ZipDownloadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ZipUploadMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\CloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage: pimcore_generic_execution_engine - Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Document\ExecutionEngine\AutomationAction\Messenger\Messages\CloneMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\BatchDeleteMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\ElementDeleteMessage: pimcore_generic_execution_engine @@ -30,6 +28,7 @@ framework: Pimcore\Bundle\StudioBackendBundle\Element\ExecutionEngine\AutomationAction\Messenger\Messages\RewriteRefMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage: pimcore_generic_execution_engine + Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\FolderCollectionMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\Tag\ExecutionEngine\AutomationAction\Messenger\Messages\BatchTagOperationMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\DeleteItemsMessage: pimcore_generic_execution_engine Pimcore\Bundle\StudioBackendBundle\RecycleBin\ExecutionEngine\Messages\RestoreItemsMessage: pimcore_generic_execution_engine diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 5cc9539be..f7e501129 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -16,7 +16,6 @@ use Exception; use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\Asset\Service\AssetServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; @@ -41,8 +40,7 @@ public function __construct( private readonly ColumnConfigurationServiceInterface $columnConfigurationService, private readonly PublishServiceInterface $publishService, private readonly UserResolverInterface $userResolver, - private readonly GridServiceInterface $gridService, - private readonly AssetServiceInterface $assetService + private readonly GridServiceInterface $gridService ) { parent::__construct(); } @@ -69,20 +67,6 @@ public function __invoke(ExportDataCollectionMessage $message): void } $jobAsset = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $asset = $this->assetService->getAssetForUser($jobAsset['id'], $user); - - if ($asset->getType() === ElementTypes::TYPE_FOLDER) { - $this->abort($this->getAbortData( - Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value, - [ - 'folderId' => $asset->getId(), - ] - )); - - return; - } - $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableAssetColumnConfiguration(); @@ -94,10 +78,10 @@ public function __invoke(ExportDataCollectionMessage $message): void try { $assetData = [ - $asset->getId() => $this->gridService->getGridValuesForElement( + $jobAsset['id'] => $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_ASSET, - $asset->getId(), + $jobAsset['id'], true ), ]; @@ -105,7 +89,6 @@ public function __invoke(ExportDataCollectionMessage $message): void $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetData); $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - if ($csvExportDataInfo === null) { $this->updateContextArrayValues( $jobRun, @@ -120,7 +103,7 @@ public function __invoke(ExportDataCollectionMessage $message): void $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $asset->getId(), + 'id' => $jobAsset['id'], 'message' => $e->getMessage(), ] )); diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php deleted file mode 100644 index 018bd76ef..000000000 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php +++ /dev/null @@ -1,158 +0,0 @@ -getJobRun($message); - if (!$this->shouldBeExecuted($jobRun)) { - return; - } - - $user = $this->userResolver->getById($jobRun->getOwnerId()); - - if ($user === null) { - $this->abort($this->getAbortData( - Config::USER_NOT_FOUND_MESSAGE->value, - [ - 'userId' => $jobRun->getOwnerId(), - ] - )); - } - - $jobFolder = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); - - $filters = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value); - - $assets = $this->gridSearch->searchElementIdsForUser( - ElementTypes::TYPE_ASSET, - new GridParameter( - $jobFolder['id'], - $columns, - $this->filterParameterMapper->fromArray($filters) - ), - $user - ); - - if (count($assets) === 0) { - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); - - return; - } - - $columnsDefinitions = $this->columnConfigurationService->getAvailableAssetColumnConfiguration(); - - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); - - foreach ($assets as $assetId) { - try { - $assetData = [ - $assetId => $this->gridService->getGridValuesForElement( - $columnCollection, - ElementTypes::TYPE_ASSET, - $assetId, - true - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetData); - } catch (Exception $e) { - $this->abort($this->getAbortData( - Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, - [ - 'id' => $assetId, - 'message' => $e->getMessage(), - ] - )); - } - } - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, - [ - 'type' => ElementTypes::TYPE_ASSET, - ] - ); - } - - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); - } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_FILTERS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_FILTERS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - } -} diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 907927ac5..a953ae80e 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -15,9 +15,7 @@ use Exception; use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; -use Pimcore\Bundle\StudioBackendBundle\Class\Repository\ClassDefinitionRepositoryInterface; use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataObjectServiceInterface; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\AutomationAction\AbstractHandler; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; @@ -40,8 +38,6 @@ final class ExportDataCollectionHandler extends AbstractHandler public function __construct( private readonly ColumnConfigurationServiceInterface $columnConfigurationService, - private readonly ClassDefinitionRepositoryInterface $classDefinitionRepository, - private readonly DataObjectServiceInterface $dataObjectService, private readonly PublishServiceInterface $publishService, private readonly UserResolverInterface $userResolver, private readonly GridServiceInterface $gridService @@ -70,22 +66,10 @@ public function __invoke(ExportDataCollectionMessage $message): void )); } - $jobDataObject = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $dataObject = $this->dataObjectService->getDataObjectForUser($jobDataObject['id'], $user); - $classId = $this->classDefinitionRepository->getClassDefinition($dataObject->getClassName())->getId(); - - if ($dataObject->getType() === ElementTypes::TYPE_FOLDER) { - $this->abort($this->getAbortData( - Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value, - [ - 'folderId' => $dataObject->getId(), - ] - )); - - return; - } - + $dataObjectId = $this->extractConfigFieldFromJobStepConfig( + $message, StepConfig::ELEMENT_TO_EXPORT->value + )['id']; + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableDataObjectColumnConfiguration( $classId, @@ -96,10 +80,10 @@ public function __invoke(ExportDataCollectionMessage $message): void try { $dataObjectData = [ - $dataObject->getId() => $this->gridService->getGridValuesForElement( + $dataObjectId => $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_OBJECT, - $dataObject->getId(), + $dataObjectId, true ), ]; @@ -123,7 +107,7 @@ public function __invoke(ExportDataCollectionMessage $message): void $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $dataObject->getId(), + 'id' => $dataObjectId, 'message' => $e->getMessage(), ] )); diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php deleted file mode 100644 index f2f016f26..000000000 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportFolderDataCollectionHandler.php +++ /dev/null @@ -1,168 +0,0 @@ -getJobRun($message); - if (!$this->shouldBeExecuted($jobRun)) { - return; - } - - $user = $this->userResolver->getById($jobRun->getOwnerId()); - - if ($user === null) { - $this->abort($this->getAbortData( - Config::USER_NOT_FOUND_MESSAGE->value, - [ - 'userId' => $jobRun->getOwnerId(), - ] - )); - } - - $jobFolder = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); - - $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); - - $filters = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value); - - $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); - $filters = $this->filterParameterMapper->fromArray($filters); - $filters->setClassId($classId); - - $dataObjects = $this->gridSearch->searchElementIdsForUser( - ElementTypes::TYPE_OBJECT, - new GridParameter($jobFolder['id'], $columns, $filters), - $user - ); - - if (count($dataObjects) === 0) { - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); - - return; - } - - $columnsDefinitions = $this->columnConfigurationService->getAvailableDataObjectColumnConfiguration( - $classId, - $jobFolder['id'], - $user - ); - - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); - - foreach ($dataObjects as $dataObjectId) { - try { - $dataObjectData = [ - $dataObjectId => $this->gridService->getGridValuesForElement( - $columnCollection, - ElementTypes::TYPE_OBJECT, - $dataObjectId, - true - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $dataObjectData); - } catch (Exception $e) { - $this->abort($this->getAbortData( - Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, - [ - 'id' => $dataObjectId, - 'message' => $e->getMessage(), - ] - )); - } - } - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, - [ - 'type' => ElementTypes::TYPE_OBJECT, - 'classId' => $classId, - ] - ); - } - - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); - } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_FILTERS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_FILTERS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_CLASS_ID->value, - StepConfig::CONFIG_TYPE_STRING->value - ); - } -} diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php deleted file mode 100644 index 8d2918026..000000000 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php +++ /dev/null @@ -1,23 +0,0 @@ -value)] #[Post( - path: self::PREFIX . '/export/csv/folder', + path: self::PREFIX . self::ROUTE, operationId: 'export_csv_folder', description: 'export_csv_folder_description', summary: 'export_csv_folder_summary', tags: [Tags::Export->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[ExportFolderDataRequestBody] #[CreatedResponse( description: 'export_csv_created_response', @@ -62,11 +67,13 @@ public function __construct( HttpResponseCodes::NOT_FOUND, ])] public function exportCsvFolder( + int $id, #[MapRequestPayload] ExportFolderParameter $exportParameter ): Response { return $this->jsonResponse( [ 'jobRunId' => $this->exportService->generateExportFileForFolders( + $id, $exportParameter, ExportFormat::CSV->value ), diff --git a/src/Export/Controller/Xlsx/FolderController.php b/src/Export/Controller/Xlsx/FolderController.php index d4b46be1d..fe9fff4f3 100644 --- a/src/Export/Controller/Xlsx/FolderController.php +++ b/src/Export/Controller/Xlsx/FolderController.php @@ -19,10 +19,12 @@ use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine\ExportServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Export\Util\Constant\ExportFormat; +use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Path\IdParameter; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\IdJson; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\CreatedResponse; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; use Symfony\Component\HttpFoundation\Response; @@ -36,6 +38,8 @@ */ final class FolderController extends AbstractApiController { + private const string ROUTE = '/export/xlsx/folder/{id}'; + public function __construct( SerializerInterface $serializer, private readonly ExportServiceInterface $exportService @@ -43,15 +47,16 @@ public function __construct( parent::__construct($serializer); } - #[Route('/export/xlsx/folder', name: 'pimcore_studio_api_export_xlsx_folder', methods: ['POST'])] + #[Route(path: self::ROUTE, name: 'pimcore_studio_api_export_xlsx_folder', methods: ['POST'])] #[IsGranted(UserPermissions::ASSETS->value)] #[Post( - path: self::PREFIX . '/export/xlsx/folder', + path: self::PREFIX . self::ROUTE, operationId: 'export_xlsx_folder', description: 'export_xlsx_folder_description', summary: 'export_xlsx_folder_summary', tags: [Tags::Export->name] )] + #[IdParameter(type: ElementTypes::TYPE_FOLDER, name: 'id')] #[ExportFolderDataRequestBody(addDelimiter: false)] #[CreatedResponse( description: 'export_xlsx_created_response', @@ -62,11 +67,13 @@ public function __construct( HttpResponseCodes::NOT_FOUND, ])] public function exportXlsxFolder( + int $id, #[MapRequestPayload] ExportFolderParameter $exportParameter ): Response { return $this->jsonResponse( [ 'jobRunId' => $this->exportService->generateExportFileForFolders( + $id, $exportParameter, ExportFormat::XLSX->value ), diff --git a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php new file mode 100644 index 000000000..230eba690 --- /dev/null +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/FolderCollectionHandler.php @@ -0,0 +1,141 @@ +getJobRun($message); + if (!$this->shouldBeExecuted($jobRun)) { + return; + } + + $validatedParameters = $this->validateFullParameters( + $message, + $jobRun, + $this->userResolver, + ); + + if ($validatedParameters instanceof AbortActionData) { + $this->abort($validatedParameters); + } + + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); + $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); + $filters = $this->filterParameterMapper->fromArray( + $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_FILTERS->value) + ); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); + if ($classId !== '') { + $filters->setClassId($classId); + } + + $elements = $this->gridSearch->searchElementIdsForUser( + $elementType, + new GridParameter($validatedParameters->getSubject()->getId(), $columns, $filters), + $validatedParameters->getUser() + ); + + if (count($elements) === 0) { + $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); + + return; + } + + $config = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $childJobRunId = $this->exportService->generateExportFileForElements( + new ExportParameter($columns, $filters, $config, $elements, $elementType, $classId), + $this->extractConfigFieldFromJobStepConfig($message, StepConfig::EXPORT_FORMAT->value), + $validatedParameters->getUser() + ); + + $this->updateJobRunContext($jobRun, JobRunContext::CHILD_JOB_RUN->value, $childJobRunId); + + $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); + } + + protected function configureStep(): void + { + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_TYPE->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::EXPORT_FORMAT->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::EXPORT_FORMAT->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_COLUMNS->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_FILTERS->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_FILTERS->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_CONFIGURATION->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setDefaults([ + StepConfig::CONFIG_CONFIGURATION->value => [], + ]); + } +} diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php similarity index 72% rename from src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php rename to src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php index ea4c4e6c6..aee009d7a 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Messages/ExportFolderDataCollectionMessage.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Messages/FolderCollectionMessage.php @@ -11,13 +11,13 @@ * @license Pimcore Open Core License (POCL) */ -namespace Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages; +namespace Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages; use Pimcore\Bundle\GenericExecutionEngineBundle\Messenger\Messages\AbstractExecutionEngineMessage; /** * @internal */ -final class ExportFolderDataCollectionMessage extends AbstractExecutionEngineMessage +final class FolderCollectionMessage extends AbstractExecutionEngineMessage { } diff --git a/src/Export/MappedParameter/ExportFolderParameter.php b/src/Export/MappedParameter/ExportFolderParameter.php index fbfda936e..ecf15ae45 100644 --- a/src/Export/MappedParameter/ExportFolderParameter.php +++ b/src/Export/MappedParameter/ExportFolderParameter.php @@ -17,7 +17,6 @@ use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportConfigValidationTrait; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; -use Pimcore\Model\Element\ElementDescriptor; /** * @internal @@ -26,21 +25,13 @@ { use ExportConfigValidationTrait; - /** - * @param array $folders - */ public function __construct( private array $columns, private ?FilterParameter $filters, private array $config, - private array $folders, private string $elementType, private ?string $classId = null ) { - if (empty($this->getFolders())) { - throw new InvalidArgumentException('No folders provided'); - } - if ($this->classId === null && $this->getValidElementType($this->elementType) === ElementTypes::TYPE_OBJECT) { throw new InvalidArgumentException('Class ID must be provided for data object exports'); } @@ -63,17 +54,6 @@ public function getConfig(): array return $this->config; } - /** - * @return array - */ - public function getFolders(): array - { - return array_map( - fn (int $id) => new ElementDescriptor($this->getElementType(), $id), - $this->folders - ); - } - public function getElementType(): string { return $this->getValidElementType($this->elementType); diff --git a/src/Export/MappedParameter/ExportParameter.php b/src/Export/MappedParameter/ExportParameter.php index 0ea09c2da..0cf552bc7 100644 --- a/src/Export/MappedParameter/ExportParameter.php +++ b/src/Export/MappedParameter/ExportParameter.php @@ -16,6 +16,7 @@ use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportConfigValidationTrait; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Model\Element\ElementDescriptor; /** @@ -33,8 +34,12 @@ public function __construct( private ?FilterParameter $filters, private array $config, private array $elements, - private string $elementType + private string $elementType, + private ?string $classId = null ) { + if (empty($this->classId) && $this->getValidElementType($this->elementType) === ElementTypes::TYPE_OBJECT) { + throw new InvalidArgumentException('Class ID must be provided for data object exports'); + } if (empty($this->getElements())) { throw new InvalidArgumentException('No elements provided'); @@ -73,4 +78,9 @@ public function getElementType(): string { return $this->getValidElementType($this->elementType); } + + public function getClassId(): ?string + { + return $this->classId; + } } diff --git a/src/Export/Service/ExecutionEngine/ExportService.php b/src/Export/Service/ExecutionEngine/ExportService.php index d7898e15b..c5c9f132d 100644 --- a/src/Export/Service/ExecutionEngine/ExportService.php +++ b/src/Export/Service/ExecutionEngine/ExportService.php @@ -17,14 +17,14 @@ use Pimcore\Bundle\GenericExecutionEngineBundle\Model\Job; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\JobStep; use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage as AssetCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\Asset\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage as AssetFolderCollectionMessage; use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportDataCollectionMessage as DataObjectCollectionMessage; -use Pimcore\Bundle\StudioBackendBundle\DataObject\ExecutionEngine\AutomationAction\Messenger\Messages\ExportFolderDataCollectionMessage as DataObjectFolderCollectionMessage; +use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidElementTypeException; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Jobs; use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\StepConfig; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage; +use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\FolderCollectionMessage; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\Util\JobSteps; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; @@ -33,6 +33,7 @@ use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Model\Element\ElementDescriptor; +use Pimcore\Model\UserInterface; /** * @internal @@ -45,9 +46,23 @@ public function __construct( ) { } - public function generateExportFileForElements(ExportParameter $exportParameter, string $exportFormat): int + /** + * {@inheritdoc} + */ + public function generateExportFileForElements( + ExportParameter $exportParameter, + string $exportFormat, + ?UserInterface $user = null, + ): int { + $elementType = $exportParameter->getElementType(); + $classId = $exportParameter->getClassId(); + if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { + throw new InvalidArgumentException('Class ID must be provided for object folder patching'); + } + $collectionSettings = [ + StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), StepConfig::ELEMENT_TYPE->value => $exportParameter->getElementType(), ]; @@ -57,56 +72,68 @@ public function generateExportFileForElements(ExportParameter $exportParameter, StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), ]; - return $this->generateExportFileJob( - $exportParameter->getElements(), - $collectionSettings, - $creationSettings, - $this->getMessageClass($exportParameter->getElementType()), - $exportFormat - ); - } - - public function generateExportFileForFolders(ExportFolderParameter $exportParameter, string $exportFormat): int - { - $collectionSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_FILTERS->value => $exportParameter->getFilters(), + $jobSteps = [ + ...$this->mapJobSteps( + $exportParameter->getElements(), + $collectionSettings, + $this->getMessageClass($exportParameter->getElementType()) + ), + ...[$this->getExportFileStep($creationSettings, $exportFormat)], ]; - if ($exportParameter->getElementType() === ElementTypes::TYPE_OBJECT) { - $collectionSettings[StepConfig::ELEMENT_CLASS_ID->value] = $exportParameter->getClassId(); + if ($user === null) { + $user = $this->securityService->getCurrentUser(); } - $creationSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), - ]; + return $this->generateExportFileJob($jobSteps, $exportFormat, $user->getId()); + } + + /** + * {@inheritdoc} + */ + public function generateExportFileForFolders( + int $folderId, + ExportFolderParameter $exportParameter, + string $exportFormat + ): int + { + $elementType = $exportParameter->getElementType(); + $classId = $exportParameter->getClassId(); + if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { + throw new InvalidArgumentException('Class ID must be provided for object folder patching'); + } return $this->generateExportFileJob( - $exportParameter->getFolders(), - $collectionSettings, - $creationSettings, - $this->getMessageClassForFolder($exportParameter->getElementType()), - $exportFormat + [ + new JobStep( + JobSteps::DATA_COLLECTION->value, + FolderCollectionMessage::class, + '', + [ + StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', + StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), + StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), + StepConfig::CONFIG_FILTERS->value => $exportParameter->getFilters(), + StepConfig::EXPORT_FORMAT->value => $exportFormat, + StepConfig::ELEMENT_TYPE->value => $elementType, + ] + ) + ], + $exportFormat, + $this->securityService->getCurrentUser()->getId(), + [new ElementDescriptor($elementType, $folderId)] ); } private function generateExportFileJob( - array $elements, - array $collectionSettings, - array $creationSettings, - string $messageFQCN, - string $exportFormat + array $jobSteps, + string $exportFormat, + int $ownerId, + array $selectedElements = [], ): int { - - $jobSteps = [ - ...$this->mapJobSteps($elements, $collectionSettings, $messageFQCN, StepConfig::ELEMENT_TO_EXPORT), - ...[$this->getExportFileStep($creationSettings, $exportFormat)], - ]; - $jobRun = $this->jobExecutionAgent->startJobExecution( - $this->createJobByFormat($jobSteps, $exportFormat), - $this->securityService->getCurrentUser()->getId(), + $this->createJobByFormat($jobSteps, $exportFormat, $selectedElements), + $ownerId, Config::CONTEXT_STOP_ON_ERROR->value ); @@ -117,14 +144,13 @@ private function mapJobSteps( array $elements, array $collectionSettings, string $messageFQCN, - StepConfig $export ): array { return array_map( static fn (ElementDescriptor $element) => new JobStep( JobSteps::DATA_COLLECTION->value, $messageFQCN, '', - array_merge([$export->value => $element], $collectionSettings) + array_merge([StepConfig::ELEMENT_TO_EXPORT->value => $element], $collectionSettings) ), $elements, ); @@ -159,17 +185,14 @@ private function getXlsxCreationStep(array $settings): JobStep ); } - private function createJobByFormat(array $jobSteps, string $exportFormat): Job + private function createJobByFormat(array $jobSteps, string $exportFormat, array $selectedElements = []): Job { $name = Jobs::CREATE_CSV->value; if ($exportFormat === ExportFormat::XLSX->value) { $name = Jobs::CREATE_XLSX->value; } - return new Job( - name: $name, - steps: $jobSteps - ); + return new Job($name, $jobSteps, $selectedElements); } private function getMessageClass(string $elementType): string @@ -180,13 +203,4 @@ private function getMessageClass(string $elementType): string default => throw new InvalidElementTypeException($elementType) }; } - - private function getMessageClassForFolder(string $elementType): string - { - return match($elementType) { - ElementTypes::TYPE_ASSET => AssetFolderCollectionMessage::class, - ElementTypes::TYPE_OBJECT => DataObjectFolderCollectionMessage::class, - default => throw new InvalidElementTypeException($elementType) - }; - } } diff --git a/src/Export/Service/ExecutionEngine/ExportServiceInterface.php b/src/Export/Service/ExecutionEngine/ExportServiceInterface.php index 14a5e12ab..847e84b4b 100644 --- a/src/Export/Service/ExecutionEngine/ExportServiceInterface.php +++ b/src/Export/Service/ExecutionEngine/ExportServiceInterface.php @@ -13,15 +13,31 @@ namespace Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine; +use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportFolderParameter; use Pimcore\Bundle\StudioBackendBundle\Export\MappedParameter\ExportParameter; +use Pimcore\Model\UserInterface; /** * @internal */ interface ExportServiceInterface { - public function generateExportFileForElements(ExportParameter $exportParameter, string $exportFormat): int; + /** + * @throws InvalidArgumentException + */ + public function generateExportFileForElements( + ExportParameter $exportParameter, + string $exportFormat, + ?UserInterface $user = null, + ): int; - public function generateExportFileForFolders(ExportFolderParameter $exportParameter, string $exportFormat): int; + /** + * @throws InvalidArgumentException + */ + public function generateExportFileForFolders( + int $folderId, + ExportFolderParameter $exportParameter, + string $exportFormat + ): int; } diff --git a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php index ddf457339..9f4e3db02 100644 --- a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php +++ b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php @@ -22,6 +22,11 @@ trait CsvExportHandlerSetupTrait { protected function configureStep(): void { + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); $this->stepConfiguration->setAllowedTypes( StepConfig::ELEMENT_TO_EXPORT->value, diff --git a/src/Grid/Service/GridService.php b/src/Grid/Service/GridService.php index b761f57d7..9088d8456 100644 --- a/src/Grid/Service/GridService.php +++ b/src/Grid/Service/GridService.php @@ -20,6 +20,7 @@ use Pimcore\Bundle\StudioBackendBundle\DataIndex\SearchResult\SearchResultItemInterface; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException; use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException; +use Pimcore\Bundle\StudioBackendBundle\ExecutionEngine\Util\Config; use Pimcore\Bundle\StudioBackendBundle\Filter\MappedParameter\FilterParameter; use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnCollectorInterface; use Pimcore\Bundle\StudioBackendBundle\Grid\Column\ColumnDefinitionInterface; @@ -179,6 +180,13 @@ public function getGridDataForElement( $databaseElement = $this->getElement($this->serviceResolver, $elementType, $elementId); } + if ($isExport && $databaseElement->getType() === ElementTypes::TYPE_FOLDER) { + throw new InvalidArgumentException( + message: 'Exporting folder elements is not supported', + errorKey: Config::ELEMENT_FOLDER_COLLECTION_NOT_SUPPORTED->value + ); + } + foreach ($columnCollection->getColumns() as $column) { // move this to the resolver if (!$this->supports($column, $elementType)) { diff --git a/src/Patcher/Service/PatchService.php b/src/Patcher/Service/PatchService.php index ea6caea81..6a481fccf 100644 --- a/src/Patcher/Service/PatchService.php +++ b/src/Patcher/Service/PatchService.php @@ -100,35 +100,35 @@ public function patchFolder( } $job = new Job( - name: Jobs::PATCH_ELEMENTS->value, - steps: [ + Jobs::PATCH_ELEMENTS->value, + [ new JobStep( - name: JobSteps::ELEMENT_FOLDER_PATCHING->value, - messageFQCN: PatchFolderMessage::class, - condition: '', - config: [ + JobSteps::ELEMENT_FOLDER_PATCHING->value, + PatchFolderMessage::class, + '', + [ StepConfig::CONFIG_FILTERS->value => $patchFolderParameter->getFilters(), StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', ] ), new JobStep( - name: JobSteps::ELEMENT_REFRESH_COUNT->value, - messageFQCN: RefreshJobMessage::class, - condition: '', - config:[], - selectionProcessingMode: SelectionProcessingMode::ONCE + JobSteps::ELEMENT_REFRESH_COUNT->value, + RefreshJobMessage::class, + '', + [], + SelectionProcessingMode::ONCE ), new JobStep( - name: JobSteps::ELEMENT_PATCHING->value, - messageFQCN: PatchMessage::class, - condition: '', - config:[ + JobSteps::ELEMENT_PATCHING->value, + PatchMessage::class, + '', + [ StepConfig::FOLDER_TO_EXPORT->value => $folderId, ] ), ], - selectedElements: [new ElementDescriptor($elementType, $folderId)], - environmentData: [$folderId => $patchFolderParameter->getData()], + [new ElementDescriptor($elementType, $folderId)], + [$folderId => $patchFolderParameter->getData()], ); $jobRun = $this->jobExecutionAgent->startJobExecution( diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml index 4b40d6185..13b5bde5b 100644 --- a/translations/studio_api_docs.en.yaml +++ b/translations/studio_api_docs.en.yaml @@ -593,8 +593,8 @@ export_csv_description: | Download has to be triggered separately via the csv download route with the {jobRunId} returned in the response export_csv_summary: Creating CSV file for elements export_csv_folder_description: | - Creating the CSV file for elements based on the folder.
Parameters are:
    -
  • folders: Array of folder ids
  • + Creating the CSV file for elements based on the folder.
    + Parameters are:
    • columns: Describes the columns that should be exported. Can be obtained via the grid endpoint. For folders you can also use filters and sorting
    • filters: Filter elements from folder based on the grid filter schema
    • config: Delimiter and header options
    • @@ -630,8 +630,9 @@ export_xlsx_description: | Download has to be triggered separately via the xlsx download route with the {jobRunId} returned in the response export_xlsx_summary: Creating XLSX file export for elements export_xlsx_folder_description: | - Creating the XLSX file for elements based on the folder.
      Parameters are:
        -
      • folders: Array of folder ids
      • + Creating the XLSX file for elements based on the folder.
        + Parameters are: +
        • columns: Describes the columns that should be exported. Can be obtained via the grid endpoint. For folders you can also use filters and sorting
        • filters: Filter elements from folder based on the grid filter schema
        • config: Header options
        • From fbd8305eaf1d3a24e98caad155cd32990532643a Mon Sep 17 00:00:00 2001 From: lukmzig <30526586+lukmzig@users.noreply.github.com> Date: Thu, 27 Nov 2025 14:32:37 +0000 Subject: [PATCH 05/13] Apply php-cs-fixer changes --- src/Export/Service/ExecutionEngine/ExportService.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Export/Service/ExecutionEngine/ExportService.php b/src/Export/Service/ExecutionEngine/ExportService.php index c5c9f132d..001870956 100644 --- a/src/Export/Service/ExecutionEngine/ExportService.php +++ b/src/Export/Service/ExecutionEngine/ExportService.php @@ -53,8 +53,7 @@ public function generateExportFileForElements( ExportParameter $exportParameter, string $exportFormat, ?UserInterface $user = null, - ): int - { + ): int { $elementType = $exportParameter->getElementType(); $classId = $exportParameter->getClassId(); if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { @@ -95,8 +94,7 @@ public function generateExportFileForFolders( int $folderId, ExportFolderParameter $exportParameter, string $exportFormat - ): int - { + ): int { $elementType = $exportParameter->getElementType(); $classId = $exportParameter->getClassId(); if ($elementType === ElementTypes::TYPE_OBJECT && empty($classId)) { @@ -117,7 +115,7 @@ public function generateExportFileForFolders( StepConfig::EXPORT_FORMAT->value => $exportFormat, StepConfig::ELEMENT_TYPE->value => $elementType, ] - ) + ), ], $exportFormat, $this->securityService->getCurrentUser()->getId(), From 6cfcff174fe7eb153c0f623247de6af98b741822 Mon Sep 17 00:00:00 2001 From: lukmzig Date: Thu, 27 Nov 2025 15:34:22 +0100 Subject: [PATCH 06/13] code style --- .../Messenger/Handler/ExportDataCollectionHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index a953ae80e..29fd66b22 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -67,7 +67,8 @@ public function __invoke(ExportDataCollectionMessage $message): void } $dataObjectId = $this->extractConfigFieldFromJobStepConfig( - $message, StepConfig::ELEMENT_TO_EXPORT->value + $message, + StepConfig::ELEMENT_TO_EXPORT->value )['id']; $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); From 02cf29ced3a6615318bd1afbc7e8c66429edc60b Mon Sep 17 00:00:00 2001 From: lukmzig Date: Fri, 28 Nov 2025 09:35:21 +0100 Subject: [PATCH 07/13] update descriptions for endpoints --- translations/studio_api_docs.en.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/translations/studio_api_docs.en.yaml b/translations/studio_api_docs.en.yaml index 13b5bde5b..72412a041 100644 --- a/translations/studio_api_docs.en.yaml +++ b/translations/studio_api_docs.en.yaml @@ -606,15 +606,19 @@ export_csv_folder_summary: Creating CSV file for elements based on folder export_delete_csv_description: | Delete the CSV file with given {jobRunId} returned in the response of the create csv endpoint export_delete_csv_summary: Delete CSV export file based on jobRunId -export_download_csv_description: | - Download the CSV file with given {jobRunId} returned in the response of the create csv endpoint +export_download_csv_description: | + Download the CSV file using the {jobRunId} returned by the create export endpoint.
          + For folder exports, you must provide the ID of the corresponding child job run.
          + The folder export first collects all elements and then creates a child job run to export them. export_download_csv_success_response: CSV File as attachment export_download_csv_summary: Download CSV file for job run id export_delete_xlsx_description: | Delete the XLSX file with given {jobRunId} returned in the response of the create export endpoint export_delete_xlsx_summary: Delete XLSX export file based on jobRunId export_download_xlsx_description: | - Download the XLSX file with given {jobRunId} returned in the response of the create export endpoint + Download the XLSX file using the {jobRunId} returned by the create export endpoint.
          + For folder exports, you must provide the ID of the corresponding child job run.
          + The folder export first collects all elements and then creates a child job run to export them. export_download_xlsx_success_response: XLSX File as attachment export_download_xlsx_summary: Download XLSX file for job run id export_xlsx_created_response: Successfully created jobRun for From d289720a1be8bea0d2807c2441e2f1215c339544 Mon Sep 17 00:00:00 2001 From: lukmzig Date: Fri, 28 Nov 2025 11:53:36 +0100 Subject: [PATCH 08/13] update export performance --- .../Handler/ExportDataCollectionHandler.php | 52 +++++++++-------- .../Handler/ExportDataCollectionHandler.php | 57 +++++++++++-------- src/ExecutionEngine/Util/StepConfig.php | 2 +- .../Messenger/Handler/CsvCreationHandler.php | 27 ++------- .../Messenger/Handler/XlsxCreationHandler.php | 27 ++------- .../Service/ExecutionEngine/ExportService.php | 44 ++++++++++---- .../Util/Trait/CsvExportHandlerSetupTrait.php | 9 +-- .../Trait/ExportCreationHandlerSetupTrait.php | 50 ++++++++++++++++ 8 files changed, 158 insertions(+), 110 deletions(-) create mode 100644 src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index f7e501129..6f2c7ffaf 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -66,49 +66,55 @@ public function __invoke(ExportDataCollectionMessage $message): void )); } - $jobAsset = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TO_EXPORT->value); + $assets = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENTS_TO_EXPORT->value); + $totalAssets = count($assets); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableAssetColumnConfiguration(); - $columnCollection = $this->gridService->getConfigurationForExport( - $columns, - $columnsDefinitions - ); + $columnCollection = $this->gridService->getConfigurationForExport($columns, $columnsDefinitions); - try { - $assetData = [ - $jobAsset['id'] => $this->gridService->getGridValuesForElement( + $assetsData = []; + foreach ($assets as $asset) { + try { + $assetsData[$asset['id']] = $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_ASSET, - $jobAsset['id'], + $asset['id'], true - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetData); + ); - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, + } catch (Exception $e) { + $this->abort($this->getAbortData( + Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'type' => ElementTypes::TYPE_ASSET, + 'id' => $asset['id'], + 'message' => $e->getMessage(), ] - ); + )); } + $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName(), $totalAssets); + } + + try { + if (!empty($assetsData)) { + $context = $jobRun->getContext(); + if (isset($context[StepConfig::GRID_EXPORT_DATA->value])) { + $assetsData = array_merge( + $context[StepConfig::GRID_EXPORT_DATA->value], + $assetsData + ); + } + $this->updateJobRunContext($jobRun, StepConfig::GRID_EXPORT_DATA->value, $assetsData); + } } catch (Exception $e) { $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $jobAsset['id'], 'message' => $e->getMessage(), ] )); } - - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } } diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 29fd66b22..7a535005e 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -66,10 +66,8 @@ public function __invoke(ExportDataCollectionMessage $message): void )); } - $dataObjectId = $this->extractConfigFieldFromJobStepConfig( - $message, - StepConfig::ELEMENT_TO_EXPORT->value - )['id']; + $dataObjects = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENTS_TO_EXPORT->value); + $totalObjects = count($dataObjects); $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $columnsDefinitions = $this->columnConfigurationService->getAvailableDataObjectColumnConfiguration( @@ -79,41 +77,52 @@ public function __invoke(ExportDataCollectionMessage $message): void ); $columnCollection = $this->gridService->getConfigurationForExport($columns, $columnsDefinitions); - try { - $dataObjectData = [ - $dataObjectId => $this->gridService->getGridValuesForElement( + $data = []; + foreach ($dataObjects as $object) { + try { + $data[$object['id']] = $this->gridService->getGridValuesForElement( $columnCollection, ElementTypes::TYPE_OBJECT, - $dataObjectId, + $object['id'], true - ), - ]; - - $this->updateContextArrayValues($jobRun, StepConfig::GRID_EXPORT_DATA->value, $dataObjectData); - - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? null; + ); - if ($csvExportDataInfo === null) { - $this->updateContextArrayValues( - $jobRun, - StepConfig::GRID_EXPORT_DATA_INFO->value, + } catch (Exception $e) { + $this->abort($this->getAbortData( + Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'type' => ElementTypes::TYPE_OBJECT, - 'classId' => $classId, + 'id' => $object['id'], + 'message' => $e->getMessage(), ] - ); + )); } + $this->updateProgress( + $this->publishService, + $jobRun, + $this->getJobStep($message)->getName(), + $totalObjects + ); + } + + try { + if (!empty($data)) { + $context = $jobRun->getContext(); + if (isset($context[StepConfig::GRID_EXPORT_DATA->value])) { + $data = array_merge( + $context[StepConfig::GRID_EXPORT_DATA->value], + $data + ); + } + $this->updateJobRunContext($jobRun, StepConfig::GRID_EXPORT_DATA->value, $data); + } } catch (Exception $e) { $this->abort($this->getAbortData( Config::CSV_DATA_COLLECTION_FAILED_MESSAGE->value, [ - 'id' => $dataObjectId, 'message' => $e->getMessage(), ] )); } - - $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } } diff --git a/src/ExecutionEngine/Util/StepConfig.php b/src/ExecutionEngine/Util/StepConfig.php index 0d4377425..7cd37668b 100644 --- a/src/ExecutionEngine/Util/StepConfig.php +++ b/src/ExecutionEngine/Util/StepConfig.php @@ -23,7 +23,7 @@ enum StepConfig: string case CUSTOM_REPORT_CONFIG = 'custom_report_config'; case CUSTOM_REPORT_TO_EXPORT = 'custom_report_to_export'; case ELEMENT_CLASS_ID = 'element_class_id'; - case ELEMENT_TO_EXPORT = 'element_to_export'; + case ELEMENTS_TO_EXPORT = 'elements_to_export'; case ELEMENT_TYPE = 'element_type'; case EXPORT_FORMAT = 'export_format'; case FOLDER_TO_EXPORT = 'folder_to_export'; diff --git a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php index 1cdf3fa66..a85f39926 100644 --- a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/CsvCreationHandler.php @@ -22,6 +22,7 @@ use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\CsvCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\Model\GridExportData; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExportServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportCreationHandlerSetupTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -31,6 +32,7 @@ #[AsMessageHandler] final class CsvCreationHandler extends AbstractHandler { + use ExportCreationHandlerSetupTrait; use HandlerProgressTrait; public function __construct( @@ -62,6 +64,8 @@ public function __invoke(CsvCreationMessage $message): void $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $settings = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $headers = $settings[StepConfig::SETTINGS_HEADER->value] ?? StepConfig::SETTINGS_HEADER_NO_HEADER->value; $delimiter = $settings[StepConfig::SETTINGS_DELIMITER->value] ?? null; @@ -73,15 +77,13 @@ public function __invoke(CsvCreationMessage $message): void } $csvData = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value]; - $csvExportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? []; - try { $this->csvExportService->createExportFile( $jobRun->getId(), new GridExportData( $columns, $csvData, - $csvExportDataInfo, + ['type' => $elementType, 'classId' => $classId], $headers !== StepConfig::SETTINGS_HEADER_NO_HEADER->value, $headers === StepConfig::SETTINGS_HEADER_NAME, ), @@ -97,23 +99,4 @@ public function __invoke(CsvCreationMessage $message): void $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_CONFIGURATION->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - - $this->stepConfiguration->setDefaults([ - StepConfig::CONFIG_COLUMNS->value => [], - StepConfig::CONFIG_CONFIGURATION->value => [], - ]); - } } diff --git a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php index 7e7a1f86b..d00859b45 100644 --- a/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php +++ b/src/Export/ExecutionEngine/AutomationAction/Messenger/Handler/XlsxCreationHandler.php @@ -22,6 +22,7 @@ use Pimcore\Bundle\StudioBackendBundle\Export\ExecutionEngine\AutomationAction\Messenger\Messages\XlsxCreationMessage; use Pimcore\Bundle\StudioBackendBundle\Export\Model\GridExportData; use Pimcore\Bundle\StudioBackendBundle\Export\Service\ExportServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Export\Util\Trait\ExportCreationHandlerSetupTrait; use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -31,6 +32,7 @@ #[AsMessageHandler] final class XlsxCreationHandler extends AbstractHandler { + use ExportCreationHandlerSetupTrait; use HandlerProgressTrait; public function __construct( @@ -62,6 +64,8 @@ public function __invoke(XlsxCreationMessage $message): void $columns = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_COLUMNS->value); $settings = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::CONFIG_CONFIGURATION->value); + $elementType = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_TYPE->value); + $classId = $this->extractConfigFieldFromJobStepConfig($message, StepConfig::ELEMENT_CLASS_ID->value); $headers = $settings[StepConfig::SETTINGS_HEADER->value] ?? StepConfig::SETTINGS_HEADER_NO_HEADER->value; if (!isset($jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value])) { @@ -72,15 +76,13 @@ public function __invoke(XlsxCreationMessage $message): void } $exportData = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA->value]; - $exportDataInfo = $jobRun->getContext()[StepConfig::GRID_EXPORT_DATA_INFO->value] ?? []; - try { $this->xlsxExportService->createExportFile( $jobRun->getId(), new GridExportData( $columns, $exportData, - $exportDataInfo, + ['type' => $elementType, 'classId' => $classId], $headers !== StepConfig::SETTINGS_HEADER_NO_HEADER->value, $headers === StepConfig::SETTINGS_HEADER_NAME ), @@ -95,23 +97,4 @@ public function __invoke(XlsxCreationMessage $message): void $this->updateProgress($this->publishService, $jobRun, $this->getJobStep($message)->getName()); } - - protected function configureStep(): void - { - $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_CONFIGURATION->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::CONFIG_COLUMNS->value, - StepConfig::CONFIG_TYPE_ARRAY->value - ); - - $this->stepConfiguration->setDefaults([ - StepConfig::CONFIG_COLUMNS->value => [], - StepConfig::CONFIG_CONFIGURATION->value => [], - ]); - } } diff --git a/src/Export/Service/ExecutionEngine/ExportService.php b/src/Export/Service/ExecutionEngine/ExportService.php index 001870956..96d1ef1a8 100644 --- a/src/Export/Service/ExecutionEngine/ExportService.php +++ b/src/Export/Service/ExecutionEngine/ExportService.php @@ -13,6 +13,7 @@ namespace Pimcore\Bundle\StudioBackendBundle\Export\Service\ExecutionEngine; +use Generator; use Pimcore\Bundle\GenericExecutionEngineBundle\Agent\JobExecutionAgentInterface; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\Job; use Pimcore\Bundle\GenericExecutionEngineBundle\Model\JobStep; @@ -40,6 +41,8 @@ */ final readonly class ExportService implements ExportServiceInterface { + private const int EXPORT_BATCH_SIZE = 500; + public function __construct( private JobExecutionAgentInterface $jobExecutionAgent, private SecurityServiceInterface $securityService @@ -63,13 +66,15 @@ public function generateExportFileForElements( $collectionSettings = [ StepConfig::ELEMENT_CLASS_ID->value => $classId ?? '', StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::ELEMENT_TYPE->value => $exportParameter->getElementType(), ]; - $creationSettings = [ - StepConfig::CONFIG_COLUMNS->value => $exportParameter->getColumns(), - StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), - ]; + $creationSettings = array_merge( + $collectionSettings, + [ + StepConfig::CONFIG_CONFIGURATION->value => $exportParameter->getConfig(), + StepConfig::ELEMENT_TYPE->value => $exportParameter->getElementType(), + ] + ); $jobSteps = [ ...$this->mapJobSteps( @@ -143,15 +148,32 @@ private function mapJobSteps( array $collectionSettings, string $messageFQCN, ): array { - return array_map( - static fn (ElementDescriptor $element) => new JobStep( + $steps = []; + + foreach ($this->chunkGenerator($elements, self::EXPORT_BATCH_SIZE) as $batch) { + + $config = [ + StepConfig::ELEMENTS_TO_EXPORT->value => $batch, + ] + $collectionSettings; + + $steps[] = new JobStep( JobSteps::DATA_COLLECTION->value, $messageFQCN, '', - array_merge([StepConfig::ELEMENT_TO_EXPORT->value => $element], $collectionSettings) - ), - $elements, - ); + $config + ); + } + + return $steps; + } + + private function chunkGenerator(array $elements, int $size): Generator + { + $total = count($elements); + + for ($i = 0; $i < $total; $i += $size) { + yield array_slice($elements, $i, $size); + } } private function getExportFileStep(array $settings, string $exportFormat): JobStep diff --git a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php index 9f4e3db02..8b593fdfc 100644 --- a/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php +++ b/src/Export/Util/Trait/CsvExportHandlerSetupTrait.php @@ -27,9 +27,9 @@ protected function configureStep(): void StepConfig::ELEMENT_CLASS_ID->value, StepConfig::CONFIG_TYPE_STRING->value ); - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TO_EXPORT->value); + $this->stepConfiguration->setRequired(StepConfig::ELEMENTS_TO_EXPORT->value); $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TO_EXPORT->value, + StepConfig::ELEMENTS_TO_EXPORT->value, StepConfig::CONFIG_TYPE_ARRAY->value ); $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); @@ -37,10 +37,5 @@ protected function configureStep(): void StepConfig::CONFIG_COLUMNS->value, StepConfig::CONFIG_TYPE_ARRAY->value ); - $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); - $this->stepConfiguration->setAllowedTypes( - StepConfig::ELEMENT_TYPE->value, - StepConfig::CONFIG_TYPE_STRING->value - ); } } diff --git a/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php b/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php new file mode 100644 index 000000000..f2017ad30 --- /dev/null +++ b/src/Export/Util/Trait/ExportCreationHandlerSetupTrait.php @@ -0,0 +1,50 @@ +stepConfiguration->setRequired(StepConfig::ELEMENT_CLASS_ID->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_CLASS_ID->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::ELEMENT_TYPE->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::ELEMENT_TYPE->value, + StepConfig::CONFIG_TYPE_STRING->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_CONFIGURATION->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_CONFIGURATION->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setRequired(StepConfig::CONFIG_COLUMNS->value); + $this->stepConfiguration->setAllowedTypes( + StepConfig::CONFIG_COLUMNS->value, + StepConfig::CONFIG_TYPE_ARRAY->value + ); + $this->stepConfiguration->setDefaults([ + StepConfig::CONFIG_COLUMNS->value => [], + StepConfig::CONFIG_CONFIGURATION->value => [], + ]); + } +} From b5bf8fac26ec290657c78f5f7b6a6231e37d0ea0 Mon Sep 17 00:00:00 2001 From: lukmzig <30526586+lukmzig@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:54:19 +0000 Subject: [PATCH 09/13] Apply php-cs-fixer changes --- .../Messenger/Handler/ExportDataCollectionHandler.php | 1 + .../Messenger/Handler/ExportDataCollectionHandler.php | 1 + src/Export/Service/ExecutionEngine/ExportService.php | 2 ++ 3 files changed, 4 insertions(+) diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 6f2c7ffaf..a36326d1d 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -26,6 +26,7 @@ use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use function count; /** * @internal diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 7a535005e..0ca9f3722 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -26,6 +26,7 @@ use Pimcore\Bundle\StudioBackendBundle\Mercure\Service\PublishServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use function count; /** * @internal diff --git a/src/Export/Service/ExecutionEngine/ExportService.php b/src/Export/Service/ExecutionEngine/ExportService.php index 96d1ef1a8..2e82c4de6 100644 --- a/src/Export/Service/ExecutionEngine/ExportService.php +++ b/src/Export/Service/ExecutionEngine/ExportService.php @@ -35,6 +35,8 @@ use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Model\Element\ElementDescriptor; use Pimcore\Model\UserInterface; +use function array_slice; +use function count; /** * @internal From 4cb77fa059fec13506bcdf242f04413f19baa2a7 Mon Sep 17 00:00:00 2001 From: lukmzig Date: Mon, 1 Dec 2025 08:48:18 +0100 Subject: [PATCH 10/13] add a permission check --- .../Handler/ExportDataCollectionHandler.php | 2 +- .../Handler/ExportDataCollectionHandler.php | 2 +- src/Grid/Service/GridService.php | 22 ++++++++++++++----- src/Grid/Service/GridServiceInterface.php | 6 +++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index a36326d1d..75627e5f1 100644 --- a/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/Asset/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -82,7 +82,7 @@ public function __invoke(ExportDataCollectionMessage $message): void $columnCollection, ElementTypes::TYPE_ASSET, $asset['id'], - true + $user ); } catch (Exception $e) { diff --git a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php index 0ca9f3722..b0a78d15e 100644 --- a/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php +++ b/src/DataObject/ExecutionEngine/AutomationAction/Messenger/Handler/ExportDataCollectionHandler.php @@ -85,7 +85,7 @@ public function __invoke(ExportDataCollectionMessage $message): void $columnCollection, ElementTypes::TYPE_OBJECT, $object['id'], - true + $user ); } catch (Exception $e) { diff --git a/src/Grid/Service/GridService.php b/src/Grid/Service/GridService.php index 9088d8456..5d22fb1f5 100644 --- a/src/Grid/Service/GridService.php +++ b/src/Grid/Service/GridService.php @@ -37,9 +37,12 @@ use Pimcore\Bundle\StudioBackendBundle\Grid\Util\Collection\ColumnCollection; use Pimcore\Bundle\StudioBackendBundle\Response\Collection; use Pimcore\Bundle\StudioBackendBundle\Response\StudioElementInterface; +use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; +use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementPermissions; use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes; use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait; use Pimcore\Model\DataObject\ClassDefinition; +use Pimcore\Model\UserInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use function array_key_exists; use function in_array; @@ -72,6 +75,7 @@ public function __construct( private readonly ColumnCollectorLoaderInterface $columnCollectorLoader, private readonly GridSearchInterface $gridSearch, private readonly EventDispatcherInterface $eventDispatcher, + private readonly SecurityServiceInterface $securityService, private readonly ServiceResolverInterface $serviceResolver, private readonly ClassDefinitionResolverInterface $classDefinitionResolver ) { @@ -164,20 +168,28 @@ public function getPreviewOfAdvancedColumn(AdvancedColumnPreviewParameter $param } /** - * @throws InvalidArgumentException + * {@inheritdoc} */ public function getGridDataForElement( ColumnCollection $columnCollection, ?StudioElementInterface $element, string $elementType, int $elementId, - bool $isExport = false + bool $isExport = false, + UserInterface $user = null, ): array { $data = []; $databaseElement = null; if ($isExport || $elementType === ElementTypes::TYPE_OBJECT) { $databaseElement = $this->getElement($this->serviceResolver, $elementType, $elementId); + if ($user !== null) { + $this->securityService->hasElementPermission( + $databaseElement, + $user, + ElementPermissions::VIEW_PERMISSION + ); + } } if ($isExport && $databaseElement->getType() === ElementTypes::TYPE_FOLDER) { @@ -230,10 +242,10 @@ public function getGridValuesForElement( ColumnCollection $columnCollection, string $elementType, int $elementId, - bool $isExport = false + UserInterface $user ): array { - $data = $this->getGridDataForElement($columnCollection, null, $elementType, $elementId, $isExport); + $data = $this->getGridDataForElement($columnCollection, null, $elementType, $elementId, true, $user); return array_map( static fn (ColumnData $columnData) => $columnData->getValue(), @@ -324,9 +336,7 @@ private function supports(Column $column, string $elementType): bool return false; } - /** @var ColumnResolverInterface $resolver */ $resolver = $this->getColumnResolvers()[$column->getType()]; - if (!in_array($elementType, $resolver->supportedElementTypes(), true)) { return false; } diff --git a/src/Grid/Service/GridServiceInterface.php b/src/Grid/Service/GridServiceInterface.php index b919a3ac9..6c4558ccf 100644 --- a/src/Grid/Service/GridServiceInterface.php +++ b/src/Grid/Service/GridServiceInterface.php @@ -26,6 +26,7 @@ use Pimcore\Bundle\StudioBackendBundle\Response\Collection; use Pimcore\Bundle\StudioBackendBundle\Response\StudioElementInterface; use Pimcore\Model\DataObject\ClassDefinition; +use Pimcore\Model\UserInterface; /** * @internal @@ -44,7 +45,8 @@ public function getGridDataForElement( ?StudioElementInterface $element, string $elementType, int $elementId, - bool $isExport = false + bool $isExport = false, + UserInterface $user = null, ): array; /** @@ -54,7 +56,7 @@ public function getGridValuesForElement( ColumnCollection $columnCollection, string $elementType, int $elementId, - bool $isExport = false + UserInterface $user ): array; /** From bec8fe3df3e78f29b9d49588cacbc9f2b4499fee Mon Sep 17 00:00:00 2001 From: lukmzig Date: Mon, 1 Dec 2025 08:50:48 +0100 Subject: [PATCH 11/13] code style --- src/Grid/Service/GridService.php | 2 +- src/Grid/Service/GridServiceInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Grid/Service/GridService.php b/src/Grid/Service/GridService.php index 5d22fb1f5..fbd3e14f8 100644 --- a/src/Grid/Service/GridService.php +++ b/src/Grid/Service/GridService.php @@ -176,7 +176,7 @@ public function getGridDataForElement( string $elementType, int $elementId, bool $isExport = false, - UserInterface $user = null, + ?UserInterface $user = null, ): array { $data = []; diff --git a/src/Grid/Service/GridServiceInterface.php b/src/Grid/Service/GridServiceInterface.php index 6c4558ccf..1337e9b02 100644 --- a/src/Grid/Service/GridServiceInterface.php +++ b/src/Grid/Service/GridServiceInterface.php @@ -46,7 +46,7 @@ public function getGridDataForElement( string $elementType, int $elementId, bool $isExport = false, - UserInterface $user = null, + ?UserInterface $user = null, ): array; /** From 3e11f53b864e5d429246289709031c2c63c40314 Mon Sep 17 00:00:00 2001 From: Marco Perberschlager Date: Mon, 1 Dec 2025 09:57:12 +0100 Subject: [PATCH 12/13] Reduced the amount of persisting/getting the job since every call resulted in (de)serialization --- .../Handler/ElementUsageReplaceHandler.php | 23 ++++++++---- .../AutomationAction/AbstractHandler.php | 16 +++++++++ .../Util/Trait/HandlerProgressTrait.php | 35 +++++++++++++++---- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php index 5ccfcf224..dd3aed27b 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php @@ -58,13 +58,14 @@ public function __invoke(ElementUsageReplaceMessage $message): void $jobRun = $this->getJobRun($message); $user = $this->getUser($jobRun); $this->initializeElements($message); + $elementCount = 0; $elements = $jobRun->getJob()?->getSelectedElements(); if (empty($elements)) { $elements = $this->sourceElement->getDependencies()->getRequiredByWithPath(); + $elementCount = count($elements); } - $elementCount = count($elements); foreach ($elements as $elementData) { $isArray = is_array($elementData); $element = $this->elementUsageService->getElementById( @@ -94,12 +95,20 @@ public function __invoke(ElementUsageReplaceMessage $message): void )); } - $this->updateProgress( - $this->publishService, - $jobRun, - $this->getJobStep($message)->getName(), - $elementCount - ); + if($elementCount > 0) { + $this->updateProgress( + $this->publishService, + $jobRun, + $this->getJobStep($message)->getName(), + $elementCount + ); + } else { + $this->updateProgress( + $this->publishService, + $jobRun, + $this->getJobStep($message)->getName() + ); + } } } diff --git a/src/ExecutionEngine/AutomationAction/AbstractHandler.php b/src/ExecutionEngine/AutomationAction/AbstractHandler.php index 09b2aa184..896f1b1ce 100644 --- a/src/ExecutionEngine/AutomationAction/AbstractHandler.php +++ b/src/ExecutionEngine/AutomationAction/AbstractHandler.php @@ -169,6 +169,22 @@ protected function updateContextArrayValues(JobRun $jobRun, string $key, array $ $this->updateJobRunContext($jobRun, $key, $contextValue); } + protected function updateJobRunContextValues( + JobRun $jobRun, + array $values, + bool $persist = true + ): void { + $jobRun->setContext( + array_merge( + $jobRun->getContext() ?? [], + $values + ) + ); + if($persist) { + $this->jobRunRepository->update($jobRun); + } + } + private function getExecutionActionData( UserInterface $user, array $jobEnvironmentData, diff --git a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php index d73cd3b06..180ed29aa 100644 --- a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php +++ b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php @@ -48,7 +48,12 @@ private function updateProgress( $processedElements = $jobRun->getContext()[self::PROCESSED_ELEMENTS] ?? 0; $processedElements++; - $this->updateJobRunContext($jobRun, self::PROCESSED_ELEMENTS, $processedElements); + $this->updateJobRunContextValues( + $jobRun, + [ + self::PROCESSED_ELEMENTS => $processedElements + ] + ); $updateFrequency = max(1, (int)($totalEvents / self::FREQUENCY)); $progress = (int)($processedElements / $totalEvents * 100); @@ -80,7 +85,13 @@ private function getTotalSteps(JobRun $jobRun): int } $totalSteps = count($jobRun->getJob()?->getSteps() ?? []); - $this->updateJobRunContext($jobRun, self::TOTAL_STEPS, $totalSteps); + $this->updateJobRunContextValues( + $jobRun, + [ + self::TOTAL_STEPS => $totalSteps + ], + false + ); return $totalSteps; } @@ -94,9 +105,15 @@ private function getCurrentStep(JobRun $jobRun): int } $currentStep = $jobRun->getCurrentStep(); - $this->updateJobRunContext($jobRun, self::PROCESSED_ELEMENTS, 0); - $this->updateJobRunContext($jobRun, self::ELEMENTS_PER_STEP, null); - $this->updateJobRunContext($jobRun, self::CURRENT_STEP, $currentStep); + $this->updatejobRunContextValues( + $jobRun, + [ + self::PROCESSED_ELEMENTS => 0, + self::CURRENT_STEP => $currentStep, + self::ELEMENTS_PER_STEP => null, + ], + false + ); return $currentStep; } @@ -113,7 +130,13 @@ private function getElementsPerStep(JobRun $jobRun, int $stepElements): int $elementsPerStep = $jobRun->getTotalElements() * $elementsPerStep; } - $this->updateJobRunContext($jobRun, self::ELEMENTS_PER_STEP, $elementsPerStep); + $this->updateJobRunContextValues( + $jobRun, + [ + self::ELEMENTS_PER_STEP => $elementsPerStep + ], + false + ); return $elementsPerStep; } From 43de8aaad3063b83dd53500164907e36d946f982 Mon Sep 17 00:00:00 2001 From: mcop1 <89011527+mcop1@users.noreply.github.com> Date: Mon, 1 Dec 2025 08:58:09 +0000 Subject: [PATCH 13/13] Apply php-cs-fixer changes --- .../Messenger/Handler/ElementUsageReplaceHandler.php | 2 +- src/ExecutionEngine/AutomationAction/AbstractHandler.php | 2 +- src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php index dd3aed27b..1a5c11278 100644 --- a/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php +++ b/src/Element/ExecutionEngine/AutomationAction/Messenger/Handler/ElementUsageReplaceHandler.php @@ -95,7 +95,7 @@ public function __invoke(ElementUsageReplaceMessage $message): void )); } - if($elementCount > 0) { + if ($elementCount > 0) { $this->updateProgress( $this->publishService, $jobRun, diff --git a/src/ExecutionEngine/AutomationAction/AbstractHandler.php b/src/ExecutionEngine/AutomationAction/AbstractHandler.php index 896f1b1ce..39cb1450a 100644 --- a/src/ExecutionEngine/AutomationAction/AbstractHandler.php +++ b/src/ExecutionEngine/AutomationAction/AbstractHandler.php @@ -180,7 +180,7 @@ protected function updateJobRunContextValues( $values ) ); - if($persist) { + if ($persist) { $this->jobRunRepository->update($jobRun); } } diff --git a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php index 180ed29aa..86dfcd8d0 100644 --- a/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php +++ b/src/ExecutionEngine/Util/Trait/HandlerProgressTrait.php @@ -51,7 +51,7 @@ private function updateProgress( $this->updateJobRunContextValues( $jobRun, [ - self::PROCESSED_ELEMENTS => $processedElements + self::PROCESSED_ELEMENTS => $processedElements, ] ); $updateFrequency = max(1, (int)($totalEvents / self::FREQUENCY)); @@ -88,7 +88,7 @@ private function getTotalSteps(JobRun $jobRun): int $this->updateJobRunContextValues( $jobRun, [ - self::TOTAL_STEPS => $totalSteps + self::TOTAL_STEPS => $totalSteps, ], false ); @@ -133,7 +133,7 @@ private function getElementsPerStep(JobRun $jobRun, int $stepElements): int $this->updateJobRunContextValues( $jobRun, [ - self::ELEMENTS_PER_STEP => $elementsPerStep + self::ELEMENTS_PER_STEP => $elementsPerStep, ], false );