From 4b8e4a190b5eaaf8d21da2d6b8178baf860e55d1 Mon Sep 17 00:00:00 2001
From: Jean-Philippe Metivier <jean-philippe.metivier@unicaen.fr>
Date: Tue, 2 Apr 2024 11:09:55 +0200
Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20privilege?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 config/merged/categorie.config.php            | 100 +++++++++++++++
 config/merged/index.config.php                |   2 +-
 config/unicaen-indicateur.global.php.dist     |   3 +
 documentation/02_privileges.sql               |   3 +-
 .../Controller/CategorieController.php        |  23 ++++
 .../Controller/CategorieControllerFactory.php |  32 +++++
 .../Controller/IndicateurController.php       |   5 +
 src/UnicaenIndicateur/Entity/Db/Categorie.php |  79 ++++++++++++
 .../Entity/Db/Indicateur.php                  |  37 ++++++
 ...caenIndicateur.Entity.Db.Categorie.dcm.xml |  16 +++
 ...aenIndicateur.Entity.Db.Indicateur.dcm.xml |   7 ++
 .../Form/Categorie/CategorieForm.php          | 117 ++++++++++++++++++
 .../Categorie/CategorieFormAwareTrait.php     |  20 +++
 .../Form/Categorie/CategorieFormFactory.php   |  29 +++++
 .../Form/Categorie/CategorieHydrator.php      |  37 ++++++
 .../Categorie/CategorieHydratorFactory.php    |  14 +++
 .../Form/Indicateur/IndicateurForm.php        |  42 ++++++-
 .../Form/Indicateur/IndicateurFormFactory.php |   7 +-
 .../Form/Indicateur/IndicateurHydrator.php    |  16 +--
 .../Privilege/IndicateurPrivileges.php        |   1 +
 .../Service/Categorie/CategorieService.php    | 113 +++++++++++++++++
 .../Categorie/CategorieServiceAwareTrait.php  |  19 +++
 .../Categorie/CategorieServiceFactory.php     |  25 ++++
 .../Service/Indicateur/IndicateurService.php  |  95 +++++++++-----
 view/unicaen-indicateur/categorie/index.phtml |  38 ++++++
 .../unicaen-indicateur/indicateur/index.phtml |  12 +-
 26 files changed, 844 insertions(+), 48 deletions(-)
 create mode 100644 config/merged/categorie.config.php
 create mode 100644 src/UnicaenIndicateur/Controller/CategorieController.php
 create mode 100644 src/UnicaenIndicateur/Controller/CategorieControllerFactory.php
 create mode 100644 src/UnicaenIndicateur/Entity/Db/Categorie.php
 create mode 100644 src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Categorie.dcm.xml
 create mode 100644 src/UnicaenIndicateur/Form/Categorie/CategorieForm.php
 create mode 100644 src/UnicaenIndicateur/Form/Categorie/CategorieFormAwareTrait.php
 create mode 100644 src/UnicaenIndicateur/Form/Categorie/CategorieFormFactory.php
 create mode 100644 src/UnicaenIndicateur/Form/Categorie/CategorieHydrator.php
 create mode 100644 src/UnicaenIndicateur/Form/Categorie/CategorieHydratorFactory.php
 create mode 100644 src/UnicaenIndicateur/Service/Categorie/CategorieService.php
 create mode 100644 src/UnicaenIndicateur/Service/Categorie/CategorieServiceAwareTrait.php
 create mode 100644 src/UnicaenIndicateur/Service/Categorie/CategorieServiceFactory.php
 create mode 100644 view/unicaen-indicateur/categorie/index.phtml

diff --git a/config/merged/categorie.config.php b/config/merged/categorie.config.php
new file mode 100644
index 0000000..57ddb60
--- /dev/null
+++ b/config/merged/categorie.config.php
@@ -0,0 +1,100 @@
+<?php
+
+use Laminas\Router\Http\Literal;
+use Laminas\Router\Http\Segment;
+use UnicaenIndicateur\Controller\CategorieController;
+use UnicaenIndicateur\Controller\CategorieControllerFactory;
+use UnicaenIndicateur\Form\Categorie\CategorieForm;
+use UnicaenIndicateur\Form\Categorie\CategorieFormFactory;
+use UnicaenIndicateur\Form\Categorie\CategorieHydrator;
+use UnicaenIndicateur\Form\Categorie\CategorieHydratorFactory;
+use UnicaenIndicateur\Provider\Privilege\IndicateurPrivileges;
+use UnicaenIndicateur\Service\Categorie\CategorieService;
+use UnicaenIndicateur\Service\Categorie\CategorieServiceFactory;
+use UnicaenPrivilege\Guard\PrivilegeController;
+
+return [
+    'bjyauthorize' => [
+        'guards' => [
+            PrivilegeController::class => [
+                [
+                    'controller' => CategorieController::class,
+                    'action' => [
+                        'index',
+                        'ajouter',
+                        'modifier',
+                    ],
+                    'privileges' => [
+                        IndicateurPrivileges::EDITER_INDICATEUR,
+                    ],
+                ],
+                [
+                    'controller' => CategorieController::class,
+                    'action' => [
+                        'afficher',
+                    ],
+                    'privileges' => [
+                        IndicateurPrivileges::AFFICHER_INDICATEUR,
+                    ],
+                ],
+                [
+                    'controller' => CategorieController::class,
+                    'action' => [
+                        'supprimer',
+                    ],
+                    'privileges' => [
+                        IndicateurPrivileges::DETRUIRE_INDICATEUR,
+                    ],
+                ],
+            ],
+        ],
+    ],
+
+    'router' => [
+        'routes' => [
+            'indicateur' => [
+                'type' => Literal::class,
+                'options' => [
+                    'route' => '/indicateur',
+                ],
+                'may_terminate' => false,
+                'child_routes' => [
+                    'categorie' => [
+                        'type' => Literal::class,
+                        'options' => [
+                            'route' => '/categorie',
+                            'defaults' => [
+                                /** @see CategorieController::indexAction() */
+                                'controller' => CategorieController::class,
+                                'action' => 'index',
+                            ],
+                        ],
+                        'may_terminate' => true,
+                        'child_routes' => [],
+                    ],
+                ],
+            ],
+        ],
+    ],
+
+    'service_manager' => [
+        'factories' => [
+            CategorieService::class => CategorieServiceFactory::class,
+        ],
+    ],
+    'controllers' => [
+        'factories' => [
+            CategorieController::class => CategorieControllerFactory::class,
+        ],
+    ],
+    'form_elements' => [
+        'factories' => [
+            CategorieForm::class => CategorieFormFactory::class,
+        ],
+    ],
+    'hydrators' => [
+        'factories' => [
+            CategorieHydrator::class => CategorieHydratorFactory::class,
+        ],
+    ],
+];
\ No newline at end of file
diff --git a/config/merged/index.config.php b/config/merged/index.config.php
index 20c50d8..7e2ccf3 100644
--- a/config/merged/index.config.php
+++ b/config/merged/index.config.php
@@ -17,7 +17,7 @@ return [
                         'abonnement'
                     ],
                     'privileges' => [
-                        IndicateurPrivileges::AFFICHER_INDICATEUR,
+                        IndicateurPrivileges::INDICATEUR_MES_INDICATEURS,
                     ],
                 ],
             ],
