Skip to content

Commit

Permalink
implement nested navigation (#13)
Browse files Browse the repository at this point in the history
add support for nested navigation
  • Loading branch information
martinlutter authored and ivanbarlog committed Mar 6, 2018
1 parent 1830803 commit 7e1c8f5
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 41 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"symfony/templating": "^3.0|^4.0",
"symfony/translation": "^3.0|^4.0",
"symfony/twig-bundle": "^3.0|^4.0",
"everlutionsk/navigation": "^2.1"
"everlutionsk/navigation": "^2.2"
},
"license": "MIT",
"authors": [
Expand Down
3 changes: 1 addition & 2 deletions src/Bridge/Matcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Everlution\NavigationBundle\Bridge;

use Everlution\Navigation\Builder\MatcherInterface;
use Everlution\Navigation\Item\ItemInterface;
use Everlution\Navigation\Item\MatchableInterface;
use Everlution\Navigation\Match\UrlMatchVoterInterface;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -29,7 +28,7 @@ public function __construct(RequestStack $requestStack, UrlMatchVoterInterface $
$this->voter = $voter;
}

public function isCurrent(ItemInterface $item): bool
public function isCurrent($item): bool
{
if (!$item instanceof MatchableInterface) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/NavigationAliasContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Everlution\NavigationBundle\Bridge;

/**
* Class NavigationAliasTranslator.
* Class NavigationAliasContainer.
*
* @author Ivan Barlog <[email protected]>
*/
Expand Down
41 changes: 41 additions & 0 deletions src/DependencyInjection/Compiler/NestedRegistryCompilerPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Everlution\NavigationBundle\DependencyInjection\Compiler;

use Everlution\Navigation\Nested\Registry;
use Everlution\NavigationBundle\Bridge\NavigationAliasContainer;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

/**
* Class NestedRegistryCompilerPass.
*
* @author Martin Lutter <[email protected]>
*/
class NestedRegistryCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$registry = $container->findDefinition(Registry::class);
$aliasContainer = $container->findDefinition(NavigationAliasContainer::class);
$services = $container->findTaggedServiceIds('everlution.advanced_navigation');

foreach ($services as $id => $tags) {
$registry->addMethodCall('addContainer', [new Reference($id)]);
$this->addAlias($aliasContainer, $id, $tags);
}
}

private function addAlias(Definition $container, string $id, array $tags)
{
foreach ($tags as $tag) {
if (array_key_exists('alias', $tag)) {
$container->addMethodCall('addAlias', [$tag['alias'], $id]);
}
}
}
}
2 changes: 2 additions & 0 deletions src/EverlutionNavigationBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Everlution\NavigationBundle;

use Everlution\NavigationBundle\DependencyInjection\Compiler\NestedRegistryCompilerPass;
use Everlution\NavigationBundle\DependencyInjection\Compiler\RegisterStandaloneItemsCompilerPass;
use Everlution\NavigationBundle\DependencyInjection\Compiler\RegistryCompilerPass;
use Everlution\NavigationBundle\DependencyInjection\Compiler\UrlProviderCompilerPass;
Expand All @@ -21,6 +22,7 @@ class EverlutionNavigationBundle extends Bundle
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new RegistryCompilerPass());
$container->addCompilerPass(new NestedRegistryCompilerPass());
$container->addCompilerPass(new UrlProviderCompilerPass());
$container->addCompilerPass(new VoterCompilerPass());
$container->addCompilerPass(new RegisterStandaloneItemsCompilerPass());
Expand Down
5 changes: 5 additions & 0 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ services:
Everlution\NavigationBundle\Bridge\Item\RequestAttributesContainer: ~

Everlution\Navigation\Registry: ~
Everlution\Navigation\Nested\Registry: ~
Everlution\Navigation\Url\UrlProviderContainer: ~
Everlution\NavigationBundle\Bridge\NavigationAliasContainer: ~
Everlution\NavigationBundle\Bridge\Matcher: ~
Expand All @@ -18,9 +19,13 @@ services:
tags: ['everlution.url_provider']

