From 6f2c0a2aac271cf6df357545791a90b0717a864b Mon Sep 17 00:00:00 2001 From: Jordan Kniest Date: Fri, 14 Jun 2024 14:45:42 +0200 Subject: [PATCH] Add simple in-memory cache for helpers, refactor helpers (#67) * Dont run vendor fixtures by default, add flag to run vendor fixtures * Trailing slashes! * Change docs formats * Use first class callable syntax * Refactor FixtureTrait * Enable phpstan rule: checkMissingIterableValueType * Enable rule checkGenericClassInNonGenericObjectType in psalm * Add database utils and first method to delete entities * Automatically assign the fixtureHelper to each fixture * Disable error warnings if generics arent provided, since they were added in SW 6.5.something * Add property trait, refactor category utils * Refactor all utils classes * Move out all locale & language related methods of the sales channel fixture * Extract currency related methods of SalesChannelUtils to CurrencyUtils * Extract tax methods into TaxUtils * wip * Remove double definition of method * Remove trait --- CHANGELOG.md | 15 ++- _examples/CustomerFixture.php | 4 +- composer.json | 3 +- src/FixtureHelper.php | 53 +++++++++- src/Utils/CategoryUtils.php | 61 +++++------ src/Utils/CmsUtils.php | 29 ++++-- src/Utils/CurrencyUtils.php | 49 +++++++++ src/Utils/CustomerUtils.php | 36 ------- src/Utils/DatabaseUtils.php | 10 ++ src/Utils/LanguageAndLocaleUtils.php | 126 ++++++++++++++++++++++ src/Utils/MediaUtils.php | 43 +++++--- src/Utils/PaymentMethodUtils.php | 30 ++++-- src/Utils/SalesChannelUtils.php | 149 +++++---------------------- src/Utils/SalutationUtils.php | 49 +++++++++ src/Utils/ShippingMethodUtils.php | 30 ++++-- src/Utils/TaxUtils.php | 60 +++++++++++ 16 files changed, 509 insertions(+), 238 deletions(-) create mode 100644 src/Utils/CurrencyUtils.php delete mode 100644 src/Utils/CustomerUtils.php create mode 100644 src/Utils/LanguageAndLocaleUtils.php create mode 100644 src/Utils/SalutationUtils.php create mode 100644 src/Utils/TaxUtils.php diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc3246..d77d78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - This option will prevent the fixtures from being executed but still prints all fixtures it would execute - Added new DatabaseUtils with a few helpful methods: - `deleteEntities` takes an entity name and criteria and deletes all entities which match the criteria +- Added a small cache for all utilities. It prevents loading data twice within the same request / command execution +- Added small helper function: `$fixtureHelper->ensureNotEmpty` which throws an exception if something is empty (using the PHP empty function) ### Changed - Changed argument type on `SalesChannelUtils::getTax()` from `int` to `float` @@ -23,11 +25,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `FixtureTrait::runFixtureGroup` is a new function to execute whole fixture groups with optionally dependencies - Each fixture now has direct access to the FixtureHelper using `$this->helper` - **Breaking** If you have the helper (or any other helper) previously assigned to `$this->helper` it will either fail or override the FixturePlugin helper +- **Breaking** Moved `SalesChannelUtils::getLanguage()` to `LanguageAndLocaleUtils::getLanguage()` +- **Breaking** Moved `SalesChannelUtils::getLocale()` to `LanguageAndLocaleUtils::getLocale()` +- **Breaking** Moved `SalesChannelUtils::getCountry()` to `LanguageAndLocaleUtils::getCountry()` +- **Breaking** Moved `SalesChannelUtils::getSnippetSet()` to `LanguageAndLocaleUtils::getSnippetSet()` +- **Breaking** Moved `SalesChannelUtils::getCurrencyEuro()` to `CurrencyUtils::getCurrencyEuro()` +- **Breaking** Moved `SalesChannelUtils::getTax19()` to `TaxUtils::getTax19()` +- **Breaking** Moved `SalesChannelUtils::getTax()` to `TaxUtils::getTax()` ### Removed - Dropped support for PHP 8.1 - Dropped support for Shopware 6.3 & 6.4 -- Removed FixtureBag +- **Breaking** Removed FixtureBag +- **Breaking** CategoryUtils + - Removed method `getFirst` on CategoryUtils + - Removed method `getByName` on CategoryUtils +- **Breaking** Renamed `CategoryUtils` to `SalutationUtils` ## [2.4.0] - 2023-11-15 ### Added diff --git a/_examples/CustomerFixture.php b/_examples/CustomerFixture.php index 456cb64..6574c0d 100644 --- a/_examples/CustomerFixture.php +++ b/_examples/CustomerFixture.php @@ -32,7 +32,7 @@ public function load(FixtureBag $bag): void 'defaultPaymentMethodId' => $this->helper->PaymentMethod()->getInvoicePaymentMethod()->getId(), 'defaultBillingAddress' => [ 'id' => self::ADDRESS_ID, - 'salutationId' => $this->helper->Customer()->getNotSpecifiedSalutation()->getId(), + 'salutationId' => $this->helper->Salutation()->getNotSpecifiedSalutation()->getId(), 'firstName' => 'John', 'lastName' => 'Doe', 'zipcode' => '1234', @@ -41,7 +41,7 @@ public function load(FixtureBag $bag): void 'countryId' => $this->helper->SalesChannel()->getCountry('DE')->getId(), ], 'defaultShippingAddressId' => self::ADDRESS_ID, - 'salutationId' => $this->helper->Customer()->getNotSpecifiedSalutation()->getId(), + 'salutationId' => $this->helper->Salutation()->getNotSpecifiedSalutation()->getId(), 'customerNumber' => '1122', 'firstName' => 'John', 'lastName' => 'Doe', diff --git a/composer.json b/composer.json index 5d62f4c..d79300e 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "php": "^8.2 || ^8.3", "shopware/core": "6.5.*|6.6.*", "shopware/administration": "6.5.*|6.6.*", - "shopware/storefront": "6.5.*|6.6.*" + "shopware/storefront": "6.5.*|6.6.*", + "spatie/once": "^3.1" }, "require-dev": { "friendsofphp/php-cs-fixer": "3.58.1", diff --git a/src/FixtureHelper.php b/src/FixtureHelper.php index b1a35e9..2e0f8d0 100644 --- a/src/FixtureHelper.php +++ b/src/FixtureHelper.php @@ -6,12 +6,15 @@ use Basecom\FixturePlugin\Utils\CategoryUtils; use Basecom\FixturePlugin\Utils\CmsUtils; -use Basecom\FixturePlugin\Utils\CustomerUtils; +use Basecom\FixturePlugin\Utils\CurrencyUtils; use Basecom\FixturePlugin\Utils\DatabaseUtils; +use Basecom\FixturePlugin\Utils\LanguageAndLocaleUtils; use Basecom\FixturePlugin\Utils\MediaUtils; use Basecom\FixturePlugin\Utils\PaymentMethodUtils; use Basecom\FixturePlugin\Utils\SalesChannelUtils; +use Basecom\FixturePlugin\Utils\SalutationUtils; use Basecom\FixturePlugin\Utils\ShippingMethodUtils; +use Basecom\FixturePlugin\Utils\TaxUtils; readonly class FixtureHelper { @@ -22,11 +25,24 @@ public function __construct( private CmsUtils $cmsUtils, private PaymentMethodUtils $paymentMethodUtils, private ShippingMethodUtils $shippingMethodUtils, - private CustomerUtils $customerUtils, + private SalutationUtils $salutationUtils, private DatabaseUtils $databaseUtils, + private LanguageAndLocaleUtils $languageAndLocaleUtils, + private CurrencyUtils $currencyUtils, + private TaxUtils $taxUtils, ) { } + /** + * @phpstan-assert !empty $something + */ + public function ensureNotEmpty(mixed $something): void + { + if (empty($something)) { + throw new \LogicException('Expected parameter not to be empty, but it was.'); + } + } + /** * Use this to access the media related features * of the fixture helper class. @@ -55,12 +71,12 @@ public function SalesChannel(): SalesChannelUtils } /** - * Use this to access the customer related features + * Use this to access the salutation related features * of the fixture helper class. */ - public function Customer(): CustomerUtils + public function Salutation(): SalutationUtils { - return $this->customerUtils; + return $this->salutationUtils; } /** @@ -90,6 +106,33 @@ public function ShippingMethod(): ShippingMethodUtils return $this->shippingMethodUtils; } + /** + * Use this to access the language & locale related features + * of the fixture helper class. + */ + public function LanguageAndLocale(): LanguageAndLocaleUtils + { + return $this->languageAndLocaleUtils; + } + + /** + * Use this to access the currency related features + * of the fixture helper class. + */ + public function Currency(): CurrencyUtils + { + return $this->currencyUtils; + } + + /** + * Use this to access the tax related features + * of the fixture helper class. + */ + public function Tax(): TaxUtils + { + return $this->taxUtils; + } + /** * Use this to access the general database helper functions * of the fixture helper class. diff --git a/src/Utils/CategoryUtils.php b/src/Utils/CategoryUtils.php index e2968c0..45f7f5d 100644 --- a/src/Utils/CategoryUtils.php +++ b/src/Utils/CategoryUtils.php @@ -1,7 +1,5 @@ helper->Category()->……(); + * ``` + */ readonly class CategoryUtils { /** @@ -23,46 +30,24 @@ public function __construct( ) { } - public function getRootCategory(): ?CategoryEntity - { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('autoIncrement', 1)) - ->addFilter(new EqualsFilter('level', 1)) - ->setLimit(1); - - $category = $this->categoryRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $category instanceof CategoryEntity ? $category : null; - } - - public function getFirst(): ?CategoryEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('level', '1'), - )->setLimit(1); - - $category = $this->categoryRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $category instanceof CategoryEntity ? $category : null; - } - /** - * Gets the first found category with the provided name. + * Gets the root category of the shop or. */ - public function getByName(string $name): ?CategoryEntity + public function getRootCategory(): ?CategoryEntity { - $criteria = new Criteria(); - $criteria->addFilter(new EqualsFilter('name', $name)); - $criteria->setLimit(1); + return once(function (): ?CategoryEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('autoIncrement', 1)) + ->addFilter(new EqualsFilter('level', 1)) + ->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - $category = $this->categoryRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $category = $this->categoryRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); - return $category instanceof CategoryEntity ? $category : null; + return $category instanceof CategoryEntity ? $category : null; + }); } } diff --git a/src/Utils/CmsUtils.php b/src/Utils/CmsUtils.php index b3aaac0..17f3122 100644 --- a/src/Utils/CmsUtils.php +++ b/src/Utils/CmsUtils.php @@ -12,6 +12,15 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +/** + * This class provides utility methods to work with the shopware cms. It has build in caching to prevent + * multiple database queries for the same data within one command execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->Cms()->……(); + * ``` + */ readonly class CmsUtils { /** @@ -24,15 +33,19 @@ public function __construct( public function getDefaultCategoryLayout(): ?CmsPageEntity { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('locked', '1')) - ->addFilter(new EqualsAnyFilter('translations.name', ['Default category layout', 'Default listing layout'])) - ->setLimit(1); + return once(function (): ?CmsPageEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('locked', '1')) + ->addFilter(new EqualsAnyFilter('translations.name', ['Default category layout', 'Default listing layout'])) + ->setLimit(1); - $cmsPage = $this->cmsPageRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - return $cmsPage instanceof CmsPageEntity ? $cmsPage : null; + $cmsPage = $this->cmsPageRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $cmsPage instanceof CmsPageEntity ? $cmsPage : null; + }); } } diff --git a/src/Utils/CurrencyUtils.php b/src/Utils/CurrencyUtils.php new file mode 100644 index 0000000..bf25e01 --- /dev/null +++ b/src/Utils/CurrencyUtils.php @@ -0,0 +1,49 @@ +helper->Currency()->……(); + * ``` + */ +class CurrencyUtils +{ + /** + * @param EntityRepository $currencyRepository + */ + public function __construct( + private EntityRepository $currencyRepository, + ) { + } + + public function getCurrencyEuro(): ?CurrencyEntity + { + return once(function (): ?CurrencyEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('isoCode', 'EUR')) + ->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $currency = $this->currencyRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $currency instanceof CurrencyEntity ? $currency : null; + }); + } +} diff --git a/src/Utils/CustomerUtils.php b/src/Utils/CustomerUtils.php deleted file mode 100644 index 678283d..0000000 --- a/src/Utils/CustomerUtils.php +++ /dev/null @@ -1,36 +0,0 @@ - $salutationRepository - */ - public function __construct( - private EntityRepository $salutationRepository, - ) { - } - - public function getNotSpecifiedSalutation(): ?SalutationEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('salutationKey', 'not_specified'), - )->setLimit(1); - - $salutation = $this->salutationRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $salutation instanceof SalutationEntity ? $salutation : null; - } -} diff --git a/src/Utils/DatabaseUtils.php b/src/Utils/DatabaseUtils.php index e1a5bb7..8cc8796 100644 --- a/src/Utils/DatabaseUtils.php +++ b/src/Utils/DatabaseUtils.php @@ -8,6 +8,16 @@ use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +/** + * This class provides utility methods to directly interact with the database. It has build in + * caching to prevent multiple database queries for the same data within one command + * execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->Database()->……(); + * ``` + */ readonly class DatabaseUtils { public function __construct( diff --git a/src/Utils/LanguageAndLocaleUtils.php b/src/Utils/LanguageAndLocaleUtils.php new file mode 100644 index 0000000..7ccefe3 --- /dev/null +++ b/src/Utils/LanguageAndLocaleUtils.php @@ -0,0 +1,126 @@ +helper->LanguageAndLocale()->……(); + * ``` + */ +readonly class LanguageAndLocaleUtils +{ + /** + * @param EntityRepository $snippetSetRepository + * @param EntityRepository $countryRepository + * @param EntityRepository $languageRepository + * @param EntityRepository $localeRepository + */ + public function __construct( + private EntityRepository $snippetSetRepository, + private EntityRepository $countryRepository, + private EntityRepository $languageRepository, + private EntityRepository $localeRepository, + ) { + } + + /** + * Return a specific language by its name or null if its was not found. + */ + public function getLanguage(string $languageName): ?LanguageEntity + { + return once(function () use ($languageName): ?LanguageEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('name', $languageName), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $language = $this->languageRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $language instanceof LanguageEntity ? $language : null; + }); + } + + /** + * Return a specific locale by its ISO code or null if its was not found. + */ + public function getLocale(string $code): ?LocaleEntity + { + return once(function () use ($code): ?LocaleEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('code', $code), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $locale = $this->localeRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $locale instanceof LocaleEntity ? $locale : null; + }); + } + + /** + * Return a specific country by its ISO code or null if its was not found. + */ + public function getCountry(string $countryIso): ?CountryEntity + { + return once(function () use ($countryIso): ?CountryEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('iso', $countryIso), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $country = $this->countryRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $country instanceof CountryEntity ? $country : null; + }); + } + + /** + * Return a specific snippet set by its country's ISO code or null if its was not found. + */ + public function getSnippetSet(string $countryCodeIso): ?SnippetSetEntity + { + return once(function () use ($countryCodeIso): ?SnippetSetEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('iso', $countryCodeIso), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $snippetSet = $this->snippetSetRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $snippetSet instanceof SnippetSetEntity ? $snippetSet : null; + }); + } +} diff --git a/src/Utils/MediaUtils.php b/src/Utils/MediaUtils.php index 297c016..b72aae9 100644 --- a/src/Utils/MediaUtils.php +++ b/src/Utils/MediaUtils.php @@ -14,6 +14,15 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +/** + * This class provides utility methods to work with media assets. It has build in caching to prevent + * multiple database queries for the same data within one command execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->Media()->……(); + * ``` + */ readonly class MediaUtils { /** @@ -30,31 +39,41 @@ public function __construct( /** * Copied from "vendor/shopware/core/Content/Media/MediaService.php". + * + * Extended it to include simple cache */ public function getDefaultFolder(string $folderName): ?MediaFolderEntity { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('media_folder.defaultFolder.entity', $folderName)) - ->addAssociation('defaultFolder') - ->setLimit(1); + return once(function () use ($folderName): ?MediaFolderEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('media_folder.defaultFolder.entity', $folderName)) + ->addAssociation('defaultFolder') + ->setLimit(1); - $mediaFolder = $this->mediaFolderRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - return $mediaFolder instanceof MediaFolderEntity ? $mediaFolder : null; + $mediaFolder = $this->mediaFolderRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $mediaFolder instanceof MediaFolderEntity ? $mediaFolder : null; + }); } + /** + * "Upload" a file within shopware. It takes a real file path ($filename) and uploads it as a full media. + */ public function upload(string $mediaId, string $folderId, string $filename, string $extension, string $contentType): void { $ctx = Context::createDefaultContext(); - $this->mediaRepository->upsert([ + $this->mediaRepository->upsert( [ - 'id' => $mediaId, - 'mediaFolderId' => $folderId, + [ + 'id' => $mediaId, + 'mediaFolderId' => $folderId, + ], ], - ], $ctx, ); diff --git a/src/Utils/PaymentMethodUtils.php b/src/Utils/PaymentMethodUtils.php index e980f07..20dbf15 100644 --- a/src/Utils/PaymentMethodUtils.php +++ b/src/Utils/PaymentMethodUtils.php @@ -12,6 +12,15 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +/** + * This class provides utility methods to work with payment methods. It has build in caching to prevent + * multiple database queries for the same data within one command execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->PaymentMethod()->……(); + * ``` + */ readonly class PaymentMethodUtils { /** @@ -22,16 +31,23 @@ public function __construct( ) { } + /** + * Return the default invoice payment method of shopware. + */ public function getInvoicePaymentMethod(): ?PaymentMethodEntity { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('handlerIdentifier', InvoicePayment::class), - )->setLimit(1); + return once(function (): ?PaymentMethodEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('handlerIdentifier', InvoicePayment::class), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - $paymentMethod = $this->paymentMethodRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $paymentMethod = $this->paymentMethodRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); - return $paymentMethod instanceof PaymentMethodEntity ? $paymentMethod : null; + return $paymentMethod instanceof PaymentMethodEntity ? $paymentMethod : null; + }); } } diff --git a/src/Utils/SalesChannelUtils.php b/src/Utils/SalesChannelUtils.php index 89f6086..a11ad6a 100644 --- a/src/Utils/SalesChannelUtils.php +++ b/src/Utils/SalesChannelUtils.php @@ -9,53 +9,47 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; -use Shopware\Core\System\Country\CountryCollection; -use Shopware\Core\System\Country\CountryEntity; -use Shopware\Core\System\Currency\CurrencyCollection; -use Shopware\Core\System\Currency\CurrencyEntity; -use Shopware\Core\System\Language\LanguageCollection; -use Shopware\Core\System\Language\LanguageEntity; -use Shopware\Core\System\Locale\LocaleCollection; -use Shopware\Core\System\Locale\LocaleEntity; use Shopware\Core\System\SalesChannel\SalesChannelCollection; use Shopware\Core\System\SalesChannel\SalesChannelEntity; -use Shopware\Core\System\Snippet\Aggregate\SnippetSet\SnippetSetCollection; -use Shopware\Core\System\Snippet\Aggregate\SnippetSet\SnippetSetEntity; -use Shopware\Core\System\Tax\TaxCollection; -use Shopware\Core\System\Tax\TaxEntity; +/** + * This class provides utility methods to work with sales channels. It has build in caching to prevent + * multiple database queries for the same data within one command execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->SalesChannel()->……(); + * ``` + */ readonly class SalesChannelUtils { /** * @param EntityRepository $salesChannelRepository - * @param EntityRepository $snippetSetRepository - * @param EntityRepository $taxRepository - * @param EntityRepository $countryRepository - * @param EntityRepository $languageRepository - * @param EntityRepository $currencyRepository - * @param EntityRepository $localeRepository */ public function __construct( private EntityRepository $salesChannelRepository, - private EntityRepository $snippetSetRepository, - private EntityRepository $taxRepository, - private EntityRepository $countryRepository, - private EntityRepository $languageRepository, - private EntityRepository $currencyRepository, - private EntityRepository $localeRepository, ) { } + /** + * Return the first sales channel with type "Storefront" or null if non was found. + */ public function getStorefrontSalesChannel(): ?SalesChannelEntity { return $this->getSalesChannelByType(Defaults::SALES_CHANNEL_TYPE_STOREFRONT); } + /** + * Return the first sales channel with type "headless" or null if non was found. + */ public function getHeadlessSalesChannel(): ?SalesChannelEntity { return $this->getSalesChannelByType(Defaults::SALES_CHANNEL_TYPE_API); } + /** + * Return the first sales channel with type "Product Comparison" or null if non was found. + */ public function getProductComparisonSalesChannel(): ?SalesChannelEntity { return $this->getSalesChannelByType(Defaults::SALES_CHANNEL_TYPE_PRODUCT_COMPARISON); @@ -63,105 +57,18 @@ public function getProductComparisonSalesChannel(): ?SalesChannelEntity public function getSalesChannelByType(string $salesChannelType): ?SalesChannelEntity { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('typeId', $salesChannelType)) - ->setLimit(1); - - $salesChannel = $this->salesChannelRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $salesChannel instanceof SalesChannelEntity ? $salesChannel : null; - } - - public function getCurrencyEuro(): ?CurrencyEntity - { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('isoCode', 'EUR')) - ->setLimit(1); - - $currency = $this->currencyRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $currency instanceof CurrencyEntity ? $currency : null; - } - - public function getLanguage(string $languageName): ?LanguageEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('name', $languageName), - )->setLimit(1); - - $language = $this->languageRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $language instanceof LanguageEntity ? $language : null; - } - - public function getLocale(string $code): ?LocaleEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('code', $code), - )->setLimit(1); - - $locale = $this->localeRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $locale instanceof LocaleEntity ? $locale : null; - } - - public function getCountry(string $countryIso): ?CountryEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('iso', $countryIso), - )->setLimit(1); - - $country = $this->countryRepository - ->search($criteria, Context::createDefaultContext(), - )->first(); - - return $country instanceof CountryEntity ? $country : null; - } - - public function getSnippetSet(string $countryCodeIso): ?SnippetSetEntity - { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('iso', $countryCodeIso), - )->setLimit(1); - - $snippetSet = $this->snippetSetRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); - - return $snippetSet instanceof SnippetSetEntity ? $snippetSet : null; - } - - public function getTax19(): ?TaxEntity - { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('taxRate', 19)) - ->setLimit(1); - - $tax = $this->taxRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + return once(function () use ($salesChannelType): ?SalesChannelEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('typeId', $salesChannelType)) + ->setLimit(1); - return $tax instanceof TaxEntity ? $tax : null; - } - - public function getTax(float $taxValue): ?TaxEntity - { - $criteria = (new Criteria()) - ->addFilter(new EqualsFilter('taxRate', $taxValue)) - ->setLimit(1); + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - $tax = $this->taxRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $salesChannel = $this->salesChannelRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); - return $tax instanceof TaxEntity ? $tax : null; + return $salesChannel instanceof SalesChannelEntity ? $salesChannel : null; + }); } } diff --git a/src/Utils/SalutationUtils.php b/src/Utils/SalutationUtils.php new file mode 100644 index 0000000..aab9d00 --- /dev/null +++ b/src/Utils/SalutationUtils.php @@ -0,0 +1,49 @@ +helper->Salutation()->……(); + * ``` + */ +readonly class SalutationUtils +{ + /** + * @param EntityRepository $salutationRepository + */ + public function __construct( + private EntityRepository $salutationRepository, + ) { + } + + public function getNotSpecifiedSalutation(): ?SalutationEntity + { + return once(function (): ?SalutationEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('salutationKey', 'not_specified'), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $salutation = $this->salutationRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $salutation instanceof SalutationEntity ? $salutation : null; + }); + } +} diff --git a/src/Utils/ShippingMethodUtils.php b/src/Utils/ShippingMethodUtils.php index 0ee6b19..0f18c8e 100644 --- a/src/Utils/ShippingMethodUtils.php +++ b/src/Utils/ShippingMethodUtils.php @@ -11,6 +11,15 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +/** + * This class provides utility methods to work with shipping methods. It has build in caching + * to prevent multiple database queries for the same data within one command execution / request. + * + * This class is designed to be used through the FixtureHelper, using: + * ```php + * $this->helper->ShippingMethod()->……(); + * ``` + */ readonly class ShippingMethodUtils { /** @@ -21,16 +30,23 @@ public function __construct( ) { } + /** + * Returns the first active shipping method or null if none exists. + */ public function getFirstShippingMethod(): ?ShippingMethodEntity { - $criteria = (new Criteria())->addFilter( - new EqualsFilter('active', '1'), - )->setLimit(1); + return once(function (): ?ShippingMethodEntity { + $criteria = (new Criteria())->addFilter( + new EqualsFilter('active', '1'), + )->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); - $shippingMethod = $this->shippingMethodRepository - ->search($criteria, Context::createDefaultContext()) - ->first(); + $shippingMethod = $this->shippingMethodRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); - return $shippingMethod instanceof ShippingMethodEntity ? $shippingMethod : null; + return $shippingMethod instanceof ShippingMethodEntity ? $shippingMethod : null; + }); } } diff --git a/src/Utils/TaxUtils.php b/src/Utils/TaxUtils.php new file mode 100644 index 0000000..a8388ee --- /dev/null +++ b/src/Utils/TaxUtils.php @@ -0,0 +1,60 @@ +helper->Tax()->……(); + * ``` + */ +class TaxUtils +{ + /** + * @param EntityRepository $taxRepository + */ + public function __construct( + private EntityRepository $taxRepository, + ) { + } + + /** + * Return the tax entity with a tax rate of 19% or null if none exists. + */ + public function getTax19(): ?TaxEntity + { + return $this->getTax(19); + } + + /** + * Return a tax entity with a specific tax rate or null if none exists. + */ + public function getTax(float $taxRate): ?TaxEntity + { + return once(function () use ($taxRate): ?TaxEntity { + $criteria = (new Criteria()) + ->addFilter(new EqualsFilter('taxRate', $taxRate)) + ->setLimit(1); + + $criteria->setTitle(sprintf('%s::%s()', __CLASS__, __FUNCTION__)); + + $tax = $this->taxRepository + ->search($criteria, Context::createDefaultContext()) + ->first(); + + return $tax instanceof TaxEntity ? $tax : null; + }); + } +}