Ajout d'un lien sans catégorie aux liens de navigation dans magento 2

29

Je ne sais pas ce que je fais mal ici. Le bloc qui contient les liens de catégorie est appelé navigation.sections. J'ai pensé qu'en dirigeant les arguments suivants vers le conteneur, je pourrais créer un nouveau lien sous celui-ci. Toute aide est appréciée.

<referenceContainer name="navigation.sections">
            <block class="Magento\Framework\View\Element\Html\Links" name="mylink">
                    <arguments>
                        <argument name="label" xsi:type="string">Mylink</argument>
                        <argument name="path" xsi:type="string">mypath</argument>
                        <argument name="css_class" xsi:type="string">mycss</argument>
                    </arguments>
            </block>
</referenceContainer>
themanwhoknowstheman
la source
Je me demande la même chose. Avez-vous trouvé une solution?
Les deux solutions énumérées ont fonctionné pour moi.
themanwhoknowstheman
Sur quelle version de Magento travaillez-vous?
Razvan Zamfir,

Réponses:

34

[EDIT]
Apparemment, dans les dernières versions de M2, cela ne fonctionne plus.
Merci à Max de l'avoir signalé.
Pour une version ultérieure, vous devez ajouter un plugin pour Magento\Theme\Block\Html\Topmenuau lieu d'un observateur.
Ajoutez ceci àetc/frontend/di.xml

<type name="Magento\Theme\Block\Html\Topmenu">
    <plugin name="[module]-topmenu" type="[Namespace]\[Module]\Plugin\Block\Topmenu" />
</type>

et créer le fichier de classe de plugin [Namespace]/[Module]/Plugin/Block/Topmenu.php

<?php 

namespace [Namespace]\[Module]\Plugin\Block;

use Magento\Framework\Data\Tree\NodeFactory;

class Topmenu
{
    /**
     * @var NodeFactory
     */
    protected $nodeFactory;

    public function __construct(
        NodeFactory $nodeFactory
    ) {
        $this->nodeFactory = $nodeFactory;
    }

    public function beforeGetHtml(
        \Magento\Theme\Block\Html\Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
        $node = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray(),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $subject->getMenu()->addChild($node);
    }

    protected function getNodeAsArray()
    {
        return [
            'name' => __('Label goes here'),
            'id' => 'some-unique-id-here',
            'url' => 'http://www.example.com/',
            'has_active' => false,
            'is_active' => false // (expression to determine if menu item is selected or not)
        ];
    }
}

[/ EDIT]
Réponse originale:
Vous pouvez ajouter des éléments au menu supérieur en utilisant l'événement page_block_html_topmenu_gethtml_before.

Vous devez donc créer un module avec ces fichiers (tous les fichiers doivent être dedans app/code/[Namespace]/[Module]):

etc/module.xml - le fichier de déclaration du module

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="[Namespace]_[Module]" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Theme"/>
        </sequence>
    </module>
</config>

registration.php - le dossier d'inscription

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    '[Namespace]_[Module]',
    __DIR__
);

etc/frontend/events.xml - le fichier de déclaration des événements

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="page_block_html_topmenu_gethtml_before">
        <observer name="[namespace]_[module]_observer" instance="[Namespace]\[Module]\Observer\Topmenu" />
    </event>
</config>

Observer/Topmenu.php - l'observateur réel

<?php
namespace [Namespace]\[Module]\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\Event\ObserverInterface;
class Topmenu implements ObserverInterface
{
    public function __construct(
        ...//add dependencies here if needed
    )
    {
    ...
    }
    /**
     * @param EventObserver $observer
     * @return $this
     */
    public function execute(EventObserver $observer)
    {
        /** @var \Magento\Framework\Data\Tree\Node $menu */
        $menu = $observer->getMenu();
        $tree = $menu->getTree();
        $data = [
            'name'      => __('Menu item label here'),
            'id'        => 'some-unique-id-here',
            'url'       => 'url goes here',
            'is_active' => (expression to determine if menu item is selected or not)
        ];
        $node = new Node($data, 'id', $tree, $menu);
        $menu->addChild($node);
        return $this;
    }
}

