src/module-elasticsuite-virtual-category/Model/ResourceModel/Product/Indexer/Fulltext/Datasource/CategoryData.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\ElasticsuiteVirtualCategory
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
* @copyright 2020 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteVirtualCategory\Model\ResourceModel\Product\Indexer\Fulltext\Datasource;
use Smile\ElasticsuiteVirtualCategory\Model\ResourceModel\Category\Product\Position as ProductPositionResourceModel;
use Magento\Catalog\Api\Data\CategoryAttributeInterface;
use Magento\Catalog\Api\Data\CategoryInterface;
/**
* Category datasource override. Saves product positions set from admin.
*
* @category Smile
* @package Smile\ElasticsuiteVirtualCategory
* @author Aurelien FOUCRET <aurelien.foucret@smile.fr>
*/
class CategoryData extends \Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Indexer\Fulltext\Datasource\CategoryData
{
/**
* @var null|CategoryAttributeInterface
*/
private $useStorePositionsAttribute = null;
/**
* {@inheritDoc}
*/
protected function getCategoryProductSelect($productIds, $storeId)
{
$select = $this->getConnection()->select()->union(
[
$this->getBaseSelectGlobal($productIds, $storeId),
$this->getBaseSelectStore($productIds, $storeId),
$this->getVirtualSelectGlobal($productIds, $storeId),
$this->getVirtualSelectStore($productIds, $storeId),
]
);
return $select;
}
/**
* Retrieve the standard categories product data (categories ids, positions, ...).
* Product positions returned are those defined globally.
*
* @param array $productIds Product ids.
* @param int $storeId Store id.
*
* @return \Zend_Db_Select
*/
private function getBaseSelectGlobal($productIds, $storeId)
{
$select = $this->getConnection()->select()
->from(['cpi' => $this->getTable($this->getCategoryProductIndexTable($storeId))], [])
->joinLeft(
['p' => $this->getTable(ProductPositionResourceModel::TABLE_NAME)],
'p.product_id = cpi.product_id AND p.category_id = cpi.category_id AND p.store_id = 0',
[]
);
$this->joinStorePosition($select, $storeId, 0);
$select->where('cpi.store_id = ?', (int) $storeId)
->where('cpi.product_id IN(?)', $productIds)
->columns([
'category_id' => 'cpi.category_id',
'product_id' => 'cpi.product_id',
'is_parent' => 'cpi.is_parent',
'is_virtual' => new \Zend_Db_Expr('"false"'),
'position' => 'p.position',
'is_blacklisted' => 'p.is_blacklisted',
]);
return $select;
}
/**
* Retrieve the standard categories product data (categories ids, positions, ...).
* Product positions returned are those defined at the store level.
*
* @param array $productIds Product ids.
* @param int $storeId Store id.
*
* @return \Zend_Db_Select
*/
private function getBaseSelectStore($productIds, $storeId)
{
$select = $this->getConnection()->select()
->from(['cpi' => $this->getTable($this->getCategoryProductIndexTable($storeId))], [])
->joinLeft(
['p' => $this->getTable(ProductPositionResourceModel::TABLE_NAME)],
'p.product_id = cpi.product_id AND p.category_id = cpi.category_id AND p.store_id = cpi.store_id',
[]
);
$select->where('cpi.store_id = ?', (int) $storeId)
->where('cpi.product_id IN(?)', $productIds)
->columns([
'category_id' => 'cpi.category_id',
'product_id' => 'cpi.product_id',
'is_parent' => 'cpi.is_parent',
'is_virtual' => new \Zend_Db_Expr('"false"'),
'position' => 'p.position',
'is_blacklisted' => 'p.is_blacklisted',
]);
return $select;
}
/**
* Retrieve the virtual categories product data (categories ids, positions, ...).
* Product positions returned are those defined globally.
*
* @param array $productIds Product ids.
* @param integer $storeId Store id.
*
* @return \Zend_Db_Select
*/
private function getVirtualSelectGlobal($productIds, $storeId)
{
$select = $this->getConnection()->select()
->from(['p' => $this->getTable(ProductPositionResourceModel::TABLE_NAME)], [])
->joinLeft(
['cpi' => $this->getTable($this->getCategoryProductIndexTable($storeId))],
'p.product_id = cpi.product_id AND p.category_id = cpi.category_id',
[]
);
$this->joinStorePosition($select, $storeId, 0);
$select->where('p.product_id IN(?)', $productIds)
->where('cpi.product_id IS NULL')
->where('p.store_id = 0')
->columns(
[
'category_id' => 'p.category_id',
'product_id' => 'p.product_id',
'is_parent' => new \Zend_Db_Expr('0'),
'is_virtual' => new \Zend_Db_Expr('"true"'),
'position' => 'p.position',
'is_blacklisted' => 'p.is_blacklisted',
]
);
return $select;
}
/**
* Retrieve the virtual categories product data (categories ids, positions, ...).
* Product positions returned are those defined locally.
*
* @param array $productIds Product ids.
* @param integer $storeId Store id.
*
* @return \Zend_Db_Select
*/
private function getVirtualSelectStore($productIds, $storeId)
{
$select = $this->getConnection()->select()
->from(['p' => $this->getTable(ProductPositionResourceModel::TABLE_NAME)], [])
->joinLeft(
['cpi' => $this->getTable($this->getCategoryProductIndexTable($storeId))],
'p.product_id = cpi.product_id AND p.category_id = cpi.category_id',
[]
);
$select->where('p.product_id IN(?)', $productIds)
->where('cpi.product_id IS NULL')
->where('p.store_id = ?', (int) $storeId)
->columns(
[
'category_id' => 'p.category_id',
'product_id' => 'p.product_id',
'is_parent' => new \Zend_Db_Expr('0'),
'is_virtual' => new \Zend_Db_Expr('"true"'),
'position' => 'p.position',
'is_blacklisted' => 'p.is_blacklisted',
]
);
return $select;
}
/**
* Process left join on a select to the 'use_store_positions' attribute backend table.
*
* @param \Magento\Framework\Db\Select $select The select to alter
* @param int $storeId The store id
* @param int $useStorePositionsValue The value of the use_store_positions attribute to select.
*/
private function joinStorePosition(\Magento\Framework\Db\Select &$select, $storeId, $useStorePositionsValue)
{
$useStorePositionsAttr = $this->getUseStorePositionsAttribute();
if ($useStorePositionsAttr && $useStorePositionsAttr->getAttributeId()) {
$linkField = $this->getEntityMetaData(CategoryInterface::class)->getLinkField();
$conditions = [
"c.{$linkField} = use_store_positions.{$linkField}",
"use_store_positions.store_id = " . (int) $storeId,
"use_store_positions.attribute_id = " . (int) $useStorePositionsAttr->getAttributeId(),
];
$joinCondition = new \Zend_Db_Expr(implode(" AND ", $conditions));
$select->joinLeft(
['c' => $this->getTable('catalog_category_entity')],
'p.category_id = c.entity_id',
[]
);
$select->joinLeft(
['use_store_positions' => $useStorePositionsAttr->getBackendTable()],
$joinCondition,
[]
);
$select->where(
$this->getConnection()->quoteInto(
"COALESCE(use_store_positions.value, 0) = ?",
$useStorePositionsValue
)
);
}
}
/**
* Returns category attribute "use store positions"
*
* @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
*/
private function getUseStorePositionsAttribute()
{
$this->useStorePositionsAttribute = $this->getEavConfig()
->getAttribute(\Magento\Catalog\Model\Category::ENTITY, 'use_store_positions');
return $this->useStorePositionsAttribute;
}
}