src/Security/CategoryVoter.php line 12

Open in your IDE?
  1. <?php
  2. namespace App\Security;
  3. use App\Entity\Category;
  4. use App\Entity\Security\AppUser;
  5. use App\Repository\CategoryRepository;
  6. use App\Repository\MalletteFileRepository;
  7. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  8. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  9. class CategoryVoter extends Voter
  10. {
  11.     public const VIEW 'view';
  12.     public const VIEW_MENU 'view_menu';
  13.     /** @var null|array<Category> */
  14.     private ?array $cachedAccessibleCategories null;
  15.     private ?int $cachedUserId null;
  16.     private ?string $cachedAttribute null;
  17.     public function __construct(
  18.         private MalletteFileRepository $malletteFileRepository,
  19.         private CategoryRepository $categoryRepository,
  20.     ) {
  21.     }
  22.     protected function supports(string $attribute$subject): bool
  23.     {
  24.         if (!in_array($attribute, [self::VIEWself::VIEW_MENU])) {
  25.             return false;
  26.         }
  27.         return $subject instanceof Category;
  28.     }
  29.     /**
  30.      * @param Category $subject
  31.      */
  32.     protected function voteOnAttribute(string $attribute$subjectTokenInterface $token): bool
  33.     {
  34.         if (!in_array($attribute, [self::VIEWself::VIEW_MENU])) {
  35.             return false;
  36.         }
  37.         $user $token->getUser();
  38.         // the user must be logged in; if not, deny access
  39.         if (!$user instanceof AppUser) {
  40.             return false;
  41.         }
  42.         $this->loadAccessibleCategoriesIfNeeded($user$attribute);
  43.         if (in_array($subject$this->cachedAccessibleCategoriestrue)) {
  44.             return true;
  45.         }
  46.         return $attribute === self::VIEW_MENU && $this->hasAccessibleChild($subject);
  47.     }
  48.     private function loadAccessibleCategoriesIfNeeded(AppUser $userstring $attribute): void
  49.     {
  50.         if ($this->cachedUserId === $user->getId()
  51.             && $this->cachedAttribute === $attribute
  52.             && $this->cachedAccessibleCategories !== null) {
  53.             return;
  54.         }
  55.         $groups $user->getClient()->getGroups();
  56.         $malletteFiles $this->malletteFileRepository->findByCategoryAccess($groups$user);
  57.         $this->cachedAccessibleCategories $this->categoryRepository->findByGroupsAndMalletteFiles($groups$malletteFiles);
  58.         $this->cachedUserId $user->getId();
  59.         $this->cachedAttribute $attribute;
  60.     }
  61.     private function hasAccessibleChild(Category $category): bool
  62.     {
  63.         foreach ($category->getSubCategories() as $subCategory) {
  64.             if (in_array($subCategory$this->cachedAccessibleCategoriestrue)) {
  65.                 return true;
  66.             }
  67.             if ($this->hasAccessibleDescendant($subCategory)) {
  68.                 return true;
  69.             }
  70.         }
  71.         return false;
  72.     }
  73.     private function hasAccessibleDescendant(Category $category): bool
  74.     {
  75.         foreach ($category->getSubCategories() as $subCategory) {
  76.             if (in_array($subCategory$this->cachedAccessibleCategoriestrue)) {
  77.                 return true;
  78.             }
  79.             if ($this->hasAccessibleDescendant($subCategory)) {
  80.                 return true;
  81.             }
  82.         }
  83.         return false;
  84.     }
  85. }