![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/amasty/shopby/Model/ResourceModel/Fulltext/ |
<?php /** * @author Amasty Team * @copyright Copyright (c) Amasty (https://www.amasty.com) * @package Improved Layered Navigation Base for Magento 2 */ namespace Amasty\Shopby\Model\ResourceModel\Fulltext; use Amasty\Shopby\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver; use Amasty\Shopby\Model\ResourceModel\Fulltext\Collection\SearchResultApplier; use Amasty\Shopby\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolver; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver as MysqlSearchCriteriaResolver; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface; use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolver as MysqlTotalRecordsResolver; use Magento\CatalogSearch\Model\Search\RequestGenerator; use Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver as ElasticSearchCriteriaResolver; use Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolver as ElasticTotalRecordsResolver; use Magento\Framework\Api\Search\SearchCriteria; use Amasty\Shopby\Model\Search\SearchCriteriaBuilderProvider; use Magento\Framework\Api\Search\SearchResultFactory; use Magento\Framework\Api\Search\SearchResultInterface; use Magento\Framework\DB\Select; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Search\EngineResolverInterface; use Magento\Framework\Search\Request\EmptyRequestDataException; use Magento\Framework\Search\Request\NonExistingRequestNameException; use Magento\Framework\Exception\StateException; class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection { public const MYSQL_ENGINE = 'mysql'; public const LIVESEARCH_ENGINE = 'livesearch'; /** * @var SearchCriteriaBuilderProvider */ private $searchCriteriaBuilderProvider; /** * @var \Magento\Framework\Api\FilterBuilder */ private $filterBuilder; /** * @var SearchCriteriaResolver */ private $searchCriteriaResolver; /** * @var array */ private $searchOrders; /** * @var \Magento\Framework\Api\Search\SearchResultInterface */ private $searchResult; /** * @var \Magento\Search\Api\SearchInterface */ private $search; /** * @var TotalRecordsResolver */ private $totalRecordsResolver; /** * @var SearchResultApplier */ private $searchResultApplier; /** * @var string */ private $queryText; /** * @var string */ private $searchRequestName; /** * @var SearchCriteriaBuilderProvider */ private $memCriteriaBuilderProvider; /** * @var EngineResolverInterface */ private $engineResolver; /** * @var SearchResultFactory */ private $searchResultFactory; public function __construct( \Magento\Framework\Data\Collection\EntityFactory $entityFactory, \Psr\Log\LoggerInterface $logger, \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Eav\Model\Config $eavConfig, \Magento\Framework\App\ResourceConnection $resource, \Magento\Eav\Model\EntityFactory $eavEntityFactory, \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, \Magento\Framework\Validator\UniversalFactory $universalFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Module\Manager $moduleManager, \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, \Magento\Catalog\Model\ResourceModel\Url $catalogUrl, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Customer\Model\Session $customerSession, \Magento\Framework\Stdlib\DateTime $dateTime, \Magento\Customer\Api\GroupManagementInterface $groupManagement, \Magento\Framework\Api\FilterBuilder $filterBuilder, SearchCriteriaBuilderProvider $searchCriteriaBuilderProvider, \Magento\Search\Api\SearchInterface $search, TotalRecordsResolver $totalRecordsResolver, SearchCriteriaResolver $searchCriteriaResolver, SearchResultApplier $searchResultApplier, EngineResolverInterface $engineResolver, SearchResultFactory $searchResultFactory, $connection = null, $searchRequestName = 'catalog_view_container' ) { $this->searchRequestName = $searchRequestName; parent::__construct( $entityFactory, $logger, $fetchStrategy, $eventManager, $eavConfig, $resource, $eavEntityFactory, $resourceHelper, $universalFactory, $storeManager, $moduleManager, $catalogProductFlatState, $scopeConfig, $productOptionFactory, $catalogUrl, $localeDate, $customerSession, $dateTime, $groupManagement, $connection ); $this->filterBuilder = $filterBuilder; $this->searchCriteriaBuilderProvider = $searchCriteriaBuilderProvider; $this->search = $search; $this->totalRecordsResolver = $totalRecordsResolver; $this->searchCriteriaResolver = $searchCriteriaResolver; $this->searchResultApplier = $searchResultApplier; $this->engineResolver = $engineResolver; $this->searchResultFactory = $searchResultFactory; } /** * Add search query filter * * @param string $query * @return $this */ public function addSearchFilter($query) { $this->queryText = trim($this->queryText . ' ' . $query); return $this; } public function getSearchCriteria(?array $attributeCodesForRemove): SearchCriteria { $searchCriteriaBuilderProvider = clone $this->searchCriteriaBuilderProvider; $this->prepareSearchTermFilter($searchCriteriaBuilderProvider); $this->preparePriceAggregation($searchCriteriaBuilderProvider); if (is_array($attributeCodesForRemove) && !empty($attributeCodesForRemove)) { foreach ($attributeCodesForRemove as $code) { $searchCriteriaBuilderProvider->removeFilter($code); } } return $this->getSearchCriteriaResolver($searchCriteriaBuilderProvider)->resolve(); } /** * @param array $filter * @return SearchCriteria */ public function getMemSearchCriteria(array $filter = []): SearchCriteria { $searchCriteriaBuilderProvider = clone $this->getMemSearchCriteriaBuilderProvider(); foreach ($filter as $field => $value) { $searchCriteriaBuilderProvider->addFilter($field, $value); } return $this->getSearchCriteriaResolver($searchCriteriaBuilderProvider)->resolve(); } /** * @return SearchCriteriaBuilderProvider */ private function getMemSearchCriteriaBuilderProvider() { if ($this->memCriteriaBuilderProvider === null) { $this->memCriteriaBuilderProvider = clone $this->searchCriteriaBuilderProvider; $this->memCriteriaBuilderProvider->addFilter('scope', $this->getStoreId()); if ($this->queryText) { $this->prepareSearchTermFilter($this->memCriteriaBuilderProvider); } } return $this->memCriteriaBuilderProvider; } /** * @throws LocalizedException */ protected function _renderFiltersBefore() { if ($this->isLoaded()) { return; } if ($this->searchRequestName !== 'quick_search_container' || strlen(trim((string)$this->queryText))) { $this->prepareSearchTermFilter($this->searchCriteriaBuilderProvider); $this->preparePriceAggregation($this->searchCriteriaBuilderProvider); $searchCriteria = $this->getSearchCriteriaResolver()->resolve(); try { $this->searchResult = $this->search->search($searchCriteria); $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve(); } catch (EmptyRequestDataException $e) { $this->searchResult = $this->createEmptyResult(); } catch (NonExistingRequestNameException $e) { $this->_logger->error($e->getMessage()); throw new LocalizedException(__('An error occurred. For details, see the error log.')); } } else { $this->searchResult = $this->createEmptyResult(); } $this->getSearchResultApplier($this->searchResult)->apply(); parent::_renderFiltersBefore(); } /** * @return SearchResultInterface */ private function createEmptyResult() { return $this->searchResultFactory->create()->setItems([]); } /** * @param SearchResultInterface $searchResult * @return MysqlTotalRecordsResolver|ElasticTotalRecordsResolver */ private function getTotalRecordsResolver(SearchResultInterface $searchResult) { return $this->totalRecordsResolver->getResolver(['searchResult' => $searchResult]); } private function prepareSearchTermFilter(SearchCriteriaBuilderProvider $searchCriteriaBuilderProvider): void { if ($this->queryText) { $searchCriteriaBuilderProvider->addFilter('search_term', $this->queryText); } } private function preparePriceAggregation(SearchCriteriaBuilderProvider $searchCriteriaBuilderProvider): void { $priceRangeCalculation = $this->_scopeConfig->getValue( \Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory::XML_PATH_RANGE_CALCULATION, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); if ($priceRangeCalculation) { $searchCriteriaBuilderProvider->addFilter('price_dynamic_algorithm', $priceRangeCalculation); } } /** * Set Order field * * @param string $attribute * @param string $dir * @return $this */ public function setOrder($attribute, $dir = Select::SQL_DESC) { $field = (string)$this->_getMappedField($attribute); $direction = strtoupper($dir) == self::SORT_ORDER_ASC ? self::SORT_ORDER_ASC : self::SORT_ORDER_DESC; $this->searchOrders[$field] = $direction; if ($this->isUseDefaultFilterStrategy()) { parent::setOrder($attribute, $dir); } return $this; } private function isUseDefaultFilterStrategy(): bool { return $this->engineResolver->getCurrentSearchEngine() == self::MYSQL_ENGINE; } private function isUseLiveSearchEngine(): bool { return $this->engineResolver->getCurrentSearchEngine() === self::LIVESEARCH_ENGINE; } /** * Stub method for compatibility with other search engines * * @return $this */ public function setGeneralDefaultQuery() { return $this; } /** * @param string $attribute * @param string $dir * @return \Magento\Catalog\Model\ResourceModel\Product\Collection */ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) { if ($this->isUseDefaultFilterStrategy()) { return parent::addAttributeToSort($attribute, $dir); } $this->setOrder($attribute, $dir); if ($attribute === 'position') { // additional sorting rule $this->sortById($dir); } return $this; } /** * @param $field * @param null $searchResult * @return array * @throws StateException */ public function getFacetedData($field, $searchResult = null) { if (!$searchResult) { $this->_renderFilters(); } $searchResult = $searchResult ?: $this->searchResult; $result = []; $aggregations = $searchResult->getAggregations(); // This behavior is for case with empty object when we got EmptyRequestDataException if (null !== $aggregations) { $bucket = $aggregations->getBucket($field . RequestGenerator::BUCKET_SUFFIX); if ($bucket) { foreach ($bucket->getValues() as $value) { $metrics = $value->getMetrics(); $result[$metrics['value']] = $metrics; } } else { throw new StateException(__("The bucket doesn't exist.")); } } return $result; } /** * @param array $visibility * @return $this|Collection */ public function setVisibility($visibility) { $this->addFieldToFilter('visibility', $visibility); if ($this->isUseDefaultFilterStrategy()) { parent::setVisibility($visibility); } return $this; } /** * @return $this */ protected function _renderFilters() { $this->_filters = []; return parent::_renderFilters(); } /** * Specify category filter for product collection * * @param \Magento\Catalog\Model\Category $category * @return $this */ public function addCategoryFilter(\Magento\Catalog\Model\Category $category) { if ($this->isUseDefaultFilterStrategy()) { $this->_productLimitationFilters['category_id'] = $category->getId(); } else { $this->setFlag('has_category_filter', true); $this->_productLimitationPrice(); } $this->addFieldToFilter('category_ids', $category->getId()); return $this; } /** * @param false $printQuery * @param false $logQuery * @return $this|Collection * @throws LocalizedException */ public function _loadEntities($printQuery = false, $logQuery = false) { $this->getEntity(); $currentSearchEngine = $this->engineResolver->getCurrentSearchEngine(); if ($this->_pageSize && $currentSearchEngine === self::MYSQL_ENGINE) { $this->getSelect()->limitPage($this->getCurPage(), $this->_pageSize); } $this->printLogQuery($printQuery, $logQuery); try { $query = $this->getSelect(); $rows = $this->_fetchAll($query); } catch (\Exception $e) { $this->printLogQuery(false, true, $query); throw $e; } $position = 0; foreach ($rows as $value) { if ($this->getFlag('has_category_filter')) { $value['cat_index_position'] = $position++; } $object = $this->getNewEmptyItem()->setData($value); $this->addItem($object); if (isset($this->_itemsById[$object->getId()])) { $this->_itemsById[$object->getId()][] = $object; } else { $this->_itemsById[$object->getId()] = [$object]; } } if ($this->getFlag('has_category_filter')) { $this->setFlag('has_category_filter', false); } return $this; } /** * @param mixed $field * @param null $condition * @return $this|Collection|\Magento\Framework\Data\Collection\AbstractDb */ public function addFieldToFilter($field, $condition = null) { if ($this->searchResult !== null) { throw new \RuntimeException('Illegal state'); } if (!is_array($condition) || !in_array(key($condition), ['from', 'to'], true)) { $this->searchCriteriaBuilderProvider->addFilter($field, $condition); } else { if (isset($condition['from'])) { $this->searchCriteriaBuilderProvider->addFilter("{$field}.from", $condition['from']); } if (isset($condition['to'])) { $this->searchCriteriaBuilderProvider->addFilter("{$field}.to", $condition['to']); } } return $this; } /** * @return $this */ protected function _beforeLoad() { // prevent sorting direction override if (empty($this->searchOrders['entity_id'])) { // additional sorting with the fewest priority $this->setOrder('entity_id'); } return parent::_beforeLoad(); } private function getSearchResultApplier(SearchResultInterface $searchResult): SearchResultApplierInterface { return $this->searchResultApplier->getApplier( [ 'collection' => $this, 'searchResult' => $searchResult, 'orders' => $this->_orders, 'size' => $this->getPageSize(), 'currentPage' => (int)$this->_curPage, ] ); } /** * @param SearchCriteriaBuilderProvider|null $searchCriteriaBuilder * @return MysqlSearchCriteriaResolver|ElasticSearchCriteriaResolver */ private function getSearchCriteriaResolver(SearchCriteriaBuilderProvider $searchCriteriaBuilderProvider = null) { $builder = $searchCriteriaBuilderProvider ? $searchCriteriaBuilderProvider->create() : $this->searchCriteriaBuilderProvider->create(); return $this->searchCriteriaResolver->getResolver( [ 'builder' => $builder, 'collection' => $this, 'searchRequestName' => $this->searchRequestName, 'currentPage' => (int)$this->_curPage, 'size' => $this->getPageSize(), 'orders' => $this->searchOrders, ] ); } /** * Additional sorting rule with search engine resolver. * * Resolve issue, when position of products is the same and ASC and DESC dir return the same value. */ private function sortById(string $dir): void { if ($this->isUseLiveSearchEngine()) { /** * Temporary disable additional sorting for Live Search. * * LiveSearch can't sort by product_id due error * "attribute product_id is not defined as sortable in magento admin". * product_id is mapped by Core but doesn't have sorting permissions. * * Sorting by entity_id is restricted in * @see \Magento\LiveSearchAdapter\Model\QueryArgumentProcessor\SortQueryArgumentProcessor */ return; } /** * Elastic search restrict to sort by entity_id. But allow to sort on mapped field. * * @see \Amasty\Shopby\Plugin\Elasticsearch\Model\Adapter\AdditionalFieldMapper */ $this->setOrder('product_id', $dir); } }