Strapi v4 : Conception d’un Système d’Inscription pour les Profils Clients et Fournisseurs

Introduction

Notre use case concerne le développement d’une plateforme sur Strapi où les utilisateurs peuvent s’inscrire en tant que clients ou fournisseurs. Chaque utilisateur aura des informations spécifiques en fonction de son rôle : les clients fourniront des détails personnels tels que le nom et le prénom, adresse, le nom de la société si nécessaire etc…, tandis que les fournisseurs donneront des informations sur leur entreprise, comme le nom de la société, adresse, téléphone etc…. L’objectif est de créer une structure de données qui permet une gestion efficace des utilisateurs tout en différenciant les clients et les fournisseurs.

Prérequis

yarn create strapi-app my-project --ts --quickstart

Exploration des Solutions Éventuelles

  1. Solution Unique avec des Champs Optionnels :
    • Créer un seul modèle User avec des champs optionnels pour les informations spécifiques des clients et fournisseurs.
    • Avantages : Structure simple.
    • Inconvénients : Possibilité de champs vides, complexité pour différencier les rôles.
  2. Solution avec Deux Modèles (Client et Fournisseur) :
    • Avantages : Clair et distinct, évite la redondance.
    • Créer des modèles distincts pour les clients et les fournisseurs avec des relations “Has One Belongs To One” vers le modèle User.
    • Inconvénients : Nécessite une gestion des relations.

Choix de la Solution et Motivations

La deuxième solution a été retenue, privilégiant des modèles distincts pour les clients et les fournisseurs, avec des liens “Has One Belongs To One” vers le modèle User. Voici les motivations derrière ce choix :

  1. Clarté et Distinction :
    • En ayant des modèles distincts pour les clients et les fournisseurs, chaque type d’utilisateur conserve une structure claire et distincte, facilitant la gestion et la compréhension.
  2. Éviter la Redondance :
    • En évitant de surcharger le modèle User avec des champs spécifiques à chaque type d’utilisateur, nous minimisons la redondance des données et préservons une structure plus légère et organisée.
  3. Gestion des Relations Facilitée :
    • Les relations “Has One Belongs To One” permettent d’établir des liens directs entre les utilisateurs, les clients et les fournisseurs, simplifiant la récupération des informations associées lors des opérations.
  4. Flexibilité pour l’Avenir :
    • En structurant notre base de données de cette manière, nous sommes mieux préparés pour des évolutions futures, comme l’ajout de nouveaux types d’utilisateurs ou de fonctionnalités spécifiques à chaque type.

En conclusion, la solution choisie offre une approche modulaire et claire pour gérer les différents types d’utilisateurs tout en minimisant la complexité et en préservant la flexibilité pour de futures évolutions de l’application.

Création du content-type Client et Fournisseur

Dans cette étape, nous allons définir les modèles nécessaires pour les User, les clients et les fournisseurs, en établissant des relations entre eux. Strapi défini par défaut un content-type “User” pour la gestion de comptes utilisateurs. Pour l’implémentation de notre fonctionnalité, nous allons créer les modèles Fournisseur et Client.

Pour le content-type Client nous aurons besoin des champs ci-dessous :

Fields: firstName, lastName, companyName, phoneNumber de Type text

Relation: User de type Has One Belongs To One

Exécutez la commande suivante yarn strapi generate content-type et suivez les instructions ci-dessous pour créer l’entité Client

# yarn
yarn strapi generate content-type

# npm
npm run strapi generate content-type

#Répondez de la manière suivante :
$ strapi generate content-type
? Content type display name: Client
? Content type singular name: client
? Content type plural name: clients
? Please choose the model type: Collection Type
? Use draft and publish: No
? Do you want to add attributes: Yes
? Name of attribute: firstName
? What type of attribute: text
? Do you want to add another attribute: Yes
? Name of attribute: lastName
? What type of attribute: text
? Do you want to add another attribute: Yes
? Name of attribute: companyName
? What type of attribute: text
? Do you want to add another attribute: Yes
? Name of attribute: phoneNumber
? What type of attribute: text
? Do you want to add another attribute: No
? Where do you want to add this model: Add model to new API
? Name of the new API: client
? Bootstrap API related files: Yes
✔  ++ /api/client/content-types/client/schema.json
✔  +- /api/client/content-types/client/schema.json
✔  ++ /api/client/controllers/client.js
✔  ++ /api/client/services/client.js
✔  ++ /api/client/routes/client.js
✨  Done in 194.66s.

Pour le content-type Fournisseur nous aurons besoin des champs ci-dessous :

