Produced by Araxis Merge on 2020-08-14 00:01:32 +0000. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.
| # | Location | File | Last Modified |
|---|---|---|---|
| 1 | Porto v3.2.3/Magento 2.x/Porto Theme/app/code/Mageplaza/LayeredNavigation/Model/ResourceModel/Fulltext | Collection.php | 2018-10-10 20:33:20 +0000 |
| 2 | Porto v3.2.4/Magento 2.x/Porto Theme/app/code/Mageplaza/LayeredNavigation/Model/ResourceModel/Fulltext | Collection.php | 2020-06-26 11:11:14 +0000 |
| Description | Between Files 1 and 2 | |
|---|---|---|
| Text Blocks | Lines | |
| Unchanged | 69 | 802 |
| Changed | 40 | 233 |
| Inserted | 23 | 119 |
| Removed | 5 | 6 |
| Whitespace | Consecutive whitespace is treated as a single space |
|---|---|
| Character case | Differences in character case are significant |
| Line endings | Differences in line endings (CR and LF characters) are ignored |
| CR/LF characters | Not shown in the comparison detail |
| Active line-pairing rules |
No regular expressions were active.
| 1 | <?php | 1 | <?php | |||
| 2 | /** | 2 | /** | |||
| 3 | * Mageplaza | 3 | * Mageplaza | |||
| 4 | * | 4 | * | |||
| 5 | * NOTICE OF LICENSE | 5 | * NOTICE OF LICENSE | |||
| 6 | * | 6 | * | |||
| 7 | * This source file is subject to the Mageplaza.com license that is | 7 | * This source file is subject to the Mageplaza.com license that is | |||
| 8 | * available through the world-wide-web at this URL: | 8 | * available through the world-wide-web at this URL: | |||
| 9 | * https://www.mageplaza.com/LICENSE.txt | 9 | * https://www.mageplaza.com/LICENSE.txt | |||
| 10 | * | 10 | * | |||
| 11 | * DISCLAIMER | 11 | * DISCLAIMER | |||
| 12 | * | 12 | * | |||
| 13 | * Do not edit or add to this file if you wish to upgrade this extension to newer | 13 | * Do not edit or add to this file if you wish to upgrade this extension to newer | |||
| 14 | * version in the future. | 14 | * version in the future. | |||
| 15 | * | 15 | * | |||
| 16 | * @category Mageplaza | 16 | * @category Mageplaza | |||
| 17 | * @package Mageplaza_LayeredNavigation | 17 | * @package Mageplaza_LayeredNavigation | |||
| 18 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) | 18 | * @copyright Copyright (c) Mageplaza (https://www.mageplaza.com/) | |||
| 19 | * @license https://www.mageplaza.com/LICENSE.txt | 19 | * @license https://www.mageplaza.com/LICENSE.txt | |||
| 20 | */ | 20 | */ | |||
| 21 | 21 | |||||
| 22 | namespace Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext; | 22 | namespace Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext; | |||
| 23 | 23 | |||||
| 24 | use Exception; | |||||
| 25 | use Magento\Catalog\Model\Category; | |||||
| 26 | use Magento\Catalog\Model\Indexer\Product\Flat\State; | |||||
| 27 | use Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory; | |||||
| 28 | use Magento\Catalog\Model\Product\OptionFactory; | |||||
| 29 | use Magento\Catalog\Model\ResourceModel\Helper; | |||||
| 30 | use Magento\Catalog\Model\ResourceModel\Url; | |||||
| 24 | use Magento\CatalogSearch\Model\Search\RequestGenerator; | 31 | use Magento\CatalogSearch\Model\Search\RequestGenerator; | |||
| 32 | use Magento\Config\Model\Config\Backend\Admin\Custom; | |||||
| 33 | use Magento\Customer\Api\GroupManagementInterface; | |||||
| 34 | use Magento\Customer\Model\Session; | |||||
| 35 | use Magento\Eav\Model\Config; | |||||
| 36 | use Magento\Eav\Model\EntityFactory as EavEntityFactory; | |||||
| 37 | use Magento\Framework\Api\FilterBuilder; | |||||
| 38 | use Magento\Framework\Api\Search\SearchResultInterface; | |||||
| 39 | use Magento\Framework\App\Config\ScopeConfigInterface; | |||||
| 25 | use Magento\Framework\App\ObjectManager; | 40 | use Magento\Framework\App\ObjectManager; | |||
| 41 | use Magento\Framework\App\Request\Http; | |||||
| 42 | use Magento\Framework\App\ResourceConnection; | |||||
| 43 | use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; | |||||
| 44 | use Magento\Framework\Data\Collection\EntityFactory; | |||||
| 45 | use Magento\Framework\DB\Adapter\AdapterInterface; | |||||
| 26 | use Magento\Framework\DB\Select; | 46 | use Magento\Framework\DB\Select; | |||
| 47 | use Magento\Framework\Event\ManagerInterface; | |||||
| 27 | use Magento\Framework\Exception\LocalizedException; | 48 | use Magento\Framework\Exception\LocalizedException; | |||
| 28 | use Magento\Framework\Exception\StateException; | 49 | use Magento\Framework\Exception\StateException; | |||
| 50 | use Magento\Framework\Module\Manager; | |||||
| 29 | use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage; | 51 | use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage; | |||
| 52 | use Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory; | |||||
| 53 | use Magento\Framework\Stdlib\DateTime; | |||||
| 54 | use Magento\Framework\Stdlib\DateTime\TimezoneInterface; | |||||
| 55 | use Magento\Framework\Validator\UniversalFactory; | |||||
| 56 | use Magento\Search\Api\SearchInterface; | |||||
| 57 | use Magento\Store\Model\ScopeInterface; | |||||
| 58 | use Magento\Store\Model\StoreManagerInterface; | |||||
| 59 | use Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder; | |||||
| 60 | use Psr\Log\LoggerInterface; | |||||
| 61 | use RuntimeException; | |||||
| 62 | use Zend_Db_Exception; | |||||
| 63 | use Zend_Db_Expr; | |||||
| 30 | 64 | |||||
| 31 | /** | 65 | /** | |||
| 32 | * Class Collection | 66 | * Class Collection | |||
| 33 | * @package Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext | 67 | * @package Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext | |||
| 34 | */ | 68 | */ | |||
| 35 | class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection | 69 | class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection | |||
| 36 | { | 70 | { | |||
| 37 | /** @var \Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext\Collection|null Clone collection */ | 71 | /** @var | |||
| 38 | public $collectionClone = null; | 72 | public $collectionClone = null; | |||
| 39 | 73 | |||||
| 40 | /** @var string */ | 74 | /** @var string */ | |||
| 41 | private $queryText; | 75 | private $queryText; | |||
| 42 | 76 | |||||
| 43 | /** @var string|null */ | 77 | /** @var string|null */ | |||
| 44 | private $order = null; | 78 | private $order = null; | |||
| 45 | 79 | |||||
| 46 | /** @var string */ | 80 | /** @var string */ | |||
| 47 | private $searchRequestName; | 81 | private $searchRequestName; | |||
| 48 | 82 | |||||
| 49 | /** @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory */ | 83 | /** @var | |||
| 50 | private $temporaryStorageFactory; | 84 | private $temporaryStorageFactory; | |||
| 51 | 85 | |||||
| 52 | /** @var \Magento\Search\Api\SearchInterface */ | 86 | /** @var | |||
| 53 | private $search; | 87 | private $search; | |||
| 54 | 88 | |||||
| 55 | /** @var \Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder */ | 89 | /** @var | |||
| 56 | private $searchCriteriaBuilder; | 90 | private $searchCriteriaBuilder; | |||
| 57 | 91 | |||||
| 58 | /** @var \Magento\Framework\Api\Search\SearchResultInterface */ | 92 | /** @var | |||
| 59 | private $searchResult; | 93 | private $searchResult; | |||
| 60 | 94 | |||||
| 61 | /** @var \Magento\Framework\Api\FilterBuilder */ | 95 | /** @var | |||
| 62 | private $filterBuilder; | 96 | private $filterBuilder; | |||
| 63 | 97 | |||||
| 64 | /** | 98 | /** | |||
| 99 | * @var Http | |||||
| 100 | */ | |||||
| 101 | protected $request; | |||||
| 102 | ||||||
| 103 | /** | |||||
| 104 | * @var array | |||||
| 105 | */ | |||||
| 106 | private $searchOrders; | |||||
| 107 | ||||||
| 108 | /** | |||||
| 65 | * Collection constructor. | 109 | * Collection constructor. | |||
| 66 | * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory | 110 | * | |||
| 67 | * @param \Psr\Log\LoggerInterface $logger | 111 | * @param | |||
| 68 | * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy | 112 | * @param | |||
| 69 | * @param \Magento\Framework\Event\ManagerInterface $eventManager | 113 | * @param | |||
| 70 | * @param \Magento\Eav\Model\Config $eavConfig | 114 | * @param | |||
| 71 | * @param \Magento\Framework\App\ResourceConnection $resource | 115 | * @param | |||
| 72 | * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory | 116 | * @param | |||
| 73 | * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper | 117 | * @param EavEntityFactory $eavEntityFactory | |||
| 74 | * @param \Magento\Framework\Validator\UniversalFactory $universalFactory | 118 | * @param | |||
| 75 | * @param \Magento\Store\Model\StoreManagerInterface $storeManager | 119 | * @param | |||
| 76 | * @param \Magento\Framework\Module\Manager $moduleManager | 120 | * @param | |||
| 77 | * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState | 121 | * @param | |||
| 78 | * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig | 122 | * @param State $productFlatState | |||
| 79 | * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory | 123 | * @param | |||
| 80 | * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl | 124 | * @param | |||
| 81 | * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate | 125 | * @param | |||
| 82 | * @param \Magento\Customer\Model\Session $customerSession | 126 | * @param | |||
| 83 | * @param \Magento\Framework\Stdlib\DateTime $dateTime | 127 | * @param | |||
| 84 | * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement | 128 | * @param | |||
| 85 | * @param \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory | 129 | * @param | |||
| 86 | * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection | 130 | * @param TemporaryStorageFactory $tempStorageFactory | |||
| 131 | * @param | |||||
| 87 | * @param string $searchRequestName | 132 | * @param string $searchRequestName | |||
| 133 | * @param Http $request | |||||
| 88 | */ | 134 | */ | |||
| 89 | public function __construct( | 135 | public function __construct( | |||
| 90 | \Magento\Framework\Data\Collection\EntityFactory $entityFactory, | 136 | | |||
| 91 | \Psr\Log\LoggerInterface $logger, | 137 | | |||
| 92 | \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy, | 138 | | |||
| 93 | \Magento\Framework\Event\ManagerInterface $eventManager, | 139 | | |||
| 94 | \Magento\Eav\Model\Config $eavConfig, | 140 | | |||
| 95 | \Magento\Framework\App\ResourceConnection $resource, | 141 | | |||
| 96 | \Magento\Eav\Model\EntityFactory $eavEntityFactory, | 142 | EavEntityFactory $eavEntityFactory, | |||
| 97 | \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper, | 143 | | |||
| 98 | \Magento\Framework\Validator\UniversalFactory $universalFactory, | 144 | | |||
| 99 | \Magento\Store\Model\StoreManagerInterface $storeManager, | 145 | | |||
| 100 | \Magento\Framework\Module\Manager $moduleManager, | 146 | | |||
| 101 | \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState, | 147 | State $productFlatState, | |||
| 102 | \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, | 148 | | |||
| 103 | \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory, | 149 | | |||
| 104 | \Magento\Catalog\Model\ResourceModel\Url $catalogUrl, | 150 | Url $catalogUrl, | |||
| 105 | \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, | 151 | | |||
| 106 | \Magento\Customer\Model\Session $customerSession, | 152 | | |||
| 107 | \Magento\Framework\Stdlib\DateTime $dateTime, | 153 | | |||
| 108 | \Magento\Customer\Api\GroupManagementInterface $groupManagement, | 154 | | |||
| 109 | \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory, | 155 | TemporaryStorageFactory $tempStorageFactory, | |||
| 110 | \Magento\Framework\DB\Adapter\AdapterInterface $connection = null, | 156 | Http $request, | |||
| 157 | | |||||
| 111 | $searchRequestName = 'catalog_view_container' | 158 | $searchRequestName = 'catalog_view_container' | |||
| 112 | ) | 159 | ) { | |||
| 113 | { | |||||
| 114 | parent::__construct( | 160 | parent::__construct( | |||
| 115 | $entityFactory, | 161 | $entityFactory, | |||
| 116 | $logger, | 162 | $logger, | |||
| 117 | $fetchStrategy, | 163 | $fetchStrategy, | |||
| 118 | $eventManager, | 164 | $eventManager, | |||
| 119 | $eavConfig, | 165 | $eavConfig, | |||
| 120 | $resource, | 166 | $resource, | |||
| 121 | $eavEntityFactory, | 167 | $eavEntityFactory, | |||
| 122 | $resourceHelper, | 168 | $resourceHelper, | |||
| 123 | $universalFactory, | 169 | $universalFactory, | |||
| 124 | $storeManager, | 170 | $storeManager, | |||
| 125 | $moduleManager, | 171 | $moduleManager, | |||
| 126 | $catalogProductFlatState, | 172 | $productFlatState, | |||
| 127 | $scopeConfig, | 173 | $scopeConfig, | |||
| 128 | $productOptionFactory, | 174 | $productOptionFactory, | |||
| 129 | $catalogUrl, | 175 | $catalogUrl, | |||
| 130 | $localeDate, | 176 | $localeDate, | |||
| 131 | $customerSession, | 177 | $customerSession, | |||
| 132 | $dateTime, | 178 | $dateTime, | |||
| 133 | $groupManagement, | 179 | $groupManagement, | |||
| 134 | $connection | 180 | $connection | |||
| 135 | ); | 181 | ); | |||
| 136 | $this->temporaryStorageFactory = $temporaryStorageFactory; | 182 | ||||
| 183 | $this->temporaryStorageFactory = $tempStorageFactory; | |||||
| 137 | $this->searchRequestName = $searchRequestName; | 184 | $this->searchRequestName = $searchRequestName; | |||
| 185 | $this->request = $request; | |||||
| 138 | } | 186 | } | |||
| 139 | 187 | |||||
| 140 | /** | 188 | /** | |||
| 141 | * MP LayerNavigation Clone collection | 189 | * MP LayerNavigation Clone collection | |||
| 142 | * | 190 | * | |||
| 143 | * @return \Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext\Collection|null | 191 | * @return Collection | |||
| 144 | */ | 192 | */ | |||
| 145 | public function getCollectionClone() | 193 | public function getCollectionClone() | |||
| 146 | { | 194 | { | |||
| 147 | if ($this->collectionClone === null) { | 195 | if ($this->collectionClone === null) { | |||
| 148 | $this->collectionClone = clone $this; | 196 | $this->collectionClone = clone $this; | |||
| 149 | $this->collectionClone->setSearchCriteriaBuilder($this->searchCriteriaBuilder->cloneObject()); | 197 | $this->collectionClone->setSearchCriteriaBuilder($this->searchCriteriaBuilder->cloneObject()); | |||
| 150 | } | 198 | } | |||
| 151 | 199 | |||||
| 152 | $searchCriterialBuilder = $this->collectionClone->getSearchCriteriaBuilder()->cloneObject(); | 200 | $searchCriterialBuilder = $this->collectionClone->getSearchCriteriaBuilder()->cloneObject(); | |||
| 153 | 201 | |||||
| 154 | /** @var \Mageplaza\LayeredNavigation\Model\ResourceModel\Fulltext\Collection $collectionClone */ | 202 | /** @var | |||
| 155 | $collectionClone = clone $this->collectionClone; | 203 | $collectionClone = clone $this->collectionClone; | |||
| 156 | $collectionClone->setSearchCriteriaBuilder($searchCriterialBuilder); | 204 | $collectionClone->setSearchCriteriaBuilder($searchCriterialBuilder); | |||
| 157 | 205 | |||||
| 158 | return $collectionClone; | 206 | return $collectionClone; | |||
| 159 | } | 207 | } | |||
| 160 | 208 | |||||
| 161 | /** | 209 | /** | |||
| 162 | * MP LayerNavigation Add multi-filter categories | 210 | * MP LayerNavigation Add multi-filter categories | |||
| 163 | * | 211 | * | |||
| 164 | * @param $categories | 212 | * @param $categories | |||
| 213 | * | |||||
| 165 | * @return $this | 214 | * @return $this | |||
| 166 | */ | 215 | */ | |||
| 167 | public function addLayerCategoryFilter($categories) | 216 | public function addLayerCategoryFilter($categories) | |||
| 168 | { | 217 | { | |||
| 169 | if ( | 218 | if ((strpos($this->getSearchEngine(), 'elasticsearch') !== false) | |||
| 219 | || $this->getSearchEngine() === 'amasty_elastic' | |||||
| 220 | ) { | |||||
| 170 | $this->addFieldToFilter('category_ids', ['in' => $categories]); | 221 | $this->addFieldToFilter('category_ids', ['in' => $categories]); | |||
| 171 | } else { | 222 | } else { | |||
| 172 | $this->addFieldToFilter('category_ids', implode(',', $categories)); | 223 | $this->addFieldToFilter('category_ids', implode(',', $categories)); | |||
| 173 | } | 224 | } | |||
| 174 | 225 | |||||
| 175 | return $this; | 226 | return $this; | |||
| 176 | } | 227 | } | |||
| 177 | 228 | |||||
| 178 | /** | 229 | /** | |||
| 179 | * MP LayerNavigation remove filter to load option item data | 230 | * MP LayerNavigation remove filter to load option item data | |||
| 180 | * | 231 | * | |||
| 181 | * @param $attributeCode | 232 | * @param $attributeCode | |||
| 233 | * | |||||
| 182 | * @return $this | 234 | * @return $this | |||
| 183 | */ | 235 | */ | |||
| 184 | public function removeAttributeSearch($attributeCode) | 236 | public function removeAttributeSearch($attributeCode) | |||
| 185 | { | 237 | { | |||
| 186 | if (is_array($attributeCode)) { | 238 | if (is_array($attributeCode)) { | |||
| 187 | foreach ($attributeCode as $attCode) { | 239 | foreach ($attributeCode as $attCode) { | |||
| 188 | $this->searchCriteriaBuilder->removeFilter($attCode); | 240 | $this->searchCriteriaBuilder->removeFilter($attCode); | |||
| 189 | } | 241 | } | |||
| 190 | } else { | 242 | } else { | |||
| 191 | $this->searchCriteriaBuilder->removeFilter($attributeCode); | 243 | $this->searchCriteriaBuilder->removeFilter($attributeCode); | |||
| 192 | } | 244 | } | |||
| 193 | 245 | |||||
| 194 | $this->_isFiltersRendered = false; | 246 | $this->_isFiltersRendered = false; | |||
| 195 | 247 | |||||
| 196 | return $this->loadWithFilter(); | 248 | return $this->loadWithFilter(); | |||
| 197 | } | 249 | } | |||
| 198 | 250 | |||||
| 199 | /** | 251 | /** | |||
| 200 | * MP LayerNavigation Get attribute condition sql | 252 | * MP LayerNavigation Get attribute condition sql | |||
| 201 | * | 253 | * | |||
| 202 | * @param $attribute | 254 | * @param $attribute | |||
| 203 | * @param $condition | 255 | * @param $condition | |||
| 204 | * @param string $joinType | 256 | * @param string $joinType | |||
| 257 | * | |||||
| 205 | * @return string | 258 | * @return string | |||
| 206 | */ | 259 | */ | |||
| 207 | public function getAttributeConditionSql($attribute, $condition, $joinType = 'inner') | 260 | public function getAttributeConditionSql($attribute, $condition, $joinType = 'inner') | |||
| 208 | { | 261 | { | |||
| 209 | return $this->_getAttributeConditionSql($attribute, $condition, $joinType); | 262 | return $this->_getAttributeConditionSql($attribute, $condition, $joinType); | |||
| 210 | } | 263 | } | |||
| 211 | 264 | |||||
| 212 | /** | 265 | /** | |||
| 213 | * MP LayerNavigation Reset Total records | 266 | * MP LayerNavigation Reset Total records | |||
| 214 | * | 267 | * | |||
| 215 | * @return $this | 268 | * @return $this | |||
| 216 | */ | 269 | */ | |||
| 217 | public function resetTotalRecords() | 270 | public function resetTotalRecords() | |||
| 218 | { | 271 | { | |||
| 219 | $this->_totalRecords = null; | 272 | $this->_totalRecords = null; | |||
| 220 | 273 | |||||
| 221 | return $this; | 274 | return $this; | |||
| 222 | } | 275 | } | |||
| 223 | 276 | |||||
| 224 | /** | 277 | /** | |||
| 278 | * @return SearchInterface | |||||
| 225 | * @deprecated | 279 | * @deprecated | |||
| 226 | * @return \Magento\Search\Api\SearchInterface | |||||
| 227 | */ | 280 | */ | |||
| 228 | private function getSearch() | 281 | private function getSearch() | |||
| 229 | { | 282 | { | |||
| 230 | if ($this->search === null) { | 283 | if ($this->search === null) { | |||
| 231 | $this->search = ObjectManager::getInstance()->get('\Magento\Search\Api\SearchInterface'); | 284 | $this->search = ObjectManager::getInstance()->get( | |||
| 232 | } | 285 | } | |||
| 233 | 286 | |||||
| 234 | return $this->search; | 287 | return $this->search; | |||
| 235 | } | 288 | } | |||
| 236 | 289 | |||||
| 237 | /** | 290 | /** | |||
| 238 | * @deprecated | 291 | * @param | |||
| 239 | * @param \Magento\Search\Api\SearchInterface $object | 292 | * | |||
| 240 | * @return void | 293 | * @return void | |||
| 294 | * @deprecated | |||||
| 295 | * | |||||
| 241 | */ | 296 | */ | |||
| 242 | public function setSearch(\Magento\Search\Api\SearchInterface $object) | 297 | public function setSearch( | |||
| 243 | { | 298 | { | |||
| 244 | $this->search = $object; | 299 | $this->search = $object; | |||
| 245 | } | 300 | } | |||
| 246 | 301 | |||||
| 247 | /** | 302 | /** | |||
| 303 | * @return SearchCriteriaBuilder | |||||
| 248 | * @deprecated | 304 | * @deprecated | |||
| 249 | * @return \Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder | |||||
| 250 | */ | 305 | */ | |||
| 251 | public function getSearchCriteriaBuilder() | 306 | public function getSearchCriteriaBuilder() | |||
| 252 | { | 307 | { | |||
| 253 | if ($this->searchCriteriaBuilder === null) { | 308 | if ($this->searchCriteriaBuilder === null) { | |||
| 254 | $this->searchCriteriaBuilder = ObjectManager::getInstance() | 309 | $this->searchCriteriaBuilder = ObjectManager::getInstance() | |||
| 255 | ->get('\Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder'); | 310 | ->get( | |||
| 256 | } | 311 | } | |||
| 257 | 312 | |||||
| 258 | return $this->searchCriteriaBuilder; | 313 | return $this->searchCriteriaBuilder; | |||
| 259 | } | 314 | } | |||
| 260 | 315 | |||||
| 261 | /** | 316 | /** | |||
| 262 | * @param \Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder $object | 317 | * @param | |||
| 263 | */ | 318 | */ | |||
| 264 | public function setSearchCriteriaBuilder(\Mageplaza\LayeredNavigation\Model\Search\SearchCriteriaBuilder $object) | 319 | public function setSearchCriteriaBuilder( | |||
| 265 | { | 320 | { | |||
| 266 | $this->searchCriteriaBuilder = $object; | 321 | $this->searchCriteriaBuilder = $object; | |||
| 267 | } | 322 | } | |||
| 268 | 323 | |||||
| 269 | /** | 324 | /** | |||
| 325 | * @return FilterBuilder | |||||
| 270 | * @deprecated | 326 | * @deprecated | |||
| 271 | * @return \Magento\Framework\Api\FilterBuilder | |||||
| 272 | */ | 327 | */ | |||
| 273 | private function getFilterBuilder() | 328 | private function getFilterBuilder() | |||
| 274 | { | 329 | { | |||
| 275 | if ($this->filterBuilder === null) { | 330 | if ($this->filterBuilder === null) { | |||
| 276 | $this->filterBuilder = ObjectManager::getInstance()->get('\Magento\Framework\Api\FilterBuilder'); | 331 | $this->filterBuilder = ObjectManager::getInstance()->get( | |||
| 277 | } | 332 | } | |||
| 278 | 333 | |||||
| 279 | return $this->filterBuilder; | 334 | return $this->filterBuilder; | |||
| 280 | } | 335 | } | |||
| 281 | 336 | |||||
| 282 | /** | 337 | /** | |||
| 283 | * @deprecated | 338 | * @param FilterBuilder $object | |||
| 284 | * @param \Magento\Framework\Api\FilterBuilder $object | 339 | * | |||
| 285 | * @return void | 340 | * @return void | |||
| 341 | * @deprecated | |||||
| 342 | * | |||||
| 286 | */ | 343 | */ | |||
| 287 | public function setFilterBuilder(\Magento\Framework\Api\FilterBuilder $object) | 344 | public function setFilterBuilder( | |||
| 288 | { | 345 | { | |||
| 289 | $this->filterBuilder = $object; | 346 | $this->filterBuilder = $object; | |||
| 290 | } | 347 | } | |||
| 291 | 348 | |||||
| 292 | /** | 349 | /** | |||
| 293 | * Apply attribute filter to facet collection | 350 | * Apply attribute filter to facet collection | |||
| 294 | * | 351 | * | |||
| 295 | * @param string $field | 352 | * @param string $field | |||
| 296 | * @param null $condition | 353 | * @param null $condition | |||
| 354 | * | |||||
| 297 | * @return $this | 355 | * @return $this | |||
| 298 | */ | 356 | */ | |||
| 299 | public function addFieldToFilter($field, $condition = null) | 357 | public function addFieldToFilter($field, $condition = null) | |||
| 300 | { | 358 | { | |||
| 301 | if ($this->searchResult !== null) { | 359 | if ($this->searchResult !== null) { | |||
| 302 | throw new \RuntimeException('Illegal state'); | 360 | throw new | |||
| 303 | } | 361 | } | |||
| 304 | 362 | |||||
| 305 | $this->getSearchCriteriaBuilder(); | 363 | $this->getSearchCriteriaBuilder(); | |||
| 306 | $this->getFilterBuilder(); | 364 | $this->getFilterBuilder(); | |||
| 307 | 365 | |||||
| 308 | if (isset($condition['in']) && | 366 | if (isset($condition['in']) | |||
| 367 | | |||||
| 368 | || $this->getSearchEngine() === 'amasty_elastic') | |||||
| 369 | ) { | |||||
| 309 | $this->filterBuilder->setField($field); | 370 | $this->filterBuilder->setField($field); | |||
| 310 | $this->filterBuilder->setValue($condition['in']); | 371 | $this->filterBuilder->setValue($condition['in']); | |||
| 311 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 372 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 312 | } else { | 373 | } elseif (!is_array($condition) || !in_array(key($condition), ['from', 'to'])) { | |||
| 313 | if (!is_array($condition) || !in_array(key($condition), ['from', 'to'])) { | |||||
| 314 | $this->filterBuilder->setField($field); | 374 | $this->filterBuilder->setField($field); | |||
| 315 | $this->filterBuilder->setValue($condition); | 375 | $this->filterBuilder->setValue($condition); | |||
| 316 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 376 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 317 | } else { | 377 | } else { | |||
| 318 | if (!empty($condition['from'])) { | 378 | if (!empty($condition['from'])) { | |||
| 319 | $this->filterBuilder->setField("{$field}.from"); | 379 | $this->filterBuilder->setField("{$field}.from"); | |||
| 320 | $this->filterBuilder->setValue($condition['from']); | 380 | $this->filterBuilder->setValue($condition['from']); | |||
| 321 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 381 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 322 | } | 382 | } | |||
| 323 | if (!empty($condition['to'])) { | 383 | if (!empty($condition['to'])) { | |||
| 324 | $this->filterBuilder->setField("{$field}.to"); | 384 | $this->filterBuilder->setField("{$field}.to"); | |||
| 325 | $this->filterBuilder->setValue($condition['to']); | 385 | $this->filterBuilder->setValue($condition['to']); | |||
| 326 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 386 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 327 | } | 387 | } | |||
| 328 | } | 388 | } | |||
| 329 | } | |||||
| 330 | 389 | |||||
| 331 | return $this; | 390 | return $this; | |||
| 332 | } | 391 | } | |||
| 333 | 392 | |||||
| 334 | /** | 393 | /** | |||
| 335 | * Add search query filter | 394 | * Add search query filter | |||
| 336 | * | 395 | * | |||
| 337 | * @param string $query | 396 | * @param string $query | |||
| 397 | * | |||||
| 338 | * @return $this | 398 | * @return $this | |||
| 339 | */ | 399 | */ | |||
| 340 | public function addSearchFilter($query) | 400 | public function addSearchFilter($query) | |||
| 341 | { | 401 | { | |||
| 342 | $this->queryText = trim($this->queryText . ' ' . $query); | 402 | $this->queryText = trim($this->queryText . ' ' . $query); | |||
| 343 | 403 | |||||
| 344 | return $this; | 404 | return $this; | |||
| 345 | } | 405 | } | |||
| 346 | 406 | |||||
| 347 | /** | 407 | /** | |||
| 348 | * @inheritdoc | 408 | * @inheritdoc | |||
| 349 | */ | 409 | */ | |||
| 410 | public function setOrder($attribute, $dir = Select::SQL_DESC) | |||||
| 411 | { | |||||
| 412 | $this->setSearchOrder($attribute, $dir); | |||||
| 413 | if ($attribute === 'relevance') { | |||||
| 414 | $this->order[$attribute] = $dir; | |||||
| 415 | } else { | |||||
| 416 | parent::setOrder($attribute, $dir); | |||||
| 417 | } | |||||
| 418 | ||||||
| 419 | return $this; | |||||
| 420 | } | |||||
| 421 | ||||||
| 422 | /** | |||||
| 423 | * Add attribute to sort order. | |||||
| 424 | * | |||||
| 425 | * @param string $attribute | |||||
| 426 | * @param string $dir | |||||
| 427 | * | |||||
| 428 | * @return $this | |||||
| 429 | * @since 101.0.2 | |||||
| 430 | */ | |||||
| 431 | public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC) | |||||
| 432 | { | |||||
| 433 | if ($attribute === 'relevance') { | |||||
| 434 | $this->setOrder($attribute, $dir); | |||||
| 435 | } else { | |||||
| 436 | parent::addAttributeToSort($attribute, $dir); | |||||
| 437 | } | |||||
| 438 | ||||||
| 439 | return $this; | |||||
| 440 | } | |||||
| 441 | ||||||
| 442 | /** | |||||
| 443 | * Set sort order for search query. | |||||
| 444 | * | |||||
| 445 | * @param string $field | |||||
| 446 | * @param string $direction | |||||
| 447 | * | |||||
| 448 | * @return void | |||||
| 449 | */ | |||||
| 450 | private function setSearchOrder($field, $direction) | |||||
| 451 | { | |||||
| 452 | $field = (string) $this->_getMappedField($field); | |||||
| 453 | $direction = strtoupper($direction) == self::SORT_ORDER_ASC ? self::SORT_ORDER_ASC : self::SORT_ORDER_DESC; | |||||
| 454 | ||||||
| 455 | $this->searchOrders[$field] = $direction; | |||||
| 456 | } | |||||
| 457 | ||||||
| 458 | /** | |||||
| 459 | * @throws LocalizedException | |||||
| 460 | * @throws Zend_Db_Exception | |||||
| 461 | */ | |||||
| 350 | protected function _renderFiltersBefore() | 462 | protected function _renderFiltersBefore() | |||
| 351 | { | 463 | { | |||
| 352 | $this->getCollectionClone(); | 464 | $this->getCollectionClone(); | |||
| 353 | 465 | |||||
| 354 | $this->getSearchCriteriaBuilder(); | 466 | $this->getSearchCriteriaBuilder(); | |||
| 355 | $this->getFilterBuilder(); | 467 | $this->getFilterBuilder(); | |||
| 356 | $this->getSearch(); | 468 | $this->getSearch(); | |||
| 357 | 469 | |||||
| 358 | if ($this->queryText) { | 470 | if ($this->queryText) { | |||
| 359 | $this->filterBuilder->setField('search_term'); | 471 | $this->filterBuilder->setField('search_term'); | |||
| 360 | $this->filterBuilder->setValue($this->queryText); | 472 | $this->filterBuilder->setValue($this->queryText); | |||
| 361 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 473 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 362 | } | 474 | } | |||
| 363 | 475 | |||||
| 364 | $priceRangeCalculation = $this->_scopeConfig->getValue( | 476 | $priceRangeCalculation = $this->_scopeConfig->getValue( | |||
| 365 | \Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory::XML_PATH_RANGE_CALCULATION, | 477 | | |||
| 366 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE | 478 | | |||
| 367 | ); | 479 | ); | |||
| 368 | if ($priceRangeCalculation) { | 480 | if ($priceRangeCalculation) { | |||
| 369 | $this->filterBuilder->setField('price_dynamic_algorithm'); | 481 | $this->filterBuilder->setField('price_dynamic_algorithm'); | |||
| 370 | $this->filterBuilder->setValue('auto'); | 482 | $this->filterBuilder->setValue('auto'); | |||
| 371 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | 483 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||
| 372 | } | 484 | } | |||
| 485 | if ($this->request->getFullActionName() === 'catalogsearch_result_index') { | |||||
| 486 | $this->searchRequestName = 'quick_search_container'; | |||||
| 487 | $this->filterBuilder->setField('visibility'); | |||||
| 488 | $this->filterBuilder->setValue([3, 4]); | |||||
| 489 | $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create()); | |||||
| 490 | } | |||||
| 373 | 491 | |||||
| 374 | $searchCriteria = $this->searchCriteriaBuilder->create(); | 492 | $searchCriteria = $this->searchCriteriaBuilder->create(); | |||
| 375 | $searchCriteria->setRequestName($this->searchRequestName); | 493 | $searchCriteria->setRequestName($this->searchRequestName); | |||
| 494 | $searchCriteria->setSortOrders($this->searchOrders); | |||||
| 495 | $searchCriteria->setCurrentPage((int) $this->_curPage); | |||||
| 376 | 496 | |||||
| 377 | try { | 497 | try { | |||
| 378 | $this->searchResult = $this->getSearch()->search($searchCriteria); | 498 | $this->searchResult = $this->getSearch()->search($searchCriteria); | |||
| 379 | } catch (\Exception $e) { | 499 | } catch ( | |||
| 380 | throw new LocalizedException(__('Sorry, something went wrong. You can find out more in the error log.')); | 500 | throw new LocalizedException(__('Sorry, something went wrong. You can find out more in the error log.')); | |||
| 381 | } | 501 | } | |||
| 382 | 502 | |||||
| 383 | $temporaryStorage = $this->temporaryStorageFactory->create(); | 503 | if (strpos($this->getSearchEngine(), 'elasticsearch') !== false) { | |||
| 384 | $table = $temporaryStorage->storeDocuments($this->searchResult->getItems()); | 504 | $this->ElasticSearchApply(); | |||
| 385 | 505 | } else { | ||||
| 386 | $this->getSelect()->joinInner( | 506 | $this->CatalogSearchApply(); | |||
| 387 | [ | |||||
| 388 | 'search_result' => $table->getName(), | |||||
| 389 | ], | |||||
| 390 | 'e.entity_id = search_result.' . TemporaryStorage::FIELD_ENTITY_ID, | |||||
| 391 | [] | |||||
| 392 | ); | |||||
| 393 | ||||||
| 394 | if ($this->order && 'relevance' === $this->order['field']) { | |||||
| 395 | $this->getSelect()->order('search_result.' . TemporaryStorage::FIELD_SCORE . ' ' . $this->order['dir']); | |||||
| 396 | } | 507 | } | |||
| 397 | 508 | |||||
| 398 | parent::_renderFiltersBefore(); | 509 | parent::_renderFiltersBefore(); | |||
| 399 | } | 510 | } | |||
| 400 | 511 | |||||
| 401 | /** | 512 | /** | |||
| 402 | * @return $this | 513 | * @return \Magento\Catalog\Model\ResourceModel\Product\Collection | |||
| 403 | */ | 514 | */ | |||
| 404 | protected function _renderFilters() | 515 | protected function _renderFilters() | |||
| 405 | { | 516 | { | |||
| 406 | $this->_filters = []; | 517 | $this->_filters = []; | |||
| 407 | 518 | |||||
| 408 | return parent::_renderFilters(); | 519 | return parent::_renderFilters(); | |||
| 409 | } | 520 | } | |||
| 410 | 521 | |||||
| 411 | /** | 522 | /** | |||
| 412 | * sort product before load | 523 | * sort product before load | |||
| 413 | */ | 524 | */ | |||
| 414 | protected function _beforeLoad() | 525 | protected function _beforeLoad() | |||
| 415 | { | 526 | { | |||
| 416 | $this->setOrder('entity_id'); | 527 | $this->setOrder('entity_id'); | |||
| 417 | 528 | |||||
| 418 | return parent::_beforeLoad(); | 529 | return parent::_beforeLoad(); | |||
| 419 | } | 530 | } | |||
| 420 | 531 | |||||
| 421 | /** | 532 | /** | |||
| 422 | * Set Order field | 533 | * Stub method for compatibility with other search engines | |||
| 423 | * | 534 | * | |||
| 424 | * @param string $attribute | |||||
| 425 | * @param string $dir | |||||
| 426 | * @return $this | 535 | * @return $this | |||
| 427 | */ | 536 | */ | |||
| 428 | public function setOrder($attribute, $dir = Select::SQL_DESC) | 537 | public function setGeneralDefaultQuery() | |||
| 429 | { | 538 | { | |||
| 430 | $this->order = ['field' => $attribute, 'dir' => $dir]; | 539 | return $this; | |||
| 431 | if ($attribute != 'relevance') { | |||||
| 432 | parent::setOrder($attribute, $dir); | |||||
| 433 | } | 540 | } | |||
| 434 | 541 | |||||
| 435 | return $this; | 542 | protected function CatalogSearchApply() | |||
| 543 | { | |||||
| 544 | $temporaryStorage = $this->temporaryStorageFactory->create(); | |||||
| 545 | $table = $temporaryStorage->storeDocuments($this->searchResult->getItems()); | |||||
| 546 | ||||||
| 547 | $this->getSelect()->joinInner( | |||||
| 548 | [ | |||||
| 549 | 'search_result' => $table->getName(), | |||||
| 550 | ], | |||||
| 551 | 'e.entity_id = search_result.' . TemporaryStorage::FIELD_ENTITY_ID, | |||||
| 552 | [] | |||||
| 553 | ); | |||||
| 554 | if ($this->order && isset($this->order['relevance'])) { | |||||
| 555 | $this->getSelect()->order( | |||||
| 556 | 'search_result.' . TemporaryStorage::FIELD_SCORE . ' ' . $this->order['relevance'] | |||||
| 557 | ); | |||||
| 558 | } | |||||
| 436 | } | 559 | } | |||
| 437 | 560 | |||||
| 438 | /** | 561 | protected function ElasticSearchApply() | |||
| 439 | * Stub method for compatibility with other search engines | |||||
| 440 | * | |||||
| 441 | * @return $this | |||||
| 442 | */ | |||||
| 443 | public function setGeneralDefaultQuery() | |||||
| 444 | { | 562 | { | |||
| 445 | return $this; | 563 | if (empty($this->searchResult->getItems())) { | |||
| 564 | $this->getSelect()->where('NULL'); | |||||
| 565 | ||||||
| 566 | return | |||||
| 567 | } | |||||
| 568 | $ids = []; | |||||
| 569 | foreach ($this->searchResult->getItems() as $item) { | |||||
| 570 | $ids[] = (int) $item->getId(); | |||||
| 571 | } | |||||
| 572 | ||||||
| 573 | $this->getSelect()->where('e.entity_id IN (?)', $ids); | |||||
| 574 | $this->getSelect()->reset(Select::ORDER); | |||||
| 575 | $this->getSelect()->order(new Zend_Db_Expr('FIELD(e.entity_id,' . implode(',', $ids) . ')')); | |||||
| 446 | } | 576 | } | |||
| 447 | 577 | |||||
| 448 | /** | 578 | /** | |||
| 449 | * Return field faceted data from faceted search result | 579 | * Return field faceted data from faceted search result | |||
| 450 | * | 580 | * | |||
| 451 | * @param string $field | 581 | * @param string $field | |||
| 582 | * | |||||
| 452 | * @return array | 583 | * @return array | |||
| 453 | * @throws StateException | 584 | * @throws StateException | |||
| 454 | */ | 585 | */ | |||
| 455 | public function getFacetedData($field) | 586 | public function getFacetedData($field) | |||
| 456 | { | 587 | { | |||
| 457 | $this->_renderFilters(); | 588 | $this->_renderFilters(); | |||
| 458 | $result = []; | 589 | $result = []; | |||
| 459 | 590 | |||||
| 460 | $aggregations = $this->searchResult->getAggregations(); | 591 | $aggregations = $this->searchResult->getAggregations(); | |||
| 461 | // This behavior is for case with empty object when we got EmptyRequestDataException | 592 | // This behavior is for case with empty object when we got EmptyRequestDataException | |||
| 462 | if (null !== $aggregations) { | 593 | if (null !== $aggregations) { | |||
| 463 | $bucket = $aggregations->getBucket($field . RequestGenerator::BUCKET_SUFFIX); | 594 | $bucket = $aggregations->getBucket($field . RequestGenerator::BUCKET_SUFFIX); | |||
| 464 | if ($bucket) { | 595 | if ($bucket) { | |||
| 465 | foreach ($bucket->getValues() as $value) { | 596 | foreach ($bucket->getValues() as $value) { | |||
| 466 | $metrics = $value->getMetrics(); | 597 | $metrics = $value->getMetrics(); | |||
| 467 | $result[$metrics['value']] = $metrics; | 598 | $result[$metrics['value']] = $metrics; | |||
| 468 | } | 599 | } | |||
| 469 | } else { | 600 | } else { | |||
| 470 | throw new StateException(__('Bucket does not exist')); | 601 | throw new StateException(__('Bucket does not exist')); | |||
| 471 | } | 602 | } | |||
| 472 | } | 603 | } | |||
| 473 | 604 | |||||
| 474 | return $result; | 605 | return $result; | |||
| 475 | } | 606 | } | |||
| 476 | 607 | |||||
| 477 | /** | 608 | /** | |||
| 478 | * Specify category filter for product collection | 609 | * Specify category filter for product collection | |||
| 479 | * | 610 | * | |||
| 480 | * @param \Magento\Catalog\Model\Category $category | 611 | * @param | |||
| 481 | * @return $this | 612 | * | |||
| 613 | * @return \Magento\Catalog\Model\ResourceModel\Product\Collection | |||||
| 482 | */ | 614 | */ | |||
| 483 | public function addCategoryFilter(\Magento\Catalog\Model\Category $category) | 615 | public function addCategoryFilter( | |||
| 484 | { | 616 | { | |||
| 485 | $this->addFieldToFilter('category_ids', $category->getId()); | 617 | $this->addFieldToFilter('category_ids', $category->getId()); | |||
| 486 | 618 | |||||
| 487 | return parent::addCategoryFilter($category); | 619 | return parent::addCategoryFilter($category); | |||
| 488 | } | 620 | } | |||
| 489 | 621 | |||||
| 490 | /** | 622 | /** | |||
| 491 | * Set product visibility filter for enabled products | 623 | * Set product visibility filter for enabled products | |||
| 492 | * | 624 | * | |||
| 493 | * @param array $visibility | 625 | * @param array $visibility | |||
| 494 | * @return $this | 626 | * | |||
| 627 | * @return \Magento\Catalog\Model\ResourceModel\Product\Collection | |||||
| 495 | */ | 628 | */ | |||
| 496 | public function setVisibility($visibility) | 629 | public function setVisibility($visibility) | |||
| 497 | { | 630 | { | |||
| 498 | $this->addFieldToFilter('visibility', $visibility); | 631 | $this->addFieldToFilter('visibility', $visibility); | |||
| 499 | 632 | |||||
| 500 | return parent::setVisibility($visibility); | 633 | return parent::setVisibility($visibility); | |||
| 501 | } | 634 | } | |||
| 502 | 635 | |||||
| 503 | /** | 636 | /** | |||
| 504 | * Get Search Engine Config | 637 | * Get Search Engine Config | |||
| 505 | * | 638 | * | |||
| 506 | * @return string | 639 | * @return string | |||
| 507 | */ | 640 | */ | |||
| 508 | public function getSearchEngine() | 641 | public function getSearchEngine() | |||
| 509 | { | 642 | { | |||
| 510 | return $this->_scopeConfig->getValue( | 643 | return $this->_scopeConfig->getValue(Custom::XML_PATH_CATALOG_SEARCH_ENGINE, ScopeInterface::SCOPE_STORE); | |||
| 511 | \Magento\Config\Model\Config\Backend\Admin\Custom::XML_PATH_CATALOG_SEARCH_ENGINE, | |||||
| 512 | \Magento\Store\Model\ScopeInterface::SCOPE_STORE | |||||
| 513 | ); | |||||
| 514 | } | 644 | } | |||
| 515 | } | 645 | } |
Araxis Merge (but not the data content of this report) is Copyright © 1993–2019 Araxis Ltd (www.araxis.com). All rights reserved.