<?php /** @noinspection PhpUndefinedClassInspection */
namespace App\Component\TwigExtension;
use App\Component\Traits\RepositoryTrait;
use App\Entity\MenuTree;
use App\Entity\User;
use App\Service\MenuService;
use App\Service\RepositoryService;
use Doctrine\Common\Annotations\Reader;
use Exception;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\Cache\Adapter\TraceableAdapter;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* Class MenuBuilder
* @package App\Component\TwigExtension
*/
class MenuBuilder extends AbstractExtension implements ContainerAwareInterface
{
use ContainerAwareTrait;
const CACHE_LIFETIME = 86400;
/** @var Environment $templating */
private Environment $templating;
/** @var TraceableAdapter */
private $cache;
/** @var Security */
private Security $security;
/** @var RouterInterface */
private RouterInterface $router;
/** @var Reader */
private Reader $annotationReader;
/** @var MenuService */
private MenuService $menuService;
/** @var RepositoryService */
private RepositoryService $repositoryService;
/**
* MenuBuilder constructor.
* @param Environment $templating
* @param TagAwareCacheInterface $cache
* @param Security $security
* @param RouterInterface $router
* @param Reader $annotationReader
* @param MenuService $menuService
* @param RepositoryService $repositoryService
*/
public function __construct(Environment $templating, TagAwareCacheInterface $cache, Security $security,
RouterInterface $router, Reader $annotationReader, MenuService $menuService,
RepositoryService $repositoryService)
{
$this->templating = $templating;
$this->cache = $cache;
$this->security = $security;
$this->router = $router;
$this->annotationReader = $annotationReader;
$this->menuService = $menuService;
$this->repositoryService = $repositoryService;
}
/**
* @return array|TwigFunction[]
*/
public function getFunctions(): array
{
return [
new TwigFunction('createMenu', [$this, 'createMenu']),
];
}
/**
* @param string $menu
* @return string|array
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
* @throws Exception
* @throws InvalidArgumentException
*
* @example php bin/console cache:pool:clear cache.app => pour vider le cache
*/
public function createMenu(string $menu = 'sidebar'): array|string
{
$currentRoute = $this->container->get('request_stack')->getCurrentRequest()->get('_route');
/** @var User $user */
$user = $this->security->getUser();
$userKey = $user ? $user->getId() : 0;
if ($menu === 'sidebar') {
$l1 = explode('_', $currentRoute);
$l1 = $l1[0] . '_' . $l1[1] . '_';
return $this->templating->render('/sidebar/menu.html.twig', [
'currentRoute' => $currentRoute,
'currentL1' => $l1,
'currentL2' => $currentRoute,
'menuTree' => $this->getCachedSidebarMenu($userKey),
]);
} elseif ($menu === 'breadcrumb') {
return $this->getCachedBreadcrumb($currentRoute);
} else {
// $menu = 'buttons'
return $this->getCachedButtons($currentRoute, $userKey);
}
}
/**
* @param int $userKey
* @return mixed
* @throws InvalidArgumentException
*/
private function getCachedSidebarMenu(int $userKey)
{
return $this->cache->get('menu_sidebar_' . $userKey, function (ItemInterface $item) use ($userKey) {
$item->expiresAfter(self::CACHE_LIFETIME);
$item->tag(['user_' . $userKey]);
$repository = $this->repositoryService->getMenuTree();
$rootNode = $repository->findOneBy(['libelle' => 'App']);
return $this->getAccessibleHierarchy($repository, $rootNode);
});
}
private function getAccessibleHierarchy($repository, $rootNode): array
{
$menuTree = $repository->childrenHierarchy(
$rootNode,
false,
['decorate' => false],
false
);
if (!is_array($menuTree)) {
return [];
}
$children = '__children';
foreach ($menuTree as $key1 => $menuLevel1) {
if ($menuLevel1['icon'] === "0"){
unset($menuTree[$key1]);
continue;
}
if ($menuLevel1['level'] == 3) {
if (!$menuLevel1['route'] || !$this->menuService->hasAccess($menuLevel1['route'])) {
unset($menuTree[$key1]);
}
} else {
if(is_array($menuLevel1[$children])) {
foreach ($menuLevel1[$children] as $key2 => $menuLevel2) {
if (!$this->menuService->hasAccess($menuLevel2['route'])) {
unset($menuTree[$key1][$children][$key2]);
}
}
}
if (empty($menuTree[$key1][$children])) {
unset($menuTree[$key1]);
}
}
}
return $menuTree;
}
/**
* @param string $currentRoute
* @return mixed
* @throws InvalidArgumentException
*/
private function getCachedBreadcrumb(string $currentRoute)
{
return $this->cache->get('menu_breadcrumb_' . $currentRoute, function (ItemInterface $item) use ($currentRoute) {
$item->expiresAfter(self::CACHE_LIFETIME);
$item->tag(['menu']);
$routeArray = explode('_', $currentRoute);
$action = array_pop($routeArray);
$repository = $this->repositoryService->getMenuTree();
$element = $repository->findOneLeafByRoute($currentRoute);
if (!$element instanceof MenuTree) {
$element = $repository->findOneLeafByRoute(implode('_', $routeArray) . '_index');
}
if ($element instanceof MenuTree) {
$path = $repository->getPath($element);
$breadcrumb = [];
foreach ($path as $elem) {
/** @var MenuTree $elem */
if ($elem->getLevel() == 0 || !$elem->getLibelle()) {
continue;
}
if($elem->getLevel() == 1 && $elem->getLibelle() == 'Autres') {
continue;
}
if($elem->getLevel() == 3 && $elem->getLibelle() == 'Update') {
$elem->setLibelle('Modification');
}
$breadcrumb[] = $elem->getLibelle();
}
}
switch ($action) {
case 'add':
$breadcrumb[] = 'Ajout';
break;
case 'edit':
$breadcrumb[] = 'Modification';
break;
case 'index':
default:
break;
}
if (empty($breadcrumb)) {
$breadcrumb[] = 'Proxidriver';
}
return implode(' > ', $breadcrumb);
});
}
/**
* @param string $currentRoute
* @param int $userKey
* @return mixed
* @throws InvalidArgumentException
*/
private function getCachedButtons(string $currentRoute, int $userKey): array
{
return $this->cache->get('menu_buttons_' . $currentRoute . '_' . $userKey, function (ItemInterface $item) use ($currentRoute, $userKey) {
$item->expiresAfter(self::CACHE_LIFETIME);
$item->tag(['user_' . $userKey]);
$repository = $this->repositoryService->getMenuTree();
/** @var MenuTree $rootNode */
$rootNode = $repository->findOneLeafByRoute($currentRoute);
if (!$rootNode) {
$tmp = explode('_', $currentRoute);
$tmp[count($tmp) - 1] = 'index';
$currentRoute = implode('_', $tmp);
$rootNode = $repository->findOneLeafByRoute($currentRoute);
}
if ($rootNode instanceof MenuTree && $rootNode->getLevel() == 3) {
$rootNode = $rootNode->getParent();
} else {
return [];
}
return $this->getAccessibleHierarchy($repository, $rootNode);
});
}
}