See the beginning here: How is a widget rendered on a CMS page
public function createCollection()
{
/** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Collection */
$collection = $this->productCollectionFactory->create();
$collection->setVisibility($this->catalogProductVisibility->getVisibleInCatalogIds());
$collection = $this->_addProductAttributesAndPrices($collection)
->addStoreFilter()
->setPageSize($this->getPageSize())
->setCurPage($this->getRequest()->getParam(self::PAGE_VAR_NAME, 1));
$conditions = $this->getConditions();
$conditions->collectValidatedAttributes($collection);
$this->sqlBuilder->attachConditionToCollection($collection, $conditions);
return $collection;
}
/**
* Decode previously encoded widget conditions
*
* @param string $value
* @return array
*/
public function decode($value)
{
$value = str_replace(['[', ']', '`', '|'], ['{', '}', '"', '\\'], $value);
$value = unserialize($value);
return $value;
}
The $value
(the function’s result) looks like this:
/**
* Initialize rule model data from array
*
* @param array $data
* @return $this
*/
public function loadPost(array $data)
{
$arr = $this->_convertFlatToRecursive($data);
if (isset($arr['conditions'])) {
$this->getConditions()->setConditions([])->loadArray($arr['conditions'][1]);
}
if (isset($arr['actions'])) {
$this->getActions()->setActions([])->loadArray($arr['actions'][1], 'actions');
}
return $this;
}
The function’s result looks like this:
public function loadArray($arr, $key = 'conditions')
{
$this->setAggregator(
isset($arr['aggregator']) ? $arr['aggregator'] : (isset($arr['attribute']) ? $arr['attribute'] : null)
)->setValue(
isset($arr['value']) ? $arr['value'] : (isset($arr['operator']) ? $arr['operator'] : null)
);
if (!empty($arr[$key]) && is_array($arr[$key])) {
foreach ($arr[$key] as $conditionArr) {
try {
$condition = $this->_conditionFactory->create($conditionArr['type']);
$this->addCondition($condition);
$condition->loadArray($conditionArr, $key);
} catch (\Exception $e) {
$this->_logger->critical($e);
}
}
}
return $this;
}
public function addToCollection($collection)
{
$attribute = $this->getAttributeObject();
if ('category_ids' == $attribute->getAttributeCode() || $attribute->isStatic()) {
return $this;
}
if ($attribute->getBackend() && $attribute->isScopeGlobal()) {
$this->addGlobalAttribute($attribute, $collection);
} else {
$this->addNotGlobalAttribute($attribute, $collection);
}
$attributes = $this->getRule()->getCollectedAttributes();
$attributes[$attribute->getAttributeCode()] = true;
$this->getRule()->setCollectedAttributes($attributes);
return $this;
}
public function attachConditionToCollection(
\Magento\Eav\Model\Entity\Collection\AbstractCollection $collection,
Combine $combine
) {
$this->_connection = $collection->getResource()->getConnection();
$this->_joinTablesToCollection($collection, $combine);
$whereExpression = (string)$this->_getMappedSqlCombination($combine);
if (!empty($whereExpression)) {
// Select ::where method adds braces even on empty expression
$collection->getSelect()->where($whereExpression);
}
How does the \Magento\Rule\Model\Condition\Sql\Builder::_joinTablesToCollection()
work?
protected function _getMappedSqlCombination(Combine $combine, $value = '')
{
$out = (!empty($value) ? $value : '');
$value = ($combine->getValue() ? '' : ' NOT ');
$getAggregator = $combine->getAggregator();
$conditions = $combine->getConditions();
foreach ($conditions as $key => $condition) {
/** @var $condition AbstractCondition|Combine */
$con = ($getAggregator == 'any' ? Select::SQL_OR : Select::SQL_AND);
$con = (isset($conditions[$key+1]) ? $con : '');
if ($condition instanceof Combine) {
$out .= $this->_getMappedSqlCombination($condition, $value);
} else {
$out .= $this->_getMappedSqlCondition($condition, $value);
}
$out .= $out ? (' ' . $con) : '';
}
return $this->_expressionFactory->create(['expression' => $out]);
}
$out
looks like this:
(IFNULL(`e`.`entity_id`, 0) IN (SELECT `catalog_category_product`.`product_id` FROM `catalog_category_product` WHERE (category_id IN ('3'))))
The $collection
's SQL looks like this:
SELECT
`e`.*, `cat_index`.`position` AS `cat_index_position`
, `price_index`.`price`
, `price_index`.`tax_class_id`
, `price_index`.`final_price`
, IF(price_index.tier_price IS NOT NULL
, LEAST(price_index.min_price, price_index.tier_price), price_index.min_price) AS `minimal_price`
, `price_index`.`min_price`
, `price_index`.`max_price`
, `price_index`.`tier_price`
FROM
`catalog_product_entity` AS `e`
INNER JOIN
`catalog_category_product_index` AS `cat_index`
ON
cat_index.product_id=e.entity_id
AND cat_index.store_id='1'
AND cat_index.visibility IN(2, 4)
AND cat_index.category_id='3'
INNER JOIN
`catalog_product_index_price` AS `price_index`
ON
price_index.entity_id = e.entity_id
AND price_index.website_id = '1'
AND price_index.customer_group_id = '2'
WHERE (((IFNULL(`e`.`entity_id`, 0) IN (
SELECT `catalog_category_product`.`product_id`
FROM `catalog_category_product`
WHERE (category_id IN ('3'))
))));