Fields: companyName, phoneNumber de Type text Relation: User de type Has One Belongs To One

yarn strapi generate content-type

#Répondez de la manière suivante :
$ strapi generate content-type
? Content type display name: Fournisseur
? Content type singular name: fournisseur
? Content type plural name: fournisseurs
? Please choose the model type: Collection Type
? Use draft and publish: No
? Do you want to add attributes: Yes
? Name of attribute: companyName
? What type of attribute: text
? Do you want to add another attribute: Yes
? Name of attribute: phoneNumber
? What type of attribute: text
? Do you want to add another attribute: No
? Where do you want to add this model: Add model to new API
? Name of the new API: fournisseur
? Bootstrap API related files: Yes
✔  ++ /api/fournisseur/content-types/fournisseur/schema.json
✔  +- /api/fournisseur/content-types/fournisseur/schema.json
✔  ++ /api/fournisseur/controllers/fournisseur.js
✔  ++ /api/fournisseur/services/fournisseur.js
✔  ++ /api/fournisseur/routes/fournisseur.js
✨  Done in 162.87s.

Configurer les Relations :

Si le serveur n’est pas déjà en cours d’exécution, dans votre terminal, fait un cd pour accéder à votre projet et exécutez npm run develop (ou yarn develop) pour le lancer.

npm run develop
ou 
yarn develop
  • Inscrivez-vous puis Connectez-vous et ajoutez un nouveau champ de type “Relation” appelé user au content-type Fournisseur.
  • Dans les paramètres de la relation, choisissez le modèle “User” comme modèle de référence.
  • Sélectionnez le type de relation comme “has one belongs to one” pour représenter le lien entre chaque client et un utilisateur.

Répéter le Processus pour le content-type Client :

  • Suivez les mêmes étapes pour le content-type “Client”, ajouter un champ de type “Relation” appelé user et le lier au modèle “User” en choisissant “has one belongs to one”.

Décision prise – Choix de la relation “Has One Belongs To One” :

La décision de choisir la relation “Has One Belongs To One” entre les modèles Client/Fournisseur et le modèle User découle de la nécessité de maintenir une distinction nette entre les clients et les fournisseurs tout en évitant de surcharger le modèle User avec des champs spécifiques à chaque profil.

Configurer les permissions

Pour configurer les autorisations d’accès pour l’entité “Fournisseur”, suivez ces étapes ci-dessous :

  1. Accédez à la section “Settings” > “Roles”.
  2. Sélectionnez le rôle “Public” (Rôle par défaut attribué à un utilisateur non authentifié.).
  3. Dans la section “Client”, activez correctement les autorisations “findOne”, “find”, “create”, “update”.
  4. Dans la section “Fournisseur”, activez correctement les autorisations “findOne”, “find”, “create”, “update”.
  5. Sous “User”, assurez-vous que les autorisations “find” et “findOne” sont correctement activées.
  6. Créer les rôles CLIENT et FOURNISSEUR dans Strapi. Pour cela, accédez à l’interface d’administration de Strapi, puis naviguez vers Settings > Users & Permissions Plugin > Roles. Ici, vous pouvez créer de nouveaux rôles en spécifiant leurs noms respectifs et en définissant les permissions appropriées pour chaque rôle en fonction de vos besoins d’affaires.

Mise en place technique

1. Conception technique

Notre système d’inscription utilise un modèle User central dans lequel chaque utilisateur possède un identifiant, un e-mail, un mot de passe, un rôle . Afin de faire une distinction entre les clients et les fournisseurs, nous avons introduit un champ d’énumération appelé “type“, prenant les valeurs “CLIENT” ou “FOURNISSEUR”.

Client :

  • Les clients disposent d’un modèle spécifique avec des champs tels que le prénom, le nom, adresse, téléphone etc…
  • Une relation est établie entre le modèle “Client” et le modèle “User” pour assurer une intégration transparente des données.

Fournisseur :

  • Les fournisseurs ont leur propre modèle avec des champs spécifiques comme le nom de la société, adresse, téléphone etc…
  • Une relation similaire est créée entre le modèle “Fournisseur” et le modèle “User”.

2. Ajouter un champ de type Enumeration dans “User”

Dans cette phase, nous souhaitons améliorer la gestion des utilisateurs en introduisant un champ d’énumération appelé type. Ce champ aura deux valeurs possibles : “CLIENT” et “FOURNISSEUR”. L’objectif est de spécifier le profil de chaque utilisateur dès son inscription. Pour mettre en œuvre cette fonctionnalité, nous allons ajuster le processus d’enregistrement des utilisateurs en ajoutant ce champ d’énumération. Ainsi, cela facilitera la distinction entre les clients et les fournisseurs.

  • Ajouter un Champ : Dans le modele User ajouter un champ de type Enumeration
  • Nommer le Champ : Donnez un nom significatif à votre champ, par exemple “type” pour représenter le rôle de l’utilisateur.

