feat(plugin): align with Shopware plugin guidelines

- add dedicated monolog channel aeon_dump_manager and log dump jobs
- migrate services.xml/routes.php to YAML
- require PHP ^8.2
- add extension icon and .config/shopware-extension.yml
- drop empty lifecycle overrides

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
haylanandClaude Bot committed 2026-10-03 23:26:21 +02:00
1 parent 29974928d2
commit 8efaef2042
12 files changed
+155 -129

No files matched your search

+21
View File
@@ -0,0 +1,21 @@
# shopware-cli extension configuration (build/package/validate)
build:
zip:
pack:
excludes:
paths:
- tests
- docs
- scripts
- phpunit.xml
- docker-compose.yml
- skills-lock.json
- AGENTS.md
- CLAUDE.md
- QWEN.md
- .claude
- .scratch
- .wayfinder
- src/Resources/config/plugin-transparent.png
validation:
ignore: []
+2 -2
View File
@@ -14,7 +14,7 @@ the full build spec live under `.scratch/admin-dump-manager/` and `.scratch/dump
- Plugin bootstrap class: `src/AeonDumpManager.php` (extends `Shopware\Core\Framework\Plugin`) — this
is the entry point Shopware calls for install/uninstall/activate/deactivate/update lifecycle hooks.
- Plugin class name is wired via `composer.json` → `extra.shopware-plugin-class`.
- Services are registered manually in `src/Resources/config/services.xml` (Symfony DI, XML format,
- Services are registered manually in `src/Resources/config/services.yaml` (Symfony DI, YAML format,
autowiring is not configured — every new service/command/task must be added here explicitly with
its tag, e.g. `console.command` or `shopware.scheduled.task`).
- Plugin store-config UI fields (e.g. `maxDumps`, `retantionDays`) are defined in
@@ -66,7 +66,7 @@ vendor/bin/phpunit tests/SomeTest.php # single file
## Adding functionality
- New console commands go in `src/Command/`, new scheduled tasks in `src/ScheduledTask/` — both must
be registered in `src/Resources/config/services.xml` with the appropriate tag or Shopware will not
be registered in `src/Resources/config/services.yaml` with the appropriate tag or Shopware will not
discover them.
- Scheduled tasks need a matching handler (tagged `messenger.message_handler`) to actually run; a bare
`ScheduledTask` subclass like `ExampleTask` only defines the schedule, not the behavior.
+1 -1
View File
@@ -25,7 +25,7 @@
"source": "https://git.arthurerlich.de/haylan/AeonDumpManager"
},
"require": {
"php": "^8.1",
"php": "^8.2",
"shopware/core": "~6.6.0"
},
"require-dev": {
+22 -31
View File
@@ -4,17 +4,33 @@ namespace AeonDumpManager;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\Plugin\Context\ActivateContext;
use Shopware\Core\Framework\Plugin\Context\DeactivateContext;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
use Shopware\Core\Framework\Plugin\Context\UpdateContext;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
class AeonDumpManager extends Plugin
{
public function install(InstallContext $installContext): void
public function build(ContainerBuilder $container): void
{
// Do stuff such as creating a new payment method
parent::build($container);
// Registers the dedicated monolog channel (Resources/config/packages/monolog.yaml)
$locator = new FileLocator('Resources/config');
$resolver = new LoaderResolver([
new YamlFileLoader($container, $locator),
new GlobFileLoader($container, $locator),
new DirectoryLoader($container, $locator),
]);
(new DelegatingLoader($resolver))->load(
\rtrim($this->getPath(), '/') . '/Resources/config/packages/*.yaml',
'glob'
);
}
public function uninstall(UninstallContext $uninstallContext): void
@@ -29,29 +45,4 @@ class AeonDumpManager extends Plugin
$connection = $this->container->get(Connection::class);
$connection->executeStatement('DROP TABLE IF EXISTS `aeon_dump_manager_job`');
}
public function activate(ActivateContext $activateContext): void
{
// Activate entities, such as a new payment method
// Or create new entities here, because now your plugin is installed and active for sure
}
public function deactivate(DeactivateContext $deactivateContext): void
{
// Deactivate entities, such as a new payment method
// Or remove previously created entities
}
public function update(UpdateContext $updateContext): void
{
// Update necessary stuff, mostly non-database related
}
public function postInstall(InstallContext $installContext): void
{
}
public function postUpdate(UpdateContext $updateContext): void
{
}
}
@@ -5,6 +5,7 @@ namespace AeonDumpManager\MessageQueue\Handler;
use AeonDumpManager\MessageQueue\Message\CreateDumpMessage;
use AeonDumpManager\Service\DumpJobStatusService;
use AeonDumpManager\Service\DumpService;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
@@ -13,6 +14,7 @@ class CreateDumpHandler
public function __construct(
private readonly DumpService $dumpService,
private readonly DumpJobStatusService $jobStatus,
private readonly LoggerInterface $logger,
) {
}
@@ -20,10 +22,12 @@ class CreateDumpHandler
{
$jobId = $message->getJobId();
$this->jobStatus->markRunning($jobId);
$this->logger->info('Dump job started.', ['jobId' => $jobId]);
try {
$this->dumpService->createAsync($jobId, $this->jobStatus);
} catch (\Throwable $exception) {
$this->logger->error('Dump job failed.', ['jobId' => $jobId, 'exception' => $exception]);
$this->jobStatus->markFailed($jobId, $exception->getMessage());
}
}
@@ -0,0 +1,2 @@
monolog:
channels: ['aeon_dump_manager']
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

-7
View File
@@ -1,7 +0,0 @@
<?php declare(strict_types=1);
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
return function (RoutingConfigurator $routes): void {
$routes->import(__DIR__ . '/../../Controller/*Controller.php', 'attribute');
};
+3
View File
@@ -0,0 +1,3 @@
aeon_dump_manager_controllers:
resource: ../../Controller/*Controller.php
type: attribute
-88
View File
@@ -1,88 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!-- Services -->
<service id="Symfony\Component\Process\ExecutableFinder"/>
<service id="aeon_dump_manager.dump_filename_parser" class="AeonDumpManager\Service\DumpFilenameParser"/>
<service id="aeon_dump_manager.dump_binary_locator" class="AeonDumpManager\Service\DumpBinaryLocator">
<argument type="service" id="Doctrine\DBAL\Connection"/>
<argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
<argument type="service" id="Symfony\Component\Process\ExecutableFinder"/>
</service>
<service id="aeon_dump_manager.dump_job_status_service" class="AeonDumpManager\Service\DumpJobStatusService">
<argument type="service" id="Doctrine\DBAL\Connection"/>
</service>
<service id="aeon_dump_manager.dump_lister" class="AeonDumpManager\Service\DumpLister">
<argument type="service" id="aeon_dump_manager.filesystem.private"/>
<argument type="service" id="aeon_dump_manager.dump_filename_parser"/>
</service>
<service id="aeon_dump_manager.dump_service" class="AeonDumpManager\Service\DumpService">
<argument type="service" id="aeon_dump_manager.dump_lister"/>
<argument type="service" id="aeon_dump_manager.filesystem.private"/>
<argument type="service" id="aeon_dump_manager.dump_binary_locator"/>
<argument type="service" id="Doctrine\DBAL\Connection"/>
<argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
<argument type="service" id="aeon_dump_manager.dump_filename_parser"/>
</service>
<!-- Async create job -->
<service id="aeon_dump_manager.message_queue.create_dump_handler" class="AeonDumpManager\MessageQueue\Handler\CreateDumpHandler">
<argument type="service" id="aeon_dump_manager.dump_service"/>
<argument type="service" id="aeon_dump_manager.dump_job_status_service"/>
<tag name="messenger.message_handler"/>
</service>
<!-- Retention -->
<service id="aeon_dump_manager.scheduled_task.purge_dumps_task" class="AeonDumpManager\ScheduledTask\PurgeDumpsTask">
<tag name="shopware.scheduled.task"/>
</service>
<service id="aeon_dump_manager.scheduled_task.purge_dumps_task_handler" class="AeonDumpManager\ScheduledTask\PurgeDumpsTaskHandler">
<argument type="service" id="scheduled_task.repository"/>
<argument type="service" id="logger"/>
<argument type="service" id="aeon_dump_manager.dump_service"/>
<argument type="service" id="aeon_dump_manager.dump_job_status_service"/>
<argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService"/>
<tag name="messenger.message_handler" handles="AeonDumpManager\ScheduledTask\PurgeDumpsTask"/>
</service>
<!-- Console commands -->
<service id="aeon_dump_manager.command.dump_list" class="AeonDumpManager\Command\DumpListCommand">
<argument type="service" id="aeon_dump_manager.dump_lister"/>
<tag name="console.command"/>
</service>
<service id="aeon_dump_manager.command.dump_create" class="AeonDumpManager\Command\DumpCreateCommand">
<argument type="service" id="aeon_dump_manager.dump_service"/>
<tag name="console.command"/>
</service>
<service id="aeon_dump_manager.command.dump_remove" class="AeonDumpManager\Command\DumpRemoveCommand">
<argument type="service" id="aeon_dump_manager.dump_service"/>
<tag name="console.command"/>
</service>
<!-- Admin API controller -->
<service id="AeonDumpManager\Controller\DumpController" public="true">
<argument type="service" id="aeon_dump_manager.dump_lister"/>
<argument type="service" id="aeon_dump_manager.dump_service"/>
<argument type="service" id="aeon_dump_manager.dump_job_status_service"/>
<argument type="service" id="messenger.default_bus"/>
</service>
</services>
</container>
+92
View File
@@ -0,0 +1,92 @@
services:
Symfony\Component\Process\ExecutableFinder: ~
aeon_dump_manager.dump_filename_parser:
class: AeonDumpManager\Service\DumpFilenameParser
aeon_dump_manager.dump_binary_locator:
class: AeonDumpManager\Service\DumpBinaryLocator
arguments:
- '@Doctrine\DBAL\Connection'
- '@Shopware\Core\System\SystemConfig\SystemConfigService'
- '@Symfony\Component\Process\ExecutableFinder'
aeon_dump_manager.dump_job_status_service:
class: AeonDumpManager\Service\DumpJobStatusService
arguments:
- '@Doctrine\DBAL\Connection'
aeon_dump_manager.dump_lister:
class: AeonDumpManager\Service\DumpLister
arguments:
- '@aeon_dump_manager.filesystem.private'
- '@aeon_dump_manager.dump_filename_parser'
aeon_dump_manager.dump_service:
class: AeonDumpManager\Service\DumpService
arguments:
- '@aeon_dump_manager.dump_lister'
- '@aeon_dump_manager.filesystem.private'
- '@aeon_dump_manager.dump_binary_locator'
- '@Doctrine\DBAL\Connection'
- '@Shopware\Core\System\SystemConfig\SystemConfigService'
- '@aeon_dump_manager.dump_filename_parser'
- '@monolog.logger.aeon_dump_manager'
# Async create job
aeon_dump_manager.message_queue.create_dump_handler:
class: AeonDumpManager\MessageQueue\Handler\CreateDumpHandler
arguments:
- '@aeon_dump_manager.dump_service'
- '@aeon_dump_manager.dump_job_status_service'
- '@monolog.logger.aeon_dump_manager'
tags:
- { name: messenger.message_handler }
# Retention
aeon_dump_manager.scheduled_task.purge_dumps_task:
class: AeonDumpManager\ScheduledTask\PurgeDumpsTask
tags:
- { name: shopware.scheduled.task }
aeon_dump_manager.scheduled_task.purge_dumps_task_handler:
class: AeonDumpManager\ScheduledTask\PurgeDumpsTaskHandler
arguments:
- '@scheduled_task.repository'
- '@monolog.logger.aeon_dump_manager'
- '@aeon_dump_manager.dump_service'
- '@aeon_dump_manager.dump_job_status_service'
- '@Shopware\Core\System\SystemConfig\SystemConfigService'
tags:
- { name: messenger.message_handler, handles: AeonDumpManager\ScheduledTask\PurgeDumpsTask }
# Console commands
aeon_dump_manager.command.dump_list:
class: AeonDumpManager\Command\DumpListCommand
arguments:
- '@aeon_dump_manager.dump_lister'
tags:
- { name: console.command }
aeon_dump_manager.command.dump_create:
class: AeonDumpManager\Command\DumpCreateCommand
arguments:
- '@aeon_dump_manager.dump_service'
tags:
- { name: console.command }
aeon_dump_manager.command.dump_remove:
class: AeonDumpManager\Command\DumpRemoveCommand
arguments:
- '@aeon_dump_manager.dump_service'
tags:
- { name: console.command }
# Admin API controller
AeonDumpManager\Controller\DumpController:
public: true
arguments:
- '@aeon_dump_manager.dump_lister'
- '@aeon_dump_manager.dump_service'
- '@aeon_dump_manager.dump_job_status_service'
- '@messenger.default_bus'
+8
View File
@@ -6,6 +6,7 @@ use AeonDumpManager\Service\Exception\DumpNotFoundException;
use AeonDumpManager\Struct\DumpFile;
use Doctrine\DBAL\Connection;
use League\Flysystem\FilesystemOperator;
use Psr\Log\LoggerInterface;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;
@@ -24,6 +25,7 @@ class DumpService
private readonly Connection $connection,
private readonly SystemConfigService $systemConfigService,
private readonly DumpFilenameParser $filenameParser,
private readonly LoggerInterface $logger,
) {
}
@@ -69,6 +71,7 @@ class DumpService
}
$this->aeonDumpManagerFilesystemPrivate->delete($path);
$this->logger->info('Dump deleted.', ['filename' => $filename]);
}
/**
@@ -100,6 +103,7 @@ class DumpService
foreach ($excess as $dumpFile) {
$this->aeonDumpManagerFilesystemPrivate->delete(DumpLister::path($dumpFile->filename));
$this->logger->info('Dump removed: maxDumps exceeded.', ['filename' => $dumpFile->filename]);
}
}
@@ -108,6 +112,7 @@ class DumpService
foreach ($this->dumpLister->list() as $dumpFile) {
if ($dumpFile->datetime < $threshold) {
$this->aeonDumpManagerFilesystemPrivate->delete(DumpLister::path($dumpFile->filename));
$this->logger->info('Dump removed: older than retention.', ['filename' => $dumpFile->filename]);
}
}
}
@@ -143,6 +148,7 @@ class DumpService
if (!$process->isSuccessful()) {
@unlink($tempPath);
$this->logger->error('Dump process failed.', ['exitCode' => $process->getExitCode()]);
throw new ProcessFailedException($process);
}
@@ -153,6 +159,8 @@ class DumpService
fclose($stream);
@unlink($tempPath);
$this->logger->info('Dump created.', ['filename' => $filename, 'sizeBytes' => $sizeBytes]);
return new DumpFile($filename, $dateTime, $sizeBytes);
}