Многоуровневая навигация для пользовательской коллекции на пользовательской странице - magento2

12

Я работаю над получением многоуровневой навигации в magento2 для пользовательской коллекции продуктов. Я получаю пользовательскую коллекцию уже на пользовательской странице, чтобы показать многоуровневую навигацию. Пытался адаптировать это решение magento1, но не смог далеко уйти.

Любая идея, как я могу добиться этого в magento2. Что я сделал до сих пор, как показано ниже:

Расширенный блок Каталог списка товаров для пользовательского списка товаров на моей странице.

class View extends \Magento\Catalog\Block\Product\ListProduct
{


    public function __construct(
    \Magento\Catalog\Block\Product\Context $context,
    \Magento\Framework\Data\Helper\PostHelper $postDataHelper,
    \Magento\Catalog\Model\Layer\Resolver $layerResolver,
    CategoryRepositoryInterface $categoryRepository,
    \Magento\Framework\Url\Helper\Data $urlHelper,
    array $data = [],
    \Custom\LayerNavigation\Model\Layer $testlayerobj
    ) {
        parent::__construct($context,$postDataHelper,$layerResolver,
        $categoryRepository,$urlHelper,$data);
        $this->_coreRegistry = $context->getRegistry();
        $this->_testlayer = $testlayerobj;
    }

    protected function _getProductCollection()
    {
        if ($this->_productCollection === null) {
          $this->_productCollection = $this->getLayer()->getProductCollection();
        }
        return $this->_productCollection;
     }

    public function getLayer()
    {

       $layer = $this->_coreRegistry->registry('current_layer');
       if ($layer) {
          return $layer;
        }
        return $this->_testlayer;
     }

}

Использовал основной блок поиска для многоуровневой навигации в файле макета.

<referenceContainer name="sidebar.main">
        <block class="Magento\LayeredNavigation\Block\Navigation\Search" name="catalogsearch.leftnav" before="-" template="layer/view.phtml">
            <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalogsearch.navigation.state" as="state" />
            <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalogsearch.navigation.renderer" as="renderer" template="layer/filter.phtml"/>
        </block>
</referenceContainer>

Модель расширенного основного слоя для изменения коллекции.

class Layer extends \Magento\Catalog\Model\Layer
{
    public function __construct(
      optionStoreFactory  $optionstoreFactory,
      Attrhelper $attrhelper,
      productCollectionFactory $productCollectionFactory,
      AttributevalueFactory $attributevalueFactory,
      CategoryRepositoryInterface $categoryRepository,
      \Magento\Store\Model\StoreManagerInterface $storeManager,
      \Magento\Framework\App\Request\Http $request,
      \Magento\Framework\Registry $registry,
      \Magento\Catalog\Model\Layer\Search\CollectionFilter $filter,
      array $data = []
    ) {
       $this->optionstoreFactory       = $optionstoreFactory;
       $this->attributevalueFactory    = $attributevalueFactory;
       $this->_attrhelper              = $attrhelper;
       $this->request                  = $request;
       $this->productCollectionFactory = $productCollectionFactory;
       $this->categoryRepository = $categoryRepository;
       $this->_storeManager = $storeManager;
       $this->filter = $filter;
       $this->registry = $registry;
    }

    public function getProductCollection()
    {
        $attributevalue = $this->getAttributeValue(); //eg: Manufacturer Attribute details
        $attr_code = $attributevalue->getAttributeCode();
        $attr_option_value = $attributevalue->getOptionId();
        $collection = $this->productCollectionFactory->create();
        $store_id = $this->request->getParam('store_id');
        $collection->addAttributeToSelect('*')
               ->addAttributeToFilter($attr_code , ['finset'=>$attr_option_value ])
               ->addAttributeToFilter('visibility','4')
               ->setStore($store_id)
               ->addFieldToFilter('status', array('eq' => 1))
               ->setOrder('id', 'DESC');
        $this->prepareProductCollection($collection);
        return $collection;
    }

    public function prepareProductCollection($collection)
    {
        $this->filter->filter($collection, $this->getCurrentCategory());
       return $this;
    }