Étape 3 : Gestion de l’inscription

Dans cette étape, nous allons configurer le processus d’insertion de données pour permettre à chaque utilisateur d’être enregistré en tant que client ou fournisseur en fonction du type choisi lors de son inscription. Pour ce faire, nous allons détailler la mise en œuvre de cette fonctionnalité dans Strapi, en expliquant comment ajouter un client ou un fournisseur lors de l’inscription d’un utilisateur à l’aide d’un hook personnalisé. Nous explorerons le cycle de vie de cette opération pour une compréhension approfondie du processus.

Ajout Simultané avec un Hook dans le Cycle de Vie :

Dans notre cas, nous voulons ajouter un client ou un fournisseur lors de la création d’un utilisateur. Pour cela, nous utiliserons un hooks personnalisé dans le cycle de vie de la création d’un utilisateur.

  1. Cycle de Vie dans Strapi :
    • Strapi utilise des lifecycles pour intercepter différentes étapes de vie des entités. Ces lifecycles peuvent être utilisés pour personnaliser le comportement à différentes étapes, comme la création, la mise à jour ou la suppression d’une entité.
  2. Emplacement du Hook :
    • Les hooks de lifecycles pour les utilisateurs peuvent être placés dans le dossier ./api/user-permissions/content-types/user-permissons/services/ du module User. Nous allons créer un service nommé register.ts. Voici les étapes à suivre pour créer ce service:

Creation du service

  1. Naviguez vers le dossier de l’extension : Votre extension users-permissions devrait se situer dans extensions/users-permissions à la racine de votre projet.
  2. Créez un nouveau service : Dans le dossier extensions/users-permissions/services, créez un nouveau fichier pour votre service, par exemple register.ts. Ensuite écrivez le code de votre service :
import { factories } from "@strapi/strapi";

export default factories.createCoreService(
  "plugin::users-permissions.user",
  ({ strapi }) => ({
    // Méthode pour s'abonner aux événements de création d'utilisateur
    subscribe(event) {
      const { model } = event;
      if (
        !(
          ["plugin::users-permissions.user"].includes(model.uid) &&
          ["afterCreate"].includes(event.action)
        )
      ) {
        return;
      }
      this.handleUserCreation(event);
    },
    
    // Cette fonction est appelée automatiquement après la création d'un utilisateur.
    // Elle utilise les informations fournies dans la requête pour créer un profil client ou fournisseur associé.
    async handleUserCreation(event) {
      const { result } = event;
      const body = strapi.requestContext.get().request.body;
      switch (result.type) {
        case "CLIENT":
          await this.createClientProfile(result, body);
          break;
        case "FOURNISSEUR":
          await this.createClientProfile(result, body);
          break;
      }
    },

    // Crée un profil de client en utilisant les informations fournies par l'utilisateur lors de l'inscription.
    async createClientProfile(result, body) {
      await strapi.service("api::client.client").create({
        data: {
          // Utilisation de l'opérateur ?. pour éviter les erreurs si les champs ne sont pas fournis.
          // Conversion des valeurs en chaînes de caractères pour assurer la compatibilité des types de données.
          firstName: body.client?.firstName.toString(),
          lastName: body.client?.lastName?.toString(),
          companyName: body.client?.companyName?.toString(),
          // Associe ce profil de client à l'utilisateur créé en utilisant son ID.
          user: [result.id],
        },
      });
      await this.updateUser(result);
    },

    // Crée un profil de fournisseur avec les données fournies lors de l'inscription.
    async createFournisseurProfile(result, body) {
      await strapi.service("api::fournisseur.fournisseur").create({
        data: {
          // Conversion sécurisée des champs en chaînes de caractères et association avec l'utilisateur.
          companyName: body.fournisseur?.companyName?.toString(),
          phoneNumber: body.fournisseur?.phoneNumber?.toString(),
          user: [result.id],
        },
      });
      await this.updateUser(result);
    },

    // Cette fonction met à jour le rôle de l'utilisateur créé en fonction de son type (CLIENT ou FOURNISSEUR).
    async updateUser(result) {
      // Trouve le rôle correspondant au type d'utilisateur dans la base de données de Strapi.
      // `result.type` contient le type d'utilisateur spécifié lors de l'inscription (par exemple, 'CLIENT' ou 'FOURNISSEUR').
      let userRole = await strapi
        .query("plugin::users-permissions.role")
        .findOne({ where: { name: result.type } });

      // Met à jour l'utilisateur avec le rôle correspondant.
      // Cela garantit que les permissions associées au rôle s'appliquent à l'utilisateur.
      await strapi.entityService.update(
        "plugin::users-permissions.user",
        result.id,
        {
          data: {
            role: userRole.id, // Associe l'ID du rôle trouvé à l'utilisateur.
          },
        }
      );
    },
  })
);

