diff --git a/src/bundle/Command/PageFieldTypeCleanupCommand.php b/src/bundle/Command/PageFieldTypeCleanupCommand.php index 6de1df4..18a0a20 100644 --- a/src/bundle/Command/PageFieldTypeCleanupCommand.php +++ b/src/bundle/Command/PageFieldTypeCleanupCommand.php @@ -4,6 +4,8 @@ namespace MateuszBieniek\EzPlatformDatabaseHealthCheckerBundle\Command; +use Exception; +use eZ\Publish\API\Repository\Repository; use MateuszBieniek\EzPlatformDatabaseHealthChecker\Persistence\Legacy\Content\Gateway\PageFieldTypeGatewayInterface as Gateway; use Symfony\Bundle\MakerBundle\Validator; use Symfony\Component\Console\Command\Command; @@ -22,9 +24,13 @@ class PageFieldTypeCleanupCommand extends Command /** @var Gateway */ private $gateway; - public function __construct(Gateway $gateway) + /** @var \eZ\Publish\API\Repository\Repository */ + private $repository; + + public function __construct(Gateway $gateway, Repository $repository) { $this->gateway = $gateway; + $this->repository = $repository; parent::__construct(); } @@ -90,10 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $limit = (int) $helper->ask($input, $output, $question); - if ($this->countOrphanedPageRelations() <= 0) { - return 0; - } - + $this->countOrphanedPageRelations(); $this->deleteOrphanedPageRelations($limit); $this->io->success('Done'); @@ -101,22 +104,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } - private function countOrphanedPageRelations(): int + private function countOrphanedPageRelations(): void { $count = $this->gateway->countOrphanedPageRelations(); $count <= 0 ? $this->io->success('Found: 0') : $this->io->caution(sprintf('Found: %d orphaned pages', $count)); - - return $count; } private function deleteOrphanedPageRelations(int $limit): void { if (!$this->io->confirm( sprintf('Are you sure that you want to proceed? The maximum number of pages that will be cleaned - in this iteration is equal to %d.', $limit), + in this iteration is equal to %d. If the number is equal to 0 you can still continue to run the remaining + cleaning methods.', $limit), false) ) { return; @@ -132,5 +134,60 @@ private function deleteOrphanedPageRelations(int $limit): void $this->gateway->removePage((int) $records[$i]); } } + + $this->deleteUnrelatedLeftovers($limit); + } + + private function deleteUnrelatedLeftovers(int $limit): void + { + $this->io->info('Orphaned blocks and related items which cannot be deleted using the standard procedure will be searched for now.'); + + $orphanedBlocks = $this->gateway->getOrphanedBlockIds($limit); + $orphanedAttributes = $this->gateway->getOrphanedAttributeIds($orphanedBlocks); + + $this->io->caution(sprintf('Found %d orphaned blocks within the chosen limit.', count($orphanedBlocks))); + + $this->io->caution(sprintf('Found %d orphaned attributes related to the found blocks.', count($orphanedAttributes))); + + if (!$this->io->confirm('Are you sure that you want to proceed? The block-related records will be now removed.', false)) { + return; + } + + $this->repository->beginTransaction(); + + try { + $progressBar = $this->io->createProgressBar(6); + + $this->io->info('Removing orphaned ezpage_map_attributes_blocks records'); + $this->gateway->removeOrphanedBlockAttributes($orphanedAttributes); + $progressBar->advance(); + + $this->io->info('Removing orphaned ezpage_attributes records'); + $this->gateway->removeOrphanedAttributes($orphanedAttributes); + $progressBar->advance(); + + $this->io->info('Removing orphaned ezpage_blocks_design records'); + $this->gateway->removeOrphanedBlockDesigns($orphanedBlocks); + $progressBar->advance(); + + $this->io->info('Removing orphaned ezpage_blocks_visibility records'); + $this->gateway->removeOrphanedBlockVisibilities($orphanedBlocks); + $progressBar->advance(); + + $this->io->info('Removing orphaned ezpage_blocks records'); + $this->gateway->removeOrphanedBlocks($orphanedBlocks); + $progressBar->advance(); + + $this->io->info('Removing orphaned ezpage_map_blocks_zones records'); + $this->gateway->removeOrphanedBlocksZones($orphanedBlocks); + $progressBar->advance(); + + $this->io->newLine(); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } } } diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index ef04af2..7f2932c 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -26,6 +26,7 @@ services: MateuszBieniek\EzPlatformDatabaseHealthCheckerBundle\Command\PageFieldTypeCleanupCommand: arguments: $gateway: '@MateuszBieniek\EzPlatformDatabaseHealthChecker\Persistence\Legacy\Content\Gateway\PageFieldTypeDoctrineDatabase' + $repository: '@ezpublish.api.repository' tags: - { name: 'console.command', command: 'ezplatform:page-fieldtype-cleanup' } diff --git a/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeDoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeDoctrineDatabase.php index c26b025..a708f5f 100644 --- a/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeDoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeDoctrineDatabase.php @@ -91,4 +91,180 @@ public function removePage(int $pageId): void $this->pageFieldTypeGateway->removeZone((int) $zone['id']); } } + + /** + * @inheritDoc + */ + public function getOrphanedBlockIds(int $limit): array + { + //fetching the first set via ezpage_map_blocks_zones table + $zonesQuery = $this->connection->createQueryBuilder(); + $zonesQuery = $zonesQuery->select('id') + ->from('ezpage_zones') + ->getSQL(); + + $orphanedBlocksQuery = $this->connection->createQueryBuilder(); + $orphanedBlocksQuery->select('block_id') + ->from('ezpage_map_blocks_zones', 'bz') + ->where( + $orphanedBlocksQuery->expr()->notIn( + 'zone_id', + $zonesQuery + ) + ) + ->setMaxResults($limit); + + $firstResult = $orphanedBlocksQuery->execute()->fetchAll(FetchMode::COLUMN); + + //fetching the second set via ezpage_map_zones_pages table + $pagesQuery = $this->connection->createQueryBuilder(); + $pagesQuery = $pagesQuery->select('id') + ->from('ezpage_pages') + ->getSQL(); + + $secondZonesQuery = $this->connection->createQueryBuilder(); + $secondZonesQuery->select('zone_id') + ->from('ezpage_map_zones_pages', 'zp') + ->where( + $secondZonesQuery->expr()->notIn( + 'page_id', + $pagesQuery + ) + ); + + $secondOrphanedBlocksQuery = $this->connection->createQueryBuilder(); + $secondOrphanedBlocksQuery->select('block_id') + ->from('ezpage_map_blocks_zones', 'bz') + ->where( + $secondOrphanedBlocksQuery->expr()->in( + 'zone_id', + $secondZonesQuery->getSQL() + ) + ) + ->setMaxResults($limit); + + $secondResult = $secondOrphanedBlocksQuery->execute()->fetchAll(FetchMode::COLUMN); + + return array_unique(array_merge($firstResult, $secondResult)); + } + + /** + * @inheritDoc + */ + public function getOrphanedAttributeIds(array $blockIds): array + { + $orphanedAttributesQuery = $this->connection->createQueryBuilder(); + $orphanedAttributesQuery->select('attribute_id') + ->from('ezpage_map_attributes_blocks') + ->where( + $orphanedAttributesQuery->expr()->in( + 'block_id', + $orphanedAttributesQuery->createPositionalParameter($blockIds, Connection::PARAM_INT_ARRAY) + ) + ); + + return $orphanedAttributesQuery->execute()->fetchAll(FetchMode::COLUMN); + } + + /** + * @inheritDoc + */ + public function removeOrphanedBlockAttributes(array $attributeIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_map_attributes_blocks') + ->where( + $query->expr()->in( + 'attribute_id', + $query->createPositionalParameter($attributeIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } + + /** + * @inheritDoc + */ + public function removeOrphanedAttributes(array $attributeIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_attributes') + ->where( + $query->expr()->in( + 'id', + $query->createPositionalParameter($attributeIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } + + /** + * @inheritDoc + */ + public function removeOrphanedBlockDesigns(array $blockIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_blocks_design') + ->where( + $query->expr()->in( + 'block_id', + $query->createPositionalParameter($blockIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } + + /** + * @inheritDoc + */ + public function removeOrphanedBlockVisibilities(array $blockIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_blocks_visibility') + ->where( + $query->expr()->in( + 'block_id', + $query->createPositionalParameter($blockIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } + + /** + * @inheritDoc + */ + public function removeOrphanedBlocksZones(array $blockIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_map_blocks_zones') + ->where( + $query->expr()->in( + 'block_id', + $query->createPositionalParameter($blockIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } + + /** + * @inheritDoc + */ + public function removeOrphanedBlocks(array $blockIds): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezpage_blocks') + ->where( + $query->expr()->in( + 'id', + $query->createPositionalParameter($blockIds, Connection::PARAM_INT_ARRAY) + ) + ); + + $query->execute(); + } } diff --git a/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeGatewayInterface.php b/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeGatewayInterface.php index 826cbef..9b04c35 100644 --- a/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeGatewayInterface.php +++ b/src/lib/Persistence/Legacy/Content/Gateway/PageFieldTypeGatewayInterface.php @@ -9,4 +9,45 @@ interface PageFieldTypeGatewayInterface public function countOrphanedPageRelations(): int; public function getOrphanedPageRelations(int $limit): array; + + /** + * @return int[] + */ + public function getOrphanedBlockIds(int $limit): array; + + /** + * @param int[] $blockIds + * @return int[] + */ + public function getOrphanedAttributeIds(array $blockIds): array; + + /** + * @param int[] $attributeIds + */ + public function removeOrphanedBlockAttributes(array $attributeIds): void; + + /** + * @param int[] $attributeIds + */ + public function removeOrphanedAttributes(array $attributeIds): void; + + /** + * @param int[] $blockIds + */ + public function removeOrphanedBlockDesigns(array $blockIds): void; + + /** + * @param int[] $blockIds + */ + public function removeOrphanedBlockVisibilities(array $blockIds): void; + + /** + * @param int[] $blockIds + */ + public function removeOrphanedBlocksZones(array $blockIds): void; + + /** + * @param int[] $blockIds + */ + public function removeOrphanedBlocks(array $blockIds): void; }