# twig extension
Everlution\NavigationBundle\Twig\ItemHelper: ~
Everlution\NavigationBundle\Twig\NavigationHelper: ~
Everlution\NavigationBundle\Twig\NestedNavigationHelper: ~
Everlution\NavigationBundle\Twig\NavigationExtension:
tags: ['twig.extension']
Everlution\NavigationBundle\Twig\NestedNavigationExtension:
tags: ['twig.extension']
Everlution\NavigationBundle\Twig\ItemExtension:
tags: ['twig.extension']

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
aria-expanded="false">&dtrif;</a>
<div class="dropdown-menu">
{% for child in node.children %}
<a class="dropdown-item{% if helper.isCurrent(identifier, child.item) %} active{% endif %}" href="{{ helper.getUrl(child.item) }}">{{ helper.getLabel(child.item) }}</a>
<a class="dropdown-item{% if navigation_helper.isCurrent(identifier, child.item) %} active{% endif %}" href="{{ helper.getUrl(child.item) }}">{{ helper.getLabel(child.item) }}</a>
{% endfor %}
</div>
</li>
Expand Down
4 changes: 2 additions & 2 deletions src/Resources/views/bootstrap_navigation.html.twig
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<ul class="nav nav-tabs">
{% for node in root.children if node.item.isHidden == false %}
{% set item = node.item %}
{% set active = helper.isCurrent(identifier, item) %}
{% set active = navigation_helper.isCurrent(identifier, item) %}