    public function getCurrentCategory()
    {
       $category = $this->registry->registry('current_category');
       if ($category) {
           $this->setData('current_category', $category);
       } else {
           $category = $this->categoryRepository->get($this->getCurrentStore()->getRootCategoryId());
       $this->setData('current_category', $category);
       }
      return $category;
    }

    public function getCurrentStore()
    {
        return $this->_storeManager->getStore();
    }

}

На данный момент у меня отображается навигация по слоям, но она не специфична для моей пользовательской коллекции. Согласно моей отладке, коллекция фильтруется полностью из корневой категории (которая содержит весь каталог продуктов) и, в соответствии с ней, извлекает слои.

mp196
источник
Любое предложение будет высоко ценится!
mp196
Вы нашли какое-нибудь решение для этого? Если это так, пожалуйста, поделитесь своим ответом
Nalin Savaliya
Ребята, есть кто-нибудь из вас имеет какое-либо руководство по этому вопросу?
aton1004
Мне удалось получить многоуровневую навигацию на пользовательской странице для моей пользовательской коллекции ... Потребуется некоторое время, чтобы опубликовать решение здесь, поскольку оно очень длинное (возможно, через день или два я опубликую решение)
mp196

Ответы:

3

Мне удалось выполнить навигацию с приведенными ниже изменениями, возможно, я не совсем правильно с этим решением, поэтому комментарии и предложения приветствуются.

1) Подготовьте di.xml для раздела веб-интерфейса, как показано ниже:

<!-- Magento only includes 2 type of layer resolvers i.e Category and search whereas our custom page is neither a category page nor a search page so we need to add a new layer resolver on our custom page-->
<type name="Magento\Catalog\Model\Layer\Resolver">
    <arguments>
        <argument name="layersPool" xsi:type="array">
            <item name="category" xsi:type="string">Magento\Catalog\Model\Layer\Category</item>
            <item name="search" xsi:type="string">Magento\Catalog\Model\Layer\Search</item>
            <item name="customlayer" xsi:type="string">Custom\Navigation\Model\Layer</item>
        </argument>
    </arguments>
</type>

<!-- To prepare the filterlist for our custom collection which would be passed to the left navigation we need below virtual types for our custom page navigation -->
<virtualType name="customFilterList" type="Custom\Navigation\Model\Layer\FilterList">
    <arguments>
        <argument name="filterableAttributes" xsi:type="object">Custom\Navigation\Model\Layer\FilterableAttributeList</argument>
        <argument name="filters" xsi:type="array">
            <item name="attribute" xsi:type="string">Custom\Navigation\Model\Layer\Filter\Attribute</item>
            <item name="category" xsi:type="string">Custom\Navigation\Model\Layer\Filter\Category</item>
        </argument>
    </arguments>
</virtualType>

<!-- once the filter list virtual type is ready we can pass the same to our navigation , I have prepared the virtual type of the core navigation for my custom module and have passed the custom filter list to it -->
<virtualType name="Custom\Navigation\Block\Navigation\Custnavigation" type="Magento\LayeredNavigation\Block\Navigation">
    <arguments>
        <argument name="filterList" xsi:type="object">customFilterList</argument>
    </arguments>
</virtualType>

<!-- As we will be modifying the layer model collection we will need to extend the core model layer, Below virtual type will be required to extend the Catalog model layer else it will throw error for the context in construct method-->
<virtualType name="Custom\Navigation\Model\Layer\Context" type="Magento\Catalog\Model\Layer\Context">
    <arguments>
        <argument name="collectionProvider" xsi:type="object">Custom\Navigation\Model\Layer\ItemCollectionProvider</argument>
        <argument name="stateKey" xsi:type="object">Custom\Navigation\Model\Layer\StateKey</argument>
        <argument name="collectionFilter" xsi:type="object">Custom\Navigation\Model\Layer\CollectionFilter</argument>
    </arguments>
</virtualType>
<type name="Custom\Navigation\Model\Layer">
    <arguments>
        <argument name="context" xsi:type="object">Custom\Navigation\Model\Layer\Context</argument>
    </arguments>
