Navigation en couches pour une collection personnalisée sur une page personnalisée - magento2

12

Je travaille à récupérer la navigation en couches dans magento2 pour une collection de produits personnalisée. Je reçois déjà une collection personnalisée sur la page personnalisée pour montrer la navigation en couches. J'ai essayé d'adapter cette solution magento1 mais je n'ai pas pu aller loin.

Toute idée comment pourrais-je y arriver dans magento2. Ce que j'ai fait jusqu'à présent est le suivant:

Extension du bloc Catalog ListProduct pour la liste de produits personnalisée sur ma page personnalisée.

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

}

Utilisé le bloc de recherche principal pour la navigation en couches dans le fichier de disposition

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

Modèle de couche Core étendu pour modifier la collection.

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

}

À partir de maintenant, la navigation des calques s'affiche, mais elle n'est pas spécifique à ma collection personnalisée. Selon mon débogage, la collection est filtrée de la catégorie racine (qui contient le catalogue de produits complet) et, selon elle, récupère les couches.

mp196
la source
Toute suggestion serait très appréciée ..!
mp196
Avez-vous trouvé une solution? Si oui, partagez votre réponse
Nalin Savaliya
Les gars, l'un de vous a-t-il une piste?
aton1004
J'ai réussi à obtenir la navigation en couches sur une page personnalisée pour ma collection personnalisée ... Il faudra du temps pour publier la solution ici car elle est très longue (probablement dans un jour ou deux, je publierai la solution)
mp196

Réponses:

3

J'ai pu réaliser la navigation avec les modifications ci-dessous, je ne suis peut-être pas complètement correct avec cette solution, donc les commentaires et suggestions sont les bienvenus.

1) Préparez di.xml pour la section frontend comme ci-dessous:

<!-- 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) Fichier de couche de modèle: étendez le fichier de couche de modèle avec votre modèle personnalisé et modifiez la collection.

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) J'ai étendu les fichiers ci-dessous tels qu'utilisés dans di.xml (j'ai gardé le fichier en étendant simplement je n'ai pas instancié la méthode de construction car je n'ai pas exigé de changements dans ces fichiers si nécessaire, vous pouvez modifier la fonction spécifique dans le fichier étendu en conséquence), Actuellement, la solution que j'ai appliquée n'a pas pu résoudre le problème de filtre de catégorie, elle inclut toujours les filtres de catégorie racine, donc a dû faire une solution de contournement pour inclure les données à facettes (solution de contournement mentionnée au 4ème point)

- 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) J'ai dû faire une solution de contournement pour le filtre de catégorie dans la navigation en couches car il ne montrait pas d'option par rapport à la collection filtrée, si quelqu'un trouve la solution, veuillez mettre à jour. Ci-dessous, le code que j'ai utilisé pour corriger l'erreur de données à facettes rencontrée lors de l'inclusion de la catégorie dans ma liste de filtres personnalisée.En raison de l'application de ce correctif, l'option de catégorie n'a pas été affichée dans ma navigation, les autres filtres étaient appropriés selon ma collection.

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) Lorsque votre page personnalisée est chargée dans la méthode d'exécution de votre contrôleur, vous devez définir votre couche personnalisée que nous avons ajoutée dans le di.xml avec la catégorie et la couche de recherche.

 - 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) Dans votre fichier xml de mise en page pour la page personnalisée, ajoutez le code ci-dessous dans la section du corps.

<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
la source
J'utilise votre code avec une collection personnalisée dans magento 2.1.7. mais je reçois une page blanche. Je pense que Custom \ Navigation \ Block \ Navigation \ Custnavigation est manquant dans les codes ci-dessus. Pouvez-vous s'il vous plaît me donner le code complet?
Magecode
non, il n'y a pas un tel bloc à la place, j'avais créé un type virtuel pour cela, vous pouvez vérifier le code di.xml et le code affiché ici est le code complet lui-même.
mp196
Ok pouvez-vous s'il vous plaît m'envoyer un zip de code entier?
Magecode
Désolé, vous ne pouvez pas envoyer le zip car le code ci-dessus n'a été qu'une petite partie de l'extension payante que nous avons développée.
mp196
1
vérifiez si vous avez ajouté $ this-> layerResolver = $ layerResolver; dans votre construction pour la classe ajoutée dans le constructeur de fichier. \ Magento \ Catalog \ Model \ Layer \ Resolver $ layerResolver
mp196
2

J'ai appliqué avec succès ma collection de produits personnalisés sur la navigation en couches et la barre d'outils de la page de catégorie.

Par exemple, je récupère la collection de ces produits dont le prix est inférieur à 100.

Étape 1: ajouter l'extrait de code ci-dessous

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>

Étape 2: créer un plugin pour la collection de produits

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

Étape 3: créer un plugin pour la barre d'outils

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
la source
Bonjour, j'ai utilisé votre code, il met à jour la collection de produits correctement, mais il ne met pas à jour FIlterList comme la catégorie, le prix, les attributs et également le nombre de leur produit. Pouvez-vous aider pour cela, c'est urgent pour moi ?? @DineshYadav
Sujeet Pandit
votre solution a parfaitement fonctionné. J'ai ajouté quelques cloches et sifflets supplémentaires pour écouter certaines catégories, puis réécrire complètement leur collection.
pixiemedia
@pixiemedia Vous pouvez voter pour ma réponse si cela a fonctionné pour vous ☺️
Dinesh Yadav
j'ai déjà;) a trouvé un petit problème en passant - dans le code du plugin, la dernière partie après la dernière rupture des résultats de recherche. commenter ce bit
pixiemedia
arrêté de travailler sur M2.2 - erreur SQL SQLSTATE [42S22]: Colonne introuvable: 1054 Colonne inconnue 'e.min_price' dans la 'liste des champs' - élaboration de la solution
pixiemedia
0

J'ai pu obtenir une navigation en couches fonctionnant principalement pour une collection de produits personnalisée sur une page personnalisée. J'ai téléchargé le code source de mon module ici . Le seul problème est que le filtre Prix n'affiche pas le nombre de produits correct. Pour cette raison, j'ai modifié la vue de navigation en couches pour masquer le filtre Prix. Mais parce que j'ai utilisé un fichier d'affichage personnalisé, pour une raison quelconque, les filtres de la barre latérale ne peuvent plus être réduits.

Si quelqu'un peut résoudre ce problème, n'hésitez pas à faire une demande d'extraction. J'essaie également de comprendre comment implémenter cela pour une page CMS créée via le backend Magento 2, plutôt que pour une page créée manuellement via xml.

R. Townley
la source