# Entity Manager

L'entity manager est la classe au coeur du requêtage des données. Il assure la liaison entre l'api et
le store pinia-orm.

## Modèles et entités

Les modèles d'entités sont définis dans le répertoire `~/models`, sous forme de modèles
[Pinia-ORM](https://pinia-orm.codedredd.de/guide/model/getting-started) et en correspondance
avec leur définition côté API.

### ApiResource et ApiModel

Les ApiResources représentent toute entité pouvant être obtenue via une requête à l'API. C'est la classe de base
gérée par l'entity manager.

Les ApiModel sont des ApiResources qui supportent toutes les opérations de type CRUD.

Ces deux classes correspondent aux ApiResources et aux Entities de Api-Platform.

## Format Json-Ld (Hydra) et normalizer

L'API renvoie ses données au format Json-Ld. La classe `hydraNormalizer` s'occupe ensuite de transformer ces données
dans un format compréhensible par l'entity manager.

Les entités simples sont castées en une instance de leur modèle.
Les collections sont parsées en une liste d'instances de leur modèle.

Les métadonnées, en particulier en ce qui concerne la pagination, sont aussi extraites.

Le résultat est retourné sous la forme d'un objet de la forme :

    {
      data: [...],
      metadata: {...},
    }

### Les champs IriEncoded

Les relations entre les entités sont fournies par l'API sous forme d'IRI. De même, lors des opérations PUT/POST/PATCH,
celle-ci attend aussi des IRI pour représenter ces relations.

Hors le choix a été fait de stocker les ids des entités sous leurs formes numériques dans les stores Pinia-ORM.

Pour permettre cette inter-opérabilité dans les deux sens, on utilise le décorateur `IriEncoded`, qui va permettre
de signaler au normalizer qu'une propriété doit être décodée en entrée (son id sera extrait au format numérique), et
qu'elle doit être retransformée en IRI dans l'autre sens.

Exemple d'utilisation (classe `Access.ts`):

    @IriEncoded(Organization)
    declare organization: number | null

## Entity Manager

### Créer une nouvelle entité (non persistée)

On créé une nouvelle instance d'entité en se servant de la méthode `newInstance`, à laquelle on peut passer les
propriétés du nouvel objet sous forme d'objet JS.

    const newOrganizationInstance = em.newInstance(Organization)

    const newAccessInstance = em.newInstance(Access, { id: 1 })

La nouvelle instance sera crée et enregistrée dans le store Pinia-ORM (mais pas persistée côté API)

Les nouvelles entités (non persistée) se voient attribuer un id temporaire sous forme d'uuid.

### Persister une entité

Une entité existante ou nouvellement crée peut-être persistée côté API. Pour ce faire, on utilise la méthode `persist`
à laquelle on passe le modèle et l'instance de l'entité à persister.

    em.persist(Organization, myOrganization)

C'est entre autres la méthode utilisée par les formulaires lors de l'action "enregistrer".

**Attention** : Lorsqu'on persiste une entité, l'entity manager va utiliser les données de retour de la requête envoyée
à l'API, et mettre à jour le store en fonction (et ce afin d'éviter des écarts involontaires entre les données front
et back). Ce qui veut dire que selon le traitement effectué par l'API, l'entité envoyée et celle qui résulte
de l'opération peuvent différer.

### Fetch une entité simple

Si l'entité a déjà été fetchée, et que l'argument `forceRefresh` est faux, l'entité est simplement retournée depuis le
store.

Sinon, l'opération fetch se déroule en deux temps.

D'abord, une requête GET est envoyée à l'API au moyen de la classe ApiRequestService (surcouche de la librairie
ohfetch). Le résultat de cette requête est converti en ApiResource par le normalizer, puis enregistrée dans le store.

On la récupère ensuite dans le store sous forme de référence avant de la retourner. En effet, si on retournait
directement le résultat de l'appel à l'API plutôt qu'une référence au store Pinia-ORM, on perdrait la réactivité.

### Fetch une Collection

Fetcher une collection est plus compliqué. La requête implique des conditions de filtre, de tri, de pagination.
Afin de pouvoir garder un lien entre les résultats retournés par l'API et les résultats à aller
ensuite chercher dans le store, il faut un système permettant de passer les mêmes conditions à ces deux
interfaces (API / PiniaOrm). C'est le rôle de la classe Query et des Filters (voir plus bas).

Une fois définies les conditions au moyen de l'objet Query, l'appel à fetchCollection se passe de la même manière
que lors de l'appel à la méthode `fetch` (appel, puis récupération des résultats dans le store). La seule nuance
est que le résultat est un objet Collection, incluant les résultats sous la forme d'une référence
calculée (`ComputedRef`), et les données de pagination :

    {
      items,
      totalItems: 100,
      pagination: {
        first: 1,
        last: 10,
        next: 5,
        previous: 3,
      },
    }

#### L'objet Query et les Filters

Les filtres, les tris et la pagination sont donc empaquetés dans un objet Query :

    const query = new Query()

On pourra ensuite ajouter des filtres à cette query :

    query.add(new SearchFilter('name', searchFilter, SEARCH_STRATEGY.IPARTIAL))
    query.add(new OrderBy('name', ORDER_BY_DIRECTION.ASC))
    query.add(new PageFilter(page, itemsPerPage))

Cette query traduira ensuite ces conditions en URL query pour l'appel à l'API d'une part,
et en query PiniaORM d'autre part.

> NB : les filtres de type 'where' seront toujours appliqués en premier, puis les tris, et enfin la pagination.

### Reset une entité

L'entity manager stocke dans le store un clone non modifié de chaque entité fetchée depuis l'API. Ces clones sont
enregistrés avec des ids préfixés par `__clone__`, et ils sont automatiquement exclus des requêtes Pinia-ORM
lorsque celles-ci sont construites au moyen de la méthode `getQuery` de l'entity manager.

La méthode `reset` permettra de réinitialiser une entité modifiée dans le store pour la ramener à l'état qu'elle
avait la dernière fois que l'API l'a renvoyée.

### Supprimer une entité

On peut supprimer une entité au moyen de la méthode `delete` de l'entity manager.
Si l'entité existe dans l'API, une requête de suppression sera envoyée à celle ci.

## EnumManager et ImageManager

### EnumManager

La classe EnumManager permettra de fetcher auprès de l'API des Enum, et de les retourner sous forme de listes JS.

### ImageManager

La classe ImageManger donnera accès à des méthodes permettant de télécharger une image depuis l'API et de la retourner
sous forme de Base64, ou d'uploader une image.

## Composables et useAsyncData

### Utiliser les services dans Vue

Les différentes classes de manager, ainsi que le service de requête à l'API, sont disponibles sous forme de
composables : `useEntityManger`, `useImageManager`, ...etc.

Exemple :

    const ap2iRequestService = useAp2iRequestService()

### Fetch avec useAsyncData

Les composables `useEntityFetch`, `useEnumFetch` et `useImageFetch` permettent d'accéder aux différentes
méthodes `fetch` des managers, sous la forme d'un appel à [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data).

Exemple d'utilisation :

    const { data: parameters, pending, refresh } = fetch(Parameters, 123)