</type>

2) Файл слоя модели: Расширьте файл слоя модели своей пользовательской моделью и измените коллекцию.

namespace Custom\Navigation\Model;
class Layer extends \Magento\Catalog\Model\Layer
{

//Apart from the default construct argument you need to add your model from which your product collection is fetched.

    public function __construct(
        \Magento\Catalog\Model\Layer\ContextInterface $context,
        \Magento\Catalog\Model\Layer\StateFactory $layerStateFactory,
        AttributeCollectionFactory $attributeCollectionFactory,
        \Magento\Catalog\Model\ResourceModel\Product $catalogProduct,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Registry $registry,
        CategoryRepositoryInterface $categoryRepository,
        array $data = []
    ) {
    parent::__construct(
            $context,
            $layerStateFactory,
            $attributeCollectionFactory,
            $catalogProduct,
            $storeManager,
            $registry,
            $categoryRepository,
            $data
        );
    }

    public function getProductCollection()
    {

        /*Unique id is needed so that when product is loaded /filtered in the custom listing page it will be set in the
         $this->_productCollections array with unique key else you will not get the updated or proper collection.
        */

        if (isset($this->_productCollections['some_uinique_id'])) {
            $collection = $this->_productCollections['some_uinique_id'];
        } else {
            //$collection = Your logic to get your custom collection.
            $this->prepareProductCollection($collection);
            $this->_productCollections['some_unique_id'] = $collection;
        }

        return $collection;
    }

3) Я расширил файлы ниже, используемые в di.xml (сохранил файл, просто расширив его, не создавал экземпляр метода construct, так как я не требовал никаких изменений в этом файле, если нужно, вы можете изменить определенную функцию в расширенном файле соответственно), в настоящее время решение, которое я применил, не смогло решить проблему с фильтром категорий, оно все еще включает фильтры корневой категории, поэтому пришлось сделать обходной путь, чтобы включить в него фасетированные данные (обходной путь, упомянутый в 4-м пункте)

- Custom\Navigation\Model\Layer\FilterList extends
           \Magento\Catalog\Model\Layer\FilterList



 - Custom\Navigation\Model\Layer\FilterableAttributeList extends
   \Magento\Catalog\Model\Layer\Category\FilterableAttributeList



 - Custom\Navigation\Model\Layer\Filter\Attribute extends
   \Magento\Catalog\Model\Layer\Filter\Attribute



 - Custom\Navigation\Model\Layer\Filter\Category extends
   \Magento\CatalogSearch\Model\Layer\Filter\Category (Why catalog
           search is used i have mentioned the same in 4th point)



 - Custom\Navigation\Model\Layer\ItemCollectionProvider extends
   \Magento\Catalog\Model\Layer\Category\ItemCollectionProvider



 - Custom\Navigation\Model\Layer\StateKey extends
   \Magento\Catalog\Model\Layer\Category\StateKey



 - Custom\Navigation\Model\Layer\CollectionFilter extends
   \Magento\Catalog\Model\Layer\Category\CollectionFilter

4) Пришлось сделать обходной путь для фильтра категорий в многоуровневой навигации, поскольку он не показывал опцию в отношении отфильтрованной коллекции. Если кто-то найдет решение, обновите его. Ниже приведен код, который я использовал, чтобы исправить ошибку с фацетированными данными, с которой я столкнулся при включении категории в мой список пользовательских фильтров. Из-за применения этого патча опция категории не отображалась в моей навигации, остальные фильтры соответствовали моей коллекции.

namespace Custom\Navigation\Model\Layer\Filter;

/**
 * Layer category filter
 */
class Category extends \Magento\CatalogSearch\Model\Layer\Filter\Category
{
     /**
      * @var \Magento\Framework\Escaper
      */
    private $escaper;

    /**
     * @var CategoryDataProvider
     */
    private $dataProvider;

