<?php
/**
* Menu
*
* @author Vincent van Waasbergen <v.vanwaasbergen@visualmedia.nl>
*/
namespace VisualMedia\LisaBundle\Component;
use Exception;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use VisualMedia\LisaBundle\Component\Interfaces\MenuInterface;
use VisualMedia\LisaBundle\Component\Interfaces\MenuItemInterface;
use VisualMedia\LisaBundle\Helper\Helper;
/**
* Menu
*/
class Menu implements MenuInterface
{
/**
* @var array
*/
protected $items = array();
/**
* @var bool
*/
protected $initialized = false;
/**
* @var RequestStack
*/
protected $requestStack;
/**
* @var TranslatorInterface
*/
protected $translator;
/**
* @var AuthorizationCheckerInterface
*/
protected $authorizationChecker;
/**
* @var active
*/
protected $active;
/**
* @param RequestStack $requestStack
* @param TranslatorInterface $translator
* @param AuthorizationCheckerInterface $authorizationChecker
*/
public function __construct(?RequestStack $requestStack, ?TranslatorInterface $translator, ?AuthorizationCheckerInterface $authorizationChecker)
{
$this->requestStack = $requestStack;
$this->translator = $translator;
$this->authorizationChecker = $authorizationChecker;
}
/**
* @param array $items
* @return void
*/
public function setItems(array $items): void
{
$this->items = $items;
}
/**
* @param string $key
* @param MenuItem $item
* @return MenuItemInterface
*/
public function setItem($key, MenuItem $item): MenuItemInterface
{
$this->items[$key] = $item;
return $item;
}
/**
* @param MenuItem $item
* @return MenuItemInterface
*/
public function addItem(MenuItem $item): MenuItemInterface
{
$this->items[] = $item;
return $item;
}
/**
* @return array
*/
public function getItems(): array
{
return $this->items;
}
/**
* @return boolean
*/
public function getActive(): ?bool
{
return $this->active;
}
/**
* @param bool $active
* @return void
*/
public function setActive(bool $active): void
{
$this->active = $active;
}
/**
* @return void
*/
public function setActiveItems(): void
{
$request = $this->requestStack->getMasterRequest();
$uri = $request !== null ? $request->getUri() : '';
$setActiveFunction = function(&$menuItem) {
$menuItem->setActive(true);
$this->setActive(true);
$parent = $menuItem->getParent();
while ($parent !== null) {
$parent = $this->items[$parent];
$parent->setActive(true);
$parent = $parent->getParent();
}
};
foreach ($this->items as $menuItem) {
// Active Regex.
if ($menuItem->getActiveRegex() !== null) {
if (preg_match(Helper::phpRegex($menuItem->getActiveRegex()), $uri, $matches) === 1) {
$setActiveFunction($menuItem);
}
}
// Active Singular Regex
if(null !== $singularActiveRegex = $menuItem->getSingularActiveRegex()) {
if (preg_match(Helper::phpRegex($singularActiveRegex), $uri, $matches) === 1) {
$setActiveFunction($menuItem);
}
}
// Active Route (& parameters).
$requestedRouteName = $request->attributes->get('_route');
$routeName = $menuItem->getActiveRoute();
if (null !== $routeName && $requestedRouteName !== null) {
if ($routeName === $requestedRouteName) {
$setActiveFunction($menuItem);
}
}
}
}
/**
* @return MenuInterface
*/
public function initialize(): MenuInterface
{
// Menu mag maar 1 keer worden opgebouwd.
if ($this->initialized === true) {
throw new Exception($this->translator->trans('menu.already_initialized', array(
'%menu%' => get_class($this),
), 'exception'));
}
// Initialize Menu Items.
foreach ($this->items as $menuItem) {
$menuItem->initialize();
}
// Set active.
$this->setActiveItems();
// Set children.
$items = array();
foreach ($this->items as $key=>$menuItem) {
if ($menuItem->getParent() === null) {
$items[$menuItem->getKey() ?? $key] = $menuItem;
}
else {
$parentMenuItem = $this->items[$menuItem->getParent()];
$parentMenuItem->addChild($menuItem);
}
}
$this->items = $items;
// Transform
$this->transform();
// Initialized.
$this->initialized = true;
return $this;
}
/**
* @return bool
*/
public function isInitialized(): bool
{
return $this->initialized;
}
/**
* @param bool $initialized
* @return void
*/
public function setInitialized(bool $initialized = true): void
{
$this->initialized = $initialized;
}
/**
* @return void
*/
public function transform(): void
{
$this->cleanItems($this->items);
}
/**
* @param array $items
* @return void
*/
protected function cleanItems(array &$items): void
{
foreach ($items as $key=>&$menuItem) {
if ($menuItem !== null) {
$children = $menuItem->getChildren();
$this->cleanItems($children);
$menuItem->setChildren($children);
}
else {
unset($items[$key]);
}
}
}
/**
* @param string|array $key
* @return MenuItemInterface
*/
protected function extractMenuItem($key): ?MenuItemInterface
{
$menuItem = null;
if (is_array($key)) {
if (array_key_exists(current($key), $this->items)) {
$items = $this->items;
while (isset($items[current($key)])) {
$menuItem = $items[current($key)];
$items = $menuItem->getChildren();
next($key);
}
}
if ($menuItem->getKey() !== end($key)) {
return null;
}
}
else {
if (array_key_exists($key, $this->items)) {
$menuItem = $this->items[$key];
}
}
if ($menuItem !== null && $menuItem->getPermission() !== null) {
if (!$this->authorizationChecker->isGranted($menuItem->getPermission())) {
return null;
}
}
return $menuItem;
}
}