Skip to content

Commit 83c63a0

Browse files
committed
Add command to clean orphaned components and groups
1 parent 1a3031d commit 83c63a0

2 files changed

Lines changed: 92 additions & 3 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Silverback\ApiComponentsBundle\Command;
4+
5+
use Doctrine\ORM\EntityManagerInterface;
6+
use Silverback\ApiComponentsBundle\Entity\Core\AbstractComponent;
7+
use Silverback\ApiComponentsBundle\Entity\Core\ComponentGroup;
8+
use Silverback\ApiComponentsBundle\Helper\OrphanedResourceHelper;
9+
use Symfony\Component\Console\Attribute\AsCommand;
10+
use Symfony\Component\Console\Command\Command;
11+
use Symfony\Component\Console\Helper\ProgressBar;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Output\OutputInterface;
14+
15+
#[AsCommand(name: 'silverback:api-components:clean-orphaned')]
16+
class CleanOrphanedCommand extends Command
17+
{
18+
public function __construct(private readonly OrphanedResourceHelper $orphanedResourceHelper, private readonly EntityManagerInterface $entityManager)
19+
{
20+
parent::__construct();
21+
}
22+
23+
protected function configure(): void
24+
{
25+
$this
26+
->setDescription('Checks every component and component group and will delete all orphaned components.')
27+
->setDefinition([]);
28+
}
29+
30+
protected function execute(InputInterface $input, OutputInterface $output): int
31+
{
32+
$count = 0;
33+
$componentGroupRepository = $this->entityManager->getRepository(ComponentGroup::class);
34+
$componentGroups = $componentGroupRepository->findAll();
35+
$groupsProgressBar = new ProgressBar($output, count($componentGroups));
36+
$groupsProgressBar->start();
37+
foreach ($componentGroups as $componentGroup) {
38+
$groupsProgressBar->advance();
39+
if ($this->orphanedResourceHelper->checkAndRemoveOrphanedComponentGroup($componentGroup)) {
40+
$count++;
41+
}
42+
}
43+
$groupsProgressBar->finish();
44+
45+
$componentRepository = $this->entityManager->getRepository(AbstractComponent::class);
46+
$components = $componentRepository->findAll();
47+
$componentsProgressBar = new ProgressBar($output, count($componentGroups));
48+
$componentsProgressBar->start();
49+
foreach ($components as $component) {
50+
$componentsProgressBar->advance();
51+
if ($this->orphanedResourceHelper->checkAndRemoveOrphanedComponent($component)) {
52+
$count++;
53+
}
54+
}
55+
$componentsProgressBar->finish();
56+
57+
$output->writeln(\sprintf('Removed <comment>%d</comment> orphaned components (excluding cascades)', $count));
58+
return 0;
59+
}
60+
}

src/Helper/OrphanedResourceHelper.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Silverback\ApiComponentsBundle\Metadata\Factory\PageDataMetadataFactoryInterface;
1717
use Symfony\Component\PropertyAccess\PropertyAccess;
1818

19-
final class OrphanedResourceHelper
19+
final readonly class OrphanedResourceHelper
2020
{
2121
public function __construct(
2222
private PageDataMetadataFactoryInterface $pageDataMetadataFactory,
@@ -63,6 +63,28 @@ public function handleRemovedRoutable(RoutableInterface $resource): void {
6363
}
6464
}
6565

66+
public function checkAndRemoveOrphanedComponentGroup(ComponentGroup $componentGroup): bool {
67+
if ($componentGroup->pages->count() || $componentGroup->layouts->count() || $componentGroup->components->count()) {
68+
return false;
69+
}
70+
$groupManager = $this->registry->getManagerForClass(ComponentGroup::class);
71+
72+
$positionManager = $this->registry->getManagerForClass(ComponentPosition::class);
73+
foreach ($componentGroup->componentPositions as $componentPosition) {
74+
$positionManager?->remove($componentPosition);
75+
$this->removeOrphanedComponentPosition($componentPosition);
76+
}
77+
78+
$groupManager?->remove($componentGroup);
79+
$groupManager?->flush();
80+
$positionManager?->flush();
81+
return true;
82+
}
83+
84+
public function checkAndRemoveOrphanedComponent(AbstractComponent $component): bool {
85+
return $this->removeOrphanedComponent($component, 0, true);
86+
}
87+
6688
private function isComponentGroupInOtherLocations(ComponentGroup $componentGroup, AbstractComponent|Page|Layout|null $deletedLocation = null): bool
6789
{
6890
if (!$deletedLocation) {
@@ -109,13 +131,20 @@ private function removeOrphanedComponentPosition(ComponentPosition $componentPos
109131
}
110132
}
111133

112-
private function removeOrphanedComponent(ComponentInterface $component): void
134+
private function removeOrphanedComponent(ComponentInterface $component, int $countCheck = 1, bool $doFlush = false): bool
113135
{
114136
$metadata = $this->usageMetadataFactory->create($component);
115-
if (1 === $metadata->getTotal()) {
137+
if ($countCheck === $metadata->getTotal()) {
116138
$resourceClass = get_class($component);
117139
$manager = $this->registry->getManagerForClass($resourceClass);
118140
$manager?->remove($component);
141+
if ($doFlush) {
142+
$manager?->flush();
143+
}
144+
return true;
119145
}
146+
return false;
120147
}
148+
149+
121150
}

0 commit comments

Comments
 (0)