diff --git a/config/unicaen-indicateur.global.php.dist b/config/unicaen-indicateur.global.php.dist
index a590ee5..8eea281 100644
--- a/config/unicaen-indicateur.global.php.dist
+++ b/config/unicaen-indicateur.global.php.dist
@@ -1,5 +1,8 @@
 <?php
 
+use UnicaenIndicateur\Controller\IndexController;
+use UnicaenPrivilege\Guard\PrivilegeController;
+
 return [
     'navigation'      => [
         'default' => [
diff --git a/documentation/02_privileges.sql b/documentation/02_privileges.sql
index e51c4e2..7b46e7a 100644
--- a/documentation/02_privileges.sql
+++ b/documentation/02_privileges.sql
@@ -4,7 +4,8 @@ INSERT INTO unicaen_privilege_privilege(CATEGORIE_ID, CODE, LIBELLE, ORDRE)
 WITH d(code, lib, ordre) AS (
     SELECT 'afficher_indicateur', 'Afficher un indicateur', 1   UNION
     SELECT 'editer_indicateur', 'Éditer un indicateur', 2   UNION
-    SELECT 'detruire_indicateur', 'Effacer un indicateur', 3
+    SELECT 'detruire_indicateur', 'Effacer un indicateur', 3 UNION
+    SELECT 'indicateur_mes_indicateurs', 'Affichage du menu - Mes Indicateurs -', 100
 )
 SELECT cp.id, d.code, d.lib, d.ordre
 FROM d
diff --git a/src/UnicaenIndicateur/Controller/CategorieController.php b/src/UnicaenIndicateur/Controller/CategorieController.php
new file mode 100644
index 0000000..3b0231b
--- /dev/null
+++ b/src/UnicaenIndicateur/Controller/CategorieController.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace UnicaenIndicateur\Controller;
+
+use Laminas\Mvc\Controller\AbstractActionController;
+use Laminas\View\Model\ViewModel;
+use UnicaenIndicateur\Form\Categorie\CategorieFormAwareTrait;
+use UnicaenIndicateur\Service\Categorie\CategorieServiceAwareTrait;
+
+class CategorieController extends AbstractActionController
+{
+    use CategorieServiceAwareTrait;
+    use CategorieFormAwareTrait;
+
+    public function indexAction(): ViewModel
+    {
+        $categories  = $this->getCategorieService()->getCategories();
+
+        return new ViewModel([
+            'categories' => $categories,
+        ]);
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Controller/CategorieControllerFactory.php b/src/UnicaenIndicateur/Controller/CategorieControllerFactory.php
new file mode 100644
index 0000000..d70636e
--- /dev/null
+++ b/src/UnicaenIndicateur/Controller/CategorieControllerFactory.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace UnicaenIndicateur\Controller;
+
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
+use UnicaenIndicateur\Form\Categorie\CategorieForm;
+use UnicaenIndicateur\Service\Categorie\CategorieService;
+
+class CategorieControllerFactory {
+
+    /**
+     * @throws ContainerExceptionInterface
+     * @throws NotFoundExceptionInterface
+     */
+    public function __invoke(ContainerInterface $container): CategorieController
+    {
+        /**
+         * @var CategorieService $categorieService
+         * @var CategorieForm $categorieForm
+         */
+        $categorieService = $container->get(CategorieService::class);
+//        $categorieForm = $container->get('FormElementManager')->get(CategorieForm::class);
+
+        $controller = new CategorieController();
+        $controller->setCategorieService($categorieService);
+//        $controller->setCategorieForm($categorieForm);
+        return $controller;
+
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Controller/IndicateurController.php b/src/UnicaenIndicateur/Controller/IndicateurController.php
index c5a0112..81a5bc7 100644
--- a/src/UnicaenIndicateur/Controller/IndicateurController.php
+++ b/src/UnicaenIndicateur/Controller/IndicateurController.php
@@ -89,6 +89,10 @@ class IndicateurController extends AbstractActionController {
 
                 $this->getIndicateurService()->create($indicateur);
                 $this->getIndicateurService()->createView($indicateur);
+                $count = $this->getIndicateurService()->getCount($indicateur);
+                $indicateur->setNbElements($count);
+                $this->getIndicateurService()->update($indicateur);
+                exit();
             }
         }
 
@@ -107,6 +111,7 @@ class IndicateurController extends AbstractActionController {
         $namespace = $this->params()->fromQuery('namespace');
 
         $form = $this->getIndicateurForm();
+        $form->setOldCode($indicateur->getCode());
         $form->setAttribute('action', $this->url()->fromRoute('indicateur/modifier', ['indicateur' => $indicateur->getId()], ['query' => ['namespace' => $namespace]], true));
         if ($namespace) {
             $indicateur->setNamespace($namespace);
diff --git a/src/UnicaenIndicateur/Entity/Db/Categorie.php b/src/UnicaenIndicateur/Entity/Db/Categorie.php
new file mode 100644
index 0000000..839c014
--- /dev/null
+++ b/src/UnicaenIndicateur/Entity/Db/Categorie.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace UnicaenIndicateur\Entity\Db;
+
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
+
+class Categorie {
+
+    private ?int $id = null;
+    private ?string $code = null;
+    private ?string $libelle = null;
+    private ?string $description = null;
+    private ?int $ordre = 9999;
+
+    private Collection $indicateurs;
+
+
+    public function __construct()
+    {
+        $this->indicateurs = new ArrayCollection();
+    }
+
+    public function getId() : ?int
+    {
+        return $this->id;
+    }
+
+    public function getCode(): ?string
+    {
+        return $this->code;
+    }
+
+    public function setCode(?string $code): void
+    {
+        $this->code = $code;
+    }
+
+    public function getLibelle(): ?string
+    {
+        return $this->libelle;
+    }
+
+    public function setLibelle(?string $libelle): void
+    {
+        $this->libelle = $libelle;
+    }
+
+    public function getDescription(): ?string
+    {
+        return $this->description;
+    }
+
+    public function setDescription(?string $description): void
+    {
+        $this->description = $description;
+    }
+
+    public function getOrdre(): ?int
+    {
+        return $this->ordre;
+    }
+
+    public function setOrdre(?int $ordre): void
+    {
+        $this->ordre = $ordre;
+    }
+
+    public function getIndicateurs(): Collection
+    {
+        return $this->indicateurs;
+    }
+
+    public function setIndicateurs(Collection $indicateurs): void
+    {
+        $this->indicateurs = $indicateurs;
+    }
+
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Entity/Db/Indicateur.php b/src/UnicaenIndicateur/Entity/Db/Indicateur.php
index dccec39..f43cf54 100644
--- a/src/UnicaenIndicateur/Entity/Db/Indicateur.php
+++ b/src/UnicaenIndicateur/Entity/Db/Indicateur.php
@@ -5,6 +5,7 @@ namespace UnicaenIndicateur\Entity\Db;
 use DateTime;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
+use UnicaenIndicateur\Entity\Db\Categorie;
 
 class Indicateur {
 
@@ -14,8 +15,11 @@ class Indicateur {
     const ENTITY_ADAPTATIF = 'Adaptatif';
 
     private ?int $id = null;
+    private ?string $code = null;
     private ?string $titre = null;
     private ?string $description = null;
+    private ?Categorie  $categorie = null;
+
     private ?string $requete = null;
     private ?DateTime $dernierRafraichissement = null;
     private ?string $viewId = null;
@@ -23,6 +27,7 @@ class Indicateur {
     private ?string $namespace = null;
     private Collection $abonnements;
     private Collection $tableaux;
+    private ?int $nbElements = null;
 
     public function __construct()
     {
@@ -35,6 +40,16 @@ class Indicateur {
         return $this->id;
     }
 
+    public function getCode(): ?string
+    {
+        return $this->code;
+    }
+
+    public function setCode(?string $code): void
+    {
+        $this->code = $code;
+    }
+
     public function getTitre() : ?string
     {
         return $this->titre;
@@ -55,6 +70,18 @@ class Indicateur {
         $this->description = $description;
     }
 
+    public function getCategorie(): ?Categorie
+    {
+        return $this->categorie;
+    }
+
+    public function setCategorie(?Categorie $categorie): void
+    {
+        $this->categorie = $categorie;
+    }
+
+
+
     public function getRequete() : ?string
     {
         return $this->requete;
@@ -105,6 +132,16 @@ class Indicateur {
         $this->namespace = $namespace;
     }
 
+    public function getNbElements(): ?int
+    {
+        return $this->nbElements;
+    }
+
+    public function setNbElements(?int $nbElements): void
+    {
+        $this->nbElements = $nbElements;
+    }
+
     /** Abonnements **************************************************************** */
 
     /**
diff --git a/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Categorie.dcm.xml b/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Categorie.dcm.xml
new file mode 100644
index 0000000..416fc2a
--- /dev/null
+++ b/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Categorie.dcm.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
+    <entity name="UnicaenIndicateur\Entity\Db\Categorie" table="unicaen_indicateur_categorie">
+
+        <id name="id" type="integer" column="id">
+            <generator strategy="IDENTITY"/>
+        </id>
+
+        <field name="code"              type="string" length="255"      column="code"                 nullable="true"/>
+        <field name="libelle"           type="string" length="1024"     column="libelle"              nullable="false"/>
+        <field name="description"       type="text"                     column="description"          nullable="true"/>
+        <field name="ordre"             type="integer"                  column="ordre"                nullable="false"/>
+
+        <one-to-many target-entity="UnicaenIndicateur\Entity\Db\Indicateur" field="indicateurs" mapped-by="categorie" />
+    </entity>
+</doctrine-mapping>
diff --git a/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Indicateur.dcm.xml b/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Indicateur.dcm.xml
index c1f38dd..1c3c60c 100644
--- a/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Indicateur.dcm.xml
+++ b/src/UnicaenIndicateur/Entity/Db/Mapping/UnicaenIndicateur.Entity.Db.Indicateur.dcm.xml
@@ -6,6 +6,7 @@
             <generator strategy="IDENTITY"/>
         </id>
 
+        <field name="code"              type="string" length="255"      column="code"                 nullable="false"/>
         <field name="titre"             type="string" length="255"      column="titre"                nullable="false"/>
         <field name="description"       type="string" length="2047"     column="description"          nullable="false"/>
         <field name="requete"           type="string" length="4095"     column="requete"              nullable="false"/>
@@ -13,6 +14,12 @@
         <field name="viewId"            type="string" length="255"      column="view_id"                nullable="true"/>
         <field name="entity"            type="string" length="255"      column="entity"                nullable="true"/>
         <field name="namespace"         type="string" length="1024"     column="namespace"             nullable="true"/>
+        <field name="nbElements"        type="integer"                  column="nb_elements"             nullable="true"/>
+
+
+        <many-to-one target-entity="UnicaenIndicateur\Entity\Db\Categorie"  field="categorie">
+            <join-column name="categorie_id" referenced-column-name="id"/>
+        </many-to-one>
 
         <one-to-many target-entity="UnicaenIndicateur\Entity\Db\Abonnement" field="abonnements" mapped-by="indicateur" />
 
diff --git a/src/UnicaenIndicateur/Form/Categorie/CategorieForm.php b/src/UnicaenIndicateur/Form/Categorie/CategorieForm.php
new file mode 100644
index 0000000..95c04aa
--- /dev/null
+++ b/src/UnicaenIndicateur/Form/Categorie/CategorieForm.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace UnicaenIndicateur\Form\Categorie;
+
+use Laminas\Form\Element\Button;
+use Laminas\Form\Element\Text;
+use Laminas\Form\Form;
+use Laminas\InputFilter\Factory;
+use Laminas\Validator\Callback;
+use Unicaen\Console\Prompt\Number;
+use UnicaenIndicateur\Service\Categorie\CategorieServiceAwareTrait;
+
+class CategorieForm extends Form
+{
+    use CategorieServiceAwareTrait;
+
+    private ?string $oldCode = null;
+
+    public function setOldCode(?string $oldCode): void
+    {
+        $this->oldCode = $oldCode;
+    }
+
+    public function init(): void
+    {
+        // code
+        $this->add([
+            'type' => Text::class,
+            'name' => 'code',
+            'options' => [
+                'label' => "Code unique identifiant la catégorie <span class='icon icon-obligatoire' title='champ obligatoire'></span> :",
+                'label_options' => ['disable_html_escape' => true,],
+            ],
+            'attributes' => [
+                'id' => 'code',
+            ],
+        ]);
+        // libelle
+        $this->add([
+            'type' => Text::class,
+            'name' => 'libelle',
+            'options' => [
+                'label' => "Libellé <span class='icon icon-obligatoire' title='champ obligatoire'></span> :",
+                'label_options' => ['disable_html_escape' => true,],
+            ],
+            'attributes' => [
+                'id' => 'libelle',
+            ],
+        ]);
+        // description
+        $this->add([
+            'name' => 'description',
+            'type' => 'textarea',
+            'options' => [
+                'label' => 'Description : ',
+                'label_attributes' => [
+                    'class' => 'control-label',
+                ],
+            ],
+            'attributes' => [
+                'class' => 'type2 form-control',
+            ]
+        ]);
+        // ordre
+        $this->add([
+            'type' => Number::class,
+            'name' => 'ordre',
+            'options' => [
+                'label' => "Ordre <span class='icon icon-obligatoire' title='champ obligatoire'></span> :",
+                'label_options' => ['disable_html_escape' => true,],
+            ],
+            'attributes' => [
+                'id' => 'ordre',
+                'min' => 0,
+                'max' => 9999,
+            ],
+        ]);
+        // submit
+        $this->add([
+            'type' => Button::class,
+            'name' => 'creer',
+            'options' => [
+                'label' => '<i class="fas fa-save"></i> Enregistrer',
+                'label_options' => ['disable_html_escape' => true,],
+            ],
+            'attributes' => [
+                'type' => 'submit',
+                'class' => 'btn btn-primary',
+            ],
+        ]);
+
+
+        $this->setInputFilter((new Factory())->createInputFilter([
+            'code' => [
+                'required' => true,
+                'validators' => [[
+                    'name' => Callback::class,
+                    'options' => [
+                        'messages' => [
+                            Callback::INVALID_VALUE => "Ce code existe déjà",
+                        ],
+                        'callback' => function ($value, $context = []) {
+                            if ($value == $this->oldCode) return true;
+//                            return ($this->getEntityManager()->getRepository(EtatType::class)->findOneBy(['code'=>$value],[]) == null);;
+                            return ($this->getCategorieService()->getCategorieByCode($value) == null);
+                        },
+                        //'break_chain_on_failure' => true,
+                    ],
+                ]],
+            ],
+
+            'libelle' => ['required' => true,],
+            'description' => ['required' => false,],
+            'ordre' => ['required' => true,],
+        ]));
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Form/Categorie/CategorieFormAwareTrait.php b/src/UnicaenIndicateur/Form/Categorie/CategorieFormAwareTrait.php
new file mode 100644
index 0000000..17a85d7
--- /dev/null
+++ b/src/UnicaenIndicateur/Form/Categorie/CategorieFormAwareTrait.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace UnicaenIndicateur\Form\Categorie;
+
+trait CategorieFormAwareTrait
+{
+
+    private CategorieForm $categorieForm;
+
+    public function getCategorieForm(): CategorieForm
+    {
+        return $this->categorieForm;
+    }
+
+    public function setCategorieForm(CategorieForm $categorieForm): void
+    {
+        $this->categorieForm = $categorieForm;
+    }
+
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Form/Categorie/CategorieFormFactory.php b/src/UnicaenIndicateur/Form/Categorie/CategorieFormFactory.php
new file mode 100644
index 0000000..3ffedd8
--- /dev/null
+++ b/src/UnicaenIndicateur/Form/Categorie/CategorieFormFactory.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace UnicaenIndicateur\Form\Categorie;
+
+use Interop\Container\ContainerInterface;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
+use UnicaenIndicateur\Service\Categorie\CategorieService;
+
+class CategorieFormFactory {
+
+    /**
+     * @throws ContainerExceptionInterface
+     * @throws NotFoundExceptionInterface
+     */
+    public function __invoke(ContainerInterface $container) : CategorieForm
+    {
+        /**
+         * @var CategorieService $categorieService
+         * @var CategorieHydrator $hydrator */
+        $categorieService = $container->get(CategorieService::class);
+        $hydrator = $container->get('HydratorManager')->get(CategorieHydrator::class);
+
+        $form = new CategorieForm();
+        $form->setCategorieService($categorieService);
+        $form->setHydrator($hydrator);
+        return $form;
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Form/Categorie/CategorieHydrator.php b/src/UnicaenIndicateur/Form/Categorie/CategorieHydrator.php
new file mode 100644
index 0000000..dcf5ea6
--- /dev/null
+++ b/src/UnicaenIndicateur/Form/Categorie/CategorieHydrator.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace UnicaenIndicateur\Form\Categorie;
+
+use UnicaenIndicateur\Entity\Db\Categorie;
+use Laminas\Hydrator\HydratorInterface;
+
+class CategorieHydrator implements HydratorInterface {
+
+    public function extract($object): array
+    {
+        /** @var Categorie $object */
+        $data = [
+            'code' => $object->getCode(),
+            'libelle' => $object->getLibelle(),
+            'description' => $object->getDescription(),
+            'ordre' => $object->getOrdre(),
+        ];
+        return $data;
+    }
+
+    public function hydrate(array $data, object $object) : object
+    {
+        $code = (isset($data['code']) AND trim($data['code']) !== '')?trim($data['code']):null;
+        $libelle = (isset($data['libelle']) AND trim($data['libelle']) !== '')?trim($data['libelle']):null;
+        $description = (isset($data['description']) AND trim($data['description']) !== '')?trim($data['description']):null;
+        $ordre = (isset($data['ordre']) AND trim($data['ordre']) !== '')?trim($data['ordre']):null;
+
+        /** @var Categorie $object */
+        $object->setCode($code);
+        $object->setLibelle($libelle);
+        $object->setDescription($description);
+        $object->setOrdre($ordre);
+        return $object;
+    }
+
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Form/Categorie/CategorieHydratorFactory.php b/src/UnicaenIndicateur/Form/Categorie/CategorieHydratorFactory.php
new file mode 100644
index 0000000..c957013
--- /dev/null
+++ b/src/UnicaenIndicateur/Form/Categorie/CategorieHydratorFactory.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace UnicaenIndicateur\Form\Categorie;
+
+use Psr\Container\ContainerInterface;
+
+class CategorieHydratorFactory
+{
+    public function __invoke(ContainerInterface $container): CategorieHydrator
+    {
+        $hydrator = new CategorieHydrator();
+        return $hydrator;
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Form/Indicateur/IndicateurForm.php b/src/UnicaenIndicateur/Form/Indicateur/IndicateurForm.php
index 180e967..55ca1eb 100644
--- a/src/UnicaenIndicateur/Form/Indicateur/IndicateurForm.php
+++ b/src/UnicaenIndicateur/Form/Indicateur/IndicateurForm.php
@@ -2,17 +2,39 @@
 
 namespace UnicaenIndicateur\Form\Indicateur;
 
+use Laminas\Validator\Callback;
 use UnicaenIndicateur\Entity\Db\Indicateur;
 use Laminas\Form\Element\Button;
 use Laminas\Form\Element\Select;
 use Laminas\Form\Element\Text;
 use Laminas\Form\Form;
 use Laminas\InputFilter\Factory;
+use UnicaenIndicateur\Service\Indicateur\IndicateurServiceAwareTrait;
 
 class IndicateurForm extends Form {
+    use IndicateurServiceAwareTrait;
 
-    public function init()
+    private ?string $oldCode = null;
+
+    public function setOldCode(?string $oldCode): void
+    {
+        $this->oldCode = $oldCode;
+    }
+
+    public function init(): void
     {
+        // code
+        $this->add([
+            'type' => Text::class,
+            'name' => 'code',
+            'options' => [
+                'label' => "Code unique identifiant l'indicateur <span class='icon icon-obligatoire' title='champ obligatoire'></span> :",
+                'label_options' => [ 'disable_html_escape' => true, ],
+            ],
+            'attributes' => [
+                'id' => 'code',
+            ],
+        ]);
         // libelle
         $this->add([
             'type' => Text::class,
@@ -113,6 +135,24 @@ class IndicateurForm extends Form {
 
 
         $this->setInputFilter((new Factory())->createInputFilter([
+            'code'                  => [
+                'required' => true,
+                'validators' => [[
+                    'name' => Callback::class,
+                    'options' => [
+                        'messages' => [
+                            Callback::INVALID_VALUE => "Ce code existe déjà",
+                        ],
+                        'callback' => function ($value, $context = []) {
+                            if($value == $this->oldCode) return true;
+//                            return ($this->getEntityManager()->getRepository(EtatType::class)->findOneBy(['code'=>$value],[]) == null);;
+                            return ($this->getIndicateurService()->getIndicateurByCode($value) == null);
+                        },
+                        //'break_chain_on_failure' => true,
+                    ],
+                ]],
+            ],
+
             'libelle'               => [ 'required' => true,  ],
             'description'           => [ 'required' => false,  ],
             'namespace'             => [ 'required' => false,  ],
diff --git a/src/UnicaenIndicateur/Form/Indicateur/IndicateurFormFactory.php b/src/UnicaenIndicateur/Form/Indicateur/IndicateurFormFactory.php
index 983dff5..bd511fa 100644
--- a/src/UnicaenIndicateur/Form/Indicateur/IndicateurFormFactory.php
+++ b/src/UnicaenIndicateur/Form/Indicateur/IndicateurFormFactory.php
@@ -5,6 +5,7 @@ namespace UnicaenIndicateur\Form\Indicateur;
 use Interop\Container\ContainerInterface;
 use Psr\Container\ContainerExceptionInterface;
 use Psr\Container\NotFoundExceptionInterface;
+use UnicaenIndicateur\Service\Indicateur\IndicateurService;
 
 class IndicateurFormFactory {
 
@@ -16,10 +17,14 @@ class IndicateurFormFactory {
      */
     public function __invoke(ContainerInterface $container) : IndicateurForm
     {
-        /** @var IndicateurHydrator $hydrator */
+        /**
+         * @var IndicateurService $indicateurService
+         * @var IndicateurHydrator $hydrator */
+        $indicateurService = $container->get(IndicateurService::class);
         $hydrator = $container->get('HydratorManager')->get(IndicateurHydrator::class);
 
         $form = new IndicateurForm();
+        $form->setIndicateurService($indicateurService);
         $form->setHydrator($hydrator);
         $form->init();
         return $form;
diff --git a/src/UnicaenIndicateur/Form/Indicateur/IndicateurHydrator.php b/src/UnicaenIndicateur/Form/Indicateur/IndicateurHydrator.php
index b4fb273..e6de0cc 100644
--- a/src/UnicaenIndicateur/Form/Indicateur/IndicateurHydrator.php
+++ b/src/UnicaenIndicateur/Form/Indicateur/IndicateurHydrator.php
@@ -7,13 +7,11 @@ use Laminas\Hydrator\HydratorInterface;
 
 class IndicateurHydrator implements HydratorInterface {
 
-    /**
-     * @param Indicateur $object
-     * @return array
-     */
     public function extract($object): array
     {
+        /** @var Indicateur $object */
         $data = [
+            'code' => $object->getCode(),
             'libelle' => $object->getTitre(),
             'description' => $object->getDescription(),
             'namespace' => $object->getNamespace(),
@@ -24,15 +22,13 @@ class IndicateurHydrator implements HydratorInterface {
         return $data;
     }
 
-    /**
-     * @param array $data
-     * @param Indicateur $object
-     * @return Indicateur
-     */
-    public function hydrate(array $data, $object) : object
+    public function hydrate(array $data, object $object) : object
     {
+        $code = (isset($data['code']) AND trim($data['code']) !== '')?trim($data['code']):null;
         $namespace = (isset($data['namespace']) AND trim($data['namespace']) !== '')?trim($data['namespace']):null;
 
+        /** @var Indicateur $object */
+        $object->setCode($code);
         $object->setTitre($data['libelle']);
         $object->setDescription($data['description']);
         $object->setNamespace($namespace);
diff --git a/src/UnicaenIndicateur/Provider/Privilege/IndicateurPrivileges.php b/src/UnicaenIndicateur/Provider/Privilege/IndicateurPrivileges.php
index dc19cc6..92ec16a 100644
--- a/src/UnicaenIndicateur/Provider/Privilege/IndicateurPrivileges.php
+++ b/src/UnicaenIndicateur/Provider/Privilege/IndicateurPrivileges.php
@@ -6,6 +6,7 @@ use UnicaenPrivilege\Provider\Privilege\Privileges;
 
 class IndicateurPrivileges extends Privileges
 {
+    const INDICATEUR_MES_INDICATEURS = 'indicateur-indicateur_mes_indicateurs';
     const AFFICHER_INDICATEUR = 'indicateur-afficher_indicateur';
     const EDITER_INDICATEUR = 'indicateur-editer_indicateur';
     const DETRUIRE_INDICATEUR = 'indicateur-detruire_indicateur';
diff --git a/src/UnicaenIndicateur/Service/Categorie/CategorieService.php b/src/UnicaenIndicateur/Service/Categorie/CategorieService.php
new file mode 100644
index 0000000..a42576d
--- /dev/null
+++ b/src/UnicaenIndicateur/Service/Categorie/CategorieService.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace UnicaenIndicateur\Service\Categorie;
+
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\NonUniqueResultException;
+use Doctrine\ORM\QueryBuilder;
+use DoctrineModule\Persistence\ProvidesObjectManager;
+use Laminas\Mvc\Controller\AbstractActionController;
+use RuntimeException;
+use UnicaenIndicateur\Entity\Db\Categorie;
+
+/**
+ * @property EntityManager $objectManager
+ */
+class CategorieService
+{
+    use ProvidesObjectManager;
+
+    /** GESTION DES ENTITES ***********************************************************************************************/
+
+    public function create(Categorie $categorie): Categorie
+    {
+        $this->getObjectManager()->persist($categorie);
+        $this->getObjectManager()->flush($categorie);
+        return $categorie;
+    }
+
+    public function update(Categorie $categorie): Categorie
+    {
+        $this->getObjectManager()->flush($categorie);
+        return $categorie;
+    }
+
+    public function delete(Categorie $categorie): Categorie
+    {
+        $this->getObjectManager()->remove($categorie);
+        $this->getObjectManager()->flush($categorie);
+        return $categorie;
+    }
+
+    /** REQUETAGE *****************************************************************************************************/
+
+    public function createQueryBuilder(): QueryBuilder
+    {
+        $qb = $this->getObjectManager()->getRepository(Categorie::class)->createQueryBuilder('categorie')
+            ->leftJoin('categorie.indicateurs', 'indicateur')->addSelect('indicateur');
+        return $qb;
+    }
+
+    /** @return Categorie[] */
+    public function getCategories(string $champ = 'ordre', string $ordre = 'ASC'): array
+    {
+        $qb = $this->createQueryBuilder()
+            ->orderBy('categorie.' . $champ, $ordre);
+
+        $result = $qb->getQuery()->getResult();
+        return $result;
+    }
+
+    public function getCategoriesAsOptions(string $champ = 'ordre', string $ordre = 'ASC'): array
+    {
+        $qb = $this->createQueryBuilder()
+            ->orderBy('categorie.' . $champ, $ordre);
+
+        $result = $qb->getQuery()->getResult();
+
+        $options = [];
+        foreach ($result as $item) {
+            $options[$item->getId()] = $item->getLibelle();
+        }
+        return $options;
+    }
+
+    public function getCategorie(?int $id): ?Categorie
+    {
+        $qb = $this->createQueryBuilder()
+            ->andWhere('categorie.id = :id')->setParameter('id', $id);
+        try {
+            $result = $qb->getQuery()->getOneOrNullResult();
+        } catch (NonUniqueResultException $e) {
+            throw new RuntimeException("Plusieurs [" . Categorie::class . "] partagent le même id [" . $id . "]", $e);
+        }
+        return $result;
+    }
+
+    /**
+     * @param AbstractActionController $controller
+     * @param string $paramName
+     * @return Categorie|null
+     */
+    public function getRequestedCategorie(AbstractActionController $controller, string $paramName = "categorie"): ?Categorie
+    {
+        $id = $controller->params()->fromRoute($paramName);
+        return $this->getCategorie($id);
+    }
+
+    public function getCategorieByCode(?string $code): ?Categorie
+    {
+        $qb = $this->createQueryBuilder()
+            ->andWhere('categorie.code = :code')->setParameter('code', $code);
+        try {
+            $result = $qb->getQuery()->getOneOrNullResult();
+        } catch (NonUniqueResultException $e) {
+            throw new RuntimeException("Plusieurs [" . Categorie::class . "] partagent le même code [" . $code . "]", $e);
+        }
+        return $result;
+    }
+
+    /** FACADE **************************************************************/
+
+
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Service/Categorie/CategorieServiceAwareTrait.php b/src/UnicaenIndicateur/Service/Categorie/CategorieServiceAwareTrait.php
new file mode 100644
index 0000000..f620b1f
--- /dev/null
+++ b/src/UnicaenIndicateur/Service/Categorie/CategorieServiceAwareTrait.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace UnicaenIndicateur\Service\Categorie;
+
+trait CategorieServiceAwareTrait {
+
+    private CategorieService $categorieService;
+
+    public function getCategorieService(): CategorieService
+    {
+        return $this->categorieService;
+    }
+
+    public function setCategorieService(CategorieService $categorieService): void
+    {
+        $this->categorieService = $categorieService;
+    }
+
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Service/Categorie/CategorieServiceFactory.php b/src/UnicaenIndicateur/Service/Categorie/CategorieServiceFactory.php
new file mode 100644
index 0000000..20ab6db
--- /dev/null
+++ b/src/UnicaenIndicateur/Service/Categorie/CategorieServiceFactory.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace UnicaenIndicateur\Service\Categorie;
+
+use Doctrine\ORM\EntityManager;
+use Interop\Container\ContainerInterface;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\NotFoundExceptionInterface;
+
+class CategorieServiceFactory {
+
+    /**
+     * @throws ContainerExceptionInterface
+     * @throws NotFoundExceptionInterface
+     */
+    public function __invoke(ContainerInterface $container) : CategorieService
+    {
+        /** @var EntityManager $entityManager */
+        $entityManager = $container->get('doctrine.entitymanager.orm_default');
+
+        $service = new CategorieService();
+        $service->setObjectManager($entityManager);
+        return $service;
+    }
+}
\ No newline at end of file
diff --git a/src/UnicaenIndicateur/Service/Indicateur/IndicateurService.php b/src/UnicaenIndicateur/Service/Indicateur/IndicateurService.php
index 6763fd1..e96c0bb 100644
--- a/src/UnicaenIndicateur/Service/Indicateur/IndicateurService.php
+++ b/src/UnicaenIndicateur/Service/Indicateur/IndicateurService.php
@@ -3,37 +3,36 @@
 namespace UnicaenIndicateur\Service\Indicateur;
 
 use DateTime;
-use Doctrine\DBAL\Exception as DBA_Exception;
 use Doctrine\DBAL\Driver\Exception as DBA_Driver_Exception;
+use Doctrine\DBAL\Exception as DBA_Exception;
 use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\Exception\ORMException;
 use Doctrine\ORM\NonUniqueResultException;
+use Doctrine\ORM\QueryBuilder;
 use DoctrineModule\Persistence\ProvidesObjectManager;
 use Exception;
+use Laminas\Mvc\Controller\AbstractActionController;
 use RuntimeException;
 use UnicaenIndicateur\Entity\Db\Indicateur;
-use Laminas\Mvc\Controller\AbstractActionController;
 
 /**
  * @property EntityManager $objectManager
  */
-class IndicateurService {
+class IndicateurService
+{
     use ProvidesObjectManager;
 
-/** GESTION DES ENTITES ***********************************************************************************************/
+    /** GESTION DES ENTITES ***********************************************************************************************/
 
     /**
      * @param Indicateur $indicateur
      * @return Indicateur
      */
-    public function create(Indicateur $indicateur) : Indicateur
+    public function create(Indicateur $indicateur): Indicateur
     {
-        try {
-            $this->getObjectManager()->persist($indicateur);
-            $this->getObjectManager()->flush($indicateur);
-        } catch (ORMException $e) {
-            throw new RuntimeException("Un problème est survenue lors de l'enregistrement en base.", $e);
-        }
+        $this->getObjectManager()->persist($indicateur);
+        $this->getObjectManager()->flush($indicateur);
+        if ($indicateur->getCode() === null) $indicateur->setCode($indicateur->getId());
         return $indicateur;
     }
 
@@ -41,7 +40,7 @@ class IndicateurService {
      * @param Indicateur $indicateur
      * @return Indicateur
      */
-    public function update(Indicateur $indicateur) : Indicateur
+    public function update(Indicateur $indicateur): Indicateur
     {
         try {
             $this->getObjectManager()->flush($indicateur);
@@ -55,7 +54,7 @@ class IndicateurService {
      * @param Indicateur $indicateur
      * @return Indicateur
      */
-    public function delete(Indicateur $indicateur) : Indicateur
+    public function delete(Indicateur $indicateur): Indicateur
     {
         try {
             $this->getObjectManager()->remove($indicateur);
@@ -68,7 +67,7 @@ class IndicateurService {
 
     /** GESTION DES VUES **********************************************************************************************/
 
-    public function verifierExistanceMaterializedView($viewname) : bool
+    public function verifierExistanceMaterializedView($viewname): bool
     {
         try {
             $sql = "SELECT EXISTS (SELECT FROM pg_matviews WHERE matviewname='" . $viewname . "')";
@@ -84,7 +83,8 @@ class IndicateurService {
      * @param Indicateur $indicateur
      * @return array|null
      */
-    public function fetch(Indicateur $indicateur) : ?array {
+    public function fetch(Indicateur $indicateur): ?array
+    {
 
         $exist = $this->verifierExistanceMaterializedView($indicateur->getViewId());
         if ($exist === false) return null;
@@ -122,6 +122,8 @@ class IndicateurService {
             throw new RuntimeException("Un problème est survenu lors de la récupération de des données de l'indicateur.", 0, $e);
         }
         $indicateur->setDernierRafraichissement(new DateTime());
+        $count = $this->getCount($indicateur);
+        $indicateur->setNbElements($count);
         $this->update($indicateur);
     }
 
@@ -148,7 +150,7 @@ class IndicateurService {
      */
     public function createView(Indicateur $indicateur)
     {
-        $sql = "CREATE MATERIALIZED VIEW ".$indicateur->getViewId(). " AS ".$indicateur->getRequete();
+        $sql = "CREATE MATERIALIZED VIEW " . $indicateur->getViewId() . " AS " . $indicateur->getRequete();
         try {
             $query = $this->getObjectManager()->getConnection()->prepare($sql);
         } catch (DBA_Exception $e) {
@@ -160,7 +162,8 @@ class IndicateurService {
             throw new RuntimeException("Un problème est survenu lors de la récupération de des données de l'indicateur.", 0, $e);
         }
         $indicateur->setDernierRafraichissement(new DateTime());
-        $this->update($indicateur);
+        $count = $this->getCount($indicateur);
+        $indicateur->setNbElements($count);
     }
 
     /**
@@ -170,16 +173,25 @@ class IndicateurService {
     {
         $this->dropView($indicateur);
         $this->createView($indicateur);
+        $count = $this->getCount($indicateur);
+        $indicateur->setNbElements($count);
     }
 
     /** REQUETAGE *****************************************************************************************************/
 
+    public function createQueryBuilder(): QueryBuilder
+    {
+        $qb = $this->getObjectManager()->getRepository(Indicateur::class)->createQueryBuilder('indicateur');
+        return $qb;
+
+    }
+
     /**
      * @param string $attribut
      * @param string $ordre
      * @return Indicateur[]
      */
-    public function getIndicateurs(string $attribut = 'id', string $ordre = 'ASC') : array
+    public function getIndicateurs(string $attribut = 'id', string $ordre = 'ASC'): array
     {
         $qb = $this->getObjectManager()->getRepository(Indicateur::class)->createQueryBuilder('indicateur')
             ->orderBy('indicateur.' . $attribut, $ordre);
@@ -192,7 +204,7 @@ class IndicateurService {
      * @param int|null $id
      * @return Indicateur|null
      */
-    public function getIndicateur(?int $id) : ?Indicateur
+    public function getIndicateur(?int $id): ?Indicateur
     {
         if ($id === null) return null;
 
@@ -202,7 +214,7 @@ class IndicateurService {
         try {
             $result = $qb->getQuery()->getOneOrNullResult();
         } catch (NonUniqueResultException $e) {
-            throw new RuntimeException("Plusieurs Indicateur partagent le même id [".$id."]", $e);
+            throw new RuntimeException("Plusieurs Indicateur partagent le même id [" . $id . "]", $e);
         }
         return $result;
     }
@@ -212,16 +224,28 @@ class IndicateurService {
      * @param string $paramName
      * @return Indicateur|null
      */
-    public function getRequestedIndicateur(AbstractActionController $controller, string $paramName = "indicateur") : ?Indicateur
+    public function getRequestedIndicateur(AbstractActionController $controller, string $paramName = "indicateur"): ?Indicateur
     {
         $id = $controller->params()->fromRoute($paramName);
         return $this->getIndicateur($id);
     }
 
+    public function getIndicateurByCode(string $code): ?Indicateur
+    {
+        $qb = $this->createQueryBuilder()
+            ->andWhere('indicateur.code = :code')->setParameter('code', $code);
+        try {
+            $indicateur = $qb->getQuery()->getOneOrNullResult();
+        } catch (NonUniqueResultException $e) {
+            throw new RuntimeException("Plusieurs [" . Indicateur::class . "] partagent le même code [".$code."]",0,$e);
+        }
+        return $indicateur;
+    }
+
     /**
      * @return Indicateur[]
      */
-    public function getIndicateursByNamespace(?string $namespace) : array
+    public function getIndicateursByNamespace(?string $namespace): array
     {
         $qb = $this->getObjectManager()->getRepository(Indicateur::class)->createQueryBuilder('indicateur')
             ->andWhere('indicateur.namespace = :namespace')
@@ -236,7 +260,7 @@ class IndicateurService {
      * @return array|null
      */
 
-    public function getIndicateurData(Indicateur $indicateur) : ?array
+    public function getIndicateurData(Indicateur $indicateur): ?array
     {
         $exist = $this->verifierExistanceMaterializedView($indicateur->getViewId());
         if (!$exist) return null;
@@ -257,19 +281,19 @@ class IndicateurService {
         }
         if ($indicateur->getEntity() === Indicateur::ENTITY_STRUCTURE) {
             $rubriques = [
-                'Code'                  => 'code',
-                'Libelle'               => 'libelle_court',
-                'Libelle long'          => 'libelle_long',
-                'Type'                  => 'type',
+                'Code' => 'code',
+                'Libelle' => 'libelle_court',
+                'Libelle long' => 'libelle_long',
+                'Type' => 'type',
 
             ];
         }
         if ($indicateur->getEntity() === Indicateur::ENTITY_AGENT) {
             $rubriques = [
-                'ID'                    => 'c_src_individu',
-                'SOURCE'                => 'c_source',
-                'Prenom'                => 'prenom',
-                'Nom'                   => 'nom_usage',
+                'ID' => 'c_src_individu',
+                'SOURCE' => 'c_source',
+                'Prenom' => 'prenom',
+                'Nom' => 'nom_usage',
             ];
         }
 
@@ -285,5 +309,14 @@ class IndicateurService {
         return [$rubriques, $data];
     }
 
+    public function getCount(Indicateur $indicateur): ?int
+    {
+        $exist = $this->verifierExistanceMaterializedView($indicateur->getViewId());
+        if (!$exist) return null;
+
+        $rawdata = $this->fetch($indicateur);
+        return count($rawdata);
+    }
+
 
 }
\ No newline at end of file
diff --git a/view/unicaen-indicateur/categorie/index.phtml b/view/unicaen-indicateur/categorie/index.phtml
new file mode 100644
index 0000000..f0a7f37
--- /dev/null
+++ b/view/unicaen-indicateur/categorie/index.phtml
@@ -0,0 +1,38 @@
+<?php
+
+use UnicaenIndicateur\Entity\Db\Categorie;
+use UnicaenIndicateur\Provider\Privilege\IndicateurPrivileges;
+
+/**
+ * @see \UnicaenIndicateur\Controller\CategorieController::indexAction()
+ * @var Categorie[] $categories
+ */
+
+$this->headTitle("Catégories d'indicateur");
+
+$canAfficher = $this->isAllowed(IndicateurPrivileges::getResourceId(IndicateurPrivileges::AFFICHER_INDICATEUR));
+$canAjouter = $this->isAllowed(IndicateurPrivileges::getResourceId(IndicateurPrivileges::EDITER_INDICATEUR));
+$canModifier = $this->isAllowed(IndicateurPrivileges::getResourceId(IndicateurPrivileges::EDITER_INDICATEUR));
+$canSupprimer = $this->isAllowed(IndicateurPrivileges::getResourceId(IndicateurPrivileges::DETRUIRE_INDICATEUR));
+
+$canIndexIndicateur = $this->isAllowed(IndicateurPrivileges::getResourceId(IndicateurPrivileges::AFFICHER_INDICATEUR));
+
+?>
+
+<div class="row">
+    <div class="col-md-9">
+        <h1 class="page-header">
+            Categories d'indicateur
+        </h1>
+    </div>
+    <div class="col-md-3">
+        <?php /** @see \UnicaenIndicateur\Controller\IndicateurController::indexAction() */ ?>
+        <a href="<?php echo $this->url('indicateurs', [], [], true); ?>"
+           class="btn btn-secondary">
+            <span class="icon icon-listing"></span>
+            Accéder aux indicateurs
+        </a>
+    </div>
+</div>
+
+
diff --git a/view/unicaen-indicateur/indicateur/index.phtml b/view/unicaen-indicateur/indicateur/index.phtml
index 612ebe6..3ffbf37 100644
--- a/view/unicaen-indicateur/indicateur/index.phtml
+++ b/view/unicaen-indicateur/indicateur/index.phtml
@@ -58,6 +58,8 @@ $canTableaux = $this->isAllowed(TableaudebordPrivileges::getResourceId(Tableaude
     <table id="datatable" class="table table-condensed">
         <thead>
             <tr>
+                <th> Code </th>
+                <th> Effectif </th>
                 <th> Titre </th>
                 <th> Entity </th>
                 <th> #Abonnement </th>
@@ -67,6 +69,8 @@ $canTableaux = $this->isAllowed(TableaudebordPrivileges::getResourceId(Tableaude
         <tbody>
             <?php foreach ($indicateurs as $indicateur) : ?>
                 <tr>
+                    <td> <?php echo $indicateur->getCode(); ?> </td>
+                    <td> <?php echo $indicateur->getNbElements(); ?> </td>
                     <td>
                         <?php echo $indicateur->getTitre(); ?>
                         <?php if ($indicateur->getDescription()) : ?>
@@ -108,10 +112,12 @@ $canTableaux = $this->isAllowed(TableaudebordPrivileges::getResourceId(Tableaude
 
                         <?php if ($canDetruire): ?>
                             <?php /** @see IndicateurController::detruireAction() */?>
-                            <a href="<?php echo $this->url("indicateur/detruire", ['indicateur' => $indicateur->getId()], [], true); ?>">
-                                <span class="icon icon-retirer" title="Détruire l'indicateur"></span></a>
+                            <a href="<?php echo $this->url("indicateur/detruire", ['indicateur' => $indicateur->getId()], [], true); ?>"
+                               class="ajax-modal" data-event="modification"
+                            >
+                                <span class="text-danger icon icon-unchecked" title="Détruire l'indicateur"></span></a>
                         <?php else : ?>
-                            <span class="icon icon-retirer" style="color:lightgray"></span>
+                            <span class="icon icon-unchecked" style="color:lightgray"></span>
                         <?php endif; ?>
 
 
-- 
GitLab