    /**
     * @param \Magento\Catalog\Model\Layer\Filter\ItemFactory $filterItemFactory
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Catalog\Model\Layer $layer
     * @param \Magento\Catalog\Model\Layer\Filter\Item\DataBuilder $itemDataBuilder
     * @param \Magento\Catalog\Model\CategoryFactory $categoryFactory
     * @param \Magento\Framework\Escaper $escaper
     * @param CategoryManagerFactory $categoryManager
     * @param array $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\Escaper $escaper,
        \Magento\Catalog\Model\Layer\Filter\DataProvider\CategoryFactory $categoryDataProviderFactory,
        array $data = []
    ) {
        parent::__construct(
            $filterItemFactory,
            $storeManager,
            $layer,
            $itemDataBuilder,
            $escaper,
            $categoryDataProviderFactory,
            $data
        );
        $this->_escaper = $escaper;
        $this->_requestVar = 'cat';
        $this->dataProvider = $categoryDataProviderFactory->create(['layer' => $this->getLayer()]);
    }

    /**
     * Get data array for building category filter items
     *
     * @return array
     */
    protected function _getItemsData()
    {
        /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */
        $productCollection = $this->getLayer()->getProductCollection();

        $optionsFacetedData = '' ;// $productCollection->getFacetedData('category'); (Here i have set $optionsFacetedData as blank so that category option will not be included in layered navigation)
        $category = $this->dataProvider->getCategory();
        $categories = $category->getChildrenCategories();

        $collectionSize = $productCollection->getSize();

        if ($category->getIsActive()) {
            foreach ($categories as $category) {
                if ($category->getIsActive()
                    && isset($optionsFacetedData[$category->getId()])
                    && $this->isOptionReducesResults($optionsFacetedData[$category->getId()]['count'], $collectionSize)
                ) {
                    $this->itemDataBuilder->addItemData(
                        $this->escaper->escapeHtml($category->getName()),
                        $category->getId(),
                        $optionsFacetedData[$category->getId()]['count']
                    );
                }
            }
        }
        return $this->itemDataBuilder->build();
    }
}

5) Когда ваша пользовательская страница загружается в ваш метод execute контроллера, вам нужно установить свой пользовательский слой, который мы добавили в di.xml вместе с категорией и поисковым слоем.

 - include the below argument in your controller construct method.

     "\Magento\Catalog\Model\Layer\Resolver $layerResolver",

 - inside execute method set your custom layer resolver for your module.

    $this->layerResolver->create('customlayer');

6) В вашем макете XML-файл для пользовательской страницы добавьте ниже код в разделе тела.

<attribute name="class" value="page-with-filter"/>

<referenceContainer name="sidebar.main">
<!-- below is the virtual type of the core navigation we created -->
    <block class="Custom\Navigation\Block\Navigation\Custnavigation" name="custom.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml">
        <block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalog.navigation.state" as="state" />
        <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/>
    </block>
</referenceContainer>
mp196
источник
Я использую ваш код с пользовательской коллекцией в magento 2.1.7. но я получаю пустую страницу. Я думаю, что Custom \ Navigation \ Block \ Navigation \ Custnavigation отсутствует в вышеуказанных кодах. Можете ли вы дать мне полный код?
Магический код
нет, такого блока нет, вместо этого я создал для него виртуальный тип, вы можете проверить код di.xml, а код, размещенный здесь, сам является полным кодом.
mp196
Хорошо, вы можете отправить мне почтовый индекс всего кода?
Магический код
Извините, я не могу отправить почтовый индекс, так как указанный выше код был лишь небольшой частью платного расширения, которое мы разработали.
mp196
1
проверьте, добавили ли вы $ this-> layerResolver = $ layerResolver; в вашей конструкции для класса добавлен в конструктор файлов. \ Magento \ Каталог \ Модель \ Layer \ Resolver $ layerResolver
mp196
2

Я успешно применил свою собственную коллекцию продуктов на многоуровневой навигации и панели инструментов на странице категории.

Например, я покупаю коллекцию тех товаров, цена которых меньше 100.

Шаг 1: Добавьте приведенный ниже фрагмент кода

