addArgument('module', InputArgument::REQUIRED, 'Modul-Identifier (z.B. "billing")')
->addOption('keep-license', null, InputOption::VALUE_NONE, 'Lizenz behalten (nicht widerrufen)')
->addOption('keep-db-entry', null, InputOption::VALUE_NONE, 'Datenbank-Eintrag behalten (Permission-System)')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Keine Bestätigung erforderlich')
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$moduleIdentifier = $input->getArgument('module');
$keepLicense = $input->getOption('keep-license');
$keepDbEntry = $input->getOption('keep-db-entry');
$force = $input->getOption('force');
$io->title(sprintf('Modul "%s" entfernen', $moduleIdentifier));
// 1. Modul-Informationen sammeln
$modulePlugin = $this->moduleRegistry->getModule($moduleIdentifier);
$moduleEntity = $this->findModuleEntity($moduleIdentifier);
if (!$modulePlugin && !$moduleEntity) {
$io->error(sprintf('Modul "%s" wurde nicht gefunden (weder als Plugin noch in der Datenbank).', $moduleIdentifier));
return Command::FAILURE;
}
// 2. Informationen anzeigen
$this->displayModuleInfo($io, $modulePlugin, $moduleEntity);
// 3. Bestätigung einholen
if (!$force && !$io->confirm('Möchten Sie dieses Modul wirklich entfernen?', false)) {
$io->info('Abgebrochen.');
return Command::SUCCESS;
}
$io->section('Entferne Modul');
$steps = [];
// 4. Lizenz widerrufen
if (!$keepLicense && $modulePlugin && $modulePlugin->isLicensed()) {
$io->text('⏳ Widerrufe Lizenz...');
try {
$this->licenseValidator->revokeLicense($moduleIdentifier);
$io->success('✓ Lizenz erfolgreich widerrufen');
$steps[] = '✓ Lizenz widerrufen';
} catch (\Exception $e) {
$io->warning(sprintf('Lizenz konnte nicht widerrufen werden: %s', $e->getMessage()));
$steps[] = '⚠ Lizenz-Widerruf fehlgeschlagen';
}
} elseif ($keepLicense) {
$io->text('⏭ Lizenz wird behalten (--keep-license)');
$steps[] = '⏭ Lizenz behalten';
} else {
$io->text('⏭ Keine Lizenz vorhanden');
$steps[] = '⏭ Keine Lizenz';
}
// 5. Datenbank-Eintrag entfernen
if (!$keepDbEntry && $moduleEntity) {
$io->text('⏳ Entferne Datenbank-Eintrag (Permission-System)...');
// Warnung, wenn Permissions existieren
$permissionCount = $moduleEntity->getPermissions()->count();
if ($permissionCount > 0) {
$io->warning(sprintf(
'ACHTUNG: Dieses Modul hat %d verknüpfte Permission(s). Diese werden ebenfalls gelöscht!',
$permissionCount
));
if (!$force && !$io->confirm('Trotzdem fortfahren?', false)) {
$io->info('Abgebrochen.');
return Command::SUCCESS;
}
}
try {
$this->entityManager->remove($moduleEntity);
$this->entityManager->flush();
$io->success('✓ Datenbank-Eintrag erfolgreich entfernt');
$steps[] = '✓ Datenbank-Eintrag gelöscht';
} catch (\Exception $e) {
$io->error(sprintf('Datenbank-Eintrag konnte nicht entfernt werden: %s', $e->getMessage()));
$steps[] = '✗ Datenbank-Eintrag Fehler';
return Command::FAILURE;
}
} elseif ($keepDbEntry) {
$io->text('⏭ Datenbank-Eintrag wird behalten (--keep-db-entry)');
$steps[] = '⏭ DB-Eintrag behalten';
} else {
$io->text('⏭ Kein Datenbank-Eintrag vorhanden');
$steps[] = '⏭ Kein DB-Eintrag';
}
// 6. Nächste Schritte anzeigen
$io->section('Zusammenfassung');
$io->listing($steps);
$io->section('Nächste Schritte (manuell)');
$nextSteps = [];
// Composer-Package entfernen
if ($modulePlugin) {
$packageName = $this->guessPackageName($moduleIdentifier);
$nextSteps[] = sprintf('1. Composer-Package entfernen: composer remove %s', $packageName);
}
// Bundle aus config/bundles.php entfernen
$nextSteps[] = '2. Bundle aus config/bundles.php entfernen (falls vorhanden)';
// Migrationen
$nextSteps[] = '3. Optional: Migrationen zurückrollen (falls benötigt)';
$nextSteps[] = ' php bin/console doctrine:migrations:migrate prev';
// Cache leeren
$nextSteps[] = '4. Cache leeren: php bin/console cache:clear';
$io->listing($nextSteps);
$io->success(sprintf('Modul "%s" wurde vorbereitet zum Entfernen. Führen Sie die obigen Schritte manuell aus.', $moduleIdentifier));
return Command::SUCCESS;
}
private function displayModuleInfo(SymfonyStyle $io, $modulePlugin, ?Module $moduleEntity): void
{
$io->section('Modul-Informationen');
$rows = [];
if ($modulePlugin) {
$rows[] = ['Plugin-Informationen', ''];
$rows[] = [' Name', $modulePlugin->getDisplayName()];
$rows[] = [' Version', $modulePlugin->getVersion()];
$rows[] = [' Beschreibung', $modulePlugin->getDescription()];
$rows[] = [' Lizenziert', $modulePlugin->isLicensed() ? '✓ Ja' : '✗ Nein'];
$rows[] = [' Aktiv', $this->moduleRegistry->isModuleActive($modulePlugin->getIdentifier()) ? '✓ Ja' : '✗ Nein'];
}
if ($moduleEntity) {
if (!empty($rows)) {
$rows[] = ['', ''];
}
$rows[] = ['Datenbank-Informationen (Permission-System)', ''];
$rows[] = [' Name', $moduleEntity->getName()];
$rows[] = [' Code', $moduleEntity->getCode()];
$rows[] = [' Aktiv', $moduleEntity->isActive() ? '✓ Ja' : '✗ Nein'];
$rows[] = [' Permissions', (string)$moduleEntity->getPermissions()->count()];
}
$io->table(['Eigenschaft', 'Wert'], $rows);
}
private function findModuleEntity(string $identifier): ?Module
{
return $this->entityManager
->getRepository(Module::class)
->findOneBy(['code' => $identifier]);
}
private function guessPackageName(string $identifier): string
{
// Versuche Package-Name zu erraten (z.B. billing -> mycrm/billing-module)
return sprintf('mycrm/%s-module', $identifier);
}
}