Skip to main content

Appliquer les principes de l'unité de travail dans Apex

Objectifs de formation

Une fois cette unité terminée, vous pourrez :

  • Décrire la classe Unité de travail et ses méthodes.
  • Utiliser la classe fflib_SObjectUnitOfWork et son API dans Apex.
Remarque

Remarque

Vous souhaitez apprendre en français ? Commencez le défi dans un Trailhead Playground en français et utilisez les traductions fournies entre crochets pour naviguer. Copiez et collez uniquement les valeurs en anglais, car les validations de défi reposent sur les données en anglais. Si vous ne réussissez pas le défi dans votre organisation en français, nous vous recommandons (1) de définir le paramètre régional sur les États-Unis, (2) de définir la langue sur l’anglais en suivant les instructions ici, puis (3) de cliquer à nouveau sur le bouton « Vérifier le défi ».

Consultez le badge Trailhead dans votre langue pour découvrir comment profiter de l’expérience Trailhead traduite.

Vidéo de démonstration Trail Together

Vous souhaitez être guidé pas à pas par un expert pendant que vous travaillez sur cette étape ? Regardez cette vidéo qui fait partie de la série Trail Together.

(Ce clip commence à 1 h 08 min 31 s, au cas où vous voudriez revenir en arrière et regarder à nouveau le début de l’étape.)

Petit échauffement avec l’Unité de travail

Comme il s'agit d'un contenu avancé, il est logique d'envisager un scénario plus complexe : la création d'une opportunité et de tous les enregistrements dépendants requis à partir de zéro, ce qui, très étonnamment, représente beaucoup de travail. Le code de configuration de test suivant montre comment cela pourrait être réalisé sans l'unité de travail. Mais c’est plutôt verbeux, non ?

List<Opportunity> opps = new List<Opportunity>();
List<List<Product2>> productsByOpp = new List<List<Product2>>();
List<List<PricebookEntry>> pricebookEntriesByOpp = new List<List<PricebookEntry>>();
List<List<OpportunityLineItem>> oppLinesByOpp = new List<List<OpportunityLineItem>>();
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'Opportunity ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    opps.add(opp);
    List<Product2> products = new List<Product2>();
    List<PricebookEntry> pricebookEntries = new List<PricebookEntry>();
    List<OpportunityLineItem> oppLineItems = new List<OpportunityLineItem>();
    for(Integer i=0; i<o+1; i++) {
        Product2 product = new Product2();
        product.Name = opp.Name + ' : Product : ' + i;
        products.add(product);
        PricebookEntry pbe = new PricebookEntry();
        pbe.UnitPrice = 10;
        pbe.IsActive = true;
        pbe.UseStandardPrice = false;
        pbe.Pricebook2Id = Test.getStandardPricebookId();
        pricebookEntries.add(pbe);
        OpportunityLineItem oppLineItem = new OpportunityLineItem();
        oppLineItem.Quantity = 1;
        oppLineItem.TotalPrice = 10;
        oppLineItems.add(oppLineItem);
    }
    productsByOpp.add(products);
    pricebookEntriesByOpp.add(pricebookEntries);
    oppLinesByOpp.add(oppLineItems);
}
// Insert Opportunities
insert opps;
// Insert Products
List<Product2> allProducts = new List<Product2>();
for(List<Product2> products : productsByOpp) {
    allProducts.addAll(products);
}
insert allProducts;
// Insert Pricebooks
Integer oppIdx = 0;
List<PricebookEntry> allPricebookEntries = new List<PricebookEntry>();
for(List<PriceBookEntry> pricebookEntries : pricebookEntriesByOpp) {
    List<Product2> products = productsByOpp[oppIdx++];
    Integer lineIdx = 0;
    for(PricebookEntry pricebookEntry : pricebookEntries) {
        pricebookEntry.Product2Id = products[lineIdx++].Id;
    }
    allPricebookEntries.addAll(pricebookEntries);
}
insert allPricebookEntries;
// Insert Opportunity Lines
oppIdx = 0;
List<OpportunityLineItem> allOppLineItems = new List<OpportunityLineItem>();
for(List<OpportunityLineItem> oppLines : oppLinesByOpp) {
    List<PricebookEntry> pricebookEntries = pricebookEntriesByOpp[oppIdx];
    Integer lineIdx = 0;
    for(OpportunityLineItem oppLine : oppLines) {
        oppLine.OpportunityId = opps[oppIdx].Id;
        oppLine.PricebookEntryId = pricebookEntries[lineIdx++].Id;
    }
    allOppLineItems.addAll(oppLines);
    oppIdx++;
}
insert allOppLineItems;

Recréons le code ci-dessus à l'aide du modèle Unité de travail. Pour commencer, créons une instance Unité de travail. N'oubliez pas que les objets transmis au constructeur doivent être en ordre de dépendance pour que la méthode commitWork puisse les insérer dans l'ordre correct.

