From ea2b6cc1da5864392586079f4e52323d8ea01fb9 Mon Sep 17 00:00:00 2001 From: Yuriy Denyshchenko Date: Wed, 9 Sep 2015 14:38:57 -0700 Subject: [PATCH 1/6] Fixed MAGETWO-42699: Hidden product image cannot be visible again --- .../Magento/Catalog/view/adminhtml/web/js/product-gallery.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js b/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js index 6d4087c18ee6b..50790af825dae 100644 --- a/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/product-gallery.js @@ -380,6 +380,7 @@ define([ var $checkbox = $(event.currentTarget); var $imageContainer = $checkbox.closest('[data-role=dialog]').data('imageContainer'); $imageContainer.toggleClass('hidden-for-front', $checkbox.is(':checked')); + $imageContainer.find('[name*="disabled"]').val($checkbox.is(':checked') ? 1 : 0); }, /** From d29bc436d37b417183717a6a71ee48b6fe2ecb6a Mon Sep 17 00:00:00 2001 From: Andrey Abumuslimov Date: Wed, 9 Sep 2015 13:59:42 -0700 Subject: [PATCH 2/6] SUPEE-6842: Copies of LESS source files are created instead of symlinks in client side less compilation - Merge of solution from MAGETWO-40652 --- .../Developer/Console/Command/CssDeployCommand.php | 8 +++++--- .../Test/Unit/Console/Command/CssDeployCommandTest.php | 6 +++--- .../Framework/Less/PreProcessor/Instruction/Import.php | 7 ++++++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Developer/Console/Command/CssDeployCommand.php b/app/code/Magento/Developer/Console/Command/CssDeployCommand.php index ffa326bc08b2f..1d7c79528b40a 100644 --- a/app/code/Magento/Developer/Console/Command/CssDeployCommand.php +++ b/app/code/Magento/Developer/Console/Command/CssDeployCommand.php @@ -219,21 +219,23 @@ protected function execute(InputInterface $input, OutputInterface $output) ] ); + $rootDir = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); $sourceFile = $this->assetSource->findSource($asset); - $content = \file_get_contents($sourceFile); + $relativePath = $rootDir->getRelativePath($sourceFile); + $content = $rootDir->readFile($relativePath); $chain = $this->chainFactory->create( [ 'asset' => $asset, 'origContent' => $content, - 'origContentType' => $asset->getContentType() + 'origContentType' => $asset->getContentType(), + 'origAssetPath' => $relativePath ] ); $processedCoreFile = $sourceFileGenerator->generateFileTree($chain); $targetDir = $this->filesystem->getDirectoryWrite(DirectoryList::STATIC_VIEW); - $rootDir = $this->filesystem->getDirectoryWrite(DirectoryList::ROOT); $source = $rootDir->getRelativePath($processedCoreFile); $destination = $asset->getPath(); $rootDir->copyFile($source, $destination, $targetDir); diff --git a/app/code/Magento/Developer/Test/Unit/Console/Command/CssDeployCommandTest.php b/app/code/Magento/Developer/Test/Unit/Console/Command/CssDeployCommandTest.php index cad79e9253b0b..6b07f03591160 100644 --- a/app/code/Magento/Developer/Test/Unit/Console/Command/CssDeployCommandTest.php +++ b/app/code/Magento/Developer/Test/Unit/Console/Command/CssDeployCommandTest.php @@ -113,6 +113,8 @@ public function testExecute() ->method('create') ->with('less') ->willReturn($this->getMock('Magento\Framework\Less\FileGenerator', [], [], '', false)); + $asset = $this->getMockForAbstractClass('Magento\Framework\View\Asset\LocalInterface'); + $asset->expects($this->once())->method('getContentType')->willReturn('type'); $this->assetRepo->expects($this->once()) ->method('createAsset') ->with( @@ -123,9 +125,7 @@ public function testExecute() 'locale' => 'en_US' ] ) - ->willReturn( - $this->getMockForAbstractClass('Magento\Framework\View\Asset\LocalInterface') - ); + ->willReturn($asset); $this->assetSource->expects($this->once())->method('findSource')->willReturn('/dev/null'); $this->chainFactory->expects($this->once())->method('create')->willReturn( diff --git a/lib/internal/Magento/Framework/Less/PreProcessor/Instruction/Import.php b/lib/internal/Magento/Framework/Less/PreProcessor/Instruction/Import.php index 098849a2f25cc..3157432ab727a 100644 --- a/lib/internal/Magento/Framework/Less/PreProcessor/Instruction/Import.php +++ b/lib/internal/Magento/Framework/Less/PreProcessor/Instruction/Import.php @@ -51,7 +51,12 @@ public function process(\Magento\Framework\View\Asset\PreProcessor\Chain $chain) return $this->replace($matchContent, $asset); }; $content = $this->removeComments($chain->getContent()); - $chain->setContent(preg_replace_callback(self::REPLACE_PATTERN, $replaceCallback, $content)); + + $processedContent = preg_replace_callback(self::REPLACE_PATTERN, $replaceCallback, $content); + + if ($processedContent !== $content) { + $chain->setContent($processedContent); + } } /** From 7f64985abffc31743ad1d65196650b9a84dc98f8 Mon Sep 17 00:00:00 2001 From: Maksym Aposov Date: Wed, 16 Sep 2015 19:00:38 +0300 Subject: [PATCH 3/6] MAGETWO-42874: Search results are not showing correctly after filtering --- .../Adapter/Mysql/Filter/Preprocessor.php | 132 ++-- .../Model/Search/IndexBuilder.php | 24 +- .../Model/Search/TableMapper.php | 264 ++++++++ .../Adapter/Mysql/Filter/PreprocessorTest.php | 60 +- .../Unit/Model/Search/IndexBuilderTest.php | 30 +- .../Unit/Model/Search/TableMapperTest.php | 590 ++++++++++++++++++ .../Framework/Search/Adapter/Mysql/Mapper.php | 11 - .../Adapter/Mysql/Query/QueryContainer.php | 43 -- .../Mysql/Query/QueryContainerTest.php | 12 - 9 files changed, 978 insertions(+), 188 deletions(-) create mode 100644 app/code/Magento/CatalogSearch/Model/Search/TableMapper.php create mode 100644 app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php index f01880e61acaa..41ab0c7359895 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php @@ -5,6 +5,8 @@ */ namespace Magento\CatalogSearch\Model\Adapter\Mysql\Filter; +use Magento\Catalog\Model\Product; +use Magento\CatalogSearch\Model\Search\TableMapper; use Magento\Eav\Model\Config; use Magento\Framework\App\Resource; use Magento\Framework\App\ScopeResolverInterface; @@ -13,7 +15,11 @@ use Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface; use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface; +use Magento\Store\Model\Store; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class Preprocessor implements PreprocessorInterface { /** @@ -32,20 +38,25 @@ class Preprocessor implements PreprocessorInterface private $config; /** - * @var Resource + * @var string */ - private $resource; + private $attributePrefix; /** - * @var string + * @var AdapterInterface */ - private $attributePrefix; + private $connection; + + /** + * @var TableMapper + */ + private $tableMapper; /** * @param ConditionManager $conditionManager * @param ScopeResolverInterface $scopeResolver * @param Config $config - * @param Resource $resource + * @param TableMapper $tableMapper * @param string $attributePrefix */ public function __construct( @@ -53,13 +64,15 @@ public function __construct( ScopeResolverInterface $scopeResolver, Config $config, Resource $resource, + TableMapper $tableMapper, $attributePrefix ) { $this->conditionManager = $conditionManager; $this->scopeResolver = $scopeResolver; $this->config = $config; - $this->resource = $resource; + $this->connection = $resource->getConnection(Resource::DEFAULT_READ_RESOURCE); $this->attributePrefix = $attributePrefix; + $this->tableMapper = $tableMapper; } /** @@ -80,69 +93,66 @@ public function process(FilterInterface $filter, $isNegation, $query, QueryConta private function processQueryWithField(FilterInterface $filter, $isNegation, $query, QueryContainer $queryContainer) { $currentStoreId = $this->scopeResolver->getScope()->getId(); - + $select = null; + /** @var \Magento\Catalog\Model\Resource\Eav\Attribute $attribute */ $attribute = $this->config->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $filter->getField()); - $select = $this->getConnection()->select(); $table = $attribute->getBackendTable(); - if ($filter->getField() == 'price') { - $query = str_replace('price', 'min_price', $query); - $select->from(['main_table' => $this->resource->getTableName('catalog_product_index_price')], 'entity_id') - ->where($query); - } elseif ($filter->getField() == 'category_ids') { - return 'category_index.category_id = ' . $filter->getValue(); - } else { - if ($attribute->isStatic()) { - $select->from(['main_table' => $table], 'entity_id') - ->where($query); + if ($filter->getField() === 'price') { + $filterQuery = str_replace( + $this->connection->quoteIdentifier('price'), + $this->connection->quoteIdentifier('price_index.min_price'), + $query + ); + return $filterQuery; + } elseif ($filter->getField() === 'category_ids') { + return 'category_ids_index.category_id = ' . $filter->getValue(); + } elseif ($attribute->isStatic()) { + $alias = $this->tableMapper->getMappingAlias($filter); + $filterQuery = str_replace( + $this->connection->quoteIdentifier($attribute->getAttributeCode()), + $this->connection->quoteIdentifier($alias . '.' . $attribute->getAttributeCode()), + $query + ); + return $filterQuery; + } elseif ($filter->getType() === FilterInterface::TYPE_TERM) { + $alias = $this->tableMapper->getMappingAlias($filter); + if (is_array($filter->getValue())) { + $value = sprintf( + '%s IN (%s)', + ($isNegation ? 'NOT' : ''), + implode(',', $filter->getValue()) + ); } else { - if ($filter->getType() == FilterInterface::TYPE_TERM) { - if (is_array($filter->getValue())) { - $value = sprintf( - '%s IN (%s)', - ($isNegation ? 'NOT' : ''), - implode(',', $filter->getValue()) - ); - } else { - $value = ($isNegation ? '!' : '') . '= ' . $filter->getValue(); - } - $filterQuery = sprintf( - 'cpie.store_id = %d AND cpie.attribute_id = %d AND cpie.value %s', - $this->scopeResolver->getScope()->getId(), - $attribute->getId(), - $value - ); - $queryContainer->addFilter($filterQuery); - return ''; - } - $ifNullCondition = $this->getConnection()->getIfNullSql('current_store.value', 'main_table.value'); - - $select->from(['main_table' => $table], 'entity_id') - ->joinLeft( - ['current_store' => $table], - 'current_store.attribute_id = main_table.attribute_id AND current_store.store_id = ' - . $currentStoreId, - null - ) - ->columns([$filter->getField() => $ifNullCondition]) - ->where( - 'main_table.attribute_id = ?', - $attribute->getAttributeId() - ) - ->where('main_table.store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID) - ->having($query); + $value = ($isNegation ? '!' : '') . '= ' . $filter->getValue(); } + $filterQuery = sprintf( + '%1$s.value %2$s', + $alias, + $value + ); + return $filterQuery; + } else { + $select = $this->connection->select(); + $ifNullCondition = $this->connection->getIfNullSql('current_store.value', 'main_table.value'); + + $select->from(['main_table' => $table], 'entity_id') + ->joinLeft( + ['current_store' => $table], + 'current_store.attribute_id = main_table.attribute_id AND current_store.store_id = ' + . $currentStoreId, + null + ) + ->columns([$filter->getField() => $ifNullCondition]) + ->where( + 'main_table.attribute_id = ?', + $attribute->getAttributeId() + ) + ->where('main_table.store_id = ?', \Magento\Store\Model\Store::DEFAULT_STORE_ID) + ->having($query); } return 'search_index.entity_id IN ( select entity_id from ' . $this->conditionManager->wrapBrackets($select) . ' as filter )'; } - - /** - * @return AdapterInterface - */ - private function getConnection() - { - return $this->resource->getConnection(Resource::DEFAULT_READ_RESOURCE); - } } diff --git a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php index 6ae4e1ba0cf88..2ced9d135bdd6 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php +++ b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php @@ -48,25 +48,33 @@ class IndexBuilder implements IndexBuilderInterface */ private $conditionManager; + /** + * @var TableMapper + */ + private $tableMapper; + /** * @param \Magento\Framework\App\Resource $resource * @param ScopeConfigInterface $config * @param StoreManagerInterface $storeManager * @param ConditionManager $conditionManager * @param IndexScopeResolver $scopeResolver + * @param TableMapper $tableMapper */ public function __construct( Resource $resource, ScopeConfigInterface $config, StoreManagerInterface $storeManager, ConditionManager $conditionManager, - IndexScopeResolver $scopeResolver + IndexScopeResolver $scopeResolver, + TableMapper $tableMapper ) { $this->resource = $resource; $this->config = $config; $this->storeManager = $storeManager; $this->conditionManager = $conditionManager; $this->scopeResolver = $scopeResolver; + $this->tableMapper = $tableMapper; } /** @@ -89,19 +97,7 @@ public function build(RequestInterface $request) [] ); - if ($this->isNeedToAddFilters($request)) { - $select - ->joinLeft( - ['category_index' => $this->resource->getTableName('catalog_category_product_index')], - 'search_index.entity_id = category_index.product_id', - [] - ) - ->joinLeft( - ['cpie' => $this->resource->getTableName('catalog_product_index_eav')], - 'search_index.entity_id = cpie.entity_id AND search_index.attribute_id = cpie.attribute_id', - [] - ); - } + $select = $this->tableMapper->addTables($select, $request); $select = $this->processDimensions($request, $select); diff --git a/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php b/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php new file mode 100644 index 0000000000000..68f0ee9661679 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Model/Search/TableMapper.php @@ -0,0 +1,264 @@ +resource = $resource; + $this->storeManager = $storeManager; + $this->attributeCollection = $attributeCollectionFactory->create(); + } + + /** + * @param Select $select + * @param RequestInterface $request + * @return Select + */ + public function addTables(Select $select, RequestInterface $request) + { + $mappedTables = []; + $filters = $this->getFilters($request->getQuery()); + foreach ($filters as $filter) { + list($alias, $table, $mapOn, $mappedFields) = $this->getMappingData($filter); + if (!array_key_exists($alias, $mappedTables)) { + $select->joinLeft( + [$alias => $table], + $mapOn, + $mappedFields + ); + $mappedTables[$alias] = $table; + } + } + return $select; + } + + /** + * @param FilterInterface $filter + * @return string + */ + public function getMappingAlias(FilterInterface $filter) + { + list($alias) = $this->getMappingData($filter); + return $alias; + } + + /** + * Returns mapping data for field in format: [ + * 'table_alias', + * 'table', + * 'join_condition', + * ['fields'] + * ] + * @param FilterInterface $filter + * @return array + */ + private function getMappingData(FilterInterface $filter) + { + $alias = null; + $table = null; + $mapOn = null; + $mappedFields = null; + $field = $filter->getField(); + $fieldToTableMap = $this->getFieldToTableMap($field); + if ($fieldToTableMap) { + list($alias, $table, $mapOn, $mappedFields) = $fieldToTableMap; + $table = $this->resource->getTableName($table); + } elseif ($filter->getType() === FilterInterface::TYPE_TERM) { + $attribute = $this->getAttributeByCode($field); + if ($attribute) { + $table = $this->resource->getTableName('catalog_product_index_eav'); + $alias = $field . '_filter'; + $mapOn = sprintf( + 'search_index.entity_id = %1$s.entity_id AND %1$s.attribute_id = %2$d AND %1$s.store_id = %3$d', + $alias, + $attribute->getId(), + $this->getStoreId() + ); + $mappedFields = []; + } + } else { + $attribute = $this->getAttributeByCode($field); + if ($attribute && $attribute->getBackendType() === AbstractAttribute::TYPE_STATIC) { + $table = $attribute->getBackendTable(); + $alias = $this->getTableAlias($table); + $mapOn = 'search_index.entity_id = ' . $alias . '.entity_id'; + $mappedFields = null; + } + } + + if (!$alias && $table) { + $alias = $this->getTableAlias($table); + } + + return [$alias, $table, $mapOn, $mappedFields]; + } + + /** + * @param RequestQueryInterface $query + * @return FilterInterface[] + */ + private function getFilters($query) + { + $filters = []; + switch ($query->getType()) { + case RequestQueryInterface::TYPE_BOOL: + /** @var \Magento\Framework\Search\Request\Query\Bool $query */ + foreach ($query->getMust() as $subQuery) { + $filters = array_merge($filters, $this->getFilters($subQuery)); + } + foreach ($query->getShould() as $subQuery) { + $filters = array_merge($filters, $this->getFilters($subQuery)); + } + foreach ($query->getMustNot() as $subQuery) { + $filters = array_merge($filters, $this->getFilters($subQuery)); + } + break; + case RequestQueryInterface::TYPE_FILTER: + /** @var Filter $query */ + $filter = $query->getReference(); + if (FilterInterface::TYPE_BOOL === $filter->getType()) { + $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + } else { + $filters[] = $filter; + } + break; + default: + break; + } + return $filters; + } + + /** + * @param Bool $boolExpression + * @return FilterInterface[] + */ + private function getFiltersFromBoolFilter(Bool $boolExpression) + { + $filters = []; + /** @var Bool $filter */ + foreach ($boolExpression->getMust() as $filter) { + if ($filter->getType() === FilterInterface::TYPE_BOOL) { + $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + } else { + $filters[] = $filter; + } + } + foreach ($boolExpression->getShould() as $filter) { + if ($filter->getType() === FilterInterface::TYPE_BOOL) { + $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + } else { + $filters[] = $filter; + } + } + foreach ($boolExpression->getMustNot() as $filter) { + if ($filter->getType() === FilterInterface::TYPE_BOOL) { + $filters = array_merge($filters, $this->getFiltersFromBoolFilter($filter)); + } else { + $filters[] = $filter; + } + } + return $filters; + } + + /** + * @return int + */ + private function getWebsiteId() + { + return $this->storeManager->getWebsite()->getId(); + } + + /** + * @return int + */ + private function getStoreId() + { + return $this->storeManager->getStore()->getId(); + } + + /** + * @param string $field + * @return array|null + */ + private function getFieldToTableMap($field) + { + $fieldToTableMap = [ + 'price' => [ + 'price_index', + 'catalog_product_index_price', + $this->resource->getConnection(Resource::DEFAULT_READ_RESOURCE)->quoteInto( + 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ?', + $this->getWebsiteId() + ), + [] + ], + 'category_ids' => [ + 'category_ids_index', + 'catalog_category_product_index', + 'search_index.entity_id = category_ids_index.product_id', + [] + ] + ]; + return array_key_exists($field, $fieldToTableMap) ? $fieldToTableMap[$field] : null; + } + + /** + * @param string $table + * @return string + */ + private function getTableAlias($table) + { + return md5($table); + } + + /** + * @param string $field + * @return \Magento\Catalog\Model\Resource\Eav\Attribute + */ + private function getAttributeByCode($field) + { + $attribute = $this->attributeCollection->getItemByColumnValue('attribute_code', $field); + return $attribute; + } +} diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php index 71f45034f1390..57bf3589c0c35 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php @@ -15,6 +15,11 @@ */ class PreprocessorTest extends \PHPUnit_Framework_TestCase { + /** + * @var \Magento\CatalogSearch\Model\Search\TableMapper|\PHPUnit_Framework_MockObject_MockObject + */ + private $tableMapper; + /** * @var \Magento\Framework\DB\Adapter\AdapterInterface|MockObject */ @@ -90,7 +95,7 @@ protected function setUp() ->getMock(); $this->attribute = $this->getMockBuilder('\Magento\Eav\Model\Entity\Attribute\AbstractAttribute') ->disableOriginalConstructor() - ->setMethods(['getBackendTable', 'isStatic', 'getAttributeId']) + ->setMethods(['getBackendTable', 'isStatic', 'getAttributeId', 'getAttributeCode']) ->getMockForAbstractClass(); $this->resource = $resource = $this->getMockBuilder('\Magento\Framework\App\Resource') ->disableOriginalConstructor() @@ -104,12 +109,14 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['from', 'where', '__toString', 'joinLeft', 'columns', 'having']) ->getMock(); - $this->connection->expects($this->once()) + $this->connection->expects($this->any()) ->method('select') ->will($this->returnValue($this->select)); + $this->connection->expects($this->any()) + ->method('quoteIdentifier') + ->will($this->returnArgument(0)); $resource->expects($this->atLeastOnce()) ->method('getConnection') - ->with(\Magento\Framework\App\Resource::DEFAULT_READ_RESOURCE) ->will($this->returnValue($this->connection)); $this->filter = $this->getMockBuilder('\Magento\Framework\Search\Request\FilterInterface') ->disableOriginalConstructor() @@ -127,6 +134,10 @@ function ($select) { ) ); + $this->tableMapper = $this->getMockBuilder('\Magento\CatalogSearch\Model\Search\TableMapper') + ->disableOriginalConstructor() + ->getMock(); + $this->target = $objectManagerHelper->getObject( 'Magento\CatalogSearch\Model\Adapter\Mysql\Filter\Preprocessor', [ @@ -134,17 +145,18 @@ function ($select) { 'scopeResolver' => $this->scopeResolver, 'config' => $this->config, 'resource' => $resource, - 'attributePrefix' => 'attr_' + 'attributePrefix' => 'attr_', + 'tableMapper' => $this->tableMapper, ] ); } public function testProcessPrice() { - $expectedResult = 'search_index.entity_id IN (select entity_id from (TEST QUERY PART) as filter)'; + $expectedResult = 'price_index.min_price = 23'; $scopeId = 0; $isNegation = false; - $query = 'SELECT table.price FROM catalog_product_entity'; + $query = 'price = 23'; $this->scope->expects($this->once())->method('getId')->will($this->returnValue($scopeId)); $this->filter->expects($this->exactly(2)) @@ -154,21 +166,6 @@ public function testProcessPrice() ->method('getAttribute') ->with(\Magento\Catalog\Model\Product::ENTITY, 'price') ->will($this->returnValue($this->attribute)); - $this->resource->expects($this->once()) - ->method('getTableName') - ->with('catalog_product_index_price') - ->will($this->returnValue('table_name')); - $this->select->expects($this->once()) - ->method('from') - ->with(['main_table' => 'table_name'], 'entity_id') - ->will($this->returnSelf()); - $this->select->expects($this->once()) - ->method('where') - ->with('SELECT table.min_price FROM catalog_product_entity') - ->will($this->returnSelf()); - $this->select->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('TEST QUERY PART')); $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') ->disableOriginalConstructor() @@ -180,7 +177,7 @@ public function testProcessPrice() public function testProcessCategoryIds() { - $expectedResult = 'category_index.category_id = FilterValue'; + $expectedResult = 'category_ids_index.category_id = FilterValue'; $scopeId = 0; $isNegation = false; $query = 'SELECT category_ids FROM catalog_product_entity'; @@ -209,11 +206,15 @@ public function testProcessCategoryIds() public function testProcessStaticAttribute() { - $expectedResult = 'search_index.entity_id IN (select entity_id from (TEST QUERY PART) as filter)'; + $expectedResult = 'attr_table_alias.static_attribute LIKE %name%'; $scopeId = 0; $isNegation = false; - $query = 'SELECT field FROM table'; + $query = 'static_attribute LIKE %name%'; + $this->attribute->method('getAttributeCode') + ->willReturn('static_attribute'); + $this->tableMapper->expects($this->once())->method('getMappingAlias') + ->willReturn('attr_table_alias'); $this->scope->expects($this->once())->method('getId')->will($this->returnValue($scopeId)); $this->filter->expects($this->exactly(3)) ->method('getField') @@ -228,17 +229,6 @@ public function testProcessStaticAttribute() $this->attribute->expects($this->once()) ->method('getBackendTable') ->will($this->returnValue('backend_table')); - $this->select->expects($this->once()) - ->method('from') - ->with(['main_table' => 'backend_table'], 'entity_id') - ->will($this->returnSelf()); - $this->select->expects($this->once()) - ->method('where') - ->with('SELECT field FROM table') - ->will($this->returnSelf()); - $this->select->expects($this->once()) - ->method('__toString') - ->will($this->returnValue('TEST QUERY PART')); $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') ->disableOriginalConstructor() diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php index 5e93d20132e38..2d0cb69d4d2b3 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/IndexBuilderTest.php @@ -14,6 +14,9 @@ */ class IndexBuilderTest extends \PHPUnit_Framework_TestCase { + /** @var \Magento\CatalogSearch\Model\Search\TableMapper|MockObject */ + private $tableMapper; + /** @var \Magento\Framework\Search\Adapter\Mysql\ConditionManager|MockObject */ private $conditionManager; @@ -21,7 +24,7 @@ class IndexBuilderTest extends \PHPUnit_Framework_TestCase private $scopeResolver; /** @var \Magento\Framework\DB\Adapter\AdapterInterface|MockObject */ - private $adapter; + private $connection; /** @var \Magento\Framework\DB\Select|MockObject */ private $select; @@ -50,11 +53,11 @@ protected function setUp() ->setMethods(['from', 'joinLeft', 'where', 'joinInner']) ->getMock(); - $this->adapter = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface') + $this->connection = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface') ->disableOriginalConstructor() ->setMethods(['select', 'quoteInto']) ->getMockForAbstractClass(); - $this->adapter->expects($this->once()) + $this->connection->expects($this->once()) ->method('select') ->will($this->returnValue($this->select)); @@ -64,18 +67,12 @@ protected function setUp() ->getMock(); $this->resource->expects($this->any()) ->method('getConnection') - ->with(\Magento\Framework\App\Resource::DEFAULT_READ_RESOURCE) - ->will($this->returnValue($this->adapter)); + ->will($this->returnValue($this->connection)); $this->request = $this->getMockBuilder('\Magento\Framework\Search\RequestInterface') ->disableOriginalConstructor() ->setMethods(['getIndex', 'getDimensions', 'getQuery']) ->getMockForAbstractClass(); - $this->request->expects($this->once()) - ->method('getQuery') - ->willReturn( - $this->getMockBuilder('Magento\Framework\Search\Request\QueryInterface')->getMockForAbstractClass() - ); $this->config = $this->getMockBuilder('\Magento\Framework\App\Config\ScopeConfigInterface') ->disableOriginalConstructor() @@ -114,6 +111,14 @@ function ($left, $operator, $right) { } ); + $this->tableMapper = $this->getMockBuilder('\Magento\CatalogSearch\Model\Search\TableMapper') + ->disableOriginalConstructor() + ->getMock(); + $this->tableMapper->expects($this->once()) + ->method('addTables') + ->with($this->select, $this->request) + ->willReturnArgument(0); + $objectManagerHelper = new ObjectManagerHelper($this); $this->target = $objectManagerHelper->getObject( 'Magento\CatalogSearch\Model\Search\IndexBuilder', @@ -122,7 +127,8 @@ function ($left, $operator, $right) { 'config' => $this->config, 'storeManager' => $this->storeManager, 'conditionManager' => $this->conditionManager, - 'scopeResolver' => $this->scopeResolver + 'scopeResolver' => $this->scopeResolver, + 'tableMapper' => $this->tableMapper, ] ); } @@ -168,7 +174,7 @@ public function testBuildWithoutOutOfStock() ->method('isSetFlag') ->with('cataloginventory/options/show_out_of_stock') ->will($this->returnValue(false)); - $this->adapter->expects($this->once())->method('quoteInto') + $this->connection->expects($this->once())->method('quoteInto') ->with(' AND stock_index.website_id = ?', 1)->willReturn(' AND stock_index.website_id = 1'); $website = $this->getMockBuilder('Magento\Store\Model\Website')->disableOriginalConstructor()->getMock(); $website->expects($this->once())->method('getId')->willReturn(1); diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php new file mode 100644 index 0000000000000..9b47e0c30a797 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/TableMapperTest.php @@ -0,0 +1,590 @@ +connection = $this->getMockBuilder('\Magento\Framework\DB\Adapter\AdapterInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->connection->expects($this->any()) + ->method('quoteInto') + ->willReturnCallback( + function ($query, $expression) { + return str_replace('?', $expression, $query); + } + ); + + $this->resource = $this->getMockBuilder('\Magento\Framework\App\Resource') + ->disableOriginalConstructor() + ->getMock(); + $this->resource->method('getTableName') + ->willReturnCallback( + function ($table) { + return 'prefix_' . $table; + } + ); + $this->resource->expects($this->any()) + ->method('getConnection') + ->willReturn($this->connection); + + $this->website = $this->getMockBuilder('\Magento\Store\Model\Website') + ->disableOriginalConstructor() + ->getMock(); + $this->website->expects($this->any()) + ->method('getId') + ->willReturn(self::WEBSITE_ID); + $this->store = $this->getMockBuilder('\Magento\Store\Model\Store') + ->disableOriginalConstructor() + ->getMock(); + $this->store->expects($this->any()) + ->method('getId') + ->willReturn(self::STORE_ID); + $this->storeManager = $this->getMockBuilder('\Magento\Store\Model\StoreManagerInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->storeManager->expects($this->any()) + ->method('getWebsite') + ->willReturn($this->website); + $this->storeManager->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); + $this->attributeCollection = $this->getMockBuilder( + '\Magento\Catalog\Model\Resource\Product\Attribute\Collection' + ) + ->disableOriginalConstructor() + ->getMock(); + $attributeCollectionFactory = $this->getMockBuilder( + '\Magento\Catalog\Model\Resource\Product\Attribute\CollectionFactory' + ) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $attributeCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($this->attributeCollection); + $this->target = $objectManager->getObject( + '\Magento\CatalogSearch\Model\Search\TableMapper', + [ + 'resource' => $this->resource, + 'storeManager' => $this->storeManager, + 'attributeCollectionFactory' => $attributeCollectionFactory + ] + ); + + $this->select = $this->getMockBuilder('\Magento\Framework\DB\Select') + ->disableOriginalConstructor() + ->getMock(); + $this->request = $this->getMockBuilder('\Magento\Framework\Search\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + } + + public function testAddPriceFilter() + { + $priceFilter = $this->createRangeFilter('price'); + $query = $this->createFilterQuery($priceFilter); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->once()) + ->method('joinLeft') + ->with( + ['price_index' => 'prefix_catalog_product_index_price'], + 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ' . self::WEBSITE_ID, + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddStaticAttributeFilter() + { + $priceFilter = $this->createRangeFilter('static'); + $query = $this->createFilterQuery($priceFilter); + $this->createAttributeMock('static', 'static', 'backend_table', 0); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->once()) + ->method('joinLeft') + ->with( + ['4111c4a3daddb5c5dba31cdac705114b' => 'backend_table'], + 'search_index.entity_id = 4111c4a3daddb5c5dba31cdac705114b.entity_id', + null + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddCategoryIds() + { + $categoryIdsFilter = $this->createTermFilter('category_ids'); + $query = $this->createFilterQuery($categoryIdsFilter); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->once()) + ->method('joinLeft') + ->with( + ['category_ids_index' => 'prefix_catalog_category_product_index'], + 'search_index.entity_id = category_ids_index.product_id', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddTermFilter() + { + $this->createAttributeMock('color', null, null, 132, 0); + $categoryIdsFilter = $this->createTermFilter('color'); + $query = $this->createFilterQuery($categoryIdsFilter); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->once()) + ->method('joinLeft') + ->with( + ['color_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = color_filter.entity_id' + . ' AND color_filter.attribute_id = 132' + . ' AND color_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddBoolQueryWithTermFiltersInside() + { + $this->createAttributeMock('must1', null, null, 101, 0); + $this->createAttributeMock('should1', null, null, 102, 1); + $this->createAttributeMock('mustNot1', null, null, 103, 2); + + $query = $this->createBoolQuery( + [ + $this->createFilterQuery($this->createTermFilter('must1')), + ], + [ + $this->createFilterQuery($this->createTermFilter('should1')), + ], + [ + $this->createFilterQuery($this->createTermFilter('mustNot1')), + ] + ); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->at(0)) + ->method('joinLeft') + ->with( + ['must1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = must1_filter.entity_id' + . ' AND must1_filter.attribute_id = 101' + . ' AND must1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(1)) + ->method('joinLeft') + ->with( + ['should1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = should1_filter.entity_id' + . ' AND should1_filter.attribute_id = 102' + . ' AND should1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(2)) + ->method('joinLeft') + ->with( + ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = mustNot1_filter.entity_id' + . ' AND mustNot1_filter.attribute_id = 103' + . ' AND mustNot1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddBoolQueryWithTermAndPriceFiltersInside() + { + $this->createAttributeMock('must1', null, null, 101, 0); + $this->createAttributeMock('should1', null, null, 102, 1); + $this->createAttributeMock('mustNot1', null, null, 103, 2); + $query = $this->createBoolQuery( + [ + $this->createFilterQuery($this->createTermFilter('must1')), + $this->createFilterQuery($this->createRangeFilter('price')), + ], + [ + $this->createFilterQuery($this->createTermFilter('should1')), + ], + [ + $this->createFilterQuery($this->createTermFilter('mustNot1')), + ] + ); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->at(0)) + ->method('joinLeft') + ->with( + ['must1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = must1_filter.entity_id' + . ' AND must1_filter.attribute_id = 101' + . ' AND must1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(1)) + ->method('joinLeft') + ->with( + ['price_index' => 'prefix_catalog_product_index_price'], + 'search_index.entity_id = price_index.entity_id AND price_index.website_id = ' . self::WEBSITE_ID, + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(2)) + ->method('joinLeft') + ->with( + ['should1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = should1_filter.entity_id' + . ' AND should1_filter.attribute_id = 102' + . ' AND should1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(3)) + ->method('joinLeft') + ->with( + ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = mustNot1_filter.entity_id' + . ' AND mustNot1_filter.attribute_id = 103' + . ' AND mustNot1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddBoolFilterWithTermFiltersInside() + { + $this->createAttributeMock('must1', null, null, 101, 0); + $this->createAttributeMock('should1', null, null, 102, 1); + $this->createAttributeMock('mustNot1', null, null, 103, 2); + $query = $this->createFilterQuery( + $this->createBoolFilter( + [ + $this->createTermFilter('must1'), + ], + [ + $this->createTermFilter('should1'), + ], + [ + $this->createTermFilter('mustNot1'), + ] + ) + ); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->at(0)) + ->method('joinLeft') + ->with( + ['must1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = must1_filter.entity_id' + . ' AND must1_filter.attribute_id = 101' + . ' AND must1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(1)) + ->method('joinLeft') + ->with( + ['should1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = should1_filter.entity_id' + . ' AND should1_filter.attribute_id = 102' + . ' AND should1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(2)) + ->method('joinLeft') + ->with( + ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = mustNot1_filter.entity_id' + . ' AND mustNot1_filter.attribute_id = 103' + . ' AND mustNot1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + public function testAddBoolFilterWithBoolFiltersInside() + { + $this->createAttributeMock('must1', null, null, 101, 0); + $this->createAttributeMock('should1', null, null, 102, 1); + $this->createAttributeMock('mustNot1', null, null, 103, 2); + $query = $this->createFilterQuery( + $this->createBoolFilter( + [ + $this->createBoolFilter([$this->createTermFilter('must1')], [], []), + ], + [ + $this->createBoolFilter([$this->createTermFilter('should1')], [], []), + ], + [ + $this->createBoolFilter([$this->createTermFilter('mustNot1')], [], []), + ] + ) + ); + $this->request->expects($this->once()) + ->method('getQuery') + ->willReturn($query); + $this->select->expects($this->at(0)) + ->method('joinLeft') + ->with( + ['must1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = must1_filter.entity_id' + . ' AND must1_filter.attribute_id = 101' + . ' AND must1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(1)) + ->method('joinLeft') + ->with( + ['should1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = should1_filter.entity_id' + . ' AND should1_filter.attribute_id = 102' + . ' AND should1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $this->select->expects($this->at(2)) + ->method('joinLeft') + ->with( + ['mustNot1_filter' => 'prefix_catalog_product_index_eav'], + 'search_index.entity_id = mustNot1_filter.entity_id' + . ' AND mustNot1_filter.attribute_id = 103' + . ' AND mustNot1_filter.store_id = 2514', + [] + ) + ->willReturnSelf(); + $select = $this->target->addTables($this->select, $this->request); + $this->assertEquals($this->select, $select, 'Returned results isn\'t equal to passed select'); + } + + /** + * @param $filter + * @return \Magento\Framework\Search\Request\Query\Filter|\PHPUnit_Framework_MockObject_MockObject + */ + private function createFilterQuery($filter) + { + $query = $this->getMockBuilder('\Magento\Framework\Search\Request\Query\Filter') + ->disableOriginalConstructor() + ->getMock(); + $query->method('getType') + ->willReturn(QueryInterface::TYPE_FILTER); + $query->method('getReference') + ->willReturn($filter); + return $query; + } + + /** + * @param array $must + * @param array $should + * @param array $mustNot + * @return \Magento\Framework\Search\Request\Query\Bool|\PHPUnit_Framework_MockObject_MockObject + * @internal param $filter + */ + private function createBoolQuery(array $must, array $should, array $mustNot) + { + $query = $this->getMockBuilder('\Magento\Framework\Search\Request\Query\Bool') + ->disableOriginalConstructor() + ->getMock(); + $query->method('getType') + ->willReturn(QueryInterface::TYPE_BOOL); + $query->method('getMust') + ->willReturn($must); + $query->method('getShould') + ->willReturn($should); + $query->method('getMustNot') + ->willReturn($mustNot); + return $query; + } + + /** + * @param array $must + * @param array $should + * @param array $mustNot + * @return \Magento\Framework\Search\Request\Filter\Bool|\PHPUnit_Framework_MockObject_MockObject + * @internal param $filter + */ + private function createBoolFilter(array $must, array $should, array $mustNot) + { + $query = $this->getMockBuilder('\Magento\Framework\Search\Request\Filter\Bool') + ->disableOriginalConstructor() + ->getMock(); + $query->method('getType') + ->willReturn(FilterInterface::TYPE_BOOL); + $query->method('getMust') + ->willReturn($must); + $query->method('getShould') + ->willReturn($should); + $query->method('getMustNot') + ->willReturn($mustNot); + return $query; + } + + /** + * @param string $field + * @return \Magento\Framework\Search\Request\Filter\Range|\PHPUnit_Framework_MockObject_MockObject + */ + private function createRangeFilter($field) + { + $filter = $this->createFilterMock( + '\Magento\Framework\Search\Request\Filter\Range', + FilterInterface::TYPE_RANGE, + $field + ); + return $filter; + } + + /** + * @param string $field + * @return \Magento\Framework\Search\Request\Filter\Term|\PHPUnit_Framework_MockObject_MockObject + */ + private function createTermFilter($field) + { + $filter = $this->createFilterMock( + '\Magento\Framework\Search\Request\Filter\Term', + FilterInterface::TYPE_TERM, + $field + ); + return $filter; + } + + /** + * @param string $class + * @param string $type + * @param string $field + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function createFilterMock($class, $type, $field) + { + $filter = $this->getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + $filter->method('getType') + ->willReturn($type); + $filter->method('getField') + ->willReturn($field); + return $filter; + } + + /** + * @param string $code + * @param string $backendType + * @param string $backendTable + * @param int $attributeId + * @param int $positionInCollection + */ + private function createAttributeMock( + $code, + $backendType = null, + $backendTable = null, + $attributeId = 120, + $positionInCollection = 0 + ) { + $attribute = $this->getMockBuilder('\Magento\Catalog\Model\Resource\Eav\Attribute') + ->setMethods(['getBackendType', 'getBackendTable', 'getId']) + ->disableOriginalConstructor() + ->getMock(); + $attribute->method('getId') + ->willReturn($attributeId); + $attribute->method('getBackendType') + ->willReturn($backendType); + $attribute->method('getBackendTable') + ->willReturn($backendTable); + $this->attributeCollection->expects($this->at($positionInCollection)) + ->method('getItemByColumnValue') + ->with('attribute_code', $code) + ->willReturn($attribute); + } +} diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php index fe2da6d2a4d2a..1a1bfb83183f9 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php @@ -126,12 +126,6 @@ public function buildQuery(RequestInterface $request) $queryContainer ); - $filtersCount = $queryContainer->getFiltersCount(); - if ($filtersCount > 1) { - $select->group('entity_id'); - $select->having('COUNT(DISTINCT search_index.attribute_id) = ' . $filtersCount); - } - $select = $this->addMatchQueries( $request, $queryContainer->getDerivedQueries(), @@ -274,11 +268,6 @@ private function processBoolQueryCondition( foreach ($subQueryList as $subQuery) { $select = $this->processQuery($scoreBuilder, $subQuery, $select, $conditionType, $queryContainer); } - $filters = $queryContainer->getFilters(); - if ($filters) { - $select->where('(' . implode(' OR ', $filters) . ')'); - $queryContainer->clearFilters(); - } return $select; } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/QueryContainer.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/QueryContainer.php index 57407b14a8170..515c9e5dcf48d 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/QueryContainer.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Query/QueryContainer.php @@ -17,15 +17,6 @@ class QueryContainer */ private $queries = []; - /** - * @var string[] - */ - private $filters = []; - - /** - * @var int - */ - private $filtersCount = 0; /** * @var \Magento\Framework\Search\Adapter\Mysql\Query\MatchContainerFactory */ @@ -61,40 +52,6 @@ public function addMatchQuery( return $select; } - /** - * @param string $filter - * @return void - */ - public function addFilter($filter) - { - $this->filters[] = '(' . $filter . ')'; - $this->filtersCount++; - } - - /** - * @return void - */ - public function clearFilters() - { - $this->filters = []; - } - - /** - * @return string[] - */ - public function getFilters() - { - return $this->filters; - } - - /** - * @return int - */ - public function getFiltersCount() - { - return $this->filtersCount; - } - /** * @return MatchContainer[] */ diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/QueryContainerTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/QueryContainerTest.php index 10d77becf5a25..a7f90cd9d569c 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/QueryContainerTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Query/QueryContainerTest.php @@ -7,7 +7,6 @@ use Magento\Framework\Search\Adapter\Mysql\Query\MatchContainerFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\Query\Bool; class QueryContainerTest extends \PHPUnit_Framework_TestCase @@ -75,15 +74,4 @@ public function testGetDerivedQueries() $this->assertCount(1, $queries); $this->assertEquals('asdf', reset($queries)); } - - public function testFilters() - { - $this->assertEmpty($this->queryContainer->getFilters()); - $this->queryContainer->addFilter('filter'); - $this->assertCount(1, $this->queryContainer->getFilters()); - $this->assertEquals(1, $this->queryContainer->getFiltersCount()); - $this->queryContainer->clearFilters(); - $this->assertCount(0, $this->queryContainer->getFilters()); - $this->assertEquals(1, $this->queryContainer->getFiltersCount()); - } } From bc651d6ab509414ebb14a69d7e70cb8e9abac42e Mon Sep 17 00:00:00 2001 From: Maksym Aposov Date: Thu, 17 Sep 2015 13:16:45 +0300 Subject: [PATCH 4/6] MAGETWO-42874: Search results are not showing correctly after filtering --- .../Adapter/Mysql/Filter/Preprocessor.php | 9 +- .../Model/Search/IndexBuilder.php | 9 -- .../Adapter/Mysql/Filter/PreprocessorTest.php | 24 +--- .../Search/Adapter/Mysql/AdapterTest.php | 45 ++++++-- .../Search/_files/filterable_attribute.php | 105 ++++++++++++++++++ .../_files/filterable_attribute_rollback.php | 25 +++++ .../Framework/Search/_files/requests.xml | 45 ++++++++ .../Search/Adapter/Mysql/Filter/Builder.php | 28 ++--- .../Adapter/Mysql/Filter/BuilderInterface.php | 4 +- .../Adapter/Mysql/Filter/Preprocessor.php | 3 +- .../Mysql/Filter/PreprocessorInterface.php | 4 +- .../Framework/Search/Adapter/Mysql/Mapper.php | 2 +- .../Unit/Adapter/Mysql/Filter/BuilderTest.php | 39 +++---- 13 files changed, 249 insertions(+), 93 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php diff --git a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php index 41ab0c7359895..39507f7f2d009 100644 --- a/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php +++ b/app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Filter/Preprocessor.php @@ -13,7 +13,6 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; use Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface; use Magento\Store\Model\Store; @@ -56,6 +55,7 @@ class Preprocessor implements PreprocessorInterface * @param ConditionManager $conditionManager * @param ScopeResolverInterface $scopeResolver * @param Config $config + * @param Resource $resource * @param TableMapper $tableMapper * @param string $attributePrefix */ @@ -78,19 +78,18 @@ public function __construct( /** * {@inheritdoc} */ - public function process(FilterInterface $filter, $isNegation, $query, QueryContainer $queryContainer) + public function process(FilterInterface $filter, $isNegation, $query) { - return $this->processQueryWithField($filter, $isNegation, $query, $queryContainer); + return $this->processQueryWithField($filter, $isNegation, $query); } /** * @param FilterInterface $filter * @param bool $isNegation * @param string $query - * @param QueryContainer $queryContainer * @return string */ - private function processQueryWithField(FilterInterface $filter, $isNegation, $query, QueryContainer $queryContainer) + private function processQueryWithField(FilterInterface $filter, $isNegation, $query) { $currentStoreId = $this->scopeResolver->getScope()->getId(); $select = null; diff --git a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php index 2ced9d135bdd6..f853f7653a66a 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php +++ b/app/code/Magento/CatalogSearch/Model/Search/IndexBuilder.php @@ -181,15 +181,6 @@ private function getSelect() return $this->getReadConnection()->select(); } - /** - * @param RequestInterface $request - * @return bool - */ - private function isNeedToAddFilters(RequestInterface $request) - { - return $this->hasFilters($request->getQuery()); - } - /** * @param QueryInterface $query * @return bool diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php index 57bf3589c0c35..2e5241e8e91f0 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Adapter/Mysql/Filter/PreprocessorTest.php @@ -167,11 +167,7 @@ public function testProcessPrice() ->with(\Magento\Catalog\Model\Product::ENTITY, 'price') ->will($this->returnValue($this->attribute)); - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - - $actualResult = $this->target->process($this->filter, $isNegation, $query, $queryContainer); + $actualResult = $this->target->process($this->filter, $isNegation, $query); $this->assertSame($expectedResult, $this->removeWhitespaces($actualResult)); } @@ -196,11 +192,7 @@ public function testProcessCategoryIds() ->with(\Magento\Catalog\Model\Product::ENTITY, 'category_ids') ->will($this->returnValue($this->attribute)); - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - - $actualResult = $this->target->process($this->filter, $isNegation, $query, $queryContainer); + $actualResult = $this->target->process($this->filter, $isNegation, $query); $this->assertSame($expectedResult, $this->removeWhitespaces($actualResult)); } @@ -230,11 +222,7 @@ public function testProcessStaticAttribute() ->method('getBackendTable') ->will($this->returnValue('backend_table')); - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - - $actualResult = $this->target->process($this->filter, $isNegation, $query, $queryContainer); + $actualResult = $this->target->process($this->filter, $isNegation, $query); $this->assertSame($expectedResult, $this->removeWhitespaces($actualResult)); } @@ -286,11 +274,7 @@ public function testProcessNotStaticAttribute() ->method('__toString') ->will($this->returnValue('TEST QUERY PART')); - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - - $actualResult = $this->target->process($this->filter, $isNegation, $query, $queryContainer); + $actualResult = $this->target->process($this->filter, $isNegation, $query); $this->assertSame($expectedResult, $this->removeWhitespaces($actualResult)); } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php index 7a778326dd877..5ff7dc4db8c32 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/Adapter/Mysql/AdapterTest.php @@ -71,8 +71,6 @@ public function testMatchQuery() */ private function executeQuery() { - $this->reindexAll(); - /** @var \Magento\Framework\Search\Response\QueryResponse $queryRequest */ $queryRequest = $this->requestBuilder->create(); @@ -81,15 +79,6 @@ private function executeQuery() return $queryResponse; } - private function reindexAll() - { - /** @var \Magento\Indexer\Model\Indexer $indexer */ - $indexer = $this->objectManager->get('Magento\Indexer\Model\Indexer\CollectionFactory') - ->create() - ->getItemByColumnValue('indexer_id', 'catalogsearch_fulltext'); - $indexer->reindexAll(); - } - /** * Sample test * @@ -369,4 +358,38 @@ public function advancedSearchDataProvider() ['peoples', 'green', ['from' => '12', 'to' => '22'], 2], ]; } + + /** + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @magentoConfigFixture current_store catalog/search/engine mysql + * @magentoConfigFixture current_store catalog/search/search_type 2 + * @magentoDataFixture Magento/Framework/Search/_files/filterable_attribute.php + */ + public function testCustomFilterableAttribute() + { + /** @var \Magento\Catalog\Model\Resource\Eav\Attribute $attribute */ + $attribute = $this->objectManager->get('Magento\Catalog\Model\Resource\Eav\Attribute') + ->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'select_attribute'); + /** @var \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection $selectOptions */ + $selectOptions = $this->objectManager + ->create('Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection') + ->setAttributeFilter($attribute->getId()); + + $attribute->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'multiselect_attribute'); + /** @var \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection $multiselectOptions */ + $multiselectOptions = $this->objectManager + ->create('Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection') + ->setAttributeFilter($attribute->getId()); + + $this->requestBuilder->bind('select_attribute', $selectOptions->getLastItem()->getId()); + $this->requestBuilder->bind('multiselect_attribute', $multiselectOptions->getLastItem()->getId()); + $this->requestBuilder->bind('price.from', 9); + $this->requestBuilder->bind('price.to', 12); + $this->requestBuilder->bind('category_ids', 2); + $this->requestBuilder->setRequestName('filterable_custom_attributes'); + + $queryResponse = $this->executeQuery(); + $this->assertEquals(1, $queryResponse->count()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php new file mode 100644 index 0000000000000..993eb2d8c9756 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute.php @@ -0,0 +1,105 @@ +create( + 'Magento\Catalog\Setup\CategorySetup', + ['resourceName' => 'catalog_setup'] +); +/** @var $selectAttribute \Magento\Catalog\Model\Resource\Eav\Attribute */ +$selectAttribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Resource\Eav\Attribute' +); +$selectAttribute->setData( + [ + 'attribute_code' => 'select_attribute', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'frontend_input' => 'select', + 'is_filterable' => 1, + 'option' => [ + 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], + 'order' => ['option_0' => 1, 'option_1' => 2], + ], + 'backend_type' => 'int', + ] +); +$selectAttribute->save(); +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $selectAttribute->getId()); + +/** @var $selectOptions \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */ +$selectOptions = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' +); +$selectOptions->setAttributeFilter($selectAttribute->getId()); + +$multiselectAttribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Resource\Eav\Attribute' +); +$multiselectAttribute->setData( + [ + 'attribute_code' => 'multiselect_attribute', + 'entity_type_id' => $installer->getEntityTypeId('catalog_product'), + 'is_global' => 1, + 'frontend_input' => 'multiselect', + 'is_filterable' => 1, + 'option' => [ + 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']], + 'order' => ['option_0' => 1, 'option_1' => 2], + ], + 'backend_type' => 'varchar', + ] +); +$multiselectAttribute->save(); +/* Assign attribute to attribute set */ +$installer->addAttributeToGroup('catalog_product', 'Default', 'General', $multiselectAttribute->getId()); + +/** @var $multiselectOptions \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */ +$multiselectOptions = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' +); +$multiselectOptions->setAttributeFilter($multiselectAttribute->getId()); + + +/* Create simple products per each select(dropdown) option */ +foreach ($selectOptions as $option) { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); + $product->setTypeId( + \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE + )->setAttributeSetId( + $installer->getAttributeSetId('catalog_product', 'Default') + )->setWebsiteIds( + [1] + )->setName( + 'Simple Product ' . $option->getId() + )->setSku( + 'simple_product_' . $option->getId() + )->setPrice( + 10 + )->setCategoryIds( + [2] + )->setVisibility( + \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH + )->setStatus( + \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED + )->setStockData( + ['use_config_manage_stock' => 1, 'qty' => 5, 'is_in_stock' => 1] + )->save(); + + \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( + 'Magento\Catalog\Model\Product\Action' + )->updateAttributes( + [$product->getId()], + [ + $selectAttribute->getAttributeCode() => $option->getId(), + $multiselectAttribute->getAttributeCode() => $multiselectOptions->getLastItem()->getId() + ], + $product->getStoreId() + ); +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php new file mode 100644 index 0000000000000..04a82aafb8219 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php @@ -0,0 +1,25 @@ +create( + 'Magento\Catalog\Setup\CategorySetup', + ['resourceName' => 'catalog_setup'] +); +/** @var $attribute \Magento\Catalog\Model\Resource\Eav\Attribute */ +$attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Catalog\Model\Resource\Eav\Attribute' +); +$attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'select_attribute'); +if ($attribute->getId()) { + $attribute->delete(); +} + +$attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'multiselect_attribute'); +if ($attribute->getId()) { + $attribute->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml index 721713a0533a7..c60aa36b04842 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/requests.xml @@ -321,4 +321,49 @@ 10 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 10 + diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Builder.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Builder.php index 9b55177403ae5..3ee403951f246 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Builder.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Builder.php @@ -11,7 +11,6 @@ use Magento\Framework\Search\Adapter\Mysql\Filter\Builder\Range; use Magento\Framework\Search\Adapter\Mysql\Filter\Builder\Term; use Magento\Framework\Search\Adapter\Mysql\Filter\Builder\Wildcard; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface as RequestFilterInterface; use Magento\Framework\Search\Request\Query\Bool; @@ -58,28 +57,27 @@ public function __construct( /** * {@inheritdoc} */ - public function build(RequestFilterInterface $filter, $conditionType, QueryContainer $queryContainer) + public function build(RequestFilterInterface $filter, $conditionType) { - return $this->processFilter($filter, $this->isNegation($conditionType), $queryContainer); + return $this->processFilter($filter, $this->isNegation($conditionType)); } /** * @param RequestFilterInterface $filter * @param bool $isNegation - * @param QueryContainer $queryContainer * @return string */ - private function processFilter(RequestFilterInterface $filter, $isNegation, QueryContainer $queryContainer) + private function processFilter(RequestFilterInterface $filter, $isNegation) { if ($filter->getType() == RequestFilterInterface::TYPE_BOOL) { - $query = $this->processBoolFilter($filter, $isNegation, $queryContainer); + $query = $this->processBoolFilter($filter, $isNegation); $query = $this->conditionManager->wrapBrackets($query); } else { if (!isset($this->filters[$filter->getType()])) { throw new \InvalidArgumentException('Unknown filter type ' . $filter->getType()); } $query = $this->filters[$filter->getType()]->buildFilter($filter, $isNegation); - $query = $this->preprocessor->process($filter, $isNegation, $query, $queryContainer); + $query = $this->preprocessor->process($filter, $isNegation, $query); } return $query; @@ -88,18 +86,16 @@ private function processFilter(RequestFilterInterface $filter, $isNegation, Quer /** * @param RequestFilterInterface|\Magento\Framework\Search\Request\Filter\Bool $filter * @param bool $isNegation - * @param QueryContainer $queryContainer * @return string */ - private function processBoolFilter(RequestFilterInterface $filter, $isNegation, QueryContainer $queryContainer) + private function processBoolFilter(RequestFilterInterface $filter, $isNegation) { - $must = $this->buildFilters($filter->getMust(), Select::SQL_AND, $isNegation, $queryContainer); - $should = $this->buildFilters($filter->getShould(), Select::SQL_OR, $isNegation, $queryContainer); + $must = $this->buildFilters($filter->getMust(), Select::SQL_AND, $isNegation); + $should = $this->buildFilters($filter->getShould(), Select::SQL_OR, $isNegation); $mustNot = $this->buildFilters( $filter->getMustNot(), Select::SQL_AND, - !$isNegation, - $queryContainer + !$isNegation ); $queries = [ @@ -115,14 +111,14 @@ private function processBoolFilter(RequestFilterInterface $filter, $isNegation, * @param \Magento\Framework\Search\Request\FilterInterface[] $filters * @param string $unionOperator * @param bool $isNegation - * @param QueryContainer $queryContainer * @return string */ - private function buildFilters(array $filters, $unionOperator, $isNegation, QueryContainer $queryContainer) + private function buildFilters(array $filters, $unionOperator, $isNegation) { $queries = []; foreach ($filters as $filter) { - $queries[] = $this->processFilter($filter, $isNegation, $queryContainer); + $filterQuery = $this->processFilter($filter, $isNegation); + $queries[] = $this->conditionManager->wrapBrackets($filterQuery); } return $this->conditionManager->combineQueries($queries, $unionOperator); } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/BuilderInterface.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/BuilderInterface.php index ee0d5b83d8dd1..ed3c7b6b05909 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/BuilderInterface.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/BuilderInterface.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\Search\Adapter\Mysql\Filter; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface as RequestFilterInterface; interface BuilderInterface @@ -13,8 +12,7 @@ interface BuilderInterface /** * @param RequestFilterInterface $filter * @param string $conditionType - * @param QueryContainer $queryContainer * @return string */ - public function build(RequestFilterInterface $filter, $conditionType, QueryContainer $queryContainer); + public function build(RequestFilterInterface $filter, $conditionType); } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Preprocessor.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Preprocessor.php index 51bf992250d7f..cfdb0f0302071 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Preprocessor.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/Preprocessor.php @@ -6,7 +6,6 @@ namespace Magento\Framework\Search\Adapter\Mysql\Filter; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface; class Preprocessor implements PreprocessorInterface @@ -27,7 +26,7 @@ public function __construct(ConditionManager $conditionManager) /** * {@inheritdoc} */ - public function process(FilterInterface $filter, $isNegation, $query, QueryContainer $queryContainer) + public function process(FilterInterface $filter, $isNegation, $query) { return $query; } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/PreprocessorInterface.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/PreprocessorInterface.php index b11a69eaf6f3b..8734185ca5bdd 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/PreprocessorInterface.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Filter/PreprocessorInterface.php @@ -5,7 +5,6 @@ */ namespace Magento\Framework\Search\Adapter\Mysql\Filter; -use Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer; use Magento\Framework\Search\Request\FilterInterface; interface PreprocessorInterface @@ -14,8 +13,7 @@ interface PreprocessorInterface * @param FilterInterface $filter * @param bool $isNegation * @param string $query - * @param QueryContainer $queryContainer * @return string */ - public function process(FilterInterface $filter, $isNegation, $query, QueryContainer $queryContainer); + public function process(FilterInterface $filter, $isNegation, $query); } diff --git a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php index 1a1bfb83183f9..3eaea10754558 100644 --- a/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php +++ b/lib/internal/Magento/Framework/Search/Adapter/Mysql/Mapper.php @@ -301,7 +301,7 @@ private function processFilterQuery( $scoreBuilder->endQuery($query->getBoost()); break; case FilterQuery::REFERENCE_FILTER: - $filterCondition = $this->filterBuilder->build($query->getReference(), $conditionType, $queryContainer); + $filterCondition = $this->filterBuilder->build($query->getReference(), $conditionType); if ($filterCondition) { $select->where($filterCondition); } diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Filter/BuilderTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Filter/BuilderTest.php index 8c1cc50ded322..5467961377277 100644 --- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Filter/BuilderTest.php +++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/Filter/BuilderTest.php @@ -6,6 +6,7 @@ namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql\Filter; +use Magento\Framework\DB\Select; use Magento\Framework\Search\Adapter\Mysql\Filter\PreprocessorInterface; use Magento\Framework\Search\Adapter\Mysql\ConditionManager; use Magento\Framework\Search\Request\FilterInterface; @@ -101,7 +102,7 @@ function (FilterInterface $filter, $isNegation) { $filter->getTo() ); } - $unionOperator = $isNegation ? \Zend_Db_Select::SQL_OR : \Zend_Db_Select::SQL_AND; + $unionOperator = $isNegation ? Select::SQL_OR : Select::SQL_AND; return $this->conditionManager->combineQueries([$fromCondition, $toCondition], $unionOperator); } @@ -161,10 +162,7 @@ function ($filter, $isNegation, $queryString) { */ public function testBuildFilter($filter, $conditionType, $expectedResult) { - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - $actualResult = $this->builder->build($filter, $conditionType, $queryContainer); + $actualResult = $this->builder->build($filter, $conditionType); $this->assertEquals($expectedResult, $actualResult); } @@ -285,7 +283,7 @@ public function buildBoolFilterDataProvider() [] // mustNot ), 'conditionType' => RequestBoolQuery::QUERY_CONDITION_MUST, - 'expectedResult' => '((term1 = 1) AND (range1 >= 0 AND range1 < 10))', + 'expectedResult' => '(((term1 = 1)) AND ((range1 >= 0 AND range1 < 10)))', ], 'boolFilterWithShould' => [ 'filter' => $this->createBoolFilter( @@ -297,7 +295,7 @@ public function buildBoolFilterDataProvider() [] // mustNot ), 'conditionType' => RequestBoolQuery::QUERY_CONDITION_MUST, - 'expectedResult' => '(((term1 = 1) OR (range1 >= 0 AND range1 < 10)))', + 'expectedResult' => '((((term1 = 1)) OR ((range1 >= 0 AND range1 < 10))))', ], 'boolFilterWithMustNot' => [ 'filter' => $this->createBoolFilter( @@ -309,7 +307,7 @@ public function buildBoolFilterDataProvider() ] ), 'conditionType' => RequestBoolQuery::QUERY_CONDITION_MUST, - 'expectedResult' => '(((term1 != 1) AND (range1 < 0 OR range1 >= 10)))', + 'expectedResult' => '((((term1 != 1)) AND ((range1 < 0 OR range1 >= 10))))', ], 'boolFilterWithAllFields' => [ 'filter' => $this->createBoolFilter( @@ -327,9 +325,9 @@ public function buildBoolFilterDataProvider() ] ), 'conditionType' => RequestBoolQuery::QUERY_CONDITION_MUST, - 'expectedResult' => '((term1 = 1) AND (range1 >= 0 AND range1 < 10)' - . ' AND ((term2 = 1) OR (range2 >= 0 AND range2 < 10))' - . ' AND ((term3 != 1) AND (range3 < 0 OR range3 >= 10)))', + 'expectedResult' => '(((term1 = 1)) AND ((range1 >= 0 AND range1 < 10))' + . ' AND (((term2 = 1)) OR ((range2 >= 0 AND range2 < 10)))' + . ' AND (((term3 != 1)) AND ((range3 < 0 OR range3 >= 10))))' ], 'boolFilterInBoolFilter' => [ 'filter' => $this->createBoolFilter( @@ -361,14 +359,12 @@ public function buildBoolFilterDataProvider() ] ), 'conditionType' => RequestBoolQuery::QUERY_CONDITION_MUST, - 'expectedResult' => '((term1 = 1) AND (range1 >= 0 AND range1 < 10)' - . ' AND ((term2 = 1) OR (range2 >= 0 AND range2 < 10))' - . ' AND ((term3 != 1) AND (range3 < 0 OR range3 >= 10)' - . ' AND ((term4 != 1) AND (range4 < 0 OR range4 >= 10)' - . ' AND ((term5 != 1) OR (range5 < 0 OR range5 >= 10))' - . ' AND ((term6 = 1) AND (range6 >= 0 AND range6 < 10)))' - . '))', - + 'expectedResult' => '(((term1 = 1)) AND ((range1 >= 0 AND range1 < 10))' + . ' AND (((term2 = 1)) OR ((range2 >= 0 AND range2 < 10)))' + . ' AND (((term3 != 1)) AND ((range3 < 0 OR range3 >= 10))' + . ' AND ((((term4 != 1)) AND ((range4 < 0 OR range4 >= 10))' + . ' AND (((term5 != 1)) OR ((range5 < 0 OR range5 >= 10)))' + . ' AND (((term6 = 1)) AND ((range6 >= 0 AND range6 < 10)))))))', ], 'boolEmpty' => [ 'filter' => $this->createBoolFilter([], [], []), @@ -415,9 +411,6 @@ public function testUnknownFilterType() $filter->expects($this->any()) ->method('getType') ->will($this->returnValue('unknownType')); - $queryContainer = $this->getMockBuilder('\Magento\Framework\Search\Adapter\Mysql\Query\QueryContainer') - ->disableOriginalConstructor() - ->getMock(); - $this->builder->build($filter, RequestBoolQuery::QUERY_CONDITION_MUST, $queryContainer); + $this->builder->build($filter, RequestBoolQuery::QUERY_CONDITION_MUST); } } From dd2342d122c7d99b4229f8f15361dce041381536 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Thu, 17 Sep 2015 13:40:03 +0300 Subject: [PATCH 5/6] MAGETWO-42698: Product Listing - Selected filters on layered navigation are removed after sorting --- app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php index 29be0868558ee..e4b335cc9d6d4 100644 --- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php +++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php @@ -376,7 +376,7 @@ public function getPagerUrl($params = []) { $urlParams = []; $urlParams['_current'] = true; - $urlParams['_escape'] = true; + $urlParams['_escape'] = false; $urlParams['_use_rewrite'] = true; $urlParams['_query'] = $params; return $this->getUrl('*/*/*', $urlParams); From 6f36ffa4cfd66374e57546a166163518127a0e17 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 21 Sep 2015 10:14:33 +0300 Subject: [PATCH 6/6] MAGETWO-42577: L4 Integration Test Fails --- .../_files/attribute_with_option_rollback.php | 26 ++++++++++++++++++- .../_files/filterable_attribute_rollback.php | 25 +++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php index e37865c2a87ec..bee61d500a879 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php @@ -14,7 +14,31 @@ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( 'Magento\Catalog\Model\Resource\Eav\Attribute' ); -$attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'attribute_with_option'); +$attribute->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'attribute_with_option'); + +/* Delete simple products per each option */ +/** @var $options \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */ +$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' +); +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +$options->setAttributeFilter($attribute->getId()); + +foreach ($options as $option) { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); + $product = $product->loadByAttribute('sku', 'simple_product_' . $option->getId()); + if ($product->getId()) { + $product->delete(); + } +} + if ($attribute->getId()) { $attribute->delete(); } + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php index 04a82aafb8219..24cf533ddf5ca 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Search/_files/filterable_attribute_rollback.php @@ -14,7 +14,27 @@ $attribute = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( 'Magento\Catalog\Model\Resource\Eav\Attribute' ); -$attribute->loadByCode($installer->getEntityTypeId('catalog_product'), 'select_attribute'); +$attribute->loadByCode(\Magento\Catalog\Model\Product::ENTITY, 'select_attribute'); + +/** @var $selectOptions \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */ +$selectOptions = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + 'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection' +); +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry'); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$selectOptions->setAttributeFilter($attribute->getId()); +/* Delete simple products per each select(dropdown) option */ +foreach ($selectOptions as $option) { + /** @var $product \Magento\Catalog\Model\Product */ + $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product'); + $product = $product->loadByAttribute('sku', 'simple_product_' . $option->getId()); + if ($product->getId()) { + $product->delete(); + } +} if ($attribute->getId()) { $attribute->delete(); } @@ -23,3 +43,6 @@ if ($attribute->getId()) { $attribute->delete(); } + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false);