From 68170c1a4a7b8b849e3a19d40ffe37ae3ae06292 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Wed, 8 Jan 2020 14:10:37 +0200 Subject: [PATCH 01/15] MC-23870: Error messages on Checkout when 'Syncronize with Backend' option is turned ON --- .../ProductFrontendAction/Synchronizer.php | 31 +++-- .../Catalog/view/frontend/layout/default.xml | 3 + .../SynchronizerTest.php | 106 ++++++++++++++++++ 3 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php diff --git a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php index 24775a791e59f..ec2e697ccc849 100644 --- a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php +++ b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php @@ -125,9 +125,16 @@ private function filterNewestActions(array $productsData, $typeId) $lifetime = $this->getLifeTimeByNamespace($typeId); $actionsNumber = $lifetime * self::TIME_TO_DO_ONE_ACTION; - uasort($productsData, function (array $firstProduct, array $secondProduct) { - return $firstProduct['added_at'] > $secondProduct['added_at']; - }); + uasort( + $productsData, + function (array $firstProduct, array $secondProduct) { + if (isset($firstProduct['added_at'], $secondProduct['added_at'])) { + return $firstProduct['added_at'] > $secondProduct['added_at']; + } + + return false; + } + ); return array_slice($productsData, 0, $actionsNumber, true); } @@ -185,15 +192,17 @@ public function syncActions(array $productsData, $typeId) foreach ($productsData as $productId => $productData) { /** @var ProductFrontendActionInterface $action */ - $action = $this->productFrontendActionFactory->create([ - 'data' => [ - 'visitor_id' => $customerId ? null : $visitorId, - 'customer_id' => $this->session->getCustomerId(), - 'added_at' => $productData['added_at'], - 'product_id' => $productId, - 'type_id' => $typeId + $action = $this->productFrontendActionFactory->create( + [ + 'data' => [ + 'visitor_id' => $customerId ? null : $visitorId, + 'customer_id' => $this->session->getCustomerId(), + 'added_at' => $productData['added_at'], + 'product_id' => $productId, + 'type_id' => $typeId + ] ] - ]); + ); $this->entityManager->save($action); } diff --git a/app/code/Magento/Catalog/view/frontend/layout/default.xml b/app/code/Magento/Catalog/view/frontend/layout/default.xml index 3dff3e9b3c1f8..8f414724f51db 100644 --- a/app/code/Magento/Catalog/view/frontend/layout/default.xml +++ b/app/code/Magento/Catalog/view/frontend/layout/default.xml @@ -26,6 +26,9 @@ + + + diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php new file mode 100644 index 0000000000000..3ea30005e9f6c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/ProductFrontendAction/SynchronizerTest.php @@ -0,0 +1,106 @@ +synchronizer = $objectManager->get(Synchronizer::class); + $this->productRepository = $objectManager->get(ProductRepository::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSyncActions(): void + { + $actionsType = 'recently_viewed_product'; + $product1 = $this->productRepository->get('simple'); + $product2 = $this->productRepository->get('simple2'); + $product1Id = $product1->getId(); + $product2Id = $product2->getId(); + $productsData = [ + $product1Id => [ + 'added_at' => '1576582660', + 'product_id' => $product1Id, + ], + $product2Id => [ + 'added_at' => '1576587153', + 'product_id' => $product2Id, + ], + ]; + + $this->synchronizer->syncActions($productsData, $actionsType); + + $synchronizedCollection = $this->synchronizer->getActionsByType($actionsType); + $synchronizedCollection->addFieldToFilter( + 'product_id', + [ + $product1Id, + $product2Id, + ] + ); + + foreach ($synchronizedCollection as $item) { + $this->assertArrayHasKey($item->getProductId(), $productsData); + $this->assertEquals($productsData[$item->getProductId()]['added_at'], $item->getAddedAt()); + } + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * + * @return void + */ + public function testSyncActionsWithoutActionsType(): void + { + $product1 = $this->productRepository->get('simple'); + $product2 = $this->productRepository->get('simple2'); + $product1Id = $product1->getId(); + $product2Id = $product2->getId(); + $productsData = [ + $product1Id => [ + 'id' => $product1Id, + 'name' => $product1->getName(), + 'type' => $product1->getTypeId(), + ], + $product2Id => [ + 'id' => $product2Id, + 'name' => $product2->getName(), + 'type' => $product2->getTypeId(), + ], + ]; + + $this->synchronizer->syncActions($productsData, ''); + } +} From 4dcf4366112eda56257dcb7a736986aee17d5693 Mon Sep 17 00:00:00 2001 From: Daniel Renaud Date: Tue, 14 Jan 2020 13:54:45 -0600 Subject: [PATCH 02/15] MC-23303: [Forwardport] Support preview for staged data --- .../Product/SearchCriteriaBuilder.php | 13 +- .../Model/Resolver/Category/Products.php | 58 ++----- .../Model/Resolver/Product/PriceRange.php | 4 +- .../Model/Resolver/Products.php | 40 +---- .../Model/Resolver/Products/Query/Filter.php | 151 +++++++++++++++--- .../Products/Query/ProductQueryInterface.php | 25 +++ .../Model/Resolver/Products/Query/Search.php | 43 ++++- .../FilterProcessor/CategoryFilter.php | 23 ++- app/code/Magento/CatalogGraphQl/etc/di.xml | 2 + app/code/Magento/GraphQl/etc/graphql/di.xml | 1 + .../Magento/Catalog/_files/categories.php | 3 +- .../_files/rule_by_category_ids_rollback.php | 27 ++++ .../Test/Legacy/ModuleDBChangeTest.php | 15 -- 13 files changed, 264 insertions(+), 141 deletions(-) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php create mode 100644 dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php index 0e92bbbab4259..53d3b624285e9 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php @@ -129,7 +129,7 @@ private function addVisibilityFilter(SearchCriteriaInterface $searchCriteria, bo ? $this->visibility->getVisibleInSearchIds() : $this->visibility->getVisibleInCatalogIds(); - $this->addFilter($searchCriteria, 'visibility', $visibilityIds); + $this->addFilter($searchCriteria, 'visibility', $visibilityIds, 'in'); } /** @@ -155,13 +155,20 @@ private function preparePriceAggregation(SearchCriteriaInterface $searchCriteria * @param SearchCriteriaInterface $searchCriteria * @param string $field * @param mixed $value + * @param string|null $condition */ - private function addFilter(SearchCriteriaInterface $searchCriteria, string $field, $value): void - { + private function addFilter( + SearchCriteriaInterface $searchCriteria, + string $field, + $value, + ?string $condition = null + ): void { $filter = $this->filterBuilder ->setField($field) ->setValue($value) + ->setConditionType($condition) ->create(); + $this->filterGroupBuilder->addFilter($filter); $filterGroups = $searchCriteria->getFilterGroups(); $filterGroups[] = $this->filterGroupBuilder->create(); diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php index abc5ae7e1da7f..85b86f313de4d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Category/Products.php @@ -7,16 +7,12 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Category; -use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\CatalogGraphQl\DataProvider\Product\SearchCriteriaBuilder; -use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search; -use Magento\Framework\App\ObjectManager; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; -use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\ProductQueryInterface; /** * Category products resolver, used by GraphQL endpoints to retrieve products assigned to a category @@ -24,24 +20,7 @@ class Products implements ResolverInterface { /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface - */ - private $productRepository; - - /** - * @var Builder - * @deprecated - */ - private $searchCriteriaBuilder; - - /** - * @var Filter - * @deprecated - */ - private $filterQuery; - - /** - * @var Search + * @var ProductQueryInterface */ private $searchQuery; @@ -51,25 +30,15 @@ class Products implements ResolverInterface private $searchApiCriteriaBuilder; /** - * @param ProductRepositoryInterface $productRepository - * @param Builder $searchCriteriaBuilder - * @param Filter $filterQuery - * @param Search $searchQuery + * @param ProductQueryInterface $searchQuery * @param SearchCriteriaBuilder $searchApiCriteriaBuilder */ public function __construct( - ProductRepositoryInterface $productRepository, - Builder $searchCriteriaBuilder, - Filter $filterQuery, - Search $searchQuery = null, - SearchCriteriaBuilder $searchApiCriteriaBuilder = null + ProductQueryInterface $searchQuery, + SearchCriteriaBuilder $searchApiCriteriaBuilder ) { - $this->productRepository = $productRepository; - $this->searchCriteriaBuilder = $searchCriteriaBuilder; - $this->filterQuery = $filterQuery; - $this->searchQuery = $searchQuery ?? ObjectManager::getInstance()->get(Search::class); - $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ?? - ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); + $this->searchQuery = $searchQuery; + $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder; } /** @@ -94,18 +63,17 @@ public function resolve( 'eq' => $value['id'] ] ]; - $searchCriteria = $this->searchApiCriteriaBuilder->build($args, false); - $searchResult = $this->searchQuery->getResult($searchCriteria, $info); + $searchResult = $this->searchQuery->getResult($args, $info); //possible division by 0 - if ($searchCriteria->getPageSize()) { - $maxPages = ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize()); + if ($searchResult->getPageSize()) { + $maxPages = ceil($searchResult->getTotalCount() / $searchResult->getPageSize()); } else { $maxPages = 0; } - $currentPage = $searchCriteria->getCurrentPage(); - if ($searchCriteria->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) { + $currentPage = $searchResult->getCurrentPage(); + if ($searchResult->getCurrentPage() > $maxPages && $searchResult->getTotalCount() > 0) { $currentPage = new GraphQlInputException( __( 'currentPage value %1 specified is greater than the number of pages available.', @@ -118,7 +86,7 @@ public function resolve( 'total_count' => $searchResult->getTotalCount(), 'items' => $searchResult->getProductsSearchResult(), 'page_info' => [ - 'page_size' => $searchCriteria->getPageSize(), + 'page_size' => $searchResult->getPageSize(), 'current_page' => $currentPage, 'total_pages' => $maxPages ] diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php index 9396b1f02b975..dbb52f2010930 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/PriceRange.php @@ -86,7 +86,7 @@ private function getMinimumProductPrice(SaleableInterface $product, StoreInterfa $priceProvider = $this->priceProviderPool->getProviderByProductType($product->getTypeId()); $regularPrice = $priceProvider->getMinimalRegularPrice($product)->getValue(); $finalPrice = $priceProvider->getMinimalFinalPrice($product)->getValue(); - $minPriceArray = $this->formatPrice($regularPrice, $finalPrice, $store); + $minPriceArray = $this->formatPrice((float) $regularPrice, (float) $finalPrice, $store); $minPriceArray['model'] = $product; return $minPriceArray; } @@ -103,7 +103,7 @@ private function getMaximumProductPrice(SaleableInterface $product, StoreInterfa $priceProvider = $this->priceProviderPool->getProviderByProductType($product->getTypeId()); $regularPrice = $priceProvider->getMaximalRegularPrice($product)->getValue(); $finalPrice = $priceProvider->getMaximalFinalPrice($product)->getValue(); - $maxPriceArray = $this->formatPrice($regularPrice, $finalPrice, $store); + $maxPriceArray = $this->formatPrice((float) $regularPrice, (float) $finalPrice, $store); $maxPriceArray['model'] = $product; return $maxPriceArray; } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php index 691f93e4148bc..e3d9ba2a9b3c6 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products.php @@ -7,6 +7,7 @@ namespace Magento\CatalogGraphQl\Model\Resolver; +use Magento\CatalogGraphQl\Model\Resolver\Products\Query\ProductQueryInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Filter; use Magento\CatalogGraphQl\Model\Resolver\Products\Query\Search; @@ -24,51 +25,24 @@ class Products implements ResolverInterface { /** - * @var Builder - * @deprecated - */ - private $searchCriteriaBuilder; - - /** - * @var Search + * @var ProductQueryInterface */ private $searchQuery; - /** - * @var Filter - * @deprecated - */ - private $filterQuery; - - /** - * @var SearchFilter - * @deprecated - */ - private $searchFilter; - /** * @var SearchCriteriaBuilder */ private $searchApiCriteriaBuilder; /** - * @param Builder $searchCriteriaBuilder - * @param Search $searchQuery - * @param Filter $filterQuery - * @param SearchFilter $searchFilter + * @param ProductQueryInterface $searchQuery * @param SearchCriteriaBuilder|null $searchApiCriteriaBuilder */ public function __construct( - Builder $searchCriteriaBuilder, - Search $searchQuery, - Filter $filterQuery, - SearchFilter $searchFilter, + ProductQueryInterface $searchQuery, SearchCriteriaBuilder $searchApiCriteriaBuilder = null ) { - $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->searchQuery = $searchQuery; - $this->filterQuery = $filterQuery; - $this->searchFilter = $searchFilter; $this->searchApiCriteriaBuilder = $searchApiCriteriaBuilder ?? \Magento\Framework\App\ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); } @@ -95,11 +69,7 @@ public function resolve( ); } - //get product children fields queried - $productFields = (array)$info->getFieldSelection(1); - $includeAggregations = isset($productFields['filters']) || isset($productFields['aggregations']); - $searchCriteria = $this->searchApiCriteriaBuilder->build($args, $includeAggregations); - $searchResult = $this->searchQuery->getResult($searchCriteria, $info, $args); + $searchResult = $this->searchQuery->getResult($args, $info); if ($searchResult->getCurrentPage() > $searchResult->getTotalPages() && $searchResult->getTotalCount() > 0) { throw new GraphQlInputException( diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php index cc25af44fdfbe..670eee9c4583e 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/Filter.php @@ -7,16 +7,23 @@ namespace Magento\CatalogGraphQl\Model\Resolver\Products\Query; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Catalog\Model\Layer\Resolver as LayerResolver; +use Magento\Catalog\Model\Product; use Magento\Framework\Api\SearchCriteriaInterface; -use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product; +use Magento\Framework\Exception\InputException; +use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder as SearchCriteriaBuilder; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product as ProductProvider; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResult; use Magento\CatalogGraphQl\Model\Resolver\Products\SearchResultFactory; +use Magento\Search\Model\Query; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; /** * Retrieve filtered product data based off given search criteria in a format that GraphQL can interpret. */ -class Filter +class Filter implements ProductQueryInterface { /** * @var SearchResultFactory @@ -24,12 +31,12 @@ class Filter private $searchResultFactory; /** - * @var \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product + * @var ProductProvider */ private $productDataProvider; /** - * @var \Magento\Catalog\Model\Layer\Resolver + * @var LayerResolver */ private $layerResolver; @@ -38,50 +45,154 @@ class Filter */ private $fieldSelection; + /** + * @var SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param SearchResultFactory $searchResultFactory - * @param Product $productDataProvider - * @param \Magento\Catalog\Model\Layer\Resolver $layerResolver + * @param ProductProvider $productDataProvider + * @param LayerResolver $layerResolver * @param FieldSelection $fieldSelection + * @param SearchCriteriaBuilder $searchCriteriaBuilder + * @param ScopeConfigInterface $scopeConfig */ public function __construct( SearchResultFactory $searchResultFactory, - Product $productDataProvider, - \Magento\Catalog\Model\Layer\Resolver $layerResolver, - FieldSelection $fieldSelection + ProductProvider $productDataProvider, + LayerResolver $layerResolver, + FieldSelection $fieldSelection, + SearchCriteriaBuilder $searchCriteriaBuilder, + ScopeConfigInterface $scopeConfig ) { $this->searchResultFactory = $searchResultFactory; $this->productDataProvider = $productDataProvider; $this->layerResolver = $layerResolver; $this->fieldSelection = $fieldSelection; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->scopeConfig = $scopeConfig; } /** * Filter catalog product data based off given search criteria * - * @param SearchCriteriaInterface $searchCriteria + * @param array $args * @param ResolveInfo $info - * @param bool $isSearch * @return SearchResult */ public function getResult( - SearchCriteriaInterface $searchCriteria, - ResolveInfo $info, - bool $isSearch = false + array $args, + ResolveInfo $info ): SearchResult { $fields = $this->fieldSelection->getProductsFieldSelection($info); - $products = $this->productDataProvider->getList($searchCriteria, $fields, $isSearch); + try { + $searchCriteria = $this->buildSearchCriteria($args, $info); + $searchResults = $this->productDataProvider->getList($searchCriteria, $fields); + } catch (InputException $e) { + return $this->createEmptyResult($args); + } + $productArray = []; - /** @var \Magento\Catalog\Model\Product $product */ - foreach ($products->getItems() as $product) { + /** @var Product $product */ + foreach ($searchResults->getItems() as $product) { $productArray[$product->getId()] = $product->getData(); $productArray[$product->getId()]['model'] = $product; } + //possible division by 0 + if ($searchCriteria->getPageSize()) { + $maxPages = (int)ceil($searchResults->getTotalCount() / $searchCriteria->getPageSize()); + } else { + $maxPages = 0; + } + + return $this->searchResultFactory->create( + [ + 'totalCount' => $searchResults->getTotalCount(), + 'productsSearchResult' => $productArray, + 'pageSize' => $searchCriteria->getPageSize(), + 'currentPage' => $searchCriteria->getCurrentPage(), + 'totalPages' => $maxPages, + ] + ); + } + + /** + * Build search criteria from query input args + * + * @param array $args + * @param ResolveInfo $info + * @return SearchCriteriaInterface + */ + private function buildSearchCriteria(array $args, ResolveInfo $info): SearchCriteriaInterface + { + if (!empty($args['filter'])) { + $args['filter'] = $this->formatFilters($args['filter']); + } + + $criteria = $this->searchCriteriaBuilder->build($info->fieldName, $args); + $criteria->setCurrentPage($args['currentPage']); + $criteria->setPageSize($args['pageSize']); + + return $criteria; + } + + /** + * Reformat filters + * + * @param array $filters + * @return array + * @throws InputException + */ + private function formatFilters(array $filters): array + { + $formattedFilters = []; + $minimumQueryLength = $this->scopeConfig->getValue( + Query::XML_PATH_MIN_QUERY_LENGTH, + ScopeInterface::SCOPE_STORE + ); + + foreach ($filters as $field => $filter) { + foreach ($filter as $condition => $value) { + if ($condition === 'match') { + // reformat 'match' filter so MySQL filtering behaves like SearchAPI filtering + $condition = 'like'; + $value = str_replace('%', '', trim($value)); + if (strlen($value) < $minimumQueryLength) { + throw new InputException(__('Invalid match filter')); + } + $value = '%' . preg_replace('/ +/', '%', $value) . '%'; + } + $formattedFilters[$field] = [$condition => $value]; + } + } + + return $formattedFilters; + } + + /** + * Return and empty SearchResult object + * + * Used for handling exceptions gracefully + * + * @param array $args + * @return SearchResult + */ + private function createEmptyResult(array $args): SearchResult + { return $this->searchResultFactory->create( [ - 'totalCount' => $products->getTotalCount(), - 'productsSearchResult' => $productArray + 'totalCount' => 0, + 'productsSearchResult' => [], + 'pageSize' => $args['pageSize'], + 'currentPage' => $args['currentPage'], + 'totalPages' => 0, ] ); } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php new file mode 100644 index 0000000000000..580af5d87be26 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/Query/ProductQueryInterface.php @@ -0,0 +1,25 @@ +search = $search; $this->searchResultFactory = $searchResultFactory; @@ -72,21 +81,23 @@ public function __construct( $this->searchCriteriaFactory = $searchCriteriaFactory; $this->fieldSelection = $fieldSelection; $this->productsProvider = $productsProvider; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; } /** - * Return results of full text catalog search of given term, and will return filtered results if filter is specified + * Return product search results using Search API * - * @param SearchCriteriaInterface $searchCriteria + * @param array $args * @param ResolveInfo $info * @return SearchResult * @throws \Exception */ public function getResult( - SearchCriteriaInterface $searchCriteria, + array $args, ResolveInfo $info ): SearchResult { $queryFields = $this->fieldSelection->getProductsFieldSelection($info); + $searchCriteria = $this->buildSearchCriteria($args, $info); $realPageSize = $searchCriteria->getPageSize(); $realCurrentPage = $searchCriteria->getCurrentPage(); @@ -131,4 +142,20 @@ public function getResult( ] ); } + + /** + * Build search criteria from query input args + * + * @param array $args + * @param ResolveInfo $info + * @return SearchCriteriaInterface + */ + private function buildSearchCriteria(array $args, ResolveInfo $info): SearchCriteriaInterface + { + $productFields = (array)$info->getFieldSelection(1); + $includeAggregations = isset($productFields['filters']) || isset($productFields['aggregations']); + $searchCriteria = $this->searchCriteriaBuilder->build($args, $includeAggregations); + + return $searchCriteria; + } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php index e3b3588166163..92888a2775e17 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/SearchCriteria/CollectionProcessor/FilterProcessor/CategoryFilter.php @@ -9,11 +9,9 @@ use Magento\Catalog\Model\CategoryFactory; use Magento\Catalog\Model\ResourceModel\Category as CategoryResourceModel; -use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\Framework\Api\Filter; use Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomFilterInterface; use Magento\Framework\Data\Collection\AbstractDb; -use Magento\Framework\Exception\LocalizedException; /** * Category filter allows to filter products collection using 'category_id' filter from search criteria. @@ -50,22 +48,23 @@ public function __construct( * @param Filter $filter * @param AbstractDb $collection * @return bool Whether the filter is applied - * @throws LocalizedException */ public function apply(Filter $filter, AbstractDb $collection) { - $conditionType = $filter->getConditionType(); - - if ($conditionType !== 'eq') { - throw new LocalizedException(__("'category_id' only supports 'eq' condition type.")); + $categoryIds = $filter->getValue(); + if (!is_array($categoryIds)) { + $categoryIds = [$categoryIds]; } - $categoryId = $filter->getValue(); - /** @var Collection $collection */ - $category = $this->categoryFactory->create(); - $this->categoryResourceModel->load($category, $categoryId); - $collection->addCategoryFilter($category); + $categoryProducts = []; + foreach ($categoryIds as $categoryId) { + $category = $this->categoryFactory->create(); + $this->categoryResourceModel->load($category, $categoryId); + $categoryProducts[$categoryId] = $category->getProductCollection()->getAllIds(); + } + $categoryProductIds = array_unique(array_merge(...$categoryProducts)); + $collection->addIdFilter($categoryProductIds); return true; } } diff --git a/app/code/Magento/CatalogGraphQl/etc/di.xml b/app/code/Magento/CatalogGraphQl/etc/di.xml index 1fe62fc442ecf..d6f75259e30d7 100644 --- a/app/code/Magento/CatalogGraphQl/etc/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/di.xml @@ -71,4 +71,6 @@ + + diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index 03bae5c80e12c..2bcd44e9ae410 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -7,6 +7,7 @@ --> + diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php index 25bb55ffbc32c..4255d7d3c98e5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/categories.php @@ -20,7 +20,7 @@ ] ); -/** @var Magento\Catalog\Api\CategoryLinkManagementInterface $linkManagement */ +/** @var Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagement */ $categoryLinkManagement = $objectManager->create(\Magento\Catalog\Api\CategoryLinkManagementInterface::class); $reflectionClass = new \ReflectionClass(get_class($categoryLinkManagement)); $properties = [ @@ -115,6 +115,7 @@ ->setName('Inactive') ->setParentId(2) ->setPath('1/2/8') + ->setLevel(2) ->setAvailableSortBy('name') ->setDefaultSortBy('name') ->setIsActive(false) diff --git a/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php new file mode 100644 index 0000000000000..67584a300eacd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogRule/_files/rule_by_category_ids_rollback.php @@ -0,0 +1,27 @@ +create(\Magento\CatalogRule\Model\ResourceModel\Rule::class); + +//Retrieve rule id by name +$select = $catalogRuleResource->getConnection()->select(); +$select->from($catalogRuleResource->getMainTable(), 'rule_id'); +$select->where('name = ?', 'test_category_rule'); +$ruleId = $catalogRuleResource->getConnection()->fetchOne($select); + +try { + /** @var \Magento\CatalogRule\Api\CatalogRuleRepositoryInterface $ruleRepository */ + $ruleRepository = $objectManager->create(\Magento\CatalogRule\Api\CatalogRuleRepositoryInterface::class); + $ruleRepository->deleteById($ruleId); +} catch (\Exception $ex) { + //Nothing to remove +} +/** @var \Magento\CatalogRule\Model\Indexer\IndexBuilder $indexBuilder */ +$indexBuilder = $objectManager->get(\Magento\CatalogRule\Model\Indexer\IndexBuilder::class); +$indexBuilder->reindexFull(); diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php index 876944e0027b4..15b3dc0e0a899 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/ModuleDBChangeTest.php @@ -64,21 +64,6 @@ public static function setUpBeforeClass() } } - /** - * Test changes for module.xml files - */ - public function testModuleXmlFiles() - { - if (!self::$actualBranch) { - preg_match_all('|etc/module\.xml$|mi', self::$changedFileList, $matches); - $this->assertEmpty( - reset($matches), - 'module.xml changes for patch releases in non-actual branches are not allowed:' . PHP_EOL . - implode(PHP_EOL, array_values(reset($matches))) - ); - } - } - /** * Test changes for files in Module Setup dir */ From 7633af187af63892ce58ed89b199a9bbf7e8a590 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Mon, 20 Jan 2020 09:52:51 +0200 Subject: [PATCH 03/15] MC-30398: Filter are not present on category page --- .../Data/UpdateDefaultAttributeValue.php | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php index 1d58de1994a11..e658d837efbaf 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php @@ -3,19 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Catalog\Setup\Patch\Data; use Magento\Catalog\Setup\CategorySetup; use Magento\Catalog\Setup\CategorySetupFactory; -use Magento\Framework\App\ResourceConnection; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; use Magento\Framework\Setup\Patch\PatchVersionInterface; /** - * Class UpdateDefaultAttributeValue - * @package Magento\Catalog\Setup\Patch + * Updates 'is_anchor' attribute default value. */ class UpdateDefaultAttributeValue implements DataPatchInterface, PatchVersionInterface { @@ -30,7 +29,6 @@ class UpdateDefaultAttributeValue implements DataPatchInterface, PatchVersionInt private $categorySetupFactory; /** - * PatchInitial constructor. * @param ModuleDataSetupInterface $moduleDataSetup * @param CategorySetupFactory $categorySetupFactory */ @@ -43,17 +41,22 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function apply() { /** @var CategorySetup $categorySetup */ $categorySetup = $this->categorySetupFactory->create(['setup' => $this->moduleDataSetup]); - $categorySetup->updateAttribute(3, 54, 'default_value', 1); + $categorySetup->updateAttribute( + CategorySetup::CATEGORY_ENTITY_TYPE_ID, + 'is_anchor', + 'default_value', + 1 + ); } /** - * {@inheritdoc} + * @inheritdoc */ public static function getDependencies() { @@ -63,7 +66,7 @@ public static function getDependencies() } /** - * {@inheritdoc} + * @inheritdoc */ public static function getVersion() { @@ -71,7 +74,7 @@ public static function getVersion() } /** - * {@inheritdoc} + * @inheritdoc */ public function getAliases() { From 914bae6e0f1fefb974c47694aba0032ad9d3e2a2 Mon Sep 17 00:00:00 2001 From: OlgaVasyltsun Date: Mon, 20 Jan 2020 10:44:06 +0200 Subject: [PATCH 04/15] MC-30398: Filter are not present on category page --- .../Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php index e658d837efbaf..4500d86ad4a6c 100644 --- a/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php +++ b/app/code/Magento/Catalog/Setup/Patch/Data/UpdateDefaultAttributeValue.php @@ -53,6 +53,8 @@ public function apply() 'default_value', 1 ); + + return $this; } /** From 093e30600f58f506a3dc5e853b8b3c4b205157f4 Mon Sep 17 00:00:00 2001 From: Viktor Petryk Date: Mon, 20 Jan 2020 17:16:30 +0200 Subject: [PATCH 05/15] MC-24906: An error notification is displayed while creating return on storefront --- .../shipment_for_order_with_customer.php | 27 +++++++++++++++++++ ...pment_for_order_with_customer_rollback.php | 8 ++++++ 2 files changed, 35 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php new file mode 100644 index 0000000000000..c5304f5b8809f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer.php @@ -0,0 +1,27 @@ +setIsInProcess(true); +/** @var Transaction $transaction */ +$transaction = $objectManager->create(Transaction::class); + +$items = []; +foreach ($order->getItems() as $orderItem) { + $items[$orderItem->getId()] = $orderItem->getQtyOrdered(); +} + +$shipment = $objectManager->get(ShipmentFactory::class)->create($order, $items); +$shipment->register(); + +$transaction->addObject($shipment)->addObject($order)->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php new file mode 100644 index 0000000000000..2595d6bf4084a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/shipment_for_order_with_customer_rollback.php @@ -0,0 +1,8 @@ + Date: Tue, 21 Jan 2020 14:24:31 -0600 Subject: [PATCH 06/15] MC-24172: Fix Skipped MFTF Tests From MC-17140: MC-14770, MC-14771, MC-14772 - add sorting to fix product ordering --- .../ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index 514366fa45c52..02d6d90ae5d0e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -114,6 +114,11 @@ + + + + + From 3b629811c14c613b8b0f962dd0cb3a517cc914bf Mon Sep 17 00:00:00 2001 From: Prabhu Ram Date: Wed, 22 Jan 2020 10:48:27 -0600 Subject: [PATCH 07/15] MC-24172: Fix Skipped MFTF Tests From MC-17140: MC-14770, MC-14771, MC-14772 - unskipping MC-24172 --- .../ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index 67a2551ff239b..02d6d90ae5d0e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -17,9 +17,6 @@ - - - From 79b0aa77223c5f61d3af9f7a44c5d078b8db91af Mon Sep 17 00:00:00 2001 From: Serhii Voloshkov Date: Fri, 24 Jan 2020 14:44:43 +0200 Subject: [PATCH 08/15] MC-23753: "<" and ">" symbols are changed to "<" and ">" in the frontend catalog search line --- .../Magento/Search/view/frontend/templates/form.mini.phtml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index 0dd9c819c855a..35f3876599731 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -14,7 +14,8 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class);