// Create a Unit Of Work
fflib_SObjectUnitOfWork uow = new fflib_SObjectUnitOfWork(
    new Schema.SObjectType[] {
        Product2.SObjectType,
        PricebookEntry.SObjectType,
        Opportunity.SObjectType,
        OpportunityLineItem.SObjectType
    }
);

Dans l'unité précédente, la méthode de service applyDiscount utilisait la méthode registerDirty pour enregistrer les enregistrements Opportunité et Ligne d'opportunité. La méthode registerNew insère de nouveaux enregistrements. Lors de l'insertion d'enregistrements enfants, cette méthode assure également que l'ID parent approprié est appliqué avant d'insérer les enregistrements enfants. Vous pouvez le voir en action à la ligne 22. Si vous avez d'autres relations à établir, vous pouvez également appeler la méthode registerRelationship (ligne 21). Maintenant, mettons notre Unité de travail au travail ! Admirez le jeu de mots.

// Do some work!
for(Integer o=0; o<10; o++) {
    Opportunity opp = new Opportunity();
    opp.Name = 'UoW Test Name ' + o;
    opp.StageName = 'Open';
    opp.CloseDate = System.today();
    uow.registerNew(opp);
    for(Integer i=0; i<o+1; i++) {
      Product2 product = new Product2();
      product.Name = opp.Name + ' : Product : ' + i;
      uow.registerNew(product);
      PricebookEntry pbe = new PricebookEntry();
      pbe.UnitPrice = 10;
      pbe.IsActive = true;
      pbe.UseStandardPrice = false;
      pbe.Pricebook2Id = Test.getStandardPricebookId();
      uow.registerNew(pbe, PricebookEntry.Product2Id, product);
      OpportunityLineItem oppLineItem = new OpportunityLineItem();
      oppLineItem.Quantity = 1;
      oppLineItem.TotalPrice = 10;
      uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);
      uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
    }
}

Comme pour l'unité précédente, ces méthodes n'accomplissent pas d'opérations de base de données. Pour ce faire, vous devez valider le travail à l’aide de la méthode commitWork, qui appelle les instructions DML correctes avec des enregistrements qui ont été enregistrés dans l'ordre approprié : enfant, puis parent. En outre, pour les relations, elle attribue l'ID du parent dans les enregistrements enfants, et compose votre modèle d'objet lorsqu'il est inséré dans la base de données.

// Commit the work to the database!
uow.commitWork();

Vous noterez que le code est non seulement plus court, mais également que sa logique est beaucoup plus lisible, sans toutes ces listes agaçantes ! Voici davantage d'exemples des méthodes d’enregistrement que vous pouvez utiliser.

 // Inserts new Opportunity when committing
uow.registerNew(opp);
// Inserts new Opportunity Line Item and associates it with the given Opportunity record when committing
uow.registerNew(oppLineItem, OpportunityLineItem.OpportunityId, opp);
// Relates the given Opportunity Line Item to the given Price Book Entry when committing
uow.registerRelationship(oppLineItem, OpportunityLineItem.PricebookEntryId, pbe);

Récapitulatif

Dans ce module, vous avez appris pourquoi il est intéressant d’envisager l’implémentation de votre application en couches ayant chacune ses propres considérations et préoccupations. Tout comme dans un organisme, chacune joue un rôle pour rendre votre application plus robuste et durable.

Vous avez fait le premier pas sur ce chemin en isolant l'essence de votre application, sa logique métier. Vous pouvez vous arrêter ici ou continuer et appliquer la séparation des préoccupations au comportement des objets (code de déclenchement) et à la demande des informations dont votre application a besoin. N’oubliez pas de consulter le module Apex Enterprise Patterns : Couches Domaine et Sélecteur, qui présente plus en détail les couches d’application Domaine et Sélecteur. Dans l'intervalle, amusez-vous à développer vos services !

Préparation aux défis

Pour réussir ces défis, vous devez déployer certaines bibliothèques open source. La classe fflib_SObjectUnitOfWork fait partie de la bibliothèque open source Apex Common qui dépend de la bibliothèque open source ApexMocks Framework. Vous devez d'abord installer ApexMocks, puis Apex Commons. Vous pouvez en apprendre plus sur ces bibliothèques et leurs contrats de licence open source respectifs dans leurs dépôts.

Pour installer les bibliothèques dans votre organisation, il vous suffit d'utiliser les boutons « Déployer » ci-dessous.

Déployer la bibliothèque open source ApexMocks.

Déployer vers Salesforce

Déployer la bibliothèque open source Apex Common.

Déployer vers Salesforce

Ressources

Partagez vos commentaires sur Trailhead dans l'aide Salesforce.

Nous aimerions connaître votre expérience avec Trailhead. Vous pouvez désormais accéder au nouveau formulaire de commentaires à tout moment depuis le site d'aide Salesforce.

En savoir plus Continuer à partager vos commentaires