Nawigacja warstwowa niestandardowej kolekcji na niestandardowej stronie - magento2

12

Pracuję nad pobieraniem warstwowej nawigacji w magento2 na potrzeby niestandardowej kolekcji produktów. Dostaję kolekcję niestandardową już na niestandardowej stronie, aby wyświetlić nawigację warstwową. Próbowałem zaadaptować to rozwiązanie magento1, ale nie mogłem za daleko.

Każdy pomysł, jak mogę to osiągnąć w Magento2. Jak dotąd zrobiłem:

Rozszerzyłem blok Lista produktów Lista produktów niestandardowych na mojej stronie niestandardowej.

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;
     }

}

Wykorzystano podstawowy blok wyszukiwania do nawigacji warstwowej w pliku układu

<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>

Rozszerzony model warstwy rdzenia do modyfikacji kolekcji.

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();
    }

}

W tej chwili wyświetla się nawigacja warstwy, ale nie jest ona specyficzna dla mojej kolekcji niestandardowej. Zgodnie z moim debugowaniem kolekcja jest filtrowana od kategorii root (która zawiera cały katalog produktów) i zgodnie z nią pobiera warstwy.

MP196
źródło
Wszelkie sugestie będą mile widziane ..!
mp196
Czy znalazłeś jakieś rozwiązanie? Jeśli tak, proszę podzielić się swoją odpowiedzią
Nalin Savaliya
Chłopaki, czy któryś z was ma w tym jakieś wskazówki?
aton1004
Udało mi się uzyskać nawigację warstwową na niestandardowej stronie mojej kolekcji niestandardowej ... Potrzebuję trochę czasu, aby opublikować rozwiązanie tutaj, ponieważ jest bardzo długie (prawdopodobnie za dzień lub dwa opublikuję rozwiązanie)
mp196

Odpowiedzi:

3

Nawigacja była możliwa dzięki poniższym zmianom, być może nie w pełni poprawiłem to rozwiązanie, więc komentarze i sugestie są mile widziane.

1) Przygotuj plik di.xml dla sekcji interfejsu użytkownika, jak poniżej:

<!-- 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) Plik warstwy modelu: Rozszerz plik warstwy modelu o model niestandardowy i zmodyfikuj kolekcję.

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) Rozszerzyłem poniższe pliki używane w pliku di.xml (Zachowałem plik tylko przez rozszerzenie, nie utworzyłem instancji metody konstruowania, ponieważ nie wymagałem żadnych zmian w tym pliku, jeśli to konieczne, możesz zmodyfikować określoną funkcję w rozszerzonym pliku odpowiednio), obecnie zastosowane przeze mnie rozwiązanie nie było w stanie rozwiązać problemu z filtrem kategorii, nadal obejmuje filtry kategorii głównej, więc musiałem obejść ten problem, aby uwzględnić dane fasetowane (obejście wspomniane w punkcie 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) Musiałem zrobić obejście dla filtru kategorii w nawigacji warstwowej, ponieważ nie wyświetlało ono opcji w odniesieniu do filtrowanej kolekcji. Jeśli ktoś znajdzie rozwiązanie, zaktualizuj. Poniżej znajduje się kod, którego użyłem do naprawienia napotkanego błędu danych faceted podczas dołączania kategorii do mojej listy filtrów niestandardowych. Z powodu zastosowania tej poprawki opcja kategorii nie była wyświetlana w mojej nawigacji, pozostałe filtry były odpowiednie zgodnie z moją kolekcją.

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) Po załadowaniu strony niestandardowej do metody wykonywania kontrolera należy ustawić niestandardową warstwę dodaną w pliku di.xml wraz z warstwą kategorii i wyszukiwania.

 - 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) W pliku XML układu strony niestandardowej dodaj poniższy kod w sekcji treści.

<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
źródło
Używam twojego kodu z niestandardową kolekcją w Magento 2.1.7. ale dostaję pustą stronę. Wydaje mi się, że w powyższych kodach brakuje Custom \ Navigation \ Block \ Navigation \ Custnavigation. Czy możesz podać mi pełny kod?
Magecode
nie, nie ma takiego bloku, zamiast tego stworzyłem dla niego typ wirtualny, możesz sprawdzić kod di.xml, a kod zamieszczony tutaj jest pełnym kodem.
mp196,
Ok, możesz wysłać mi kod pocztowy w całości?
Magecode
Niestety, nie można wysłać pliku zip, ponieważ powyższy kod był tylko małą częścią opracowanego przez nas płatnego rozszerzenia.
mp196,
1
sprawdź, czy dodałeś $ this-> layerResolver = $ layerResolver; w twojej konstrukcji dla klasy dodanej w konstruktorze plików. \ Magento \ Katalog \ Model \ Warstwa \ Resolver $ layerResolver
mp196
2

Z powodzeniem zastosowałem własną kolekcję produktów na warstwowej nawigacji i pasku narzędzi strony kategorii.

Na przykład pobieram kolekcję tych produktów, których cena jest mniejsza niż 100.

Krok 1: Dodaj poniższy fragment kodu

app / code / Vendor / Module / etc / 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>

Krok 2: Utwórz wtyczkę do kolekcji produktów

app / code / Vendor / Module / Plugin / 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;
  }
}

Krok 3: Utwórz wtyczkę do paska narzędzi

app / code / Vendor / Module / Plugin / 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;
  }

}
Dinesh Yadav
źródło
Witaj, Użyłem Twojego kodu To Aktualizuje kolekcję produktów Prawidłowość Ale to nie aktualizuje FIlterList, takich jak kategoria, cena, atrybuty, a także liczba ich produktów. Czy możesz mi pomóc, to jest dla mnie pilne? @DineshYadav
Sujeet Pandit
twoje rozwiązanie działało idealnie. Dodałem kilka dodatkowych dzwonków i gwizdków, aby słuchać niektórych kategorii, a następnie całkowicie przepisałem ich kolekcję.
pixiemedia
@pixiemedia Możesz głosować za moją odpowiedzią, jeśli Ci się udało ☺️
Dinesh Yadav
przy okazji:) znalazłem mały problem - w kodzie wtyczki ostatnia część po ostatniej innej psuje wyniki wyszukiwania. skomentuj to nieco
pixiemedia
przestał działać na M2.2 - błąd SQL SQLSTATE [42S22]: Nie znaleziono kolumny: 1054 Nieznana kolumna „e.min_price” na „liście pól” - wypracowanie rozwiązania
pixiemedia
0

Udało mi się uzyskać nawigację warstwową, która głównie działa w przypadku niestandardowej kolekcji produktów na niestandardowej stronie. Wysłałem kod źródłowy dla mojego modułu tutaj . Jedynym problemem jest to, że filtr cen nie wyświetla prawidłowych liczb produktów. Z tego powodu zmodyfikowałem warstwowy widok nawigacji, aby ukryć filtr cen. Ale ponieważ użyłem niestandardowego pliku widoku, z jakiegoś powodu filtrów na pasku bocznym nie można już zwinąć.

Jeśli ktoś może rozwiązać ten problem, możesz poprosić o pobranie. Próbuję również dowiedzieć się, jak to zaimplementować dla strony CMS utworzonej za pomocą backendu Magento 2, zamiast dla strony utworzonej ręcznie za pomocą xml.

R. Townley
źródło