{% if node.hasChildren %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle{% if active %} active{% endif %}" data-toggle="dropdown" href="#" role="button" aria-haspopup="true"
aria-expanded="false">{{ helper.getLabel(item) }}</a>
<div class="dropdown-menu">
{% for child in node.children %}
<a class="dropdown-item{% if helper.isCurrent(identifier, child.item) %} active{% endif %}" href="{{ helper.getUrl(child.item) }}">{{ helper.getLabel(child.item) }}</a>
<a class="dropdown-item{% if navigation_helper.isCurrent(identifier, child.item) %} active{% endif %}" href="{{ helper.getUrl(child.item) }}">{{ helper.getLabel(child.item) }}</a>
{% endfor %}
</div>
</li>
Expand Down
47 changes: 47 additions & 0 deletions src/Twig/ItemHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Everlution\NavigationBundle\Twig;

use Everlution\Navigation\Item\ItemInterface;
use Everlution\Navigation\Url\CannotProvideUrlForItemException;
use Everlution\Navigation\Url\UrlProviderContainer;
use Everlution\NavigationBundle\Bridge\Item\TranslatableItemLabelInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
* Class Helper.
*
* @author Martin Lutter <[email protected]>
*/
class ItemHelper
{
/** @var TranslatorInterface */
private $translator;
/** @var UrlProviderContainer */
private $urlProvider;

public function __construct(TranslatorInterface $translator, UrlProviderContainer $urlProvider)
{
$this->translator = $translator;
$this->urlProvider = $urlProvider;
}

public function getLabel(ItemInterface $item, string $domain = null, string $locale = null): string
{
$label = $item->getLabel();
$parameters = $label instanceof TranslatableItemLabelInterface ? $label->getParameters() : [];

return $this->translator->trans($label->getValue(), $parameters, $domain, $locale);
}

public function getUrl(ItemInterface $item): string
{
try {
return $this->urlProvider->getUrl($item);
} catch (CannotProvideUrlForItemException $exception) {
return '#';
}
}
}
13 changes: 9 additions & 4 deletions src/Twig/NavigationExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
*/
class NavigationExtension extends \Twig_Extension
{
/** @var NavigationHelper */
/** @var ItemHelper */
private $helper;
/** @var NavigationHelper */
private $navigationHelper;

public function __construct(NavigationHelper $helper)
public function __construct(ItemHelper $helper, NavigationHelper $navigationHelper)
{
$this->helper = $helper;
$this->navigationHelper = $navigationHelper;
}

/**
Expand All @@ -33,9 +36,10 @@ public function renderNavigation(
return $environment->render(
$template,
[
'root' => $this->helper->getNavigation($identifier)->getRoot(),
'root' => $this->navigationHelper->getNavigation($identifier)->getRoot(),
'identifier' => $identifier,
'helper' => $this->helper,
'navigation_helper' => $this->navigationHelper,
]
);
}
Expand All @@ -49,7 +53,7 @@ public function renderBreadcrumbs(
string $template = '@EverlutionNavigation/bootstrap_breadcrumbs.html.twig'
): string {
try {
$items = $this->helper->getNavigation($identifier)->getBreadcrumbs();
$items = $this->navigationHelper->getNavigation($identifier)->getBreadcrumbs();
} catch (NoCurrentItemFoundException $exception) {
return 'missing breadcrumbs';
}
Expand All @@ -60,6 +64,7 @@ public function renderBreadcrumbs(
'items' => $items,
'identifier' => $identifier,
'helper' => $this->helper,
'navigation_helper' => $this->navigationHelper,
]
);
}
Expand Down
31 changes: 1 addition & 30 deletions src/Twig/NavigationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
use Everlution\Navigation\FilteredContainerInterface;
use Everlution\Navigation\Item\ItemInterface;
use Everlution\Navigation\Registry;
use Everlution\Navigation\Url\CannotProvideUrlForItemException;
use Everlution\Navigation\Url\UrlProviderContainer;
use Everlution\NavigationBundle\Bridge\Item\TranslatableItemLabelInterface;
use Everlution\NavigationBundle\Bridge\NavigationAliasContainer;
use Symfony\Component\Translation\TranslatorInterface;

/**
* Class NavigationHelper.
Expand All @@ -33,31 +29,15 @@ class NavigationHelper
private $matcher;
/** @var NavigationBuilder[] */
private $container = [];
/** @var UrlProviderContainer */
private $urlProviders;
/** @var TranslatorInterface */
private $translator;

public function __construct(
Registry $registry,
NavigationAliasContainer $aliasContainer,
MatcherInterface $matcher,
UrlProviderContainer $container,
TranslatorInterface $translator
MatcherInterface $matcher
) {
$this->registry = $registry;
$this->aliasContainer = $aliasContainer;
$this->matcher = $matcher;
$this->urlProviders = $container;
$this->translator = $translator;
}

public function getLabel(ItemInterface $item, string $domain = null, string $locale = null): string
{
$label = $item->getLabel();
$parameters = $label instanceof TranslatableItemLabelInterface ? $label->getParameters() : [];

return $this->translator->trans($label->getValue(), $parameters, $domain, $locale);
}

public function isCurrent(string $identifier, ItemInterface $item): bool
Expand All @@ -74,15 +54,6 @@ public function isAncestor(string $identifier, ItemInterface $item): bool
return $this->getNavigation($identifier)->isAncestor($item);
}

public function getUrl(ItemInterface $item): string
{
try {
return $this->urlProviders->getUrl($item);
} catch (CannotProvideUrlForItemException $exception) {
return '#';
}
}

public function getNavigation(string $navigation): NavigationBuilder
{
if (false === array_key_exists($navigation, $this->container)) {
Expand Down
56 changes: 56 additions & 0 deletions src/Twig/NestedNavigationExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Everlution\NavigationBundle\Twig;

use Twig\Environment;

/**
* Class NestedNavigationExtension.
*
* @author Martin Lutter <[email protected]>
*/
class NestedNavigationExtension extends \Twig_Extension
{
/** @var ItemHelper */
private $helper;
/** @var NestedNavigationHelper */
private $navigationHelper;

public function __construct(ItemHelper $helper, NestedNavigationHelper $navigationHelper)
{
$this->helper = $helper;
$this->navigationHelper = $navigationHelper;
}

/**
* {@inheritdoc}
*/
public function renderAdvancedNavigation(
Environment $environment,
string $identifier,
string $template
): string {
return $environment->render(
$template,
[
'root' => $this->navigationHelper->getNavigation($identifier)->getCurrent(),
'identifier' => $identifier,
'helper' => $this->helper,
'navigation_helper' => $this->navigationHelper,
]
);
}

public function getFunctions()
{
return [
new \Twig_SimpleFunction(
'render_advanced_navigation',
[$this, 'renderAdvancedNavigation'],
['needs_environment' => true, 'is_safe' => ['html']]
),
];
}
}
Loading

0 comments on commit 7e1c8f5

Please sign in to comment.