|
|
@@ -0,0 +1,182 @@
|
|
|
+# 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 Models 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
|
|
|
+
|
|
|
+...
|
|
|
+
|
|
|
+#### Réactivité
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 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)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|