Атрибуты продукта и механика фильтрации
-
16-10-2019 - |
Вопрос
TL; DR: Как отфильтровать несколько атрибутов SELECT/DUPDOWN (мульти-выборы были бы громоздкими)? Или использовать другой метод?
Рассмотрим атрибут выбора продукта/раскрывающегося списка, значения которого соответствуют каждой букве алфавита (английский в данном случае). Ярлык первого варианта - «A», его значение - 1. «B» - 2, «C» - 3, и т. Д.
У меня есть этот атрибут, назначенный для управления слоистыми навигационными фильтрами. Таким образом, пользователь может щелкнуть по ссылке для определенного значения, и он добавит ?my_attribute=2
в параметры запроса, и в списке продуктов показаны только продукты, значение атрибута, значение атрибута - «B».
Вместо этого я хотел бы отобразить любые продукты, которые значения атрибута больше или равны выбранной опции. Таким образом, в случае, если пользователь нажимает на ссылку «B» в слоистом нависе, результатами будут каждый продукт от «BZ» (2-26).
Структура URL: ?my_attribute=2&my_attribute_3&my_attribute=4...
было бы уродливым и, кроме того, это не работает, но идея есть. ?my_attribute=2-26
Очевидно, было бы идеальным, но опять же, это не то, что делает Magento.
Любопытно, что использование Advanced Search обеспечивает эту структуру URL -адреса (через расширенный контроллер поиска вместо слоистых навие):
?my_attribute%5B%5D=7&my_attribute%5B%5D=8&my_attribute%5B%5D=9&my_attribute%5B%5D=10
Это действительно дает желаемые результаты; Тем не менее, эта структура не работает с категориями/слоистыми страницами, только расширенный поиск.
Я пытаюсь решить, какой будет лучший путь на этом этапе; Должен ли я отказаться от всей встроенной полезности слоистой навигации и вместо этого переписать product_list
Блок загруженный сборник продуктов на основе моего значения атрибута? Или я должен адаптировать некоторые функциональные возможности из расширенных результатов поиска, чтобы соответствовать моим потребностям?
Похоже, было бы лучше оставить многослойную навигационную функциональность в одиночку, чтобы другие фильтры продукта продолжали работать, как и ожидалось; Но, похоже, не позволит выбрать несколько значений из одного атрибута одновременно. В качестве альтернативы, обходной путь заключается в том, чтобы использовать многоцелевой атрибут, и если бы продукт будет назначен «B», ему нужно будет назначить все варианты из «BZ». Несмотря на то, что это дало бы наибольшие мгновенные результаты (все будет работать без больших модификаций), это, безусловно, неряшливое решение, которое будет подвержено ошибкам и неуклюжим в будущем.
Решение
В этом случае я сделал переписать как модель, так и соответствующий ресурс, так что сначала перепишите класс Mage_Catalog_Model_Layer_Filter_Attribute
иметь возможность принимать несколько значений. В моем случае я использую запятые в качестве сепаратора, поэтому я могу фильтровать как ?my_attribute=1,2,3
В методе apply()
Я анализирую атрибут, чтобы поместить различные значения в массив:
public function apply(Zend_Controller_Request_Abstract $request, $filterBlock)
{
$filter = $request->getParam($this->_requestVar);
if (is_array($filter)) {
return $this;
}
// MP customized
$filter = explode(',', $filter);
if (count($filter) < 1) {
return $this;
} else if ($filter == '') {
return $this;
}
if (count($filter) == 1) {
$filter = $filter[0];
}
if ($filter == '') {
return $this;
}
if ($filter) {
$this->_initItems();
$this->_getResource()->applyFilterToCollection($this, $filter);
$text = '';
foreach ($filter as $att) {
($text == '') ? $text = $this->_getOptionText($att) : $text .= ', '.$this->_getOptionText($att);
}
if (count($filter) == 1) {
$text = $this->_getOptionText($filter);
}
$this->getLayer()->getState()->addFilter($this->_createItem($text, $filter));
}
// End MP customized
return $this;
}
Тогда в файле Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute
Вы должны убедиться, что вы можете фильтровать несколько значений в своем запросе, поэтому я переписал следующий метод, подобный этому:
public function applyFilterToCollection($filter, $value)
{
$collection = $filter->getLayer()->getProductCollection();
$attribute = $filter->getAttributeModel();
$connection = $this->_getReadAdapter();
$tableAlias = $attribute->getAttributeCode() . '_idx';
$conditions = array(
"{$tableAlias}.entity_id = e.entity_id",
$connection->quoteInto("{$tableAlias}.attribute_id = ?", $attribute->getAttributeId()),
$connection->quoteInto("{$tableAlias}.store_id = ?", $collection->getStoreId()),
$connection->quoteInto("{$tableAlias}.value IN (?)", $value)
); // Added IN (?) notation for arrays
$collection->getSelect()->join(
array($tableAlias => $this->getMainTable()),
join(' AND ', $conditions),
array()
// MP Need distinct here to avoid getting the same config product from
// different basic products when filtering for different values
// in the same attribute
)->distinct(true);
return $this;
}
Другие советы
Вот многослойная навигация с пользовательской коллекцией продуктов
<?php
require_once('app/Mage.php'); //Must have file to begin
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); //Initialize store
$catId = Mage::app()->getRequest()->getParam('categoryId'); //Pass categoryId in get variable
$storeId = Mage::app()->getWebsite(true)->getDefaultGroup()->getDefaultStoreId();
$page_no = Mage::app()->getRequest()->getParam('page_no');
$params = Mage::app()->getRequest()->getParams(); //Pass attributes in key=>value form to filter results.
$category = Mage::getModel('catalog/category')->load($catId);
$layer = Mage::getModel("catalog/layer");
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes(); //get all filterable attributes available in selected category layered navigation
$attr = array();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'price') {
$filterBlockName = 'catalog/layer_filter_price';
} elseif ($attribute->getBackendType() == 'decimal') {
$filterBlockName = 'catalog/layer_filter_decimal';
} else {
$filterBlockName = 'catalog/layer_filter_attribute';
}
$attr[$attribute->getAttributeCode()] = $attribute->getFrontendLabel();
}
$filters = array_intersect_key($params, $attr);
$collection = $category->getProductCollection()
->addAttributeToFilter(
'status', array('eq' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED))
->addAttributeToSelect('*');
foreach ($filters as $key => $value) {
if($key == 'price'){
$priceFilter = explode('-', $value);
$collection->addAttributeToFilter('price', array('gteq' => $priceFilter[0]));
$collection->addAttributeToFilter('price', array('lteq' => $priceFilter[1]));
}
else{
$codeId = Mage::getResourceModel('eav/entity_attribute')
->getIdByCode('catalog_product', $key)
$collection->getSelect()->join(array('at_'.$key =>'catalog_product_index_eav'),'at_'.$key.'.entity_id = e.entity_id and at_'.$key.'.attribute_id = '.$codeId.' and at_'.$key.'.store_id = '.Mage::helper('mobile')->getStore()->getId())->where('at_'.$key.'.value = '.$value);
}
}
$collection->setPage($page_no, 10);
foreach($collection as $product){
echo $product->getName();
}