Pour activer ce service et s’assurer qu’il réagit aux événements, il est nécessaire de l’enregistrer et de le lier aux événements du cycle de vie des utilisateurs au démarrage de l’application. Cela se fait généralement dans le fichier index.ts situé a la racine du projet.

Ajoutez le code suivant à index.ts pour enregistrer le service et écouter les événements de création d’utilisateur :

export default {
 
  register() {},


  bootstrap() {
    strapi.db.lifecycles.subscribe((event) => {

      strapi.service('plugin::users-permissions.register').subscribe(event);
    });
  },
};

Au cœur de cette solution se trouve la création d’un service personnalisé dans l’extension users-permissions de Strapi. Ce service écoute l’événement de création d’un utilisateur et, en fonction du type d’utilisateur spécifié (CLIENT ou FOURNISSEUR), crée automatiquement le profil correspondant avec les données fournies lors de l’inscription. Cette approche non seulement automatise le processus de création de profil mais garantit également l’intégrité des données en associant directement chaque profil à son utilisateur respectif.

La fonction handleUserCreation joue un rôle central dans ce processus. Dès qu’un nouvel utilisateur est créé, cette fonction analyse le type d’utilisateur et redirige vers la création du profil client ou fournisseur approprié. Les fonctions createClientProfile et createFournisseurProfile prennent en charge la logique de création de ces profils en utilisant les services Strapi pour interagir avec la base de données. Elles exploitent les données de la requête pour peupler les champs nécessaires du profil, garantissant ainsi que toutes les informations pertinentes sont capturées dès l’inscription.

Un aspect crucial de ce processus est l’association des profils aux utilisateurs. En stockant l’ID de l’utilisateur dans le profil, on établit une relation directe entre l’utilisateur et son profil, facilitant ainsi la gestion des données utilisateur et la personnalisation de l’expérience utilisateur. De plus, la fonction updateUser s’assure que chaque utilisateur se voit attribuer le rôle correspondant à son type, renforçant la gestion des permissions et l’accès aux fonctionnalités de l’application.

En résumé, ce code assure une validation préalable à la création d’utilisateurs en fonction de leur type, et après la création réussie, il crée des enregistrements associés dans d’autres modèles avec les données fournies. Cela permet de personnaliser le processus de création d’utilisateurs en fonction de différents scénarios définis par les types d’utilisateurs.

La méthode subscribe(event) sert de point d’entrée pour notre logique d’extension. Elle examine chaque événement qui se produit au sein du modèle d’utilisateur (plugin::users-permissions.user) et détermine si l’action associée à cet événement correspond à la création d’un nouvel utilisateur (afterCreate). Si ces conditions sont remplies, la méthode délègue le traitement de l’événement à une fonction de gestionnaire, dans notre cas, handle(event), qui initie alors le processus de création de profil basé sur le type d’utilisateur.

Conclusion

En résumé, l’extension du service users-permissions dans Strapi pour créer automatiquement des profils clients ou fournisseurs lors de l’inscription des utilisateurs est un exemple puissant de la flexibilité de Strapi. Cette approche non seulement simplifie la gestion des profils utilisateurs mais contribue également à une expérience utilisateur améliorée, en garantissant que les données sont cohérentes, sécurisées et facilement accessibles pour une personnalisation ultérieure.

Mariama Nour
Mariama Nour
Développeuse web full-stack

Passionnée de programmation, je maîtrise les langages tels que PHP et JAVA, C++, tout en tout en étant très adaptative. Familiarisée avec divers frameworks tels que Laravel, Tailwind CSS, Spring Boot et React Native, j'ai acquis une solide expérience en travaillant sur des projets variés, qu'il s'agisse de développement Web et mobile, de création d'API, de conception d'applications monétiques pour TPE, ou encore de projets d'art numérique.

5 2 votes
Évaluation de l'article
guest
0 Commentaires
Commentaires en ligne
Afficher tous les commentaires

Ingénierie informatique (SSII)

Applize crée des logiciels métiers pour accompagner les entreprises dans la transition vers le zéro papier.


Avez-vous un projet en tête ? Discutons-en.