commit 6236aa801051c6772fba73e20bfd44cf6747044a Author: Matthias Lodner Date: Wed Sep 10 02:53:03 2025 +0200 Init lodner set builder plugin. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4159670 --- /dev/null +++ b/composer.json @@ -0,0 +1,46 @@ +{ + "name": "lodner-set-builder/lodner-set-builder", + "description": "lodner-set-builder/lodner-set-builder", + "type": "shopware-platform-plugin", + "version": "1.0.0", + "authors": [ + { + "name": "Matthias Lodner", + "email": "matthias@lodner.de" + } + ], + "license": "MIT", + "require": { + "shopware/core": "~6.7.0" + }, + "extra": { + "shopware-plugin-class": "LodnerSetBuilder\\LodnerSetBuilder", + "copyright": "(c) Matthias Lodner", + "manufacturerLink": { + "de-DE": "example.de", + "en-GB": "example.de" + }, + "supportLink": { + "de-DE": "example.de", + "en-GB": "example.de" + }, + "label": { + "de-DE": "Lodner Set Builder", + "en-GB": "Lodner Set Builder" + }, + "description": { + "de-DE": "Lodner Set Builder ist ein Plugin um einen Kunden die Möglichkeit zu geben eigene Sets zu erstellen.", + "en-GB": "Lodner Set Builder is a plugin to let customers build thery own set." + } + }, + "autoload": { + "psr-4": { + "LodnerSetBuilder\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "LodnerSetBuilder\\Tests\\": "tests/" + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0accf2c --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,23 @@ + + + + + ./src/ + + + + + + + + + + + + tests + + + diff --git a/src/Command/ExampleCommand.php b/src/Command/ExampleCommand.php new file mode 100644 index 0000000..b46f410 --- /dev/null +++ b/src/Command/ExampleCommand.php @@ -0,0 +1,30 @@ +setDescription('Does something very special.'); + } + + // Actual code executed in the command + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('It works!'); + + // Exit code 0 for success + return 0; + } +} diff --git a/src/Core/Content/Set/SetCollection.php b/src/Core/Content/Set/SetCollection.php new file mode 100644 index 0000000..59aaa8e --- /dev/null +++ b/src/Core/Content/Set/SetCollection.php @@ -0,0 +1,22 @@ +addFlags(new Required(), new PrimaryKey()), + (new StringField('name', 'name')), + (new StringField('description', 'description')), + (new BoolField('active', 'active')) + ]); + } +} diff --git a/src/Core/Content/Set/SetEntity.php b/src/Core/Content/Set/SetEntity.php new file mode 100644 index 0000000..67c404a --- /dev/null +++ b/src/Core/Content/Set/SetEntity.php @@ -0,0 +1,47 @@ +name; + } + + public function setName(?string $name): void + { + $this->name = $name; + } + + public function getDescription(): ?string + { + return $this->description; + } + + public function setDescription(?string $description): void + { + $this->description = $description; + } + + public function isActive(): bool + { + return $this->active; + } + + public function setActive(bool $active): void + { + $this->active = $active; + } +} diff --git a/src/LodnerSetBuilder.php b/src/LodnerSetBuilder.php new file mode 100644 index 0000000..5c6c114 --- /dev/null +++ b/src/LodnerSetBuilder.php @@ -0,0 +1,71 @@ +getCustomFieldsInstaller()->install($installContext->getContext()); + } + + public function uninstall(UninstallContext $uninstallContext): void + { + parent::uninstall($uninstallContext); + + if ($uninstallContext->keepUserData()) { + return; + } + + // Remove or deactivate the data created by the plugin + } + + 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 + + $this->getCustomFieldsInstaller()->addRelations($activateContext->getContext()); + } + + 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 + { + } + + private function getCustomFieldsInstaller(): CustomFieldsInstaller + { + if ($this->container->has(CustomFieldsInstaller::class)) { + return $this->container->get(CustomFieldsInstaller::class); + } + + return new CustomFieldsInstaller( + $this->container->get('custom_field_set.repository'), + $this->container->get('custom_field_set_relation.repository') + ); + } +} diff --git a/src/Migration/Migration1757463502CreateSetTable.php b/src/Migration/Migration1757463502CreateSetTable.php new file mode 100644 index 0000000..6f75f14 --- /dev/null +++ b/src/Migration/Migration1757463502CreateSetTable.php @@ -0,0 +1,38 @@ +executeStatement($sql); + } + + public function updateDestructive(Connection $connection): void + { + } +} diff --git a/src/Resources/app/administration/src/main.js b/src/Resources/app/administration/src/main.js new file mode 100644 index 0000000..14673d3 --- /dev/null +++ b/src/Resources/app/administration/src/main.js @@ -0,0 +1,2 @@ +// Import admin module +import './module/swag-example'; diff --git a/src/Resources/app/administration/src/module/swag-example/index.js b/src/Resources/app/administration/src/module/swag-example/index.js new file mode 100644 index 0000000..01ff024 --- /dev/null +++ b/src/Resources/app/administration/src/module/swag-example/index.js @@ -0,0 +1,46 @@ +// /src/Resources/app/administration/src/module/swag-example/index.js +import deDE from './snippet/de-DE'; +import enGB from './snippet/en-GB'; + +Shopware.Module.register('swag-example', { + type: 'plugin', + name: 'Example', + title: 'swag-example.general.mainMenuItemGeneral', + description: 'sw-property.general.descriptionTextModule', + color: '#ff3d58', + icon: 'default-shopping-paper-bag-product', + + snippets: { + 'de-DE': deDE, + 'en-GB': enGB + }, + + routes: { + list: { + component: 'swag-example-list', + path: 'list' + }, + detail: { + component: 'swag-example-detail', + path: 'detail/:id', + meta: { + parentPath: 'swag.example.list' + } + }, + create: { + component: 'swag-example-create', + path: 'create', + meta: { + parentPath: 'swag.example.list' + } + } + }, + + navigation: [{ + label: 'swag-example.general.mainMenuItemGeneral', + color: '#ff3d58', + path: 'swag.example.list', + icon: 'default-shopping-paper-bag-product', + position: 100 + }] +}); diff --git a/src/Resources/app/administration/src/snippet/de-DE.json b/src/Resources/app/administration/src/snippet/de-DE.json new file mode 100644 index 0000000..7e9772a --- /dev/null +++ b/src/Resources/app/administration/src/snippet/de-DE.json @@ -0,0 +1,8 @@ +{ + "swag-example": { + "general": { + "mainMenuItemGeneral": "My custom module", + "descriptionTextModule": "Manage this custom module here" + } + } +} diff --git a/src/Resources/app/administration/src/snippet/en-GB.json b/src/Resources/app/administration/src/snippet/en-GB.json new file mode 100644 index 0000000..7e9772a --- /dev/null +++ b/src/Resources/app/administration/src/snippet/en-GB.json @@ -0,0 +1,8 @@ +{ + "swag-example": { + "general": { + "mainMenuItemGeneral": "My custom module", + "descriptionTextModule": "Manage this custom module here" + } + } +} diff --git a/src/Resources/config/config.xml b/src/Resources/config/config.xml new file mode 100644 index 0000000..b45fed5 --- /dev/null +++ b/src/Resources/config/config.xml @@ -0,0 +1,16 @@ + + + + + + Minimal configuration + + + textField + + test + + + + diff --git a/src/Resources/config/routes.xml b/src/Resources/config/routes.xml new file mode 100644 index 0000000..981968b --- /dev/null +++ b/src/Resources/config/routes.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml new file mode 100644 index 0000000..32f9141 --- /dev/null +++ b/src/Resources/config/services.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Resources/views/storefront/page/example.html.twig b/src/Resources/views/storefront/page/example.html.twig new file mode 100644 index 0000000..c02d97b --- /dev/null +++ b/src/Resources/views/storefront/page/example.html.twig @@ -0,0 +1,5 @@ +{% sw_extends '@Storefront/storefront/base.html.twig' %} + +{% block base_content %} +

{{ example }}

+{% endblock %} diff --git a/src/ScheduledTask/ExampleTask.php b/src/ScheduledTask/ExampleTask.php new file mode 100644 index 0000000..448817e --- /dev/null +++ b/src/ScheduledTask/ExampleTask.php @@ -0,0 +1,18 @@ + self::CUSTOM_FIELDSET_NAME, + 'config' => [ + 'label' => [ + 'en-GB' => 'English custom field set label', + 'de-DE' => 'German custom field set label', + Defaults::LANGUAGE_SYSTEM => 'Mention the fallback label here' + ] + ], + 'customFields' => [ + [ + 'name' => 'swag_example_size', + 'type' => CustomFieldTypes::INT, + 'config' => [ + 'label' => [ + 'en-GB' => 'English custom field label', + 'de-DE' => 'German custom field label', + Defaults::LANGUAGE_SYSTEM => 'Mention the fallback label here' + ], + 'customFieldPosition' => 1 + ] + ] + ] + ]; + + public function __construct( + private readonly EntityRepository $customFieldSetRepository, + private readonly EntityRepository $customFieldSetRelationRepository + ) { + } + + public function install(Context $context): void + { + $this->customFieldSetRepository->upsert([ + self::CUSTOM_FIELDSET + ], $context); + } + + public function addRelations(Context $context): void + { + $this->customFieldSetRelationRepository->upsert(array_map(function (string $customFieldSetId) { + return [ + 'customFieldSetId' => $customFieldSetId, + 'entityName' => 'product', + ]; + }, $this->getCustomFieldSetIds($context)), $context); + } + + /** + * @return string[] + */ + private function getCustomFieldSetIds(Context $context): array + { + $criteria = new Criteria(); + + $criteria->addFilter(new EqualsFilter('name', self::CUSTOM_FIELDSET_NAME)); + + return $this->customFieldSetRepository->searchIds($criteria, $context)->getIds(); + } +} diff --git a/src/Storefront/Controller/ExampleController.php b/src/Storefront/Controller/ExampleController.php new file mode 100644 index 0000000..910fba7 --- /dev/null +++ b/src/Storefront/Controller/ExampleController.php @@ -0,0 +1,27 @@ + [StorefrontRouteScope::ID]])] +class ExampleController extends StorefrontController +{ + #[Route( + path: '/example', + name: 'frontend.example.example', + methods: ['GET'] + )] + public function showExample(Request $request, SalesChannelContext $context): Response + { + return $this->renderStorefront('@LodnerSetBuilder/storefront/page/example.html.twig', [ + 'example' => 'Hello world' + ]); + } +} diff --git a/src/Subscriber/MySubscriber.php b/src/Subscriber/MySubscriber.php new file mode 100644 index 0000000..19319d4 --- /dev/null +++ b/src/Subscriber/MySubscriber.php @@ -0,0 +1,24 @@ + => + return [ + ProductEvents::PRODUCT_LOADED_EVENT => 'onProductsLoaded' + ]; + } + + public function onProductsLoaded(EntityLoadedEvent $event) + { + // Do something + // E.g. work with the loaded entities: $event->getEntities() + } +} diff --git a/tests/TestBootstrap.php b/tests/TestBootstrap.php new file mode 100644 index 0000000..9e043cd --- /dev/null +++ b/tests/TestBootstrap.php @@ -0,0 +1,12 @@ +addCallingPlugin() + ->addActivePlugins('LodnerSetBuilder') + ->setForceInstallPlugins(true) + ->bootstrap() + ->getClassLoader(); + +$loader->addPsr4('LodnerSetBuilder\\Tests\\', __DIR__);