diff --git a/architecture-baseline.json b/architecture-baseline.json index d791d26..ef0fe57 100644 --- a/architecture-baseline.json +++ b/architecture-baseline.json @@ -181,6 +181,13 @@ "ruleset": "Spryker", "priority": "2" }, + { + "fileName": "src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryBridge.php", + "description": "Bridges: Method `getTranslationsByGlossaryKeysAndLocaleTransfers()` must have `public function getCollection(CriteriaTransfer): CollectionTransfer` signature.", + "rule": "BridgeFacadeMethodsRule", + "ruleset": "Spryker", + "priority": "2" + }, { "fileName": "src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryInterface.php", "description": "Bridges: The bridge interface has incorrect method signature for `createAndTouchTranslation()`. Missed return type. That violates the rule \"All bridge interface methods must have exactly the same or more strict signature as their parent\"", diff --git a/composer.json b/composer.json index 5096720..75e1921 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "spryker/acl-merchant-portal-extension": "^1.0.0", "spryker/collector": "^5.1.1 || ^6.0.0", "spryker/event": "^2.1.0", - "spryker/glossary": "^3.0.0", + "spryker/glossary": "^3.16.0", "spryker/gui": "^3.33.0", "spryker/kernel": "^3.30.0", "spryker/key-builder": "^1.1.0", @@ -22,7 +22,7 @@ "spryker/store": "^1.4.0", "spryker/symfony": "^3.0.0", "spryker/touch": "^3.0.0 || ^4.0.0", - "spryker/transfer": "^3.25.0", + "spryker/transfer": "^3.27.0", "spryker/util-data-reader": "^1.0.0" }, "require-dev": { diff --git a/dependency.json b/dependency.json index 3665482..1932e90 100644 --- a/dependency.json +++ b/dependency.json @@ -1,5 +1,5 @@ { "include": { - "spryker/transfer": "Provides transfer objects decimal property type functionality." + "spryker/transfer": "Provides transfer objects definition with strict types." } } diff --git a/src/Spryker/Shared/ProductSearch/Transfer/product_search.transfer.xml b/src/Spryker/Shared/ProductSearch/Transfer/product_search.transfer.xml index 2f45fde..a1c21bc 100644 --- a/src/Spryker/Shared/ProductSearch/Transfer/product_search.transfer.xml +++ b/src/Spryker/Shared/ProductSearch/Transfer/product_search.transfer.xml @@ -67,6 +67,8 @@ + + @@ -94,4 +96,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpander.php b/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpander.php new file mode 100644 index 0000000..bfb8f54 --- /dev/null +++ b/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpander.php @@ -0,0 +1,132 @@ +localeFacade = $localeFacade; + $this->glossaryFacade = $glossaryFacade; + $this->glossaryKeyBuilder = $glossaryKeyBuilder; + } + + /** + * @param \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function expandProductSearchAttributeCollectionWithLocalizedKeys( + ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + ): ProductSearchAttributeCollectionTransfer { + $localeTransfers = $this->localeFacade->getLocaleCollection(); + $glossaryKeys = $this->extractGlossaryKeysFromProductSearchAttributeCollectionTransfer($productSearchAttributeCollectionTransfer); + + $translationTransfers = $this->glossaryFacade->getTranslationsByGlossaryKeysAndLocaleTransfers($glossaryKeys, $localeTransfers); + $translationsIndexedByKeyAndLocale = $this->getTranslationValuesIndexedByGlossaryKeyAndLocale($translationTransfers); + + foreach ($productSearchAttributeCollectionTransfer->getProductSearchAttributes() as $productSearchAttributeTransfer) { + $this->expandProductSearchAttributeWithLocalizedKeys($localeTransfers, $translationsIndexedByKeyAndLocale, $productSearchAttributeTransfer); + } + + return $productSearchAttributeCollectionTransfer; + } + + /** + * @param list<\Generated\Shared\Transfer\LocaleTransfer> $localeTransfers + * @param array> $translationsIndexedByKeyAndLocale + * @param \Generated\Shared\Transfer\ProductSearchAttributeTransfer $productSearchAttributeTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeTransfer + */ + protected function expandProductSearchAttributeWithLocalizedKeys( + array $localeTransfers, + array $translationsIndexedByKeyAndLocale, + ProductSearchAttributeTransfer $productSearchAttributeTransfer + ): ProductSearchAttributeTransfer { + foreach ($localeTransfers as $localeTransfer) { + $translation = $translationsIndexedByKeyAndLocale[$localeTransfer->getIdLocaleOrFail()][$productSearchAttributeTransfer->getKeyOrFail()] ?? null; + $localizedProductSearchAttributeKeyTransfer = new LocalizedProductSearchAttributeKeyTransfer(); + $localizedProductSearchAttributeKeyTransfer + ->setLocaleName($localeTransfer->getLocaleNameOrFail()) + ->setKeyTranslation($translation); + + $productSearchAttributeTransfer->addLocalizedKey($localizedProductSearchAttributeKeyTransfer); + } + + return $productSearchAttributeTransfer; + } + + /** + * @param list<\Generated\Shared\Transfer\TranslationTransfer> $translationTransfers + * + * @return array> + */ + protected function getTranslationValuesIndexedByGlossaryKeyAndLocale(array $translationTransfers): array + { + $translationsIndexedByKeyAndLocale = []; + + foreach ($translationTransfers as $translationTransfer) { + $key = $translationTransfer->getGlossaryKeyOrFail()->getKeyOrFail(); + $translationsIndexedByKeyAndLocale[$translationTransfer->getFkLocaleOrFail()][$key] = $translationTransfer->getValueOrFail(); + } + + return $translationsIndexedByKeyAndLocale; + } + + /** + * @param \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + * + * @return list + */ + protected function extractGlossaryKeysFromProductSearchAttributeCollectionTransfer( + ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + ): array { + $glossaryKeys = []; + + foreach ($productSearchAttributeCollectionTransfer->getProductSearchAttributes() as $productSearchAttributeTransfer) { + if (!$productSearchAttributeTransfer->getKey()) { + continue; + } + + $glossaryKeys[] = $productSearchAttributeTransfer->getKeyOrFail(); + } + + return $glossaryKeys; + } +} diff --git a/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpanderInterface.php b/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpanderInterface.php new file mode 100644 index 0000000..a7f3c87 --- /dev/null +++ b/src/Spryker/Zed/ProductSearch/Business/Expander/LocalizedProductSearchAttributeKeyExpanderInterface.php @@ -0,0 +1,22 @@ +getAttributeMapCollectors()); } + /** + * @return \Spryker\Zed\ProductSearch\Business\Expander\LocalizedProductSearchAttributeKeyExpanderInterface + */ + public function createLocalizedProductSearchAttributeKeyExpander(): LocalizedProductSearchAttributeKeyExpanderInterface + { + return new LocalizedProductSearchAttributeKeyExpander( + $this->getLocaleFacade(), + $this->getGlossaryFacade(), + $this->createFilterGlossaryKeyBuilder(), + ); + } + + /** + * @return \Spryker\Zed\ProductSearch\Business\Reader\ProductSearchAttributeReaderInterface + */ + public function createProductSearchAttributeReader(): ProductSearchAttributeReaderInterface + { + return new ProductSearchAttributeReader( + $this->getRepository(), + $this->createLocalizedProductSearchAttributeKeyExpander(), + ); + } + /** * @return array<\Spryker\Zed\ProductSearch\Business\Map\Collector\ProductSearchAttributeMapCollectorInterface> */ diff --git a/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacade.php b/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacade.php index 691094c..38e899b 100644 --- a/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacade.php +++ b/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacade.php @@ -10,6 +10,8 @@ use Generated\Shared\Transfer\LocaleTransfer; use Generated\Shared\Transfer\PageMapTransfer; use Generated\Shared\Transfer\ProductConcreteTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer; use Generated\Shared\Transfer\ProductSearchAttributeTransfer; use Generated\Shared\Transfer\ProductSearchPreferencesTransfer; use Orm\Zed\Touch\Persistence\SpyTouchQuery; @@ -263,6 +265,8 @@ public function deleteProductSearchAttribute(ProductSearchAttributeTransfer $pro * * @api * + * @deprecated Use {@link \Spryker\Zed\ProductSearch\Business\ProductSearchFacade::getProductSearchAttributeCollection()} instead. + * * @param int $idProductSearchAttribute * * @return \Generated\Shared\Transfer\ProductSearchAttributeTransfer|null @@ -280,6 +284,8 @@ public function getProductSearchAttribute($idProductSearchAttribute) * * @api * + * @deprecated Use {@link \Spryker\Zed\ProductSearch\Business\ProductSearchFacade::getProductSearchAttributeCollection()} instead. + * * @return array<\Generated\Shared\Transfer\ProductSearchAttributeTransfer> */ public function getProductSearchAttributeList() @@ -290,6 +296,23 @@ public function getProductSearchAttributeList() ->getAttributeList(); } + /** + * {@inheritDoc} + * + * @api + * + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function getProductSearchAttributeCollection( + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): ProductSearchAttributeCollectionTransfer { + return $this->getFactory() + ->createProductSearchAttributeReader() + ->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + } + /** * {@inheritDoc} * diff --git a/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacadeInterface.php b/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacadeInterface.php index 403ec34..af8f58e 100644 --- a/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacadeInterface.php +++ b/src/Spryker/Zed/ProductSearch/Business/ProductSearchFacadeInterface.php @@ -10,6 +10,8 @@ use Generated\Shared\Transfer\LocaleTransfer; use Generated\Shared\Transfer\PageMapTransfer; use Generated\Shared\Transfer\ProductConcreteTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer; use Generated\Shared\Transfer\ProductSearchAttributeTransfer; use Generated\Shared\Transfer\ProductSearchPreferencesTransfer; use Orm\Zed\Touch\Persistence\SpyTouchQuery; @@ -218,6 +220,8 @@ public function deleteProductSearchAttribute(ProductSearchAttributeTransfer $pro * * @api * + * @deprecated Use {@link \Spryker\Zed\ProductSearch\Business\ProductSearchFacadeInterface::getProductSearchAttributeCollection()} instead. + * * @param int $idProductSearchAttribute * * @return \Generated\Shared\Transfer\ProductSearchAttributeTransfer|null @@ -231,10 +235,31 @@ public function getProductSearchAttribute($idProductSearchAttribute); * * @api * + * @deprecated Use {@link \Spryker\Zed\ProductSearch\Business\ProductSearchFacadeInterface::getProductSearchAttributeCollection()} instead. + * * @return array<\Generated\Shared\Transfer\ProductSearchAttributeTransfer> */ public function getProductSearchAttributeList(); + /** + * Specification: + * - Retrieves product search attribute entities filtered by criteria from Persistence. + * - Uses `ProductSearchAttributeCriteriaTransfer.productSearchAttributeConditions.productSearchAttributeIds` to filter by product search attribute ids. + * - Uses `ProductSearchAttributeCriteriaTransfer.sort.field` to set the 'order by' field. + * - Uses `ProductSearchAttributeCriteriaTransfer.sort.isAscending` to set ascending/descending order. + * - Uses `ProductSearchAttributeCriteriaTransfer.productSearchAttributeConditions.withLocalizedAttributes` to load localized attributes. + * - Returns `ProductSearchAttributeCollectionTransfer` filled with found product search attributes. + * + * @api + * + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function getProductSearchAttributeCollection( + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): ProductSearchAttributeCollectionTransfer; + /** * Specification: * - Updates the positions of the provided product search attribute entities. diff --git a/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReader.php b/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReader.php new file mode 100644 index 0000000..0de65bc --- /dev/null +++ b/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReader.php @@ -0,0 +1,64 @@ +productSearchRepository = $productSearchRepository; + $this->localizedProductSearchAttributeKeyExpander = $localizedProductSearchAttributeKeyExpander; + } + + /** + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function getProductSearchAttributeCollection( + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): ProductSearchAttributeCollectionTransfer { + $productSearchAttributeCollectionTransfer = $this->productSearchRepository + ->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + $productSearchAttributeConditionsTransfer = $productSearchAttributeCriteriaTransfer->getProductSearchAttributeConditions(); + + if (!$productSearchAttributeConditionsTransfer) { + return $productSearchAttributeCollectionTransfer; + } + + if ($productSearchAttributeConditionsTransfer->getWithLocalizedAttributes()) { + $productSearchAttributeCollectionTransfer = $this->localizedProductSearchAttributeKeyExpander->expandProductSearchAttributeCollectionWithLocalizedKeys( + $productSearchAttributeCollectionTransfer, + ); + } + + return $productSearchAttributeCollectionTransfer; + } +} diff --git a/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReaderInterface.php b/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReaderInterface.php new file mode 100644 index 0000000..f68fd73 --- /dev/null +++ b/src/Spryker/Zed/ProductSearch/Business/Reader/ProductSearchAttributeReaderInterface.php @@ -0,0 +1,23 @@ +castId($request->query->getInt(static::PARAM_ID)); + $productSearchAttributeConditionsTransfer = (new ProductSearchAttributeConditionsTransfer()) + ->setWithLocalizedAttributes(true) + ->addIdProductSearchAttribute($idProductSearchAttribute); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); - $attributeTransfer = $this->getFacade()->getProductSearchAttribute($idProductSearchAttribute); + $productSearchAttributeCollectionTransfer = $this->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); - if (!$attributeTransfer) { + if (!$productSearchAttributeCollectionTransfer->getProductSearchAttributes()->count()) { return $this->redirectResponse('/product-search/filter-preferences'); } $deleteForm = $this->getFactory()->createDeleteFilterPreferencesForm(); return $this->viewResponse([ - 'attributeTransfer' => $attributeTransfer, + 'attributeTransfer' => $productSearchAttributeCollectionTransfer->getProductSearchAttributes()->getIterator()->current(), 'locales' => $this->getFactory()->getLocaleFacade()->getLocaleCollection(), 'deleteForm' => $deleteForm->createView(), ]); diff --git a/src/Spryker/Zed/ProductSearch/Communication/Controller/FilterReorderController.php b/src/Spryker/Zed/ProductSearch/Communication/Controller/FilterReorderController.php index f6453a5..d49073d 100644 --- a/src/Spryker/Zed/ProductSearch/Communication/Controller/FilterReorderController.php +++ b/src/Spryker/Zed/ProductSearch/Communication/Controller/FilterReorderController.php @@ -7,6 +7,10 @@ namespace Spryker\Zed\ProductSearch\Communication\Controller; +use Generated\Shared\Transfer\ProductSearchAttributeConditionsTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeTransfer; +use Generated\Shared\Transfer\SortTransfer; use Spryker\Zed\Kernel\Communication\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -28,10 +32,19 @@ class FilterReorderController extends AbstractController */ public function indexAction() { - $productSearchAttributes = $this->getFacade()->getProductSearchAttributeList(); + $sortTransfer = (new SortTransfer()) + ->setField(ProductSearchAttributeTransfer::POSITION) + ->setIsAscending(true); + $productSearchAttributeConditionsTransfer = (new ProductSearchAttributeConditionsTransfer()) + ->setWithLocalizedAttributes(true); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->addSort($sortTransfer) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + $productSearchAttributeCollectionTransfer = $this->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); return $this->viewResponse([ - 'productSearchAttributes' => $productSearchAttributes, + 'productSearchAttributes' => $productSearchAttributeCollectionTransfer->getProductSearchAttributes()->getArrayCopy(), ]); } diff --git a/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryBridge.php b/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryBridge.php index f1cf19a..855c527 100644 --- a/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryBridge.php +++ b/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryBridge.php @@ -102,4 +102,15 @@ public function createKey($keyName) { return $this->glossaryFacade->createKey($keyName); } + + /** + * @param list $glossaryKeys + * @param list<\Generated\Shared\Transfer\LocaleTransfer> $localeTransfers + * + * @return list<\Generated\Shared\Transfer\TranslationTransfer> + */ + public function getTranslationsByGlossaryKeysAndLocaleTransfers(array $glossaryKeys, array $localeTransfers): array + { + return $this->glossaryFacade->getTranslationsByGlossaryKeysAndLocaleTransfers($glossaryKeys, $localeTransfers); + } } diff --git a/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryInterface.php b/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryInterface.php index 9e8ce9a..c03e4b2 100644 --- a/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryInterface.php +++ b/src/Spryker/Zed/ProductSearch/Dependency/Facade/ProductSearchToGlossaryInterface.php @@ -76,4 +76,12 @@ public function updateKey($oldKeyName, $newKeyName); * @return int */ public function createKey($keyName); + + /** + * @param list $glossaryKeys + * @param list<\Generated\Shared\Transfer\LocaleTransfer> $localeTransfers + * + * @return list<\Generated\Shared\Transfer\TranslationTransfer> + */ + public function getTranslationsByGlossaryKeysAndLocaleTransfers(array $glossaryKeys, array $localeTransfers): array; } diff --git a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchPersistenceFactory.php b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchPersistenceFactory.php index 0909321..fca9710 100644 --- a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchPersistenceFactory.php +++ b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchPersistenceFactory.php @@ -16,6 +16,7 @@ use Orm\Zed\ProductSearch\Persistence\SpyProductSearchAttributeQuery; use Orm\Zed\ProductSearch\Persistence\SpyProductSearchQuery; use Spryker\Zed\Kernel\Persistence\AbstractPersistenceFactory; +use Spryker\Zed\ProductSearch\Persistence\Propel\Mapper\ProductSearchAttributeMapper; /** * @method \Spryker\Zed\ProductSearch\Persistence\ProductSearchQueryContainerInterface getQueryContainer() @@ -87,4 +88,12 @@ public function createProductAbstractQuery() { return SpyProductAbstractQuery::create(); } + + /** + * @return \Spryker\Zed\ProductSearch\Persistence\Propel\Mapper\ProductSearchAttributeMapper + */ + public function createProductSearchAttributeMapper(): ProductSearchAttributeMapper + { + return new ProductSearchAttributeMapper(); + } } diff --git a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepository.php b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepository.php index abebc01..f9388fc 100644 --- a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepository.php +++ b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepository.php @@ -7,8 +7,14 @@ namespace Spryker\Zed\ProductSearch\Persistence; +use ArrayObject; +use Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer; use Orm\Zed\Product\Persistence\Map\SpyProductAttributeKeyTableMap; use Orm\Zed\ProductSearch\Persistence\Map\SpyProductSearchTableMap; +use Orm\Zed\ProductSearch\Persistence\SpyProductSearchAttributeQuery; +use Propel\Runtime\ActiveQuery\Criteria; +use Propel\Runtime\ActiveQuery\ModelCriteria; use Spryker\Zed\Kernel\Persistence\AbstractRepository; use Spryker\Zed\PropelOrm\Business\Model\Formatter\PropelArraySetFormatter; @@ -68,4 +74,73 @@ public function getAllProductAttributeKeys(): array ->setFormatter(new PropelArraySetFormatter()) ->find(); } + + /** + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function getProductSearchAttributeCollection( + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): ProductSearchAttributeCollectionTransfer { + $productSearchAttributeQuery = $this->getFactory() + ->createProductSearchAttributeQuery() + ->joinWithSpyProductAttributeKey(); + + $productSearchAttributeQuery = $this->applyProductSearchAttributeFilters($productSearchAttributeQuery, $productSearchAttributeCriteriaTransfer); + + /** @var \ArrayObject $sortTransfers */ + $sortTransfers = $productSearchAttributeCriteriaTransfer->getSortCollection(); + $productSearchAttributeQuery = $this->applySorting($productSearchAttributeQuery, $sortTransfers); + + return $this->getFactory() + ->createProductSearchAttributeMapper() + ->mapProductSearchAttributeEntitiesToProductSearchAttributeCollectionTransfer( + $productSearchAttributeQuery->find(), + new ProductSearchAttributeCollectionTransfer(), + ); + } + + /** + * @param \Orm\Zed\ProductSearch\Persistence\SpyProductSearchAttributeQuery $productSearchAttributeQuery + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Orm\Zed\ProductSearch\Persistence\SpyProductSearchAttributeQuery + */ + protected function applyProductSearchAttributeFilters( + SpyProductSearchAttributeQuery $productSearchAttributeQuery, + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): SpyProductSearchAttributeQuery { + $productSearchAttributeConditionsTransfer = $productSearchAttributeCriteriaTransfer->getProductSearchAttributeConditions(); + + if (!$productSearchAttributeConditionsTransfer) { + return $productSearchAttributeQuery; + } + + if ($productSearchAttributeConditionsTransfer->getProductSearchAttributeIds()) { + $productSearchAttributeQuery->filterByIdProductSearchAttribute_In($productSearchAttributeConditionsTransfer->getProductSearchAttributeIds()); + } + + return $productSearchAttributeQuery; + } + + /** + * @param \Propel\Runtime\ActiveQuery\ModelCriteria $modelCriteria + * @param \ArrayObject $sortTransfers + * + * @return \Propel\Runtime\ActiveQuery\ModelCriteria + */ + protected function applySorting( + ModelCriteria $modelCriteria, + ArrayObject $sortTransfers + ): ModelCriteria { + foreach ($sortTransfers as $sortTransfer) { + $modelCriteria->orderBy( + $sortTransfer->getFieldOrFail(), + $sortTransfer->getIsAscending() ? Criteria::ASC : Criteria::DESC, + ); + } + + return $modelCriteria; + } } diff --git a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepositoryInterface.php b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepositoryInterface.php index 7388003..e3e41ef 100644 --- a/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepositoryInterface.php +++ b/src/Spryker/Zed/ProductSearch/Persistence/ProductSearchRepositoryInterface.php @@ -7,6 +7,9 @@ namespace Spryker\Zed\ProductSearch\Persistence; +use Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer; + interface ProductSearchRepositoryInterface { /** @@ -30,4 +33,13 @@ public function getProductSearchEntitiesCountGroupedByIdProductAndIdLocale( * @return array */ public function getAllProductAttributeKeys(): array; + + /** + * @param \Generated\Shared\Transfer\ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function getProductSearchAttributeCollection( + ProductSearchAttributeCriteriaTransfer $productSearchAttributeCriteriaTransfer + ): ProductSearchAttributeCollectionTransfer; } diff --git a/src/Spryker/Zed/ProductSearch/Persistence/Propel/Mapper/ProductSearchAttributeMapper.php b/src/Spryker/Zed/ProductSearch/Persistence/Propel/Mapper/ProductSearchAttributeMapper.php new file mode 100644 index 0000000..c0ac0c5 --- /dev/null +++ b/src/Spryker/Zed/ProductSearch/Persistence/Propel/Mapper/ProductSearchAttributeMapper.php @@ -0,0 +1,53 @@ + $productSearchAttributeEntities + * @param \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeCollectionTransfer + */ + public function mapProductSearchAttributeEntitiesToProductSearchAttributeCollectionTransfer( + ObjectCollection $productSearchAttributeEntities, + ProductSearchAttributeCollectionTransfer $productSearchAttributeCollectionTransfer + ): ProductSearchAttributeCollectionTransfer { + foreach ($productSearchAttributeEntities as $productSearchAttributeEntity) { + $productSearchAttributeTransfer = $this->mapProductSearchAttributeEntityToProductSearchAttributeTransfer( + $productSearchAttributeEntity, + new ProductSearchAttributeTransfer(), + ); + + $productSearchAttributeCollectionTransfer->addProductSearchAttribute($productSearchAttributeTransfer); + } + + return $productSearchAttributeCollectionTransfer; + } + + /** + * @param \Orm\Zed\ProductSearch\Persistence\Base\SpyProductSearchAttribute $productSearchAttributeEntity + * @param \Generated\Shared\Transfer\ProductSearchAttributeTransfer $productSearchAttributeTransfer + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeTransfer + */ + protected function mapProductSearchAttributeEntityToProductSearchAttributeTransfer( + SpyProductSearchAttribute $productSearchAttributeEntity, + ProductSearchAttributeTransfer $productSearchAttributeTransfer + ): ProductSearchAttributeTransfer { + return $productSearchAttributeTransfer + ->fromArray($productSearchAttributeEntity->toArray(), true) + ->setKey($productSearchAttributeEntity->getSpyProductAttributeKey()->getKey()); + } +} diff --git a/tests/SprykerTest/Zed/ProductSearch/Business/ProductSearchFacade/GetProductSearchAttributeCollectionTest.php b/tests/SprykerTest/Zed/ProductSearch/Business/ProductSearchFacade/GetProductSearchAttributeCollectionTest.php new file mode 100644 index 0000000..2fbd9e1 --- /dev/null +++ b/tests/SprykerTest/Zed/ProductSearch/Business/ProductSearchFacade/GetProductSearchAttributeCollectionTest.php @@ -0,0 +1,225 @@ +tester->ensureProductAttributeKeyTableIsEmpty(); + } + + /** + * @return void + */ + public function testShouldReturnEmptyCollection(): void + { + // Arrange + $this->tester->haveProductSearchAttribute(); + $productSearchAttributeConditionsTransfer = (new ProductSearchAttributeConditionsTransfer()) + ->addIdProductSearchAttribute(static::UNKNOWN_ID); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + // Act + $productSearchAttributeCollectionTransfer = $this->tester->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + // Assert + $this->assertCount(0, $productSearchAttributeCollectionTransfer->getProductSearchAttributes()); + } + + /** + * @return void + */ + public function testShouldReturnCollectionByIds(): void + { + // Arrange + $productSearchAttributeTransfer = $this->tester->haveProductSearchAttribute(); + $this->tester->haveProductSearchAttribute(); + + $productSearchAttributeConditionsTransfer = (new ProductSearchAttributeConditionsTransfer()) + ->addIdProductSearchAttribute($productSearchAttributeTransfer->getIdProductSearchAttributeOrFail()); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + // Act + $productSearchAttributeCollectionTransfer = $this->tester->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + // Assert + $this->assertCount(1, $productSearchAttributeCollectionTransfer->getProductSearchAttributes()); + $this->assertSame( + $productSearchAttributeTransfer->getIdProductSearchAttributeOrFail(), + $productSearchAttributeCollectionTransfer->getProductSearchAttributes()->getIterator()->current()->getIdProductSearchAttributeOrFail(), + ); + $this->assertCount( + 0, + $productSearchAttributeCollectionTransfer->getProductSearchAttributes()->getIterator()->current()->getLocalizedKeys(), + ); + } + + /** + * @return void + */ + public function testShouldReturnCollectionSortedByFieldAsc(): void + { + // Arrange + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 2, + ], + ); + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 1, + ], + ); + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 3, + ], + ); + + $sortTransfer = (new SortTransfer()) + ->setIsAscending(true) + ->setField(ProductSearchAttributeTransfer::POSITION); + $productSearchAttributeConditionsTransfer = new ProductSearchAttributeConditionsTransfer(); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->addSort($sortTransfer) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + // Act + $productSearchAttributeCollectionTransfer = $this->tester->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + // Assert + $this->assertCount(3, $productSearchAttributeCollectionTransfer->getProductSearchAttributes()); + + foreach ($productSearchAttributeCollectionTransfer->getProductSearchAttributes() as $key => $productSearchAttributeTransfer) { + $this->assertSame($key + 1, $productSearchAttributeTransfer->getPositionOrFail()); + } + } + + /** + * @return void + */ + public function testShouldReturnCollectionSortedByFieldDesc(): void + { + // Arrange + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 2, + ], + ); + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 1, + ], + ); + $this->tester->haveProductSearchAttribute( + [], + [ + ProductSearchAttributeTransfer::POSITION => 3, + ], + ); + + $sortTransfer = (new SortTransfer()) + ->setIsAscending(false) + ->setField(ProductSearchAttributeTransfer::POSITION); + $productSearchAttributeConditionsTransfer = new ProductSearchAttributeConditionsTransfer(); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->addSort($sortTransfer) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + // Act + $productSearchAttributeCollectionTransfer = $this->tester->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + // Assert + $this->assertCount(3, $productSearchAttributeCollectionTransfer->getProductSearchAttributes()); + + foreach ($productSearchAttributeCollectionTransfer->getProductSearchAttributes() as $key => $productSearchAttributeTransfer) { + $this->assertSame( + count($productSearchAttributeCollectionTransfer->getProductSearchAttributes()) - $key, + $productSearchAttributeTransfer->getPositionOrFail(), + ); + } + } + + /** + * @return void + */ + public function testShouldReturnCollectionWithLocalizedAttributes(): void + { + // Arrange + $glossaryKey = uniqid(); + $this->tester->haveProductSearchAttribute([ + ProductAttributeKeyTransfer::KEY => $glossaryKey, + ]); + $localeTransfers = $this->tester->getLocator()->locale()->facade()->getLocaleCollection(); + + foreach ($localeTransfers as $localeTransfer) { + $this->tester->addProductSearchKeyTranslation($glossaryKey, $localeTransfer, $glossaryKey); + } + + $productSearchAttributeConditionsTransfer = (new ProductSearchAttributeConditionsTransfer()) + ->setWithLocalizedAttributes(true); + $productSearchAttributeCriteriaTransfer = (new ProductSearchAttributeCriteriaTransfer()) + ->setProductSearchAttributeConditions($productSearchAttributeConditionsTransfer); + + // Act + $productSearchAttributeCollectionTransfer = $this->tester->getFacade()->getProductSearchAttributeCollection($productSearchAttributeCriteriaTransfer); + + // Assert + $this->assertCount(1, $productSearchAttributeCollectionTransfer->getProductSearchAttributes()); + /** @var \Generated\Shared\Transfer\ProductSearchAttributeTransfer $productSearchAttributeTransfer */ + $productSearchAttributeTransfer = $productSearchAttributeCollectionTransfer->getProductSearchAttributes()->getIterator()->current(); + $this->assertCount(count($localeTransfers), $productSearchAttributeTransfer->getLocalizedKeys()); + + foreach ($productSearchAttributeTransfer->getLocalizedKeys() as $localizedProductSearchAttributeKeyTransfer) { + $this->assertSame($glossaryKey, $localizedProductSearchAttributeKeyTransfer->getKeyTranslationOrFail()); + } + } +} diff --git a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchBusinessTester.php b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchBusinessTester.php index 7b70048..c757ff8 100644 --- a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchBusinessTester.php +++ b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchBusinessTester.php @@ -8,7 +8,11 @@ namespace SprykerTest\Zed\ProductSearch; use Codeception\Actor; +use Generated\Shared\DataBuilder\ProductSearchAttributeBuilder; +use Generated\Shared\Transfer\LocaleTransfer; +use Generated\Shared\Transfer\ProductSearchAttributeTransfer; use Orm\Zed\Product\Persistence\SpyProductAttributeKeyQuery; +use Orm\Zed\ProductSearch\Persistence\SpyProductSearchAttribute; /** * @method void wantToTest($text) @@ -23,7 +27,7 @@ * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null) * @method \Spryker\Zed\ProductSearch\Business\ProductSearchFacadeInterface getFacade() * - * @SuppressWarnings(PHPMD) + * @SuppressWarnings(\SprykerTest\Zed\ProductSearch\PHPMD) */ class ProductSearchBusinessTester extends Actor { @@ -37,6 +41,51 @@ public function ensureProductAttributeKeyTableIsEmpty(): void $this->ensureDatabaseTableIsEmpty($this->createProductAttributeKeyQuery()); } + /** + * @param array $productAttributeKeySeedData + * @param array $productSearchAttributeSeedData + * + * @return \Generated\Shared\Transfer\ProductSearchAttributeTransfer + */ + public function haveProductSearchAttribute( + array $productAttributeKeySeedData = [], + array $productSearchAttributeSeedData = [] + ): ProductSearchAttributeTransfer { + $productAttributeKeyEntity = $this->haveProductAttributeKeyEntity($productAttributeKeySeedData); + + $productSearchAttributeTransfer = (new ProductSearchAttributeBuilder($productSearchAttributeSeedData))->build(); + + $productSearchAttributeEntity = (new SpyProductSearchAttribute()) + ->fromArray($productSearchAttributeTransfer->toArray()) + ->setFkProductAttributeKey($productAttributeKeyEntity->getIdProductAttributeKey()); + + $productSearchAttributeEntity->save(); + + return (new ProductSearchAttributeTransfer())->fromArray($productSearchAttributeEntity->toArray(), true); + } + + /** + * @param string $glossaryKey + * @param \Generated\Shared\Transfer\LocaleTransfer $localeTransfer + * @param string $translation + * + * @return void + */ + public function addProductSearchKeyTranslation( + string $glossaryKey, + LocaleTransfer $localeTransfer, + string $translation + ): void { + if (!$this->getLocator()->glossary()->facade()->hasKey($glossaryKey)) { + $this->getLocator()->glossary()->facade()->createKey($glossaryKey); + } + $this->getLocator()->glossary()->facade()->createTranslation( + $glossaryKey, + $localeTransfer, + $translation, + ); + } + /** * @return \Orm\Zed\Product\Persistence\SpyProductAttributeKeyQuery */ diff --git a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPersistenceTester.php b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPersistenceTester.php index 069916c..b7a5221 100644 --- a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPersistenceTester.php +++ b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPersistenceTester.php @@ -23,7 +23,7 @@ * @method void comment($description) * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null) * - * @SuppressWarnings(PHPMD) + * @SuppressWarnings(\SprykerTest\Zed\ProductSearch\PHPMD) */ class ProductSearchPersistenceTester extends Actor { diff --git a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPresentationTester.php b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPresentationTester.php index a54cf73..a159961 100644 --- a/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPresentationTester.php +++ b/tests/SprykerTest/Zed/ProductSearch/_support/ProductSearchPresentationTester.php @@ -24,7 +24,7 @@ * @method void comment($description) * @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null) * - * @SuppressWarnings(PHPMD) + * @SuppressWarnings(\SprykerTest\Zed\ProductSearch\PHPMD) */ class ProductSearchPresentationTester extends Actor { diff --git a/tests/_data/product_search.databuilder.xml b/tests/_data/product_search.databuilder.xml index 166de98..52cf636 100644 --- a/tests/_data/product_search.databuilder.xml +++ b/tests/_data/product_search.databuilder.xml @@ -9,4 +9,13 @@ + + + + + + + + +