src/Service/Module/AccesModuleService.php line 30

Open in your IDE?
  1. <?php
  2. namespace App\Service\Module;
  3. use App\Entity\Agence;
  4. use App\Entity\AgenceModule;
  5. use App\Service\Module\ModuleMenuRegistry;
  6. use App\Service\Module\SourceModuleService;
  7. use Doctrine\ORM\EntityManagerInterface;
  8. use Symfony\Component\HttpFoundation\RequestStack;
  9. /**
  10. * Service de vérification d'accès aux modules et fonctionnalités
  11. *
  12. * Permet de vérifier si une agence a accès à un module ou une fonctionnalité spécifique
  13. * en fonction de la configuration des modules et menus actifs
  14. */
  15. class AccesModuleService
  16. {
  17. private ?Agence $agenceCourante = null;
  18. public function __construct(
  19. private EntityManagerInterface $entityManager,
  20. private RequestStack $requestStack,
  21. private ModuleMenuRegistry $menuRegistry,
  22. private SourceModuleService $sourceModuleService
  23. ) {
  24. // Récupérer l'agence depuis la session
  25. $session = $this->requestStack->getSession();
  26. if ($session && $session->has('user')) {
  27. $user = $session->get('user');
  28. if (isset($user['agence'])) {
  29. $this->agenceCourante = $this->entityManager
  30. ->getRepository(Agence::class)
  31. ->find($user['agence']);
  32. }
  33. }
  34. }
  35. /**
  36. * Vérifie si l'agence courante peut accéder à une route donnée
  37. *
  38. * @param string $route Nom de la route Symfony
  39. * @return bool
  40. */
  41. public function agencePeutAccederRoute(string $route): bool
  42. {
  43. if (!$this->agenceCourante) {
  44. return false;
  45. }
  46. return $this->agencePeutAccederRouteAvecAgence($this->agenceCourante, $route);
  47. }
  48. /**
  49. * Vérifie si une agence spécifique peut accéder à une route donnée
  50. *
  51. * @param Agence $agence
  52. * @param string $route Nom de la route Symfony
  53. * @return bool
  54. */
  55. public function agencePeutAccederRouteAvecAgence(Agence $agence, string $route): bool
  56. {
  57. $codesActifs = $this->codesModulesActifs($agence);
  58. $agenceModulesIndex = $this->indexerAgenceModules($agence);
  59. foreach ($codesActifs as $codeModule) {
  60. $am = $agenceModulesIndex[$codeModule] ?? null;
  61. $menusActifs = $am ? ($am->getMenusActifs() ?? []) : [];
  62. if (in_array($route, $menusActifs, true)) {
  63. return true;
  64. }
  65. }
  66. return false;
  67. }
  68. /**
  69. * Vérifie si l'agence courante a un module actif
  70. *
  71. * @param string $codeModule Code du module (ex: 'boulangerie')
  72. * @return bool
  73. */
  74. public function agencePeutAccederModule(string $codeModule): bool
  75. {
  76. if (!$this->agenceCourante) {
  77. return false;
  78. }
  79. return $this->agencePeutAccederModuleAvecAgence($this->agenceCourante, $codeModule);
  80. }
  81. /**
  82. * Vérifie si une agence spécifique a un module actif
  83. *
  84. * @param Agence $agence
  85. * @param string $codeModule Code du module (ex: 'boulangerie')
  86. * @return bool
  87. */
  88. public function agencePeutAccederModuleAvecAgence(Agence $agence, string $codeModule): bool
  89. {
  90. return in_array($codeModule, $this->codesModulesActifs($agence), true);
  91. }
  92. /**
  93. * Retourne la liste des fonctionnalités accessibles pour l'agence courante
  94. *
  95. * @param string|null $codeModule Filtrer par module spécifique (optionnel)
  96. * @return array Liste des routes accessibles
  97. */
  98. public function getFonctionnalitesAccessibles(?string $codeModule = null): array
  99. {
  100. if (!$this->agenceCourante) {
  101. return [];
  102. }
  103. return $this->getFonctionnalitesAccessiblesAvecAgence($this->agenceCourante, $codeModule);
  104. }
  105. /**
  106. * Retourne la liste des fonctionnalités accessibles pour une agence spécifique
  107. *
  108. * @param Agence $agence
  109. * @param string|null $codeModule Filtrer par module spécifique (optionnel)
  110. * @return array Liste des routes accessibles
  111. */
  112. public function getFonctionnalitesAccessiblesAvecAgence(Agence $agence, ?string $codeModule = null): array
  113. {
  114. $codesActifs = $this->codesModulesActifs($agence);
  115. if ($codeModule !== null) {
  116. $codesActifs = in_array($codeModule, $codesActifs, true) ? [$codeModule] : [];
  117. }
  118. $agenceModulesIndex = $this->indexerAgenceModules($agence);
  119. $fonctionnalites = [];
  120. foreach ($codesActifs as $code) {
  121. $am = $agenceModulesIndex[$code] ?? null;
  122. $menusActifs = $am ? ($am->getMenusActifs() ?? []) : [];
  123. foreach ($menusActifs as $route) {
  124. $fonctionnalites[] = ['module' => $code, 'route' => $route];
  125. }
  126. }
  127. return $fonctionnalites;
  128. }
  129. /**
  130. * Définit l'agence courante manuellement (utile pour les tests ou cas spéciaux)
  131. *
  132. * @param Agence|null $agence
  133. * @return self
  134. */
  135. public function setAgenceCourante(?Agence $agence): self
  136. {
  137. $this->agenceCourante = $agence;
  138. return $this;
  139. }
  140. /**
  141. * Retourne l'agence courante
  142. *
  143. * @return Agence|null
  144. */
  145. public function getAgenceCourante(): ?Agence
  146. {
  147. return $this->agenceCourante;
  148. }
  149. /**
  150. * Retourne tous les modules actifs de l'agence courante
  151. *
  152. * @return array Liste des codes de modules actifs
  153. */
  154. public function getModulesActifs(): array
  155. {
  156. if (!$this->agenceCourante) {
  157. return [];
  158. }
  159. return $this->getModulesActifsAvecAgence($this->agenceCourante);
  160. }
  161. /**
  162. * Retourne tous les modules actifs d'une agence spécifique
  163. *
  164. * @param Agence $agence
  165. * @return array Liste des codes de modules actifs
  166. */
  167. public function getModulesActifsAvecAgence(Agence $agence): array
  168. {
  169. return $this->codesModulesActifs($agence);
  170. }
  171. /**
  172. * Retourne les menus de navigation récursifs pour l'agence courante.
  173. * Niveau 1 : module (parent accordéon)
  174. * Niveau 2+ : arbre récursif complet (sections > nœuds > feuilles)
  175. * Seuls les nœuds ayant au moins une route active dans leur sous-arbre sont inclus.
  176. *
  177. * @return array
  178. */
  179. public function getMenusNavigation(): array
  180. {
  181. if (!$this->agenceCourante) {
  182. return [];
  183. }
  184. $codesActifs = $this->codesModulesActifs($this->agenceCourante);
  185. $agenceModulesIndex = $this->indexerAgenceModules($this->agenceCourante);
  186. $navigation = [];
  187. foreach ($codesActifs as $codeModule) {
  188. if (!$this->menuRegistry->moduleHasMenuStructure($codeModule)) {
  189. continue;
  190. }
  191. $agenceModule = $agenceModulesIndex[$codeModule] ?? null;
  192. $menusActifs = $agenceModule ? ($agenceModule->getMenusActifs() ?? []) : [];
  193. $menus = $this->menuRegistry->getMenusForModule($codeModule);
  194. $enfants = $this->filtrerArbreRecursif($menus, $menusActifs);
  195. if (empty($enfants)) {
  196. continue;
  197. }
  198. $navigation[] = [
  199. 'code' => $codeModule,
  200. 'nom' => \App\Service\Module::MODULES[$codeModule] ?? $codeModule,
  201. 'icone' => 'fas ' . (\App\Service\Module::MODULES_ICONS[$codeModule] ?? 'fa-cube'),
  202. 'route' => null,
  203. 'enfants' => $enfants,
  204. ];
  205. }
  206. return $navigation;
  207. }
  208. private function codesModulesActifs(Agence $agence): array
  209. {
  210. $agenceModules = $this->entityManager
  211. ->getRepository(AgenceModule::class)
  212. ->findBy(['agence' => $agence]);
  213. $codesManuel = array_map(
  214. fn(AgenceModule $am) => $am->getCodeModule(),
  215. array_filter($agenceModules, fn(AgenceModule $am) => $am->isActif())
  216. );
  217. $codesAbonnement = $this->sourceModuleService->codesModulesAbonnement($agence);
  218. return array_values(array_unique(array_merge($codesManuel, $codesAbonnement)));
  219. }
  220. private function indexerAgenceModules(Agence $agence): array
  221. {
  222. $agenceModules = $this->entityManager
  223. ->getRepository(AgenceModule::class)
  224. ->findBy(['agence' => $agence]);
  225. $index = [];
  226. foreach ($agenceModules as $am) {
  227. $index[$am->getCodeModule()] = $am;
  228. }
  229. return $index;
  230. }
  231. /**
  232. * Filtre récursif : ne garde que les nœuds ayant au moins une route active dans leur sous-arbre.
  233. * Les feuilles (route non null) ne sont incluses que si leur route est dans $menusActifs.
  234. */
  235. private function filtrerArbreRecursif(array $menus, array $menusActifs): array
  236. {
  237. $resultat = [];
  238. foreach ($menus as $menu) {
  239. if (!empty($menu['route'])) {
  240. // Feuille : incluse seulement si route active
  241. if (in_array($menu['route'], $menusActifs, true)) {
  242. $resultat[] = [
  243. 'code' => $menu['code'],
  244. 'nom' => $menu['nom'],
  245. 'icone' => $menu['icone'],
  246. 'route' => $menu['route'],
  247. 'enfants' => [],
  248. ];
  249. }
  250. } else {
  251. // Nœud intermédiaire : filtrer ses enfants récursivement
  252. $enfantsFiltres = $this->filtrerArbreRecursif($menu['enfants'] ?? [], $menusActifs);
  253. if (!empty($enfantsFiltres)) {
  254. $resultat[] = [
  255. 'code' => $menu['code'],
  256. 'nom' => $menu['nom'],
  257. 'icone' => $menu['icone'],
  258. 'route' => null,
  259. 'enfants' => $enfantsFiltres,
  260. ];
  261. }
  262. }
  263. }
  264. return $resultat;
  265. }
  266. }