Maintenant, exécutez dans le cli php bin/magento setup:upgradepour installer le module et vous êtes prêt à partir.

Marius
la source
Topmenu.php manque-t-il une partie du code?
themanwhoknowstheman
1
@Solide. L'ordre des liens dépend de l'ordre d'exécution des observateurs. Si votre observateur de page d'accueil est exécuté avant celui du catalogue, le lien de page d'accueil doit être ajouté en premier. Sinon, vous pouvez jeter un œil à cette approche pour changer l'ordre des liens: magento.stackexchange.com/q/7329/146 . l'approche est pour Magento1, mais vous pouvez le traduire en code M2.
Marius
1
@Marius: quel devrait être le 'is_active'. Veuillez ajouter un exemple. je veux un lien actif sur cette page.
zed Blackbeard
1
Un observateur est utilisé sur un événement. Un plugin peut fonctionner sur n'importe quelle méthode publique. Je conseillerais d'utiliser l'approche plugin car celle-ci est utilisée dans le noyau pour ajouter les catégories au menu supérieur.
Marius
1
Désolé, je me sens comme un idiot, mais comment pouvez-vous ajouter plus d'un menu? Si j'utilise $menu->addChild($node)plus d'une fois, la dernière remplace les autres. Il ne montre qu'un seul menu (le dernier).
pinicio
17

Pourquoi tout le monde veut toujours écrire un module. Je l'ai fait dans mon layout.xmlet cela a fonctionné comme un charme:

    <referenceBlock name="catalog.topnav">
        <block class="Magento\Framework\View\Element\Html\Link" name="contact-link">
            <arguments>
                <argument name="label" xsi:type="string" translate="true">Contact us</argument>
                <argument name="path" xsi:type="string" translate="true">contact</argument>
            </arguments>
        </block>
    </referenceBlock>
Johnny Longneck
la source
comment ouvrir ce lien dans un nouvel onglet?
jafar pinjar
Bonne question. J'ai trouvé quelque chose dans le code. Essayez peut-être ceci: <argument name = "attributes" xsi: type = "array"> <item name = "target" xsi: type = "string"> _ blank </item> </argument> Non testé, mais il y a le option d'attributs disponible.
Johnny Longneck
La création d'un module le rend beaucoup plus dynamique. Beaucoup de clients avec lesquels je travaille veulent faire des choses comme dans ce cas, créer des pages et les ajouter au menu supérieur dans un ordre spécifique.
Roy Jeurissen
6

Une autre solution en dehors de la création d'un module est d'écraser topmenu.phtml. Je noterai que la solution fournie par @Marius est la meilleure façon de le faire si vous souhaitez que vos liens héritent des classes de navigation. Cela apparaît dans le menu mobile de Magento, juste sans le bon CSS. Vous pouvez utiliser l'argument css_class pour styliser en conséquence.

YourTheme / Magento_Theme / templates / html / topmenu.phtml

<?php $columnsLimit = $block->getColumnsLimit() ?: 0; ?>
<?php $_menu = $block->getHtml('level-top', 'submenu', $columnsLimit) ?>

<nav class="navigation" role="navigation">
    <ul data-mage-init='{"menu":{"responsive":true, "expanded":true, "position":{"my":"left top","at":"left bottom"}}}'>
        <?php /* @escapeNotVerified */ echo $_menu; ?>
        <?php echo $block->getChildHtml() ?>
    </ul>
</nav>

YourTheme / Magento_Theme / layout / default.xml

<referenceContainer name="catalog.topnav">
               <block class="Magento\Framework\View\Element\Html\Link\Current" name="your.link">
                    <arguments>
                        <argument name="label" xsi:type="string">Link-name</argument>
                        <argument name="path" xsi:type="string">Link-url</argument>
                    </arguments>
              </block>
</referenceContainer>
themanwhoknowstheman
la source
Où puis-je trouver un exemple de l'argument de classe css?
camdixon
comment lier un fichier modèle à un fichier xml.
Sarvesh Tiwari
6

Cette réponse est fournie par Marius ♦ je viens de la modifier pour ajouter une catégorie enfant dans le menu de l'onglet catégorie, vous pouvez vous référer à la réponse de Marius ♦. Je viens de modifier le fichier enfant Topmenu.php pour ajouter une catégorie enfant dans la catégorie principale

<?php 

namespace Ktpl\Navigationlink\Plugin\Block;

use Magento\Framework\UrlInterface;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Store\Model\StoreManagerInterface;

class Topmenu
{
    /**
     * @var NodeFactory
     */
    protected $nodeFactory;
    protected $urlBuilder;
    protected $_storeManager;

    public function __construct(
        UrlInterface $urlBuilder,
        NodeFactory $nodeFactory,
        StoreManagerInterface $storeManager
    ) {
        $this->urlBuilder = $urlBuilder;
        $this->nodeFactory = $nodeFactory;
        $this->_storeManager = $storeManager;
    }

    public function beforeGetHtml(
        \Magento\Theme\Block\Html\Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
        // condition for store
        if($this->getStoreCode() == 'store_id'):
        $productNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Products','products'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $stockistsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Stockists','stockists'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $ourstoryNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Story','ourstory'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $contactsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Customer Care','contacts'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        /******* contacts's child *******/
        $warrantyRegistrationNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Warranty Registration','warranty-registration'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $faqNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Frequently Asked Questions','faq'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $ourProductGuaranteeNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Product Guarantee','our-product-guarantee'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $warrantiesNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Warranties, Repairs & Spare Parts','warranties-repairs-spare-parts'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $termsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Terms & Conditions','terms-and-conditions'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $privacyPolicyNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Privacy Policy','privacy-policy'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $bookNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Book A Viewing','book-a-viewing'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );

        $contactsNode->addChild($warrantyRegistrationNode);
        $contactsNode->addChild($faqNode);
        $contactsNode->addChild($ourProductGuaranteeNode);
        $contactsNode->addChild($warrantiesNode);
        $contactsNode->addChild($termsNode);
        $contactsNode->addChild($privacyPolicyNode);
        $contactsNode->addChild($bookNode);
        /******* end contacts's child *******/

        $subject->getMenu()->addChild($productNode);
        $subject->getMenu()->addChild($stockistsNode);
        $subject->getMenu()->addChild($ourstoryNode);
        $subject->getMenu()->addChild($contactsNode);
        endif;
    }

    protected function getNodeAsArray($name,$id)
    {
        return [
            'name' => __($name),
            'id' => $id,
            'url' => $this->urlBuilder->getUrl($id),
            'has_active' => false,
            'is_active' => false // (expression to determine if menu item is selected or not)
        ];
    }

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

Vous devez créer un nœud pour la catégorie parent et pour la catégorie enfant et après cela, vous pouvez affecter une catégorie enfant à la catégorie parent en utilisant la méthode addChild voici un exemple

$contactsNode->addChild($warrantyRegistrationNode);
Vaibhav Ahalpara
la source
Merci! Je ne savais pas que c'était aussi simple d'ajouter un sous-menu!
Juliano Vargas
et monsieur si je veux montrer ma div personnalisée sur le lien personnalisé que j'ai ajouté Topmenu. Comme lorsque je passe la souris sur le lien, cela montre ma div
Asad Khan
1

En utilisant la réponse ci-dessus de Marius, j'ai ajouté des éléments de sous-menu. Je montre également un moyen de modifier l'arborescence avant la création du HTML, puis comment modifier le HTML directement une fois qu'il est créé. Cela fonctionne dans Magento 2.1. Mettez à jour Topmenu.php avec ceci:

<?php
namespace Seatup\Navigation\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\Event\ObserverInterface;
class Topmenu implements ObserverInterface
{
    protected $_cmsBlock;

    public function __construct(
        \Magento\Cms\Block\Block $cmsBlock
    )
    {
        $this->_cmsBlock = $cmsBlock;
    }
    /**
     * @param EventObserver $observer
     * @return $this
     */
    public function execute(EventObserver $observer)
    {
        /** @var \Magento\Framework\Data\Tree\Node $menu */
        $eventName = $observer->getEvent()->getName();
        if($eventName == 'page_block_html_topmenu_gethtml_before'){
            // With the event name you can edit the tree here
            $menu = $observer->getMenu();
            $tree = $menu->getTree();
            $children = $menu->getChildren();

            foreach ($children as $child) {
                if($child->getChildren()->count() > 0){ //Only add menu items if it already has a dropdown (this could be removed)
                    $childTree = $child->getTree();
                    $data1 = [
                        'name'      => __('Menu item label here'),
                        'id'        => 'some-unique-id-here',
                        'url'       => 'url goes here',
                        'is_active' => FALSE
                    ];
                    $node1 = new Node($data1, 'id', $childTree, $child);
                    $childTree->addNode($node1, $child);
                }
            }
            return $this;
        } else if($eventName == 'page_block_html_topmenu_gethtml_after'){
            // With the event name you can edit the HTML output here
            $transport = $observer['transportObject'];

            //get the HTML
            $old_html = $transport->getHtml();

            //render the block. I am using a CMS block
            $new_output = $this->_cmsBlock->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('cms_block_identifier')->toHtml();
            //the transport now contains html for the group/class block
            //which doesn't matter, because we already extracted the HTML into a 
            //string primitive variable
            $new_html = str_replace('to find', $new_output , $old_html);    
            $transport->setHtml($new_html);
        }
    }
}
Cypher909
la source
1

Vous souhaitez ajouter un lien vers la navigation supérieure dans <header>
Ajouter un lien vers la page CMS, Galerie

Editez / placez default.xml ici:

app/design/frontend/Vendor/theme/Magento_Theme/layout/default.xml

Ajoutez le code suivant:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="catalog.topnav">
           <block class="Magento\Framework\View\Element\Html\Link\Current" name="gallery.link">
                <arguments>
                    <argument name="label" xsi:type="string">Gallery</argument>
                    <argument name="path" xsi:type="string">gallery</argument>
                </arguments>
          </block> 
       </referenceContainer>
    </body>
</page>

Cela ajoute un lien vers la page CMS, Galerie, avec les paramètres suivants:

Title = Gallery
Url Key = gallery
Link = https://example.com/gallery/

Ajoutez le style suivant pour vous assurer que le nouveau lien s'aligne correctement:

.navigation .nav.item {
margin: 0 10px 0 0;
display: inline-block;
position: relative;
}

Résultats du code (Les produits sont configurés en tant que catégorie pour un exemple)

Joshua34
la source
0

Pour ceux qui cherchent à ajouter de l' is_activeexpression, en particulier @zed Blackbeard qui a demandé ci-dessus.

J'ai l'habitude de lier le contact et cela fonctionnera également avec le module personnalisé car je le relie à un.

'is_active' => ($ this-> request-> getFrontName () == 'contact'? true: false)

// (expression pour déterminer si l'élément de menu est sélectionné ou non)

J'espère que cela aide n'importe qui.

Juliano Vargas
la source
0

C'est aussi une bonne option:

app / design / frontend / Vender / yourtheme / Magento_Theme / layout / default.xml

<referenceBlock name="header.links">
    <block class="Magento\Framework\View\Element\Html\Link" name="yourlinkname" before='wish-list-link'>
        <arguments>
            <argument name="label" xsi:type="string" translate="true">yourlink</argument>
            <argument name="path" xsi:type="string" translate="true">yourlink</argument>
        </arguments>
    </block>
</referenceBlock>
Sarfaraz bheda
la source
-1

Juste pour un lien de menu de navigation, il n'y a pas beaucoup d'étapes à réaliser, j'ai trouvé un court tutoriel pour le faire, cela implique un thème qui remplace le topmenu.phtmlfichier du Magento_Thememodule: https://linkstraffic.net/adding-custom- menu-item-inside-magento2 / Je l'ai testé avec succès, donc je le partage avec vous.

Jimmy
la source
Bienvenue dans Magento SE. Si vous postez des liens dans une réponse, assurez-vous que la réponse est toujours valable, si le lien devient mort à un moment donné: par exemple, résumez l'article lié ou citez les parties pertinentes. Ceci est important car StackExchange vise à être une base de données de connaissances, pas un forum de support qui aide une personne en ce moment. Les futurs visiteurs devraient toujours bénéficier des questions et réponses.
Siarhey Uchukhlebau