src/module-elasticsuite-catalog/Model/Layer/Filter/Attribute.php
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
* @copyright 2020 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteCatalog\Model\Layer\Filter;
use Smile\ElasticsuiteCore\Search\Request\BucketInterface;
use Smile\ElasticsuiteCatalog\Model\Attribute\Source\FilterDisplayMode;
/**
* Product attribute filter implementation.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
*/
class Attribute extends \Magento\CatalogSearch\Model\Layer\Filter\Attribute
{
/**
* @var array
*/
protected $currentFilterValue = [];
/**
* @var \Magento\Framework\Filter\StripTags
*/
private $tagFilter;
/**
* @var \Magento\Framework\Escaper
*/
private $escaper;
/**
* @var boolean
*/
private $hasMoreItems = false;
/**
* @var \Smile\ElasticsuiteCatalog\Helper\ProductAttribute
*/
private $mappingHelper;
/**
* Constructor.
*
* @param \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory Factory for item of the facets.
* @param \Magento\Store\Model\StoreManagerInterface $storeManager Store manager.
* @param \Magento\Catalog\Model\Layer $layer Catalog product layer.
* @param \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder Item data builder.
* @param \Magento\Framework\Filter\StripTags $tagFilter String HTML tags filter.
* @param \Magento\Framework\Escaper $escaper Html Escaper.
* @param \Smile\ElasticsuiteCatalog\Helper\ProductAttribute $mappingHelper Mapping helper.
* @param array $data Custom data.
*/
public function __construct(
\Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Catalog\Model\Layer $layer,
\Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder,
\Magento\Framework\Filter\StripTags $tagFilter,
\Magento\Framework\Escaper $escaper,
\Smile\ElasticsuiteCatalog\Helper\ProductAttribute $mappingHelper,
array $data = []
) {
parent::__construct(
$filterItemFactory,
$storeManager,
$layer,
$itemDataBuilder,
$tagFilter,
$data
);
$this->tagFilter = $tagFilter;
$this->escaper = $escaper;
$this->mappingHelper = $mappingHelper;
}
/**
* {@inheritDoc}
*/
public function apply(\Magento\Framework\App\RequestInterface $request)
{
$attributeValue = $request->getParam($this->_requestVar);
if (null !== $attributeValue) {
if (!is_array($attributeValue)) {
$attributeValue = [$attributeValue];
}
$this->currentFilterValue = array_values($attributeValue);
/** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */
$productCollection = $this->getLayer()->getProductCollection();
$productCollection->addFieldToFilter($this->getFilterField(), $this->currentFilterValue);
$layerState = $this->getLayer()->getState();
foreach ($this->currentFilterValue as $currentFilter) {
$filter = $this->_createItem($this->tagFilter->filter($currentFilter), $this->currentFilterValue);
$layerState->addFilter($filter);
}
}
return $this;
}
/**
* Indicates if the facets has more documents to be displayed.
*
* @return boolean
*/
public function hasMoreItems()
{
return $this->hasMoreItems;
}
/**
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
* @SuppressWarnings(PHPMD.ElseExpression)
*
* {@inheritDoc}
*/
protected function _getItemsData()
{
/** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */
$productCollection = $this->getLayer()->getProductCollection();
$optionsFacetedData = $productCollection->getFacetedData($this->getFilterField());
$items = [];
if (isset($optionsFacetedData['__other_docs'])) {
$this->hasMoreItems = $optionsFacetedData['__other_docs']['count'] > 0;
unset($optionsFacetedData['__other_docs']);
}
$minCount = !empty($optionsFacetedData) ? min(array_column($optionsFacetedData, 'count')) : 0;
$attribute = $this->getAttributeModel();
$forceDisplay = $attribute->getFacetDisplayMode() == FilterDisplayMode::ALWAYS_DISPLAYED;
if (!empty($this->currentFilterValue) || $minCount < $productCollection->getSize() || $forceDisplay) {
foreach ($optionsFacetedData as $value => $data) {
$items[$value] = [
'label' => $this->tagFilter->filter($value),
'value' => $value,
'count' => $data['count'],
];
}
}
$items = $this->addOptionsData($items);
return $items;
}
/**
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
* @SuppressWarnings(PHPMD.ElseExpression)
*
* {@inheritDoc}
*/
protected function _initItems()
{
parent::_initItems();
foreach ($this->_items as $item) {
$applyValue = $item->getLabel();
if (($valuePos = array_search($applyValue, $this->currentFilterValue)) !== false) {
$item->setIsSelected(true);
$applyValue = $this->currentFilterValue;
unset($applyValue[$valuePos]);
} else {
$applyValue = array_merge($this->currentFilterValue, [$applyValue]);
}
$item->setApplyFilterValue(array_values($applyValue));
}
return $this;
}
/**
* Retrieve ES filter field.
*
* @return string
*/
protected function getFilterField()
{
return $this->mappingHelper->getFilterField($this->getAttributeModel());
}
/**
* Resort items according option position defined in admin.
*
* @param array $items Items to be sorted.
*
* @return array
*/
private function addOptionsData(array $items)
{
if ($this->getAttributeModel()->getFacetSortOrder() == BucketInterface::SORT_ORDER_MANUAL) {
$options = $this->getAttributeModel()->getFrontend()->getSelectOptions();
$optionPosition = 0;
if (!empty($options)) {
foreach ($options as $option) {
if (isset($option['label']) && !empty($option['label'])) {
$optionLabel = trim((string) $option['label']);
$optionPosition++;
if (isset($items[$optionLabel])) {
$items[$optionLabel]['adminSortIndex'] = $optionPosition;
$items[$optionLabel]['value'] = $optionLabel;
}
}
}
}
$items = $this->sortOptionsData($items);
}
return $items;
}
/**
* Sort items by adminSortIndex key.
*
* @param array $items to be sorted.
*
* @return array
*/
private function sortOptionsData(array $items)
{
usort($items, function ($item1, $item2) {
if (!isset($item1['adminSortIndex']) or !isset($item2['adminSortIndex'])) {
return 0;
}
return $item1['adminSortIndex'] <= $item2['adminSortIndex'] ? -1 : 1;
});
return $items;
}
}