Приложение / код / ​​Vendor / модуль / и т.д. / di.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <type name="Magento\Catalog\Model\Layer">
        <plugin name="custom_product_model_layer" type="Vendor\Module\Plugin\Layer" />
    </type>

    <type name="Magento\Catalog\Block\Product\ProductList\Toolbar">
        <plugin name="custom_product_toolbar" type="Vendor\Module\Plugin\Toolbar" />
    </type>

    <virtualType name="categoryFilterList" type="Magento\Catalog\Model\Layer\FilterList">
        <arguments>
            <argument name="filters" xsi:type="array">
                <item name="attribute" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Attribute</item>
                <item name="price" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Price</item>
                <item name="decimal" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Decimal</item>
                <item name="category" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Category</item>
            </argument>
        </arguments>
    </virtualType>

</config>

Шаг 2: Создать плагин для сбора продуктов

Приложение / код / ​​Vendor / модуль / плагин / Layer.php

<?php
namespace Vendor\Module\Plugin;
class Layer
{
  public function aroundGetProductCollection(
    \Magento\Catalog\Model\Layer $subject,
    \Closure $proceed
  ) {

    $result = $proceed();
    $result->addAttributeToFilter('price', array('lt' => 100));
    return $result;
  }
}

Шаг 3: Создать плагин для панели инструментов

Приложение / код / ​​Vendor / модуль / плагин / Toolbar.php

<?php
namespace Vendor\Module\Plugin;
class Toolbar
{

  protected $_objectManager;
  protected $request;

  public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectmanager,
    \Magento\Framework\App\Request\Http $request
  ) {
    $this->_objectManager = $objectmanager;
    $this->request = $request;
  }

  public function aroundSetCollection(
    \Magento\Catalog\Block\Product\ProductList\Toolbar $subject,
    \Closure $proceed,
    $request
  ) {
    $result = $proceed($request);

    $this->_collection = $request;
    $category = $this->_objectManager->get('Magento\Framework\Registry')->registry('current_category');
    if($category)
    {
      $page = $this->request->getParam('p');
      if($page == '')
      {
        $page = 1;
      }
      $this->_collection->getCurPage();
      $this->_collection->setCurPage($page);  
    }
    //else
    //{
    //  $this->_collection->setCurPage($this->getCurrentPage());
    //}

    return $result;
  }

}
Динеш Ядав
источник
Здравствуйте, я использовал ваш код, он обновляет коллекцию продуктов Correclty, но он не обновляет список FIlterList, такой как категория, цена, атрибуты, а также количество их продуктов. Можете ли вы помочь в этом, его срочно для меня? @DineshYadav
Суджит Пандит
Ваше решение сработало отлично. Я добавил несколько дополнительных звонков, чтобы прослушать определенные категории, а затем полностью переписать их коллекцию.
pixiemedia
@pixiemedia Вы можете подтвердить мой ответ, если он сработал для вас ☺️
Динеш Ядав,
Кстати, я уже нашел небольшую проблему - в коде плагина последняя часть после последней ломает результаты поиска. комментарий, который выдал
pixiemedia
перестал работать на M2.2 - ошибка sql SQLSTATE [42S22]: столбец не найден: 1054 Неизвестный столбец «e.min_price» в «списке полей» - разработка решения
pixiemedia
0

Мне удалось получить многоуровневую навигацию, в основном работающую для пользовательской коллекции продуктов на пользовательской странице. Я загрузил исходный код для моего модуля здесь . Единственные проблемы состоят в том, что ценовой фильтр не отображает правильное количество товаров. Из-за этого я изменил многоуровневое представление навигации, чтобы скрыть фильтр «Цена». Но поскольку я использовал пользовательский файл представления, по некоторым причинам фильтры на боковой панели больше не могут быть свернуты.

Если кто-то может решить эту проблему, не стесняйтесь сделать запрос на извлечение. Я также пытаюсь выяснить, как реализовать это для страницы CMS, созданной с помощью бэкэнда Magento 2, а не для страницы, созданной вручную с помощью xml.

Р. Таунли
источник