Install the Navigation module
Edit on GitHubPrerequisites
To prepare your project to work with Navigation:
-
Require the Navigation modules in your
composer.json
. -
Install the new database tables By running
vendor/bin/console propel:diff
. Propel will generate a migration file with the changes. -
Apply the database changes by running
vendor/bin/console propel:migrate
. -
Generate ORM models by running
vendor/bin/console propel:model:build
. -
After running this command you’ll find some new classes in your project under
\Orm\Zed\Navigation\Persistence
namespace.It’s important to make sure that they extend the base classes from the Spryker core, e.g.:
\Orm\Zed\Navigation\Persistence\SpyNavigation
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigation
\Orm\Zed\Navigation\Persistence\SpyNavigationNode
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigationNode
\Orm\Zed\Navigation\Persistence\SpyNavigationNodeLocalizedAttributes
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigationNodeLocalizedAttributes
\Orm\Zed\Navigation\Persistence\SpyNavigationQuery
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigationQuery
\Orm\Zed\Navigation\Persistence\SpyNavigationNodeQuery
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigationNodeQuery
\Orm\Zed\Navigation\Persistence\SpyNavigationNodeLocalizedAttributesQuery
extends\Spryker\Zed\Navigation\Persistence\Propel\AbstractSpyNavigationNodeLocalizedAttributesQuery
-
To get the new transfer objects, run
vendor/bin/console transfer:generate
. -
Make sure that the new Zed UI assets are also prepared for use by running the
npm run zed
command (orantelope build zed
for older versions). -
To make the navigation management UI available in Zed navigation, run the
vendor/bin/console application:build-navigation-cache
command. -
Activate the navigation menu collector by adding the
NavigationMenuCollectorStoragePlugin
to the storage collector plugin stack. To do that, see the following example:
<?php
namespace Pyz\Zed\Collector;
use Spryker\Shared\Navigation\NavigationConfig;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\NavigationCollector\Communication\Plugin\NavigationMenuCollectorStoragePlugin;
// ...
class CollectorDependencyProvider extends SprykerCollectorDependencyProvider
{
/**
* @param \Spryker\Zed\Kernel\Container $container
*
* @return \Spryker\Zed\Kernel\Container
*/
public function provideBusinessLayerDependencies(Container $container)
{
// ...
$container[self::STORAGE_PLUGINS] = function (Container $container) {
return [
// ...
NavigationConfig::RESOURCE_TYPE_NAVIGATION_MENU => new NavigationMenuCollectorStoragePlugin(),
];
};
// ...
}
}
Data setup
You should now be able to manage navigation menus from Zed UI, and the collectors should also be able to export the navigation menus to the KV storage. This is a good time to implement an installer in your project to install a selection of frequently used navigation menus.
Usage in Yves
The KV storage should by now have some navigation menus we can display in our frontend.
The Navigation
module ships with a twig extension that provides the spyNavigation()
twig function which renders a navigation menu.
spyNavigation()
accepts two parameters:
$navigationKey
: Reference of a navigation menu by its key field (for example, “MAIN_NAVIGATION”).$template
: Template path used to render the navigation menu (for example,"@application/layout/navigation/main.twig"
).
To enable the navigation twig function, register \Spryker\Yves\Navigation\Plugin\Provider\NavigationTwigServiceProvider
in your application’s bootstrap.
<?php
namespace Pyz\Yves\Application;
use Spryker\Yves\Navigation\Plugin\Provider\NavigationTwigServiceProvider;
// ...
class YvesBootstrap
{
/**
* @return void
*/
protected function registerServiceProviders()
{
// ...
$this->application->register(new NavigationTwigServiceProvider());
}
}
Example of rendering navigation in an Yves twig template:
{{ spyNavigation('MAIN_NAVIGATION', '@application/layout/navigation/main.twig') }}
Rendering Navigation Templates
The templates used to render a navigation menu use the navigationTree
template variable to traverse the navigation tree. The variable contains an instance of \Generated\Shared\Transfer\NavigationTreeTransfer
with only one localized attribute per node for the current locale.
The following code examples show the Demoshop implementation of how to render MAIN_NAVIGATION
which is a multi-level navigation menu. For styling we used the Menu and Dropdown components from Foundation framework.
In Pyz/Yves/Application/Theme/default/layout/navigation/main.twig
we traverse the root navigation nodes of the navigation tree and for each root node we render their children nodes as well.
Code sample:
<div class="callout show-for-large">
<div class="row">
<div class="large-12 columns">
<ul class="menu">
{% for node in navigationTree.nodes %}
{% embed '@Application/layout/navigation/_partials/base-node.twig' %}
{% block url %}
<li {% if node.children|length %}data-toggle="navigation-node-{{ node.navigationNode.idNavigationNode }}-children"{% endif %} class="{{ class }}">
<a href="{{ url }}">{{ title }}</a>
</li>
{% endblock %}
{% block link %}
<li {% if node.children|length %}data-toggle="navigation-node-{{ node.navigationNode.idNavigationNode }}-children"{% endif %} class="{{ class }}">
<a href="{{ link }}">{{ title }}</a>
</li>
{% endblock %}
{% block externalUrl %}
<li {% if node.children|length %}data-toggle="navigation-node-{{ node.navigationNode.idNavigationNode }}-children"{% endif %} class="{{ class }}">
<a href="{{ externalUrl }}" target="_blank">{{ title }}</a>
</li>
{% endblock %}
{% block other %}
<li {% if node.children|length %}data-toggle="navigation-node-{{ node.navigationNode.idNavigationNode }}-children"{% endif %} class="menu-text {{ class }}">
{{ title }}
</li>
{% endblock %}
{% endembed %}
{% endfor %}
</ul>
{% for node in navigationTree.nodes %}
{% if node.navigationNode.isActive %}
{% if node.children|length %}
<div class="dropdown-pane" id="navigation-node-{{ node.navigationNode.idNavigationNode }}-children" data-dropdown data-hover="true" data-hover-pane="true">
{% include '@Application/layout/navigation/_partials/nodes.twig' with {nodes: node.children} %}
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>
</div>
</div>
The children nodes are rendered recursively by Pyz/Yves/Application/Theme/default/layout/navigation/_partials/nodes.twig
.
Code sample:
<ul class="vertical menu {% if nested|default(false) %}nested{% endif %}">
{% for node in nodes %}
{% embed '@Application/layout/navigation/_partials/base-node.twig' %}
{% block nodeContainer %}
<li id="navigation-node-{{ node.navigationNode.idNavigationNode }}" data-id-navigation-node="{{ node.navigationNode.idNavigationNode }}" class="{{ class }}">
{{ parent() }}
{% if node.children|length %}
{% include '@Application/layout/navigation/_partials/nodes.twig' with {nodes: node.children, nested:true} %}
{% endif %}
</li>
{% endblock %}
{% block url %}
<a href="{{ url }}">{{ title }}</a>
{% endblock %}
{% block link %}
<a href="{{ link }}">{{ title }}</a>
{% endblock %}
{% block externalUrl %}
<a href="{{ externalUrl }}" target="_blank">{{ title }}</a>
{% endblock %}
{% block other %}
<span class="menu-text">{{ title }}</span>
{% endblock %}
{% endembed %}
{% endfor %}
</ul>
To prevent code duplication we implemented the Pyz/Yves/Application/Theme/default/layout/navigation/_partials/base-node.twig
template which we use to render a node by embedding it in the templates above.
This is also the place where we take the visibility controller parameters into account : valid_from
, valid_to
, and is_active
.
Code sample:
{% set class = node.navigationNode.navigationNodeLocalizedAttributes[0].cssClass %}
{% set url = node.navigationNode.navigationNodeLocalizedAttributes[0].url %}
{% set externalUrl = node.navigationNode.navigationNodeLocalizedAttributes[0].externalUrl %}
{% set link = node.navigationNode.navigationNodeLocalizedAttributes[0].link %}
{% set title = node.navigationNode.navigationNodeLocalizedAttributes[0].title %}
{% set today = "now"|date("Ymd") %}
{% if node.navigationNode.isActive and
(node.navigationNode.validFrom is empty or node.navigationNode.validFrom|date("Ymd") ‹= today) and
(node.navigationNode.validTo is empty or node.navigationNode.validTo|date("Ymd") >= today)
%}
{% block nodeContainer %}
{% if url %}
{% block url %}{% endblock %}
{% elseif link %}
{% block link %}{% endblock %}
{% elseif externalUrl %}
{% block externalUrl %}{% endblock %}
{% else %}
{% block other %}{% endblock %}
{% endif %}
{% endblock %}
{% endif %}
Thank you!
For submitting the form