/**
  * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  * @return void
  */
 protected function setUp()
 {
     $this->select = $this->getMockBuilder('\\Magento\\Framework\\DB\\Select')->disableOriginalConstructor()->setMethods(['from', 'joinLeft', 'where', 'joinInner'])->getMock();
     $this->connection = $this->getMockBuilder('\\Magento\\Framework\\DB\\Adapter\\AdapterInterface')->disableOriginalConstructor()->setMethods(['select', 'quoteInto'])->getMockForAbstractClass();
     $this->connection->expects($this->once())->method('select')->will($this->returnValue($this->select));
     $this->resource = $this->getMockBuilder('\\Magento\\Framework\\App\\ResourceConnection')->disableOriginalConstructor()->setMethods(['getConnection', 'getTableName'])->getMock();
     $this->resource->expects($this->any())->method('getConnection')->will($this->returnValue($this->connection));
     $this->request = $this->getMockBuilder('\\Magento\\Framework\\Search\\RequestInterface')->disableOriginalConstructor()->setMethods(['getIndex', 'getDimensions', 'getQuery'])->getMockForAbstractClass();
     $this->config = $this->getMockBuilder('\\Magento\\Framework\\App\\Config\\ScopeConfigInterface')->disableOriginalConstructor()->setMethods(['isSetFlag'])->getMockForAbstractClass();
     $this->storeManager = $this->getMockBuilder('Magento\\Store\\Model\\StoreManagerInterface')->getMock();
     $this->scopeResolver = $this->getMockBuilder('\\Magento\\Framework\\Indexer\\ScopeResolver\\IndexScopeResolver')->disableOriginalConstructor()->getMock();
     $this->conditionManager = $this->getMockBuilder('\\Magento\\Framework\\Search\\Adapter\\Mysql\\ConditionManager')->setMethods(['combineQueries', 'wrapBrackets', 'generateCondition'])->disableOriginalConstructor()->getMock();
     $this->conditionManager->expects($this->any())->method('combineQueries')->willReturnCallback(function (array $queries, $expression) {
         return implode(' ' . $expression . ' ', $queries);
     });
     $this->conditionManager->expects($this->any())->method('wrapBrackets')->willReturnCallback(function ($expression) {
         return '(' . $expression . ')';
     });
     $this->conditionManager->expects($this->any())->method('generateCondition')->willReturnCallback(function ($left, $operator, $right) {
         return $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);
     $this->dimensionScopeResolver = $this->getMockForAbstractClass('\\Magento\\Framework\\App\\ScopeResolverInterface', [], '', false);
     $this->scopeInterface = $this->getMockForAbstractClass('\\Magento\\Framework\\App\\ScopeInterface', [], '', false);
     $this->stockConfiguration = $this->getMockBuilder('\\Magento\\CatalogInventory\\Api\\StockConfigurationInterface')->getMock();
     $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
     $this->target = $objectManagerHelper->getObject('Magento\\CatalogSearch\\Model\\Search\\IndexBuilder', ['resource' => $this->resource, 'config' => $this->config, 'storeManager' => $this->storeManager, 'conditionManager' => $this->conditionManager, 'scopeResolver' => $this->scopeResolver, 'tableMapper' => $this->tableMapper, 'dimensionScopeResolver' => $this->dimensionScopeResolver]);
     // Todo: \Magento\Framework\TestFramework\Unit\Helper\ObjectManager to do this automatically (MAGETWO-49793)
     $reflection = new \ReflectionClass(get_class($this->target));
     $reflectionProperty = $reflection->getProperty('stockConfiguration');
     $reflectionProperty->setAccessible(true);
     $reflectionProperty->setValue($this->target, $this->stockConfiguration);
 }
 /**
  * Build index query
  *
  * @param RequestInterface $request
  * @return Select
  */
 public function build(RequestInterface $request)
 {
     $searchIndexTable = $this->scopeResolver->resolve($request->getIndex(), $request->getDimensions());
     $select = $this->resource->getConnection()->select()->from(['search_index' => $searchIndexTable], ['entity_id' => 'entity_id'])->joinLeft(['cea' => $this->resource->getTableName('catalog_eav_attribute')], 'search_index.attribute_id = cea.attribute_id', []);
     $select = $this->tableMapper->addTables($select, $request);
     $select = $this->processDimensions($request, $select);
     $isShowOutOfStock = $this->config->isSetFlag('cataloginventory/options/show_out_of_stock', ScopeInterface::SCOPE_STORE);
     if ($isShowOutOfStock === false) {
         $select->joinLeft(['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')], 'search_index.entity_id = stock_index.product_id' . $this->resource->getConnection()->quoteInto(' AND stock_index.website_id = ?', $this->storeManager->getWebsite()->getId()), []);
         $select->where('stock_index.stock_status = ?', 1);
     }
     return $select;
 }
 /**
  * @param FilterInterface $filter
  * @param bool $isNegation
  * @param string $query
  * @return string
  */
 private function processQueryWithField(FilterInterface $filter, $isNegation, $query)
 {
     /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */
     $attribute = $this->config->getAttribute(Product::ENTITY, $filter->getField());
     if ($filter->getField() === 'price') {
         $resultQuery = str_replace($this->connection->quoteIdentifier('price'), $this->connection->quoteIdentifier('price_index.min_price'), $query);
     } elseif ($filter->getField() === 'category_ids') {
         return 'category_ids_index.category_id = ' . $filter->getValue();
     } elseif ($attribute->isStatic()) {
         $alias = $this->tableMapper->getMappingAlias($filter);
         $resultQuery = str_replace($this->connection->quoteIdentifier($attribute->getAttributeCode()), $this->connection->quoteIdentifier($alias . '.' . $attribute->getAttributeCode()), $query);
     } elseif ($filter->getType() === FilterInterface::TYPE_TERM && in_array($attribute->getFrontendInput(), ['select', 'multiselect'], true)) {
         $alias = $this->tableMapper->getMappingAlias($filter);
         if (is_array($filter->getValue())) {
             $value = sprintf('%s IN (%s)', $isNegation ? 'NOT' : '', implode(',', $filter->getValue()));
         } else {
             $value = ($isNegation ? '!' : '') . '= ' . $filter->getValue();
         }
         $resultQuery = sprintf('%1$s.value %2$s', $alias, $value);
     } else {
         $table = $attribute->getBackendTable();
         $select = $this->connection->select();
         $ifNullCondition = $this->connection->getIfNullSql('current_store.value', 'main_table.value');
         $currentStoreId = $this->scopeResolver->getScope()->getId();
         $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 = ?', Store::DEFAULT_STORE_ID)->having($query);
         $resultQuery = 'search_index.entity_id IN (
             select entity_id from  ' . $this->conditionManager->wrapBrackets($select) . ' as filter
         )';
     }
     return $resultQuery;
 }
 /**
  * @param FilterInterface $filter
  * @param bool $isNegation
  * @return string
  */
 private function processTermSelect(FilterInterface $filter, $isNegation)
 {
     $alias = $this->tableMapper->getMappingAlias($filter);
     if (is_array($filter->getValue())) {
         $value = sprintf('%s IN (%s)', $isNegation ? 'NOT' : '', implode(',', $filter->getValue()));
     } else {
         $value = ($isNegation ? '!' : '') . '= ' . $filter->getValue();
     }
     $resultQuery = sprintf('%1$s.value %2$s', $alias, $value);
     return $resultQuery;
 }
 /**
  * @dataProvider testTermFilterDataProvider
  */
 public function testProcessTermFilter($frontendInput, $fieldValue, $isNegation, $expected)
 {
     $this->config->expects($this->exactly(1))->method('getAttribute')->with(\Magento\Catalog\Model\Product::ENTITY, 'termField')->will($this->returnValue($this->attribute));
     $this->attribute->expects($this->once())->method('isStatic')->will($this->returnValue(false));
     $this->filter->expects($this->once())->method('getType')->willReturn(FilterInterface::TYPE_TERM);
     $this->attribute->expects($this->once())->method('getFrontendInput')->willReturn($frontendInput);
     $this->tableMapper->expects($this->once())->method('getMappingAlias')->willReturn('termAttrAlias');
     $this->filter->expects($this->exactly(3))->method('getField')->willReturn('termField');
     $this->filter->expects($this->exactly(2))->method('getValue')->willReturn($fieldValue);
     $actualResult = $this->target->process($this->filter, $isNegation, 'This filter is not depends on used query');
     $this->assertSame($expected, $this->removeWhitespaces($actualResult));
 }
 public function testAddBoolFilterWithBoolFiltersInside()
 {
     $this->createAttributeMock('must1', null, null, 101, 'select', 0);
     $this->createAttributeMock('should1', null, null, 102, 'select', 1);
     $this->createAttributeMock('mustNot1', null, null, 103, 'select', 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');
 }