Guide du développeur de code Apex Force.com
Transcription
Guide du développeur de code Apex Force.com
salesforce: Spring ’13 Guide du développeur de code Apex Force.com © Copyright 2000–2013 salesforce.com, inc. Tous droits réservés. Salesforce.com, le logo "no software" et Team Edition sont des marques déposées, et AppExchange, "Success On Demand" et "The Business Web" sont des marques de commerce de salesforce.com, inc. Toutes les autres marques mentionnées dans ce document sont la propriété de leur détenteur respectif. salesforce | Table des matières | i Table des matières Chapitre 1: Introduction au langage Apex..............................................................................................1 Qu'est-ce que le langage Apex ?................................................................................................................................................2 Comment fonctionne le code Apex ?.............................................................................................................................4 Qu'est-ce que le processus de développement Apex ?....................................................................................................4 Utilisation d'une organisation Developer ou Sandbox.......................................................................................5 Apprentissage du langage Apex.........................................................................................................................7 Écriture de code Apex........................................................................................................................................9 Écriture de tests...............................................................................................................................................10 Déploiement de code Apex dans une organisation Sandbox............................................................................10 Déploiement de code Apex dans une organisation de production Salesforce..................................................11 Ajout de code Apex à une application Force.com AppExchange....................................................................11 Quand dois-je utiliser un code Apex ?.........................................................................................................................12 Quelles sont les limitations du langage Apex ?............................................................................................................12 Nouveautés..............................................................................................................................................................................13 Démarrage rapide avec le langage Apex..................................................................................................................................13 Conventions typographiques de la documentation......................................................................................................13 Compréhension des concepts de base du langage Apex...............................................................................................15 Écriture de votre première classe et de votre premier déclencheur Apex.....................................................................20 Création d'un objet personnalisé......................................................................................................................20 Ajout d'une classe Apex...................................................................................................................................21 Ajout d'un déclencheur Apex...........................................................................................................................22 Ajout d'une classe de test.................................................................................................................................24 Déploiement de composants en production.....................................................................................................26 Chapitre 2: Constructions du langage..................................................................................................28 Types de données....................................................................................................................................................................29 Type de données Primitive..........................................................................................................................................29 Types sObject..............................................................................................................................................................32 Accès aux champs sObject...............................................................................................................................34 Accès aux champs sObject via des relations.....................................................................................................36 Validation de sObjects et de champs ..............................................................................................................37 Collections...................................................................................................................................................................38 Listes................................................................................................................................................................38 Sets..................................................................................................................................................................46 Maps................................................................................................................................................................47 Type paramétré................................................................................................................................................49 Utilisation de types personnalisés dans des clés et ensembles de mappages.....................................................49 Itération de collections.....................................................................................................................................53 Enumérations..............................................................................................................................................................53 Compréhension des règles de conversion....................................................................................................................55 Variables..................................................................................................................................................................................56 salesforce | Table des matières | ii Sensibilité à la casse.....................................................................................................................................................57 Constantes...................................................................................................................................................................58 Expressions..............................................................................................................................................................................59 Compréhension des expressions..................................................................................................................................59 Compréhension des opérateurs d'expression................................................................................................................60 Compréhension des précédences d'opérateur..............................................................................................................66 Extension d'expressions sObject et List.......................................................................................................................67 Utilisation de commentaires........................................................................................................................................67 Instructions d'attribution.........................................................................................................................................................68 Instructions conditionnelles (If-Else)......................................................................................................................................69 Boucles....................................................................................................................................................................................70 Boucles Do-While.......................................................................................................................................................71 Boucles While..............................................................................................................................................................71 Boucles For..................................................................................................................................................................72 Boucles For traditionnelles..............................................................................................................................73 Boucles For d'itération de List ou de Set.........................................................................................................73 Boucles For SOQL..........................................................................................................................................74 Requêtes SOQL et SOSL.......................................................................................................................................................77 Utilisation des résultats de requêtes SOQL et SOSL..................................................................................................78 Utilisation de fonctions Aggregate SOQL..................................................................................................................79 Utilisation de très grandes requêtes SOQL.................................................................................................................80 Utilisation de requêtes SOQL renvoyant un enregistrement.......................................................................................83 Amélioration des performances en annulant la recherche des valeurs nulles................................................................83 Compréhension des requêtes SOQL de clé étrangère et de relations parent-enfant...................................................84 Utilisation de relations polymorphiques dans des requêtes SOQL..............................................................................85 Utilisation de variables Apex dans des requêtes SOQL et SOSL................................................................................87 Demande de tous les enregistrements avec une instruction SOQL.............................................................................90 Instructions de verrouillage......................................................................................................................................................90 Verrouillage d'une boucle For SOQL..........................................................................................................................90 Éviter les impasses.......................................................................................................................................................91 Contrôle des transactions........................................................................................................................................................91 Instructions d'exception...........................................................................................................................................................92 Instructions Throw......................................................................................................................................................92 Instructions Try-Catch-Finally...................................................................................................................................93 Chapitre 3: Invocation de code Apex...................................................................................................95 Déclencheurs...........................................................................................................................................................................96 Déclencheurs en masse................................................................................................................................................97 Syntaxe de déclencheur................................................................................................................................................97 Variables de contexte de déclencheur...........................................................................................................................98 Considérations sur les variables de contexte..............................................................................................................101 Idiomes de déclencheur en masse courants................................................................................................................102 Utilisation de mappages et d'ensembles dans des déclencheurs en masse......................................................102 Corrélation d'enregistrements avec des résultats de requête dans des déclencheurs en masse........................103 Utilisation de déclencheurs pour insérer ou mettre à jour des enregistrements avec des champs uniques......103 salesforce | Table des matières | iii Définition de déclencheurs........................................................................................................................................104 Déclencheurs et instructions Merge..........................................................................................................................106 Déclencheurs et enregistrements restaurés.................................................................................................................107 Déclencheurs et séquence d'exécution.......................................................................................................................107 Opérations n'invoquant pas de déclencheurs.............................................................................................................109 Considérations sur les entités et les champs dans les déclencheurs............................................................................110 Exceptions de déclencheur.........................................................................................................................................112 Meilleures pratiques pour les déclencheurs et les requêtes en masse..........................................................................112 Planificateur Apex.................................................................................................................................................................113 Blocs anonymes.....................................................................................................................................................................120 Apex dans AJAX...................................................................................................................................................................121 Chapitre 4: Classes, objets et interfaces..............................................................................................124 Compréhension des classes....................................................................................................................................................125 Définition de classes Apex.........................................................................................................................................125 Exemple de classe étendue.........................................................................................................................................127 Déclaration de variables de classe..............................................................................................................................132 Définition de méthodes de classe...............................................................................................................................133 Utilisation de constructeurs.......................................................................................................................................136 Modificateurs d'accès.................................................................................................................................................138 Statique et instance....................................................................................................................................................139 Utilisation de méthodes et de variables statiques...........................................................................................140 Utilisation de méthodes et de variables d'instance.........................................................................................141 Utilisation d'un code d'initialisation..............................................................................................................142 Propriétés Apex.........................................................................................................................................................144 Interfaces et extension de classes...........................................................................................................................................147 Itérateurs personnalisés..............................................................................................................................................149 Mots clés...............................................................................................................................................................................152 Utilisation du mot clé final........................................................................................................................................152 Utilisation du mot clé instanceof...............................................................................................................................152 Utilisation du mot clé super.......................................................................................................................................153 Utilisation du mot clé this.........................................................................................................................................154 Utilisation du mot clé transient.................................................................................................................................155 Utilisation des mots clés with sharing ou without sharing.........................................................................................156 Annotations...........................................................................................................................................................................159 Annotation Deprecated.............................................................................................................................................160 Annotation Future.....................................................................................................................................................161 Annotations IsTest....................................................................................................................................................162 Annotation ReadOnly...............................................................................................................................................167 Annotation RemoteAction........................................................................................................................................167 Annotations REST Apex..........................................................................................................................................168 Annotation RestResource..............................................................................................................................169 Annotation HttpDelete.................................................................................................................................169 Annotation HttpGet......................................................................................................................................169 Annotation HttpPatch...................................................................................................................................170 salesforce | Table des matières | iv Annotation HttpPost.....................................................................................................................................170 Annotation HttpPut......................................................................................................................................170 Classes et conversion.............................................................................................................................................................170 Classes et collections..................................................................................................................................................172 Conversion de collection............................................................................................................................................172 Différences entre les classes Apex et les classes Java..............................................................................................................173 Création d'une définition de classe........................................................................................................................................174 Conventions de nommage.........................................................................................................................................176 Occultation de noms..................................................................................................................................................176 Sécurité des classes.................................................................................................................................................................177 Application des autorisations d'objet et de champ.................................................................................................................178 Préfixe d'espace de noms.......................................................................................................................................................179 Utilisation d'espace de noms lors de l'invocation de méthodes..................................................................................179 Précédence des noms d'espace de noms, de classe et de variable................................................................................180 Résolution du type et espace de noms système pour les types....................................................................................181 Paramètres de version............................................................................................................................................................181 Définition de la version de l'API Salesforce pour des classes et des déclencheurs.....................................................181 Définition de versions de package pour des classes des déclencheurs Apex...............................................................183 Chapitre 5: Test du code Apex...........................................................................................................184 Compréhension du test dans Apex........................................................................................................................................185 Pourquoi tester le code Apex ?...................................................................................................................................185 Éléments à tester dans un code Apex.........................................................................................................................185 Test unitaire Apex.................................................................................................................................................................186 Isolation des données test de celles de l'organisation dans les tests unitaires.............................................................187 Utilisation de la méthode runAs................................................................................................................................189 Utilisation de limitations, startTest, et stopTest........................................................................................................191 Ajout de requêtes SOSL à des tests unitaires............................................................................................................191 Exécution de méthodes de test unitaire.................................................................................................................................192 Meilleures pratiques de test...................................................................................................................................................194 Exemple de test.....................................................................................................................................................................195 Chapitre 6: Apex dynamique.............................................................................................................203 Compréhension de l'information Describe Apex..................................................................................................................204 Requête SOQL dynamique...................................................................................................................................................217 Requête SOSL dynamique....................................................................................................................................................218 Requêtes DML dynamiques..................................................................................................................................................219 Chapitre 7: Apex par lot....................................................................................................................223 Utilisation d'une tâche Apex par lot......................................................................................................................................224 Compréhension du partage géré Apex...................................................................................................................................237 Compréhension du partage........................................................................................................................................237 Partage d'un enregistrement en utilisant Apex..........................................................................................................240 Recalcul du partage géré Apex...................................................................................................................................247 Chapitre 8: Débogage du langage Apex..............................................................................................255 salesforce | Table des matières | v Compréhension du journal de débogage...............................................................................................................................256 Utilisation de journaux dans la Console du développeur...........................................................................................261 Débogage des appels d'API Apex..............................................................................................................................271 Gestion des exceptions non détectées....................................................................................................................................273 Compréhension des limitations et des gouverneurs d'exécution............................................................................................273 Utilisation d'avertissements par e-mail pour les limitations du gouverneur...........................................................................279 Chapitre 9: Développement de code Apex dans des packages gérés......................................................280 Versions de package...............................................................................................................................................................281 Dépréciation du code Apex...................................................................................................................................................282 Comportement dans les versions de package.........................................................................................................................282 Gestion des versions de comportement dans un code Apex......................................................................................282 Éléments de code Apex invariables dans les différentes versions...............................................................................283 Test du comportement dans les versions de package.................................................................................................284 Chapitre 10: Exposition de méthodes Apex en tant que services Web SOAP........................................288 Méthodes WebService...........................................................................................................................................................289 Exposition des données avec des méthodes WebService...........................................................................................289 Considérations sur l'utilisation du mot clé WebService.............................................................................................289 Surcharge des méthodes WebService........................................................................................................................292 Chapitre 11: Exposition de classes Apex en tant que services Web REST.............................................293 Introduction à REST Apex...................................................................................................................................................294 Annotations REST Apex......................................................................................................................................................294 Méthodes REST Apex..........................................................................................................................................................294 Exposition de données avec des méthodes de services Web REST Apex.............................................................................301 Exemples de code REST Apex.............................................................................................................................................301 Exemple de code de base REST Apex.......................................................................................................................301 Exemple de code REST Apex utilisant RestRequest................................................................................................304 Chapitre 12: Invocation d'appels en utilisant le langage Apex..............................................................306 Ajout des paramètres de site distant......................................................................................................................................307 Services SOAP : Définition d'une classe à partir d'un document WSDL.............................................................................307 Invocation d'un service externe..................................................................................................................................308 Prise en charge d'en-tête HTTP...............................................................................................................................309 Fonctionnalités WSDL prises en charge...................................................................................................................310 Compréhension du code généré.................................................................................................................................313 Test des appels de service Web..................................................................................................................................318 Considérations sur l'utilisation de WSDL.................................................................................................................321 Mappage d'en-têtes........................................................................................................................................321 Compréhension des événements à l'exécution...............................................................................................321 Compréhension des caractères non pris en charge dans les noms de variables...............................................321 Débogage de classes générées à partir de fichiers WSDL..............................................................................321 Invocation d'appels HTTP....................................................................................................................................................322 Utilisation de certificats.........................................................................................................................................................322 Génération de certificats............................................................................................................................................322 salesforce | Table des matières | vi Utilisation de certificats avec des services SOAP.......................................................................................................323 Utilisation de certificats avec des requêtes HTTP.....................................................................................................324 Limitations des appels...........................................................................................................................................................325 Chapitre 13: Référence.....................................................................................................................326 Opérations DML (langage de manipulation de données) Apex............................................................................................327 Opération ConvertLead............................................................................................................................................328 Opération Delete.......................................................................................................................................................332 Opération Insert........................................................................................................................................................334 Instruction Merge......................................................................................................................................................338 Opération Undelete...................................................................................................................................................339 Opération Update......................................................................................................................................................342 Opération Upsert.......................................................................................................................................................345 sObjects qui ne prennent pas en charge les opérations DML....................................................................................351 sObjects non utilisables dans des opérations DML...................................................................................................351 Gestion des exceptions DML en masse.....................................................................................................................353 Méthodes et classes standard Apex........................................................................................................................................354 Méthodes Primitives Apex........................................................................................................................................355 Méthodes Blob..............................................................................................................................................355 Méthodes Boolean.........................................................................................................................................356 Méthodes Date..............................................................................................................................................357 Méthodes Datetime.......................................................................................................................................360 Méthodes Decimal.........................................................................................................................................367 Méthodes Double..........................................................................................................................................373 Méthodes ID.................................................................................................................................................375 Méthodes Integer...........................................................................................................................................377 Méthodes Long.............................................................................................................................................378 Méthodes String............................................................................................................................................379 Méthodes Time.............................................................................................................................................409 Méthodes Collection Apex........................................................................................................................................410 Méthodes List................................................................................................................................................411 Méthodes Map..............................................................................................................................................420 Méthodes Set.................................................................................................................................................427 Méthodes Enum........................................................................................................................................................431 Méthodes sObject Apex............................................................................................................................................432 Méthodes Schéma..........................................................................................................................................432 Méthodes sObject..........................................................................................................................................437 Méthodes de résultat de description de sObject.............................................................................................442 Méthodes Describe Field Result....................................................................................................................446 Méthodes Schema.FieldSet...........................................................................................................................455 Méthodes Custom Settings...........................................................................................................................459 Méthodes Système Apex...........................................................................................................................................470 Méthodes ApexPages.....................................................................................................................................471 Méthodes Approval.......................................................................................................................................471 Méthodes Database.......................................................................................................................................472 salesforce | Table des matières | vii Prise en charge JSON....................................................................................................................................490 Méthodes Limits...........................................................................................................................................513 Méthodes Math.............................................................................................................................................516 Méthodes MultiStaticResourceCalloutMock................................................................................................521 REST Apex...................................................................................................................................................522 Méthodes Search...........................................................................................................................................528 Méthodes StaticResourceCalloutMock.........................................................................................................528 Méthodes System...........................................................................................................................................529 Méthodes Test...............................................................................................................................................542 Méthodes TimeZone.....................................................................................................................................548 Méthodes Type..............................................................................................................................................550 Méthodes URL..............................................................................................................................................554 Méthodes UserInfo........................................................................................................................................557 Méthodes Version..........................................................................................................................................560 Utilisation de méthodes d'exception..........................................................................................................................562 Classes Apex..........................................................................................................................................................................565 Classes E-mail Apex..................................................................................................................................................566 E-mail sortant................................................................................................................................................566 E-mail entant.................................................................................................................................................579 Classe d'exception......................................................................................................................................................585 Construction d'une exception........................................................................................................................586 Utilisation de variables d'exception................................................................................................................587 Classes Visualforce.....................................................................................................................................................588 Classe Action.................................................................................................................................................588 Méthodes et propriétés de composants dynamiques......................................................................................590 Classe IdeaStandardController......................................................................................................................591 Classe IdeaStandardSetController.................................................................................................................594 Classe KnowledgeArticleVersionStandardController....................................................................................600 Classe Message..............................................................................................................................................604 Classe PageReference....................................................................................................................................605 Classe SelectOption.......................................................................................................................................611 Classe StandardController.............................................................................................................................614 Classe StandardSetController........................................................................................................................617 Classe Flow.Interview................................................................................................................................................620 Classes Pattern et Matcher........................................................................................................................................621 Utilisation de classes Pattern et Matcher.......................................................................................................621 Utilisation de classes Region..........................................................................................................................622 Utilisation d'opérations Match......................................................................................................................622 Utilisation de limites......................................................................................................................................623 Compréhension des groupes de capture.........................................................................................................623 Exemple de classe Pattern et Matcher...........................................................................................................624 Méthodes Pattern..........................................................................................................................................626 Méthodes Matcher........................................................................................................................................627 Classes de services (RESTful) HTTP.......................................................................................................................634 Classes HTTP...............................................................................................................................................634 salesforce | Table des matières | viii Classe Crypto.................................................................................................................................................648 Classe EncodingUtil......................................................................................................................................656 Classes XML.............................................................................................................................................................657 Classes XmlStream........................................................................................................................................657 Classes DOM................................................................................................................................................667 Classes de gestion des approbations Apex.................................................................................................................674 Exemple de gestion des approbations Apex...................................................................................................675 Classe ProcessRequest...................................................................................................................................676 Classe ProcessResult......................................................................................................................................677 Classe ProcessSubmitRequest........................................................................................................................678 Classe ProcessWorkitemRequest...................................................................................................................679 Classes de support Apex............................................................................................................................................680 Classe BusinessHours....................................................................................................................................680 Classe Cases...................................................................................................................................................682 Classes de communauté Apex....................................................................................................................................683 Classe Answers..............................................................................................................................................683 Classe Ideas....................................................................................................................................................684 Classe Publishing Service pour la gestion de Knowledge..........................................................................................688 Classe Site..................................................................................................................................................................693 Classe Cookie............................................................................................................................................................700 Classe Network..........................................................................................................................................................703 Connect in Apex - Aperçu du développeur...............................................................................................................704 Interfaces Apex......................................................................................................................................................................704 Interface Auth.RegistrationHandler..........................................................................................................................705 Interface Comparable................................................................................................................................................710 Interface HttpCalloutMock.......................................................................................................................................712 Interface InstallHandler.............................................................................................................................................713 Interface Support.EmailTemplateSelector................................................................................................................716 Interface Site.UrlRewriter..........................................................................................................................................718 Utilisation de l'interface Process.Plugin.....................................................................................................................727 Interface Process.Plugin.................................................................................................................................727 Classe Process.PluginRequest........................................................................................................................729 Classe Process.PluginResult...........................................................................................................................730 Classe Process.PluginDescribeResult.............................................................................................................731 Conversions de type de donnéesProcess.Plugin.............................................................................................734 Exemple de mise en oeuvre Process.Plugin pour la conversion de piste.........................................................735 Interface UninstallHandler........................................................................................................................................746 Interface WebServiceMock.......................................................................................................................................748 Chapitre 14: Déploiement de code Apex............................................................................................750 Utilisation d'ensembles de modifications pour déployer un code Apex.................................................................................751 Utilisation de l'IDE Force.com pour déployer un code Apex................................................................................................751 Utilisation de l'Outil de migration Force.com.......................................................................................................................752 Compréhension de deploy.........................................................................................................................................753 Compréhension de retrieveCode...............................................................................................................................755 salesforce | Table des matières | ix Compréhension de runTests()...................................................................................................................................757 Utilisation de l'API SOAP pour déployer un code Apex......................................................................................................757 Annexes...........................................................................................................................................758 Annexe A: Exemple de bordereau d'expédition..........................................................................758 Présentation d'un exemple de bordereau d'expédition...............................................................................................758 Code de l'exemple de bordereau d'expédition............................................................................................................761 Annexe B: Mots clés réservés....................................................................................................776 Annexe C: Conseils relatifs à la sécurité pour le développement de code Apex et de pages Visualforce..........................................................................................................................778 Cross Site Scripting (XSS)........................................................................................................................................778 Sortie et formules non échappées dans des pages Visualforce....................................................................................780 Cross-Site Request Forgery (CSRF).........................................................................................................................782 Injection SOQL........................................................................................................................................................783 Contrôle de l'accès aux données.................................................................................................................................785 Annexe D: API SOAP et en-têtes SOAP pour Apex..................................................................788 compileAndTest()......................................................................................................................................................788 CompileAndTestRequest..............................................................................................................................790 CompileAndTestResult.................................................................................................................................791 compileClasses()........................................................................................................................................................793 compileTriggers()......................................................................................................................................................795 executeanonymous()..................................................................................................................................................795 ExecuteAnonymousResult.............................................................................................................................796 runTests()..................................................................................................................................................................797 RunTestsRequest...........................................................................................................................................799 RunTestsResult..............................................................................................................................................800 DebuggingHeader.....................................................................................................................................................803 PackageVersionHeader..............................................................................................................................................804 Glossaire..........................................................................................................................................806 salesforce | Table des matières | x Chapitre 1 Introduction au langage Apex Sujets : • • • Qu'est-ce que le langage Apex ? Nouveautés Démarrage rapide avec le langage Apex Salesforce.com a transformé la façon dont les entreprises exercent leur activité en déplaçant les applications professionnelles, qui étaient traditionnellement basées sur une relation client-serveur, vers un environnement Web à la demande et mutualisé, la plate-forme Force.com. Cet environnement permet aux entreprises d'exécuter et de personnaliser des applications, telles que Automatisation et Service et Support Salesforce, et d'élaborer des applications personnalisées basées sur des besoins métier spécifiques. Bien que de nombreuses options de personnalisation soient disponibles à travers l'interface utilisateur de Salesforce, notamment la possibilité de définir de nouveaux champs, des objets, un workflow et des processus d'approbation, les développeurs peuvent également utiliser l'API SOAP pour émettre des commandes de manipulation de données, telles que delete(), update() ou upsert(), à partir de programmes côté client. Ces programmes côté client, généralement écrits en Java, JavaScript et .NET, ou d'autres langages de programmation, offrent aux organisations davantage de flexibilité dans leurs personnalisations. Cependant, comme la logique de contrôle de ces programmes côté client ne réside dans les serveurs de la plate-forme Force.com, ils sont restreints par les éléments suivants : • • Les coûts en performance des multiples allers-retours vers le site salesforce.com pour accomplir des transactions commerciales courantes Le coût et la complexité de l'hébergement du code serveur, tel que Java ou .NET, dans un environnement sécurisé et robuste Pour apporter une réponse à ces problèmes et révolutionner la façon dont les développeurs créent des applications à la demande, salesforce.com présente le code Apex Force.com, le premier langage de programmation à la demande et mutualisé destiné aux développeurs qui élaborent la prochaine génération d'applications professionnelles. • • • Qu'est-ce que le code Apex ? Pour en savoir plus sur l'utilisation du code Apex, le processus de développement et ses limitations Nouveautés dans cette version Apex ? Démarrage rapide avec Apex. Explorez directement ce code, et écrivez votre première classe et votre premier déclencheur Apex Introduction au langage Apex salesforce | Qu'est-ce que le langage Apex ? | 2 Qu'est-ce que le langage Apex ? Apex est un langage de programmation orienté objet, fortement typé, qui permet aux développeurs d'exécuter des instructions de contrôle de flux et de transactions sur le serveur de la plate-forme Force.com, en conjonction avec des appels à l'API Force.com. Avec une syntaxe semblable à Java et qui se comporte comme les procédures stockées dans une base de données, le langage Apex permet aux développeurs d'ajouter une logique applicative à la plupart des événements système, y compris aux clics de bouton, aux mises à jour d'enregistrements associés et aux pages Visualforce. Le code Apex peut être initialisé par des demandes émanant de services Web et de déclencheurs d'objets. Figure 1: Vous pouvez ajouter un code Apex à la plupart des événements système. En tant que langage, Apex est : Intégré Apex fournit une prise en charge intégrée pour des idiomes communs de la plate-forme Force.com qui comprennent : • Les appels DML (langage de manipulation de données), tels que INSERT, UPDATE et DELETE, qui inclut le traitement DmlException intégré • Des requêtes SOQL (langage de requête d'objets) Salesforce et SOSL (langage de recherche d'objets) Salesforce qui renvoient des listes d'enregistrements sObject • Des boucles qui permettent de traiter en masse plusieurs enregistrements même temps • Une syntaxe de verrouillage qui empêche les conflits de mise à jour d'enregistrements Introduction au langage Apex • • salesforce | Qu'est-ce que le langage Apex ? | 3 Des appels API Force.com publics personnalisés qui peuvent être créés à partir de méthodes Apex stockées Des avertissements et des erreurs émis lorsqu'un utilisateur tente de modifier ou de supprimer un objet personnalisé ou un champ qui est référencé par un code Apex Facilité d'utilisation Le langage Apex est basé sur des idiomes Java familiers, tels qu'une syntaxe de variables et d'expressions, une syntaxe de déclaration de bloc et conditionnelle, une syntaxe de boucle, une notation d'objet et de tableau, etc. Lorsque le langage Apex introduit de nouveaux éléments, il utilise une syntaxe et une sémantique faciles à comprendre et favorise une utilisation efficace de la plate-forme Force.com. Par conséquent, le langage Apex produit un code à la fois succinct et facile à écrire. Orienté vers les données Le langage Apex est conçu pour relier entre elles plusieurs requêtes et déclarations DML dans une seule unité de travail sur le serveur de la plate-forme Force.com, de la même façon que les développeurs utilisent des procédures stockées dans une base de données pour relier entre elles plusieurs déclarations de transaction sur un serveur de base de données. Notez que comme d'autres procédures stockées dans une base de données, le langage Apex n'essaie pas de fournir un support général pour le rendu des éléments dans l'interface utilisateur. Rigoureux L'Apex est un langage fortement typé qui utilise des références directes à des objets de schéma, tels que des noms d'objet et de champ. Il échoue rapidement lors de la compilation si des références ne sont pas valides, et stocke toutes les dépendances de champ, d'objet et de classe personnalisés dans des métadonnées afin de les protéger contre la suppression lorsqu'elles sont demandées par un code Apex actif. Hébergé Le langage Apex est entièrement interprété, exécuté et contrôlé par la plate-forme Force.com. Mutualisé Comme le reste de la plate-forme Force.com, le langage Apex est exécuté dans un environnement mutualisé (multitenant). Par conséquent, le moteur d'exécution du langage Apex est conçu pour empêcher qu'un emballement de code ne monopolise des ressources partagées. Tout code qui viole ces limites échoue avec des messages d'erreur faciles à comprendre. Automatiquement mis à niveau Il n'est jamais nécessaire de réécrire le code Apex lorsque d'autres parties de la plate-forme Force.com sont mises à niveau. Le code étant compilé et stocké sous forme de métadonnées sur la plate-forme, il est toujours automatiquement mis à niveau avec le reste du système. Facile à tester Le langage Apex fournit une prise en charge intégrée de la création et de l'exécution de tests unitaires, qui comprennent des résultats de tests indiquant la quantité de code couverte et les parties de votre code qui peuvent être améliorées. Salesforce.com garantit la fiabilité du fonctionnement du code Apex, en exécutant des tests unitaires stockés dans des métadonnées avant toute mise à niveau de la plate-forme. Versions multiples Vous pouvez enregistrer votre code Apex dans plusieurs versions API Force.com. Vous pouvez ainsi préserver des comportements. Le langage Apex est inclus dans les versions Unlimited Edition, Developer Edition, Enterprise Edition et Database.com. Introduction au langage Apex salesforce | Comment fonctionne le code Apex ? | 4 Comment fonctionne le code Apex ? Tout le code Apex est exécuté entièrement à la demande sur la plate-forme Force.com, comme indiqué dans le diagramme d'architecture suivant : Figure 2: Le code Apex est entièrement compilé, stocké et exécuté sur la plate-forme Force.com. Lorsqu'un développeur écrit et enregistre un code Apex sur la plate-forme, le serveur d'applications de la plate-forme commence par compiler le code dans un ensemble d'instructions abstraites compréhensibles par l'interpréteur d'exécution Apex, puis il enregistre ces instructions sous forme de métadonnées. Lorsqu'un utilisateur final déclenche l'exécution du code Apex, par exemple en cliquant sur un bouton ou en accédant à une page Visualforce, le serveur d'applications de la plate-forme récupère les instructions compilées à partir des métadonnées, et les envoie via l'interpréteur d'exécution, avant de renvoyer le résultat. L'utilisateur final ne remarque aucune différence en termes de délais d'exécution par rapport aux requêtes standard de la plate-forme. Qu'est-ce que le processus de développement Apex ? Nous recommandons le processus suivant pour le développement de code Apex : 1. 2. 3. 4. 5. 6. Obtenir un compte Developer Edition. Apprendre le langage Apex. Écrire votre code Apex. En écrivant un code Apex, vous devez également écrire des tests. (Facultatif) Déployer votre code Apex dans une organisation sandbox et effectuer des tests unitaires finaux. Déployer votre code Apex dans votre organisation de production Salesforce. En plus de du déploiement de votre code Apex, une fois écrit et testé, vous pouvez également ajouter vos classes et déclencheurs à un package d'applications Force.com AppExchange. Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ?|5 Utilisation d'une organisation Developer ou Sandbox Il existe trois types d'organisation dans lesquels vous pouvez exécuter votre code Apex : • • • Une organisation de développeur : organisation créée avec un compte Developer Edition. Une organisation de production : organisation comprenant des utilisateurs actifs qui accèdent vos données. Une organisation sandbox : organisation créée dans votre organisation de production, mais qui est une copie de cette organisation. Remarque: Des déclencheurs Apex sont disponibles dans la version d'évaluation (Trial Edition) de Salesforce. Ils sont toutefois désactivés lorsque vous passez à une autre version. Si votre nouvelle organisation inscrite inclut le langage Apex, vous devez déployer votre code vers votre organisation en utilisant l'une des méthodes de déploiement. Vous ne pouvez pas développer un code Apex dans votre organisation de production Salesforce. Les utilisateurs actifs qui accèdent au système pendant que vous développez risquent de déstabiliser vos données ou de corrompre votre application. À la place, nous recommandons de réaliser tout le travail de développement dans un environnement sandbox ou dans une organisation Developer Edition. Si vous n'êtes pas déjà membre d'une communauté de développeurs, accédez à http://developer.force.com/join et suivez les instructions pour vous inscrire à un compte Developer Edition. Un compte Developer Edition vous donne accès à une organisation Developer Edition gratuite. Même si vous avez déjà une organisation Enterprise Edition ou Unlimited Edition et un environnement sandbox pour créer un code Apex, nous recommandons vivement de bénéficier des ressources disponibles dans la communauté de développeurs. Remarque: Vous ne pouvez pas modifier un code Apex en utilisant l'interface utilisateur de Salesforce dans une organisation de production Salesforce. Création d'une organisation Sandbox Pour créer ou actualiser une sandbox : 1. Cliquez sur Votre nom > Configuration > Gestion des données > Sandbox. 2. Effectuez l'une des opérations suivantes : • • Cliquez sur Nouvelle version test. Pour plus d'informations sur les différents types de sandbox, reportez-vous à Présentation de sandbox. Salesforce désactive le bouton Nouvelle version test Sandbox lorsqu'une entreprise atteint le nombre maximal d'environnements Sandbox. Si nécessaire, contactez salesforce.com pour commander davantage d'environnements sandbox pour votre organisation. Notez que Salesforce désactive tous les liens d'actualisation lorsque vous atteignez votre limite en environnements sandbox. Cliquez sur Actualiser pour remplacer un environnement Sandbox existant par une nouvelle copie. Salesforce affiche le lien Actualiser uniquement pour les environnements sandbox éligibles pour l'actualisation. Pour des sandbox complets, la valeur correspond toute période après 29 jours à compter de la création ou de l'actualisation précédente des sandbox. Pour des sandbox de type configuration-uniquement (y compris les sandbox de développeur), vous pouvez actualiser une fois par jour. Votre copie existante de cet environnement sandbox reste disponible en attendant la fin de l'actualisation. La copie actualisée reste inactive jusqu'à son activation. 3. Saisissez un nom et une description pour le sandbox. Vous pouvez modifier le nom uniquement lors de la création ou de l'actualisation d'un sandbox. Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ?|6 Conseil: Nous vous recommandons de choisir un nom qui : • • Reflète l'objectif de ce sandbox, tel que « QA ». Contient peu de caractères, car Salesforce ajoute automatiquement le nom du sandbox au nom d'utilisateur et à l'adresse e-mail dans les enregistrements utilisateur de l'environnement sandbox. Les noms contenant peu de caractères facilitent la saisie des informations de connexion sandbox. 4. Sélectionnez le type de sandbox : • • • Configuration uniquement : Les sandbox de type configuration-uniquement copient tous les rapports, tableaux de bord, catalogues de prix, produits, applications et personnalisations de votre organisation de production sous Votre nom > Configuration, mais excluent tous les enregistrements d'objet personnalisé et standard, les documents et pièces jointes de votre organisation. La création d'un sandbox de type configuration-uniquement peut diminuer le délai de création ou d'actualisation d'un sandbox de quelques heures à seulement quelques minutes, mais peut inclure uniquement 500 Mo de données. Vous pouvez actualiser un sandbox de type configuration-uniquement une fois par jour. Développeur : Les sandbox Developer sont des versions test spéciales de configuration-uniquement qui permettent à un seul développeur de coder et de tester des programmes. Plusieurs utilisateurs peuvent se connecter à un seul sandbox de développeur, mais leur objet principal consiste à offrir un environnement permettant d'isoler les modifications en cours de développement tant qu'elles ne sont pas prêtes à être partagées. Semblables aux sandbox de configuration-uniquement, les sandbox Developer copient toutes les informations d'application et de configuration dans l'environnement sandbox. Les sandbox Developer sont limités à 10 Mo de données test ou d'essai, valeur suffisante pour de nombreuses tâches de développement et de test. Vous pouvez actualiser un sandbox de développeur une fois par jour. Intégral : Les environnements sandbox complets copient l'ensemble de votre organisation de production et de ses données, notamment les enregistrements d'objet personnalisé et standard, les documents et les pièces jointes. Vous pouvez actualiser un environnement sandbox complet tous les 29 jours. Si vous avez diminué le nombre de sandbox achetés et que vous possédez toujours un nombre de sandbox d'un type donné supérieur à celui autorisé, vous devez ajuster le nombre de sandbox utilisés au nombre de sandbox achetés. Par exemple, si vous disposez de deux environnements sandbox complets, alors que vous en avez acheté qu'un seul, vous ne pouvez pas actualiser l'environnement sandbox en tant que sandbox complet. Vous devez choisir un sandbox complet pour le convertir en environnement sandbox réduit, tel qu'un sandbox de configuration-uniquement ou de développeur, selon le type de sandbox disponible. Remarque: Les sandbox de type configuration-uniquement et développeur copient tous les rapports, tableaux de bord, catalogues de prix, produits, applications et personnalisations de votre organisation de production sous Votre nom > Configuration, mais excluent tous les enregistrements d'objet personnalisé et standard, les documents et pièces jointes de votre organisation. Puisque ces types de sandbox copient un volume inférieur de données, leur création peut fortement diminuer le délai de création ou d'actualisation d'un environnement sandbox. Si vous actualisez un sandbox existant, la case présélectionne généralement le type de sandbox correspondant au sandbox que vous actualisez. Par exemple, si vous actualisez un Sandbox de type configuration-uniquement, la case présélectionne Configuration Uniquement. Si vous actualisez un sandbox existant ou créez un sandbox, certaines cases peuvent être désactivées si vous avez déjà atteint le nombre de sandbox de ce type autorisé pour votre organisation. 5. Pour un sandbox complet, sélectionnez la quantité d'historique d'objet, d'historique de requête et d'historique d'opportunité à copier, et s'il est nécessaire ou non de copier les données Chatter. L'historique d'objet est le suivi de l'historique des champs des objets personnalisés et de la plupart des objets standard. L'historique de requête et l'historique d'opportunité jouent le même rôle pour les requêtes et les opportunités. Vous pouvez copier de 0 à 180 jours d'historique, par incréments de 30 jours. La valeur par défaut est 0 jour. Les données Chatter comprennent des fils, des messages et des rubriques de découverte. La diminution de la quantité de données copiée peut accélérer de façon significative le délai de copie de votre sandbox. Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ?|7 6. Cliquez sur Lancer la copie. Le processus peut durer plusieurs minutes, heures ou mêmes plusieurs jours, en fonction de la taille de votre organisation et du type de copie que vous créez, à savoir une copie complète ou une copie de type configuration-uniquement. Conseil: Essayez de limiter les modifications de votre organisation de production pendant la copie d'une sandbox. 7. Vous recevez une notification par e-mail une fois la copie de votre sandbox créée ou actualisée. Si vous créez un sandbox, l'environnement est maintenant prêt à l'emploi. Si vous actualisez un sandbox existant, vous devez exécuter une étape supplémentaire pour terminer le processus de copie de l'environnement sandbox. Le nouveau sandbox doit être activé. Pour supprimer votre sandbox existant et en activer un nouveau : a. Revenez à la liste de sandbox en vous connectant à votre organisation de production et en accédant à Votre nom > Configuration > Gestion des données > Sandbox. b. Cliquez sur le lien Activer en regard du sandbox à activer. Vous êtes dirigé vers une page qui vous informe de la suppression de votre sandbox existant. c. Lisez attentivement cet avertissement. Si vous acceptez la suppression, saisissez le texte demandé à l'invite, puis cliquez sur le bouton Activer. Une fois le processus d'activation terminé, vous recevez une notification par e-mail. ATTENTION: L'activation d'un sandbox de remplacement créé en utilisant le lien Actualiser entraîne la suppression complète du sandbox actualisé. Toutes les configurations et les données de la copie du sandbox précédent sont perdues, y compris les modifications d'applications ou de données effectuées. Lisez attentivement les avertissements, puis cliquez sur le lien Activer uniquement si vous ne souhaitez pas conserver le contenu de la copie de l'environnement sandbox actuellement utilisé. Votre organisation de production et ses données ne sont pas affectées. 8. Une fois votre nouveau sandbox terminé, ou votre sandbox actualisé activé, cliquez sur le lien de la notification par e-mail pour accéder à votre environnement sandbox. Vous pouvez vous connecter au sandbox à l'adresse test.salesforce.com/login.jsp en ajoutant .nom_sandbox à votre nom d'utilisateur Salesforce. Par exemple, si votre nom d'utilisateur pour votre organisation de production est [email protected], votre nom d'utilisateur pour un sandbox appelé « test » est [email protected]. Remarque: Salesforce modifie automatiquement les noms d'utilisateur de sandbox, mais pas les mots de passe. Les nouveaux environnements sandbox créés incluent le paramètre de livraison d'e-mail par défaut E-mail système uniquement. Le paramètre E-mail système uniquement est particulièrement utile pour contrôler les e-mails envoyés à partir d'environnements sandbox, afin d'empêcher les opérations de test et de développement d'envoyer des e-mails tests à vos utilisateurs. Apprentissage du langage Apex Avec un compte Developer, vous disposez de nombreuses ressources pour apprendre le langage Apex : Guides Force.com Workbook : Get Started Building Your First App in the Cloud (Premiers pas d'élaboration de votre première application dans le Cloud) Programmeurs débutants Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ?|8 Une série de dix tutoriels de 30 minutes qui présentent diverses fonctionnalités de la plate-forme Force.com. Les tutoriels Force.com Workbook présentent l'élaboration d'un système de gestion des stocks extrêmement simple. Vous apprenez à développer entièrement une application, c.-à-d. vous commencez par élaborer un modèle de base de données pour assurer le suivi des marchandises. Vous ajoutez ensuite une logique métier : des règles de validation qui vérifient si le stock est suffisant, un workflow pour mettre à jour l'inventaire suite à la vente de marchandises, des approbations afin d'envoyer des notifications par e-mail pour des montants de facture importants, enfin une logique de déclenchement pour mettre à jour les prix dans les factures ouvertes. Une fois la base de données et la logique métier terminées, vous créez une interface utilisateur pour présenter l'inventaire produit au personnel, un site Web public pour présenter un catalogue de produits, puis vous commencez à réaliser une simple vitrine. Si vous souhaitez développer hors ligne, puis intégrer à l'application, nous avons ajouté un dernier tutoriel consacré à l'utilisation d'Adobe Flash Builder pour Force.com. Guides Force.com Workbook : HTML | PDF Guide Apex Workbook Programmeurs débutants Le guide Apex Workbook présente le langage de programmation Apex dans une série de tutoriels. Vous apprenez les concepts de base de Apex, et comment les utiliser sur la plate-forme Force.com pour ajouter une logique métier personnalisée via des déclencheurs, des tests unitaires, un code Apex planifié, un code Apex par lot, des services Web REST et des contrôleurs Visualforce. Guide Apex Workbook : HTML | PDF Page Apex Developer Force Programmeurs débutants et avancés La page Apex sur Developer Force inclut des liens vers diverses ressources qui comprennent des articles sur le langage de programmation Apex. Ces ressources présentent une vue d'ensemble du langage Apex, ainsi que les meilleures pratiques de développement Apex. Guide Force.com Cookbook Programmeurs débutants et avancés Ce site collaboratif offre plusieurs recettes d'utilisation des services Web API, de développement de code Apex et de création de pages Visualforce. Le guide Force.com Cookbook aide les développeurs à se familiariser avec les techniques et les meilleures pratiques de programmation Force.com courantes. Vous pouvez consulter et commenter les recettes existantes ou soumettre vos propres recettes à l'adresse developer.force.com/cookbook. Development Life Cycle: Enterprise Development on the Force.com Platform (Cycle de vie du développement Développement d'entreprise sur la plate-forme Force.com) Programmeurs architectes et avancés Que vous soyez architecte, administrateur, développeur ou dirigeant, le guide Development Life Cycle Guide vous prépare au développement et à la livraison d'applications complexes sur la plate-forme Force.com. Cours de formation Des cours de formation sont également disponibles dans la rubrique Formation et certification de salesforce.com. Pour consulter la liste complète des cours, accédez au site Formation et Certification. Dans ce manuel (Guide du développeur Apex) Nous invitons les programmeurs débutants à consulter les rubriques suivantes : • Introduction au langage Apex, et en particulier : Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ?|9 ◊ Conventions de documentation ◊ Concepts de base ◊ Tutoriel de démarrage rapide • • • Classes, objets et interfaces Test du code Apex Compréhension des limitations et des gouverneurs d'exécution En plus des documents ci-dessus, nous invitons les programmeurs avancés à consulter les rubriques suivantes : • Meilleures pratiques pour les déclencheurs et les requêtes en masse • Exemple de programmation Apex avancée • Compréhension de l'information Describe Apex • Exécution asynchrone (Annotation @future) • Apex par lot et Planificateur Apex Écriture de code Apex Vous pouvez écrire un code Apex et le tester dans les environnements d'édition suivants : • L'IDE Force.com, un complément de l'IDE Eclipse. L'IDE Force.com offre une interface unifiée pour l'élaboration et le déploiement d'applications Force.com. Conçu pour les développeurs et les équipes de développement, l'IDE fournit des outils qui permettent d'accélérer le développement d'applications Force.com, notamment des éditeurs de code source, des outils d'exécution de tests, des assistants et une aide intégrée. Ces outils incluent un code couleur de base, une vue de la structure hiérarchique, des tests unitaires intégrés et une compilation automatique lors de l'enregistrement avec un affichage des messages d'erreur. Pour plus d'informations sur son installation et son utilisation, reportez-vous au site Web. Remarque: L'IDE Force.com est une ressource gratuite offerte par salesforce.com pour aider ses utilisateurs et ses partenaires, mais qui n'est pas considérée comme faisant partie de nos services dans le cadre du Contrat d'abonnement principal salesforce.com. • L'interface utilisateur de Salesforce Toutes les classes et tous les déclencheurs sont compilés lors de leur enregistrement, et les erreurs de syntaxe sont signalées. Vous ne pouvez pas enregistrer votre code tant qu'il contient des erreurs. L'interface utilisateur de Salesforce numérote également les lignes de code et utilise un code couleur pour distinguer les différents éléments, notamment les commentaires, les mots clés, les chaînes littérales etc. ◊ Pour un déclencheur dans un objet standard, cliquez sur Votre nom > Configuration > Personnaliser, cliquez sur le nom de l'objet, puis sur Déclencheurs. Dans la page de détails Déclencheurs, cliquez sur Nouveau, puis saisissez votre code dans la zone de texte Corps. ◊ Pour un déclencheur dans un objet personnalisé, cliquez sur Votre nom > Configuration > Développer > Objets, puis cliquez sur le nom de l'objet. Dans la liste associée Déclencheurs, cliquez sur Nouveau, puis saisissez votre code dans la zone de texte Corps. ◊ Pour une classe, cliquez sur Votre nom > Configuration > Développer > Classes Apex. Cliquez sur Nouveau, puis saisissez votre code dans la zone de texte Corps. Remarque: Vous ne pouvez pas modifier un code Apex en utilisant l'interface utilisateur de Salesforce dans une organisation de production Salesforce. Introduction au langage Apex • salesforce | Qu'est-ce que le processus de développement Apex ? | 10 N'importe quel éditeur de texte, tel que le Bloc-notes. Vous pouvez écrire votre code Apex, puis le copier et le coller dans votre application, ou utiliser l'un des appels API pour le déployer. Conseil: Si vous souhaitez étendre le plug-in Eclipse ou développer votre propre IDE Apex, l'API SOAP inclut des méthodes de compilation de déclencheurs et de classes, et d'exécution de méthodes de test, tandis que l'API de métadonnées inclut des méthodes de déploiement de code dans des environnements de production. Pour plus d'informations, reportez-vous à Déploiement de code Apex à la page 750 et API SOAP et en-têtes SOAP pour Apex à la page 788. Écriture de tests Le test est la clé de la réussite du développement à long terme et forme un composant essentiel du processus de développement. Nous recommandons vivement d'utiliser un processus de développement piloté par des tests, c.-à-d. des tests effectués parallèlement au développement du code. Pour faciliter le développement d'un code robuste et sans erreur, Apex prend en charge la création et l'exécution de tests unitaires. Les test unitaires sont des méthodes de classe qui vérifient le fonctionnement normal d'une portion de code. Les méthodes de test unitaires ne prennent aucun argument, ne soumettent aucune donnée à la base de données, n'envoient aucun e-mail et sont marquées avec le mot clé testMethod dans la définition de la méthode. De plus, avant de pouvoir déployer votre code Apex ou de l'empaqueter pour Force.com AppExchange, le point suivant doit être vrai. • Au moins 75 % de votre code Apex doit être couvert par des tests unitaires, et tous ces tests doivent être réussis. Notez les points suivants : ◊ Lors du déploiement d'une organisation de production, chaque test unitaire dans l'espace de noms de votre organisation est exécuté. ◊ Les appels de System.debug ne sont pas pris en compte dans la couverture du code Apex. ◊ Les méthodes de test et les classes de test ne sont pas prises en compte dans la couverture du code Apex. ◊ Alors que seulement 75 % de votre code Apex doit être couvert par des tests, votre attention ne doit pas se porter sur le pourcentage du code couvert. Assurez-vous plutôt que chaque cas d'utilisation de votre application est couvert, y compris les cas positifs et négatifs, ainsi que les enregistrements en masse et uniques. Ils doivent représenter 75 % ou plus de couverture de votre code par des tests unitaires. • • Chaque déclencheur doit avoir une couverture de test. Toutes les classes et tous les déclencheurs doivent être compilés avec succès. Pour plus d'informations sur l'écriture de tests, reportez-vous à Tests Apex à la page 184. Déploiement de code Apex dans une organisation Sandbox Salesforce permet de créer plusieurs copies de votre organisation dans des environnements distincts avec le test et la formation comme objectifs principaux, sans altérer les données et les applications de votre organisation de production Salesforce. Ces copies, appelées sandbox, sont presque identiques à votre organisation de production Salesforce. Les sandbox sont totalement isolées de votre organisation de production Salesforce. Par conséquent, les opérations que vous effectuez dans vos sandbox n'affectent pas votre organisation de production Salesforce, et inversement. Pour déployer un code Apex d'un projet local dans Force.com IDE vers une organisation Salesforce, utilisez l'Assistant de déploiement de composants Force.com. Pour plus d'informations sur Force.com IDE, reportez-vous à http://wiki.developerforce.com/index.php/Force.com_IDE. Introduction au langage Apex salesforce | Qu'est-ce que le processus de développement Apex ? | 11 Vous pouvez utiliser l'appel API de métadonnées deploy() pour déployer votre code Apex depuis une organisation de développeur vers une organisation sandbox. Un appel API utile est runTests(). Dans une organisation de développement ou sandbox, vous pouvez exécuter des tests unitaires pour une classe spécifique, une liste de classes ou un espace de noms. Salesforce inclut un Outil de migration Force.com qui permet d'émettre ces commandes dans une fenêtre de console. Vous pouvez également mettre en oeuvre votre propre code de déploiement. Remarque: Force.com IDE et l'Outil de migration Force.com sont des ressources fournies gratuitement par salesforce.com pour aider ses utilisateurs et partenaires, mais qui ne sont pas considérées comme faisant partie de nos services dans le cadre du Contrat d'abonnement principal salesforce.com. Pour plus d'informations, reportez-vous à Utilisation de l'outil de migration Force.com et Déploiement de code Apex. Déploiement de code Apex dans une organisation de production Salesforce Lorsque vous avez terminé tous vos tests unitaires et vérifié que votre code Apex est exécuté correctement, l'étape finale consiste à déployer le code Apex vers votre organisation de production Salesforce. Pour déployer un code Apex d'un projet local dans Force.com IDE vers une organisation Salesforce, utilisez l'Assistant de déploiement de composants Force.com. Pour plus d'informations sur Force.com IDE, reportez-vous à http://wiki.developerforce.com/index.php/Force.com_IDE. Vous pouvez également déployer Apex via des ensembles de modifications dans l'interface utilisateur de Salesforce. Pour plus d'informations et des options de déploiement supplémentaires, reportez-vous à Déploiement de code Apex à la page 750. Ajout de code Apex à une application Force.com AppExchange Vous pouvez également inclure une classe ou un déclencheur Apex à une application que vous avez créée pour AppExchange. Tout code Apex inclus dans un package doit avoir au moins 75 % de couverture de test cumulée. Chaque déclencheur doit avoir une couverture de test. Lorsque vous chargez votre package sur AppExchange, tous les tests sont exécutés pour vérifier l'absence d'erreur. De plus, des tests avec l'annotation @isTest(OnInstall=true) sont exécutés lors de l'installation du package dans l'organisation de l'installateur. Vous pouvez spécifier les tests à exécuter durant l'installation du package en les annotant avec @isTest(OnInstall=true). Ce sous-ensemble de tests doit être exécuté avec succès pour que l'installation du package réussisse. De plus, salesforce.com recommande que tout package AppExchange contenant un code Apex soit un package géré. Pour plus d'informations, reportez-vous au document Force.com Quick Reference for Developing Packages. Pour plus d'informations sur le code Apex dans des packages gérés, reportez-vous à Développement de code Apex dans des packages gérés à la page 280. Remarque: Empaquetage de classes Apex contenant des références à des étiquettes personnalisées qui ont des traductions : Pour inclure les traductions dans le package, activez le Système de traduction, puis empaquetez explicitement les langues individuelles à utiliser dans les étiquettes personnalisées traduites. Reportez-vous à Présentation des étiquettes personnalisées. Introduction au langage Apex salesforce | Quand dois-je utiliser un code Apex ? | 12 Quand dois-je utiliser un code Apex ? Les applications Salesforce pré-construites offrent des fonctionnalités CRM puissantes. Salesforce offre en outre la possibilité de personnaliser les applications pré-construites en fonction de votre organisation. Cependant, votre organisation peut avoir des processus métier complexes non pris en charge par les fonctionnalités existantes. Dans ce cas, la plate-forme Force.com comprend plusieurs méthodes qui permettent aux administrateurs et aux développeurs de mettre en oeuvre des fonctionnalités personnalisées. Elles comprennent Apex, Visualforce et l'API SOAP. Apex Utilisez Apex si vous souhaitez : • • • • • • Créer des services Web. Créer des services de messagerie. Effectuer une validation complexe sur des objets multiples. Créer des processus métier complexes qui ne sont pas pris en charge par le workflow. Créer une logique transactionnelle personnalisée (qui se produit sur la transaction complète, pas seulement sur un enregistrement ou un objet unique). Joindre une logique personnalisée à une autre opération, par exemple la sauvegarde d'un enregistrement, qui se produit ainsi lorsque l'opération est exécutée, qu'elle soit issue ou non de l'interface utilisateur, d'une page Visualforce ou de l'API SOAP. Visualforce Visualforce est formé d'un langage de balisage qui offre aux développeurs un moyen plus puissant pour concevoir des applications et personnaliser l'interface utilisateur de Salesforce. Avec Visualforce, vous pouvez : • • • Élaborer des assistants et d'autres processus à plusieurs étapes. Créer votre propre contrôle de flux personnalisé via une application. Définir des modèles de navigation et des règles spécifiques aux données pour une interaction d'application optimale et efficace. Pour plus d'informations, reportez-vous au guide Visualforce Developer's Guide. API SOAP Utilisez des appels API SOAP standard si vous souhaitez ajouter à une application composite une fonctionnalité qui traite un seul type d'enregistrement à la fois et ne nécessite aucun contrôle transactionnel (telle que la définition d'un enregistrement ou l'annulation de modifications). Pour plus d'informations, reportez-vous au guide SOAP API Developer's Guide. Quelles sont les limitations du langage Apex ? Le langage Apex change radicalement la façon dont les développeurs créent des applications professionnelles à la demande, mais il n'a pas actuellement pour objectif de devenir un langage de programmation général. La version actuelle du langage Apex ne peut pas être utilisée pour : • • • Rendre des éléments autres que des messages d'erreur dans l'interface utilisateur. Modifier une fonctionnalité standard : le langage Apex peut uniquement empêcher l'exécution d'une fonctionnalité ou ajouter une fonctionnalité supplémentaire Créer des fichiers temporaires Introduction au langage Apex • salesforce | Nouveautés | 13 Engendrer des fils Conseil: Tout le code Apex est exécuté sur la plate-forme Force.com, qui est une ressource partagée, utilisée par toutes les autres organisations. Pour garantir une cohérence en termes de performances et d'évolutivité, l'exécution de code Apex est encadrée par des limitations du gouverneur qui empêchent une exécution Apex unique d'affecter le service global de Salesforce. Cela signifie que tout le code Apex est limité en nombre d'opérations (telles que DML ou SOQL) qu'il peut effectuer dans un seul processus. Toutes les requêtes Apex renvoient une collection qui contient de 1 à 50 000 enregistrements. Vous ne pouvez pas supposer que votre code fonctionne uniquement sur un seul enregistrement à la fois. Par conséquent, vous devez mettre en oeuvre des modèles de programmation qui tiennent compte du traitement global. Sans cette protection, vous risquez de dépasser les limitations du gouverneur. Voir aussi : Compréhension des limitations et des gouverneurs d'exécution Meilleures pratiques pour les déclencheurs et les requêtes en masse Nouveautés Reportez-vous aux Notes de publications de Winter ’13 pour prendre connaissance des fonctionnalités Apex nouvelles et modifiées dans la version Winter ’13. Démarrage rapide avec le langage Apex Lorsque vous avez une organisation Developer Edition ou sandbox, vous pouvez apprendre quelques concepts de base du langage Apex. Comme le langage Apex est très similaire au langage Java, vous pouvez reconnaître la plupart de ses fonctionnalités. Une fois les concepts de base révisés, vous pouvez écrire votre premier programme Apex : une classe, un déclencheur et un test unitaire extrêmement simples. Vous pouvez également parcourir un exemple de bordereau d'expédition plus complexe. Cet exemples illustre de nombreuses autres fonctionnalités du langage. Remarque: Les exemples Hello World et du bordereau d'expédition nécessitent des champs et des objets personnalisés. Vous pouvez créer les vôtres, ou télécharger les objets, les champs et le code Apex dans package géré sur Force.com AppExchange. Pour plus d'informations, reportez-vous à wiki.developerforce.com/index.php/Documentation. Conventions typographiques de la documentation La documentation Apex et Visualforce utilise les conventions typographiques ci-dessous. Introduction au langage Apex salesforce | Conventions typographiques de la documentation | 14 Convention Description Police Courier Dans les descriptions de syntaxe, les polices monospace indiquent des éléments que vous devez saisir tels quels, sans les parenthèses. Par exemple : Public class HelloWorld Italique Dans les descriptions de syntaxe, les caractères en italique représentent des variables. Vous renseignez la valeur. Dans l'exemple suivant, trois valeurs doivent être saisies : datatype variable_name [ = value]; Si la syntaxe est en gras et en italique, le texte représente un élément de code pour lequel vous devez saisir une valeur, par exemple un nom de classe ou une valeur de variable : public static class YourClassHere { ... } Police Courier gras Dans les exemples de code et les descriptions de syntaxe, la police Courier gras souligne une portion du code ou de la syntaxe. <> Dans les descriptions de syntaxe, les symboles inférieur à et supérieur à (< >) sont saisis tels quels. <apex:pageBlockTable value="{!account.Contacts}" var="contact"> <apex:column value="{!contact.Name}"/> <apex:column value="{!contact.MailingCity}"/> <apex:column value="{!contact.Phone}"/> </apex:pageBlockTable> {} Dans les descriptions de syntaxe, les accolades ({ }) sont saisies telles quelles. <apex:page> Hello {!$User.FirstName}! </apex:page> [] Dans les descriptions de syntaxe, tout élément entre parenthèses est facultatif. Dans l'exemple suivant, la spécification value est facultative : data_type variable_name [ = value]; Introduction au langage Apex salesforce | Compréhension des concepts de base du langage Apex | 15 Convention Description | Dans les descriptions de syntaxe, le caractère pipe signifie « ou », c.-à-d. que vous pouvez effectuer l'une des actions (pas toutes). Dans l'exemple suivant, vous pouvez créer un nouvel ensemble non renseigné de deux façons différentes, ou renseigner l'ensemble : Set<data_type> set_name [= new Set<data_type>();] | [= new Set<data_type{value [, value2. . .] };] | ; Compréhension des concepts de base du langage Apex Le code Apex contient généralement de nombreux éléments courants dans d'autres langages de programmation : Figure 3: Éléments de programmation dans le langage Apex La section présente les principales fonctionnalités du langage Apex, ainsi que quelques concepts de base. Utilisation des paramètres de version Dans l'interface utilisateur de Salesforce, vous pouvez spécifier une version de l'API Salesforce.com pour laquelle vous enregistrez votre classe ou déclencheur Apex. Ce paramètre indique la version de l'API SOAP à utiliser, mais également la version Apex. Vous pouvez changer la version après l'enregistrement. Chaque nom de classe ou de déclencheur doit être unique. Vous ne pouvez pas enregistrer la même classe ou le même déclencheur pour différentes versions. Vous pouvez également utiliser des paramètres de version afin d'associer une classe ou un déclencheur à une version particulière d'un package géré installé dans votre organisation à partir d'AppExchange. La classe ou le déclencheur continue d'utiliser cette Introduction au langage Apex salesforce | Compréhension des concepts de base du langage Apex | 16 version du package géré si des versions ultérieures du package sont installées, sauf si vous mettez à jour manuellement le paramètre de version. Pour ajouter un package géré installé à la liste des paramètres, sélectionnez un package dans la liste des packages disponibles. Cette liste s'affiche uniquement si un package géré installé n'est pas déjà associé à la classe ou au déclencheur. Pour plus d'informations sur l'utilisation de paramètres de version avec des packages gérés, reportez-vous à « À propos des versions de package » dans l'aide en ligne de Salesforce. Nommage de variables, de méthodes et de classes Vous ne pouvez utiliser aucun mot clé Apex réservé pour nommer des variables, des méthodes ou des classes. Sont inclus les termes faisant partie du code Apex et de la plate-forme Force.com, tels que list, test ou account, ainsi que les mots clés réservés. Utilisation de variables et d'expressions Apex est un langage fortement typé, c.-à-d. que vous devez déclarer le type de données d'une variable en la référençant pour la première fois. Le types de données Apex incluent des types de base, tels que Nombre entier, Date et Booléen, ainsi que les types plus avancés, tels que lists, maps, objects et sObjects. Les variables sont déclarées avec un nom et un type de données. Vous pouvez attribuer une valeur à une variable lorsque vous la déclarez. Vous pouvez également attribuer des valeurs ultérieurement. Lors de la déclaration de variables, utilisez la syntaxe suivante : datatype variable_name [ = value]; Conseil: Notez que le point-virgule final ci-dessus n'est pas facultatif. Vous devez terminer toutes les instructions par un point-virgule. Les exemples ci-dessous sont des instructions de variables : // The following variable has the data type of Integer with the name Count, // and has the value of 0. Integer Count = 0; // The following variable has the data type of Decimal with the name Total. Note // that no value has been assigned to it. Decimal Total; // The following variable is an account, which is also referred to as an sObject. Account MyAcct = new Account(); Introduction au langage Apex salesforce | Compréhension des concepts de base du langage Apex | 17 Dans le langage Apex, tous les arguments de type de données primitifs, tels que Nombre entier ou Chaîne, sont transmis en méthodes par valeur. Cela signifie que toute modification apportée aux arguments existe uniquement dans la portée de la méthode. Lorsque la méthode retourne, les modifications de l'argument sont perdues. Les arguments de type de données non primitifs, tels que sObjects, sont également transmis en méthodes par valeur. Cela signifie que lorsque la méthode retourne, l'argument passé référence toujours le même objet qu'avant l'appel de la méthode et ne peut pas être modifié pour pointer vers un autre objet. Cependant, les valeurs des champs de l'objet peuvent être modifiées dans la méthode. Utilisation d'instructions Une instruction correspond à toute instruction codée pour exécuter une action. Dans le langage Apex, les instructions doivent se terminer par un point-virgule et peuvent correspondre à l'un des types suivants : Attribution, par exemple attribution d'une valeur à une variable Conditionnel (if-else) Boucles : • • • ◊ Do-while ◊ While ◊ For Verrouillage Langage de manipulation des données (DML) Contrôle des transactions Invocation de méthode Gestion des exceptions • • • • • Un block est une série d'instructions regroupées par des accolades, qui peut être utilisé partout où une instruction unique est autorisée. Par exemple : if (true) { System.debug(1); System.debug(2); } else { System.debug(3); System.debug(4); } Lorsqu'un bloc contient une seule instruction, les accolades peuvent être abandonnées. Par exemple : if (true) System.debug(1); else System.debug(2); Introduction au langage Apex salesforce | Compréhension des concepts de base du langage Apex | 18 Utilisation de collections Le langage Apex inclut les types de collection suivants : • • • Lists (tableaux) Maps Sets Une list (liste) est une collection d'éléments, telle que des Nombres entiers, des Chaînes, des objets ou d'autres collections. Utilisez une liste lorsque la séquence d'élément est importante. Une liste peut inclure des éléments dupliqués. La première position d'index dans une liste est toujours 0. Pour créer une liste : • • Utilisez le mot clé new Utilisez le mot clé List suivi du type d'élément entre crochets <>. Utilisez la syntaxe suivante pour créer une liste : List <datatype> list_name [= new List<datatype>();] | [=new List<datatype>{value [, value2. . .]};] | ; L'exemple suivant crée une liste de Nombres entiers et l'attribue à la variable My_List. Notez que comme le langage Apex est fortement typé, vous devez déclarer le type de données My_List en tant que liste de Nombres entiers. List<Integer> My_List = new List<Integer>(); Pour plus informations, reportez-vous à Lists à la page 38. Un set (ensemble) est une collection d'éléments uniques non organisés. Il contient des types de données primitifs, tels que Chaîne, Nombre entier, Date, etc. Il inclut également des types de données plus complexes tels que des sObjects. Pour créer un ensemble : • • Utilisez le mot clé new Utilisez le mot clé Set suivi du type de données primitif entre crochets <>. Utilisez la syntaxe suivante pour créer un ensemble : Set<datatype> set_name [= new Set<datatype>();] | [= new Set<datatype>{value [, value2. . .] };] | ; L'exemple suivant crée un ensemble de Chaînes. Les valeurs de l'ensemble sont transmises en utilisant des accolades {}. Set<String> My_String = new Set<String>{'a', 'b', 'c'}; Introduction au langage Apex salesforce | Compréhension des concepts de base du langage Apex | 19 Pour plus informations, reportez-vous à Sets à la page 46. Un map (mappage) est une collection de paires clé-valeur. Les clés peuvent avoir n'importe quel type de données primitif. Les valeurs peuvent inclure des types de données primitifs, ainsi que des objets et d'autres collections. Utilisez un mappage lorsque la recherche par clé est importante. Un mappage peut inclure des valeurs dupliquées, mais chaque clé doit être unique. Pour créer un mappage : Utilisez le mot clé new Utilisez le mot clé Map suivi d'une paire clé-valeur, séparée par une virgule et placée entre crochets <>. • • Utilisez la syntaxe suivante pour créer un mappage : Map<key_datatype, value_datatype> map_name [=new map<key_datatype, value_datatype>();] | [=new map<key_datatype, value_datatype> {key1_value => value1_value [, key2_value => value2_value. . .]};] | ; L'exemple suivant crée un mappage qui a un type de données Integer pour clé et String pour valeur. Dans cet exemple, les valeurs du mappage sont transmises entre accolades {} lors de la création du mappage. Map<Integer, String> My_Map = new Map<Integer, String>{1 => 'a', 2 => 'b', 3 => 'c'}; Pour plus informations, reportez-vous à Maps à la page 47. Utilisation de branchements Une instruction if est un test vrai-faux qui permet à votre application d'exécuter plusieurs opérations en fonction d'une condition. La syntaxe de base est la suivante : if (Condition){ // Do this if the condition is true } else { // Do this if the condition is not true } Pour plus d'informations, reportez-vous à Instructions conditionnelles (If-Else) à la page 69. Utilisation de boucles Tandis que l'instruction if permet à votre organisation d'exécuter des opérations basées sur une condition, les boucles indiquent à votre application d'exécuter la même opération en boucle en fonction d'une condition. Le langage Apex prend en charge les types de boucle suivants : • • • Do-while While For Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 20 Une boucle Do-while vérifie la condition après l'exécution du code. Une boucle While vérifie la condition au début, avant l'exécution du code. Une boucle For permet d'affiner le contrôle de la condition utilisée par la boucle. De plus, le langage Apex prend charge les boucles traditionnelles For dans lesquels vous définissez les conditions, ainsi que les boucles For qui utilisent des listes et des requêtes SOQL dans la condition. Pour plus informations, reportez-vous à Boucles à la page 70. Écriture de votre première classe et de votre premier déclencheur Apex Ce tutoriel pas à pas présente la création d'une simple classe et d'un simple déclencheur Apex. Il montre également comment déployer ces composants dans une organisation de production. Ce tutoriel est basé sur un objet personnalisé appelé Book qui est créé à la première étape. Cet objet personnalisé est mis à jour via un déclencheur. Voir aussi : Création d'un objet personnalisé Ajout d'une classe Apex Ajout d'un déclencheur Apex Ajout d'une classe de test Déploiement de composants en production Création d'un objet personnalisé Prérequis : Un compte Salesforce dans une organisation sandbox Unlimited Edition ou Enterprise Edition, ou un compte dans une organisation Developer. Pour plus d'informations sur la création d'une organisation sandbox, reportez-vous à « Présentation de Sandbox » dans l'aide en ligne de Salesforce. Pour vous inscrire à une organisation Developer gratuite, reportez-vous à Page d'inscription à un environnement Developer Edition. Dans cette étape, vous créez un objet personnalisé appelé Book avec un champ personnalisé appelé Price. 1. 2. 3. 4. 5. Connectez-vous à votre organisation sandbox ou Developer. Cliquez sur Votre nom > Configuration > Créer > Objets, puis cliquez sur Nouvel objet personnalisé. Saisissez l'étiquette Book. Saisissez l'étiquette au pluriel Books. Cliquez sur Enregistrer. Terminé ! Nous avons créé notre premier objet personnalisé. Créons maintenant un champ personnalisé. 6. Dans la section Champs personnalisés et relations de la page de détail Book, cliquez sur Nouveau. 7. Sélectionnez le type de données Numéro, puis cliquez sur Suivant. 8. Saisissez l'étiquette de champ Price. 9. Saisissez 16 dans la zone de texte de la longueur. 10. Saisissez 2 dans la zone de texte des décimales, puis cliquez sur Suivant. 11. Cliquez sur Suivant pour accepter les valeurs par défaut de sécurité au niveau du champ. 12. Cliquez sur Enregistrer. Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 21 Vous venez de créer un objet personnalisé appelé Book et ajouté un champ personnalisé à cet objet. Les objets personnalisés contiennent déjà des champs standard, tels que Nom et Créé par. Ils permettent d'ajouter d'autres champs plus spécifiques à votre mise en oeuvre. Dans ce tutoriel, le champ Price fait partie de votre objet Book, et il est accessible via la classe Apex que vous allez écrire à la prochaine étape. Voir aussi : Écriture de votre première classe et de votre premier déclencheur Apex Ajout d'une classe Apex Ajout d'une classe Apex Prérequis : • • Un compte Salesforce dans une organisation sandbox Unlimited Edition ou Enterprise Edition, ou un compte dans une organisation Developer. L'objet personnalisé Book Dans cette étape, vous allez ajouter une classe Apex qui inclut une méthode de mise à jour du prix du livre. Cette méthode est appelée par le déclencheur que vous allez ajouter à la prochaine étape. 1. Cliquez sur Votre nom > Configuration > Développer > Classes Apex, puis cliquez sur Nouveau. 2. Dans l'éditeur de classe, saisissez la définition de classe suivante : public class MyHelloWorld { } Le code précédent correspond à la définition de classe à laquelle vous allez ajouter une méthode supplémentaire à la prochaine étape. Le code Apex est généralement contenu dans une classe. Cette classe est définie comme public, ce qui signifie qu'elle est disponible pour d'autres classes et déclencheurs Apex. Pour plus d'informations, reportez-vous à Classes, objets et interfaces à la page 124. 3. Ajoutez cette définition de méthode entre les parenthèses ouvrante et fermante de la classe. public static void applyDiscount(Book__c[] books) { for (Book__c b :books){ b.Price__c *= 0.9; } } Cette méthode est appelée applyDiscount, et elle est à la fois publique et statique. Puisque cette méthode est statique, il n'est pas nécessaire de créer une instance de la classe pour accéder à la méthode, il suffit d'utiliser le nom de la classe suivie d'un point (.) et du nom de la méthode. Pour plus informations, reportez-vous à Statique et instance à la page 139. Cette méthode prend un paramètre, une liste d'enregistrements Book, qui est attribué à la variable books. Notez le __c dans le nom de l'objet Book__c. Il indique que vous avez créé un objet personnalisé. Les objets standard fournis dans l'application Salesforce, tels que Account, ne se terminent pas par ce suffixe. Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 22 La section suivante du code contient le reste de la définition de la méthode : for (Book__c b :books){ b.Price__c *= 0.9; } Notez le __c après le nom de champ Price__c. Il indique que vous avez créé un champ personnalisé. Les champs standard fournis par défaut dans Salesforce sont accessibles en utilisant le même type de notation pointée, mais sans le __c, par exemple Name ne se termine pas par __c dans Book__c.Name. L'instruction b.Price__c *= 0.9; prend l'ancienne valeur de b.Price__c, la multiplie par 0,9, ce qui signifie qu'elle est diminuée de 10 %, puis stocke la nouvelle valeur dans le champ b.Price__c. L'opérateur *= est un raccourci. Une autre façon d'écrire cette instruction est b.Price__c = b.Price__c * 0.9;. Reportez-vous à Compréhension des opérateurs d'expression à la page 60. 4. Cliquez sur Enregistrer pour enregistrer la nouvelle classe. Vous devez maintenant obtenir cette définition de classe complète. public class MyHelloWorld { public static void applyDiscount(Book__c[] books) { for (Book__c b :books){ b.Price__c *= 0.9; } } } Vous avez désormais une classe contenant un code qui itère dans une liste de livres et met à jour le champ Price pour chaque livre. Ce code fait partie de la méthode statique applyDiscount qui est appelé par le déclencheur que vous allez créer à la prochaine étape. Voir aussi : Écriture de votre première classe et de votre premier déclencheur Apex Création d'un objet personnalisé Ajout d'un déclencheur Apex Ajout d'un déclencheur Apex Prérequis : • • Un compte Salesforce dans une organisation sandbox Unlimited Edition ou Enterprise Edition, ou un compte dans une organisation Developer. La classe Apex MyHelloWorld. Dans cette étape, vous créez un déclencheur pour l'objet personnalisé Book__c qui appelle la méthode applyDiscount de la classe MyHelloWorld que vous avez créée à l'étape précédente. Un déclencheur est une partie de code qui s'exécute avant ou après l'insertion, la mise à jour ou la suppression d'enregistrements d'un type spécifique à partir de la base de données de la plate-forme Force.com. Chaque déclencheur est exécuté avec un Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 23 ensemble de variables contextuelles qui fournissent l'accès aux enregistrements ayant activé le déclencheur. Tous les déclencheurs sont activés en masse, c.-à-d. qu'ils traitent plusieurs enregistrements à la fois. 1. Cliquez sur Votre nom > Configuration > Créer > Objets, puis sur le nom de l'objet que vous venez de créer, Book. 2. Dans la section des déclencheurs, cliquez sur Nouveau. 3. Dans l'éditeur de déclencheurs, supprimez le code du modèle par défaut, puis saisissez la définition de déclencheur suivante : trigger HelloWorldTrigger on Book__c (before insert) { Book__c[] books = Trigger.new; MyHelloWorld.applyDiscount(books); } La première ligne du code définit le déclencheur : trigger HelloWorldTrigger on Book__c (before insert) { Elle attribue un nom au déclencheur, spécifie l'objet sur lequel il opère et définit les événements qui l'activent. Par exemple, ce déclencheur est appelé HelloWorldTrigger, il opère sur l'objet Book__c et est exécuté avant l'insertion de nouveaux livres dans la base de données. La ligne suivante du déclencheur crée une liste d'enregistrements de livres nommée books et attribue le contenu d'une variable de contexte de déclencheur appelée Trigger.new. Les variables de contexte de déclencheur, telles que Trigger.new, sont définies implicitement dans tous les déclencheurs et fournissent l'accès aux enregistrements qui activent le déclencheur. Dans ce cas, Trigger.new contient les nouveaux livres qui vont être insérés. Book__c[] books = Trigger.new; La ligne suivante du code appelle la méthode applyDiscount dans la classe MyHelloWorld. Il transmet le tableau des nouveaux livres. MyHelloWorld.applyDiscount(books); Vous disposez maintenant de tout le code nécessaire pour mettre à jour le prix de tous les livres qui sont insérés. Cependant, une pièce du puzzle est manquante. Les tests unitaires sont une partie importante de l'écriture de code et sont obligatoires. Dans la prochaine étape, vous allez en découvrir les raisons et vous pourrez ajouter une classe de test. Voir aussi : Écriture de votre première classe et de votre premier déclencheur Apex Ajout d'une classe Apex Ajout d'une classe de test Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 24 Ajout d'une classe de test Prérequis : • • Un compte Salesforce dans une organisation sandbox Unlimited Edition ou Enterprise Edition, ou un compte dans une organisation Developer. Le déclencheur Apex HelloWorldTrigger. Dans cette étape, vous ajoutez une classe de test avec une méthode de test. Vous exécutez également le test et vérifiez la couverture de code. La méthode de test exerce et valide le code dans le déclencheur et la classe. Il permet également d'atteindre 100 % de couverture de code pour le déclencheur et la classe. Remarque: Le test est une partie importante du processus de développement. Avant de pouvoir déployer votre code Apex ou de l'empaqueter pour Force.com AppExchange, les conditions suivantes doivent être remplies. • Au moins 75 % de votre code Apex doit être couvert par des tests unitaires, et tous ces tests doivent être réussis. Notez les points suivants : ◊ Lors du déploiement d'une organisation de production, chaque test unitaire dans l'espace de noms de votre organisation est exécuté. ◊ Les appels de System.debug ne sont pas pris en compte dans la couverture du code Apex. ◊ Les méthodes de test et les classes de test ne sont pas prises en compte dans la couverture du code Apex. ◊ Alors que seulement 75 % de votre code Apex doit être couvert par des tests, votre attention ne doit pas se porter sur le pourcentage du code couvert. Assurez-vous plutôt que chaque cas d'utilisation de votre application est couvert, y compris les cas positifs et négatifs, ainsi que les enregistrements en masse et uniques. Ils doivent représenter 75 % ou plus de couverture de votre code par des tests unitaires. • • Chaque déclencheur doit avoir une couverture de test. Toutes les classes et tous les déclencheurs doivent être compilés avec succès. 1. Cliquez sur Votre nom > Configuration > Développer > Classes Apex, puis cliquez sur Nouveau. 2. Dans l'éditeur de classe, ajoutez cette définition de classe de test, puis cliquez sur Enregistrer. @isTest private class HelloWorldTestClass { static testMethod void validateHelloWorld() { Book__c b = new Book__c(Name='Behind the Cloud', Price__c=100); System.debug('Price before inserting new book: ' + b.Price__c); // Insert book insert b; // Retrieve the new book b = [SELECT Price__c FROM Book__c WHERE Id =:b.Id]; System.debug('Price after trigger fired: ' + b.Price__c); Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 25 // Test that the trigger correctly updated the price System.assertEquals(90, b.Price__c); } } Cette classe est définie en utilisant l'annotation @isTest. Les classes définies ainsi ne peuvent contenir que des méthodes de test. L'un des avantages de la création d'une classe séparée pour le test par rapport à l'ajout de méthodes de test à une classe existante, est que les classes définies avec isTest ne sont pas prises en compte dans la limite de votre organisation de 3 Mo pour tout le code Apex. Vous pouvez également ajouter l'annotation @isTest à des méthodes individuelles. Pour plus d'informations, reportez-vous à Annotation IsTest à la page 162 et Compréhension des limitations et des gouverneurs d'exécution à la page 273. La méthode validateHelloWorld est définie comme testMethod. Cela signifie que si des modifications sont apportées à la base de données, elles sont automatiquement annulées une fois l'exécution terminée, ce qui vous évite de supprimer les données de test créées dans la méthode de test. La méthode de test commence par créer un livre, puis l'insère temporairement dans la base de données. L'instruction System.debug écrit la valeur du livre dans le journal de débogage. Book__c b = new Book__c(Name='Behind the Cloud', Price__c=100); System.debug('Price before inserting new book: ' + b.Price__c); // Insert book insert b; Une fois le livre inséré, le code récupère le nouveau livre inséré à l'aide de l'ID initialement attribué au livre lors de son insertion, puis consigne le nouveau prix que le déclencheur à modifié : // Retrieve the new book b = [SELECT Price__c FROM Book__c WHERE Id =:b.Id]; System.debug('Price after trigger fired: ' + b.Price__c); Lors de l'exécution de la classe MyHelloWorld, il met à jour le champ Price__c et réduit sa valeur de 10 %. La ligne suivante correspond au test, qui vérifie si la méthode applyDiscount a été exécutée et a produit le résultat attendu : // Test that the trigger correctly updated the price System.assertEquals(90, b.Price__c); 3. Cliquez sur Exécuter le test dans la page de la classe pour exécuter toutes les méthodes de test de cette classe. Dans le cas présent, nous savons une seule méthode de test. Introduction au langage Apex salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 26 La page Résultat de test Apex s'affiche une fois l'exécution du test terminée. Elle contient les détails des résultats du test, notamment le nombre d'échecs au test, les informations sur la couverture de code et un lien vers le fichier journal à télécharger. 4. Cliquez sur Télécharger, puis sélectionnez l'ouverture du fichier journal. Vous pouvez consulter les informations consignées sur l'événement déclencheur, l'appel de la méthode de classe applyDiscount et la sortie de débogage du prix avant et après le déclencheur. Vous pouvez également utiliser la Console du développeur pour le débogage du code Apex. Reportez-vous à « Console du développeur » dans l'aide en ligne de Salesforce. 5. Vous pouvez également exécuter le test via la page Exécution de test Apex, qui exécute le test de façon asynchrone. Cela signifie qu'il n'est pas nécessaire d'attendre la fin de l'exécution du reste du code pour obtenir les résultats, vous pouvez effectuer d'autres tâches dans l'interface utilisateur pendant l'exécution du test, et visiter la page ultérieurement pour consulter le statut du test. a. Cliquez sur Votre nom > Configuration > Développer > Exécution de test Apex. b. Cliquez sur Exécuter le test. c. Sélectionnez la classe HelloWorldTestClass, puis cliquez sur Exécuter. Une fois l'exécution terminée, vous pouvez : • • Cliquer sur le test pour afficher les détails du résultat ; si un test échoue, le premier message d'erreur et la trace de pile s'affichent. Cliquez sur Afficher pour visualiser la source du code Apex. 6. Une fois l'exécution du test terminée, vérifiez la quantité de couverture de code. a. Cliquez sur Votre nom > Configuration > Développer > Classes Apex. b. Cliquez sur Calculer la couverture du code de votre organisation afin d'examiner la quantité de code de votre organisation qui est couverte par des tests unitaires. c. Dans la colonne Couverture de code, cliquez sur 100 % pour afficher les lignes de code couvertes par des tests unitaires. Examinez la liste des déclencheurs en cliquant sur Votre nom > Configuration > Développer > Déclencheurs Apex. Le déclencheur que vous avez écrit a également 100 % de son code couvert. Vous avez maintenant effectué toutes les étapes requises pour obtenir un code Apex testé qui est exécuté dans votre environnement de développement. Dans la réalité, lorsque vous avez suffisamment testé votre code et que vous êtes satisfait(e) des résultats, vous pouvez le déployer avec d'autres composants prérequis dans une organisation de production. La prochaine étape montre comment procéder pour le code et l'objet personnalisé que vous venez de créer. Voir aussi : Écriture de votre première classe et de votre premier déclencheur Apex Ajout d'un déclencheur Apex Déploiement de composants en production Déploiement de composants en production Prérequis : • • Un compte Salesforce dans l'une organisation sandbox Unlimited Edition ou Enterprise Edition. La classe de test Apex HelloWorldTestClass. Introduction au langage Apex • • salesforce | Écriture de votre première classe et de votre premier déclencheur Apex | 27 Une connexion de déploiement entre les organisations sandbox et de production qui permet à l'organisation de production de recevoir des ensembles de modifications entrants. Reportez-vous à « Présentation des ensembles de modifications » dans l'aide en ligne de Salesforce. Des autorisations utilisateur Créer et charger des ensembles d'autorisations pour créer, modifier ou charger des ensembles d'autorisations entrants. Dans cette étape, vous déployez le code Apex et l'objet personnalisé, que vous avez créés précédemment dans votre organisation de production, en utilisant des ensembles de modifications. Cette procédure ne s'applique pas aux organisations Developer, car les ensembles de modifications sont disponibles uniquement dans les organisations Unlimited Edition, Enterprise Edition ou Database.com Edition. Si vous avez un compte Developer Edition, vous pouvez utiliser d'autres méthodes de déploiement. Reportez-vous à Déploiement de code Apex. 1. 2. 3. 4. Cliquez sur Votre nom > Configuration > Déployer > Ensembles de modifications sortants. Si une page d'introduction s'affiche, cliquez sur Continuer. Dans la liste Ensembles d'autorisations, cliquez sur Nouveau. Saisissez un nom pour votre ensemble de modifications, par exemple HelloWorldChangeSet, puis description (facultative). Cliquez sur Enregistrer. 5. Dans la section des composants d'ensembles de modifications, cliquez sur Ajouter. 6. Sélectionnez Classe Apex dans la liste déroulante du type de composant, sélectionnez les classes MyHelloWorld et HelloWorldTestClass dans la liste, puis cliquez sur Ajouter à l'ensemble de modifications. 7. Cliquez sur Afficher / ajouter des dépendances pour ajouter des composants dépendants. 8. Cochez la case supérieure pour sélectionner tous les composants. Cliquez sur Ajouter à l'ensemble de modifications. 9. Dans la section de détails de la page de l'ensemble de modifications, cliquez sur Charger. 10. Sélectionnez l'organisation cible, dans le cas présent production, puis cliquez sur Charger. 11. Une fois le chargement de l'ensemble de modifications terminé, déployez-le dans votre organisation de production. a. b. c. d. Connectez-vous à votre organisation de production. Cliquez sur Votre nom > Configuration > Déployer > Ensembles de modifications entrants. Si une page d'introduction s'affiche, cliquez sur Continuer. Dans la liste des ensembles de modifications en attente de déploiement, cliquez sur le nom de votre ensemble de modifications. e. Cliquez sur Déployer. Dans ce tutoriel, vous avez appris comment créer un objet personnalisé, comment ajouter un déclencheur, une classe et une classe de test Apex, et comment tester votre code. Pour terminer, vous avez également appris comment charger le code et l'objet personnalisé en utilisant des ensembles de modifications. Voir aussi : Écriture de votre première classe et de votre premier déclencheur Apex Ajout d'une classe de test Chapitre 2 Constructions du langage Sujets : • • • • • • • • • • Types de données Variables Expressions Instructions d'attribution Instructions conditionnelles (If-Else) Boucles Requêtes SOQL et SOSL Instructions de verrouillage Contrôle des transactions Instructions d'exception Le langage de programmation Apex est fortement typé, orienté objet et insensible à la casse. Le langage Apex est formé de blocs de construction qui permettent d'écrire des programmes dans du code Apex. En utilisant ces constructions du langage, vous pouvez déclarer des variables et des constantes de types de données intégrés (primitifs et sObjects) énumérations, et des types de données personnalisées basées sur le système et des types Apex fournis par l'utilisateur. Le langage Apex fournit des expressions, des instructions d'attribution et conditionnelles. Comme d'autres langages de programmation, le langage Apex fournit un traitement des exceptions et différents types de boucle. Contrairement à d'autres, le langage Apex a un type de boucle spécial appelé SOQL for loop, qui permet de traiter les résultats de requêtes par lot. Le langage Apex est intégré à la base de données. Il permet d'écrire des requêtes en ligne, d'effectuer un verrouillage d'enregistrement et de contrôler les transactions. Les constructions de langage suivantes forment les éléments de base du langage Apex : • • • • • • • • • • Types de données Variables Expressions Instructions d'attribution Instructions conditionnelles (If-Else) Boucles Requêtes SOQL et SOSL Instructions de verrouillage Contrôle des transactions Instructions d'exception Le code Apex est contenu dans un déclencheur ou dans une classe. Pour plus d'informations, reportez-vous à Déclencheurs à la page 96 et Classes, objets et interfaces à la page 124. Constructions du langage salesforce | Types de données | 29 Types de données Dans le langage Apex, toutes les variables et expressions ont l'un des types de données suivants : • • • Un primitif, qui comprend Integer, Double, Long, Date, Datetime, String, ID ou Boolean (reportez-vous à Types de données Primitive à la page 29) Un sObject, générique ou spécifique, qui comprend Account, Contact ou MyCustomObject__c (reportez-vous à Types sObject à la page 32) Une collection, qui comprend : ◊ Une liste (ou un tableau) de primitifs, sObjects, objets définis par l'utilisateur ou créés à partir de classes Apex, ou des collections (reportez-vous à Lists à la page 38) ◊ Un ensemble de primitifs (reportez-vous à Sets à la page 46) ◊ Un mappage d'un primitif avec un primitif, un sObject ou une collection (reportez-vous à Maps à la page 47) • • • • Une liste de valeurs typée, également appelée enum (reportez-vous à Enumérations à la page 53) Des objets créés à partir de classes Apex définies par l'utilisateur (reportez-vous à Classes, objets et interfaces à la page 124) Des objets créés à partir de classes Apex fournies par le système (reportez-vous à Classes Apex à la page 565) Type null (pour la constante null, qui peut être attribué à n'importe quelle variable) Les méthodes peuvent renvoyer n'importe quel type de valeur ci-dessus, ou ne renvoyer aucune valeur et être de type Void. La vérification du type s'applique uniquement lors de la compilation. Par exemple, l'analyseur génère une erreur si un champ objet de type Nombre entier est attribué à une valeur de type Chaîne. Cependant, toutes les exceptions de compilation sont renvoyées en tant que code d'erreur spécifique, avec le numéro de ligne et la colonne de l'erreur. Pour plus d'informations, reportez-vous à Débogage Apex à la page 255. Type de données Primitive Le langage Apex utilise les mêmes types de données primitifs que l'API SOAP. Tous les types de données primitifs sont transmis par valeur. Toutes les variables Apex, qu'elles soient des variables membres de classe ou des variables de méthode, sont initialisées sur null. Assurez-vous d'initialiser vos variables sur les valeurs appropriées avant de les utiliser. Par exemple, initialisez une variable Boolean sur false. Les types de données primitifs Apex comprennent : Type de données Description Blob Une collection de données binaires stockées en tant qu'objet unique. Vous pouvez convertir ce type de données en String ou à partir de String utilisant les méthodes toString et valueOf, respectivement. Les Blobs peuvent être acceptés en tant qu'arguments de services Web, stockés dans un document (le corps d'un document est un Blob), ou envoyés en tant que pièces jointes. Pour plus d'informations, reportez-vous à Classe Crypto à la page 648. Boolean Une valeur qui peut être attribuée uniquement à true, false ou null. Par exemple : Boolean isWinner = true; Constructions du langage salesforce | Type de données Primitive | 30 Type de données Description Date Une valeur qui indique un jour spécifique. Contrairement aux valeurs Datetime, les valeurs Date ne contiennent aucune information horaire. Les valeurs Date doivent toujours être créées avec une méthode statique système. Vous ne pouvez pas manipuler une valeur Date, par exemple ajouter des jours, en ajoutant simplement un chiffre à une variable Date. Vous devez utiliser à la place des méthodes Date. Datetime Une valeur qui indique un jour et une heure spécifiques, telle qu'un horodatage. Les valeurs Datetime doivent toujours être créées avec une méthode statique système. Vous ne pouvez pas manipuler une valeur Datetime, par exemple ajouter des minutes, en ajoutant simplement un chiffre à une variable Datetime. Vous devez utiliser à la place des méthodes Datetime. Decimal Un chiffre qui indique un point décimal. Le type Decimal est une valeur de précision arbitraire. Les champs de devise sont automatiquement attribués au type Decimal. Si vous ne définissez pas explicitement l'échelle, c.-à-d. le nombre de décimales pour un type Decimal qui utilise la méthode setScale, l'échelle est déterminée par l'élément à partir duquel le type Decimal a été créé. • • • Double Si le Decimal est créé dans le cadre d'une requête, l'échelle est basée sur l'échelle du champ renvoyé à partir de la requête. Si le Decimal est créé à partir d'une String, l'échelle correspond au nombre de caractères après le point décimal de la chaîne. Si le Decimal est créé à partir d'un chiffre non décimal, l'échelle est déterminée en convertissant le nombre en String, puis en utilisant le nombre de caractères après le point décimal. Un chiffre 64 bits qui indique un point décimal. Les types Double ont une valeur minimale de -263 et une valeur maximale de 263-1. Par exemple : Double d=3.14159; Notez la notation scientifique (e) pour les Doubles n'est pas prise en charge. ID N'importe quel identifiant d'enregistrement Force.com de 18 caractères valide. Par exemple : ID id='00300000003T2PGAA0'; Notez si vous définissez ID sur une valeur de 15 caractères, Apex convertit automatiquement la valeur en représentation de 18 caractères. Toutes les valeurs ID non valides sont rejetées avec une exception d'exécution. Integer Un chiffre 32 bits qui n'inclut pas de point décimal. Les types Integer ont une valeur minimale de -2 147 483 648 est une valeur maximale de 2 147 483 647. Par exemple : Integer i = 1; Constructions du langage salesforce | Type de données Primitive | 31 Type de données Description Long Un chiffre 64 bits qui n'inclut pas de point décimal. Les types Long ont une valeur minimale de -263 et une valeur maximale de 263-1. Utilisez ce type de données lorsque vous avez besoin d'une plage de valeurs plus large que celle fournie par le type Integer. Par exemple : Long l = 2147483648L; String N'importe quel jeu de caractères entre guillemets. Par exemple : String s = 'The quick brown fox jumped over the lazy dog.'; Taille de chaîne : Le nombre de caractères que les types String peuvent inclure est limité. À la place, la limite du segment mémoire est utilisée pour éviter que vos programmes Apex ne soient trop volumineux. Chaînes vides et espaces de fin : Les valeurs de champ sObject String appliquent les mêmes règles que l'API SOAP : elles ne peuvent jamais être vides (uniquement null), et ne peuvent jamais inclure d'espaces de début et de fin. Ces conventions sont nécessaires pour le stockage de la base de données. Inversement, les types String dans le langage Apex peuvent être null ou vides, et peuvent inclure des espaces de début et de fin (par exemple pour construire un message). Le champ sObject de solution, SolutionNote, fonctionne en tant que type String spécial. Si vous avez des solutions HTML activées, toutes les balises HTML utilisées dans ce champ sont vérifiées avant la création ou la mise à jour de l'objet. Si des balises HTML non valides sont saisies, une erreur est générée. Tout JavaScript utilisé dans ce champ est supprimé avant la création ou la mise à jour de l'objet. Dans l'exemple suivant, lorsque la Solution s'affiche dans une page de détail, une mise en forme HTML H1 est appliquée au champ SolutionNote : trigger t on Solution (before insert) { Trigger.new[0].SolutionNote ='<h1>hello</h1>'; } Dans l'exemple suivant, lorsque la Solution s'affiche dans une page de détail, le champ SolutionNote contient uniquement HelloGoodbye : trigger t2 on Solution (before insert) { Trigger.new[0].SolutionNote = '<javascript>Hello</javascript>Goodbye'; } Pour plus d'informations, reportez-vous à « Présentation des solutions HTML ». Séquences d'échappement : Tous les types String dans le langage Apex utilisent les mêmes séquences d'échappement que les chaînes SOQL : \b (retour arrière), \t (tabulation), \n Constructions du langage Type de données salesforce | Types sObject | 32 Description (saut de ligne), \f (saut de page), \r (retour chariot), \" (guillemets), \' (apostrophe) et \\ (barre oblique inverse). Opérateurs de comparaison : Contrairement à Java, le type String Apex prend en charge l'utilisation des opérateurs de comparaison ==, !=, <, <=, > et >=. Comme le langage Apex utilise des sémantiques de comparaison SOQL, les résultats des types String sont comparés en fonction des paramètres régionaux de l'utilisateur contextuel, et ne sont pas sensibles à la casse. Pour plus informations, reportez-vous à Opérateurs à la page 60. Méthodes de chaîne : Comme dans le langage Java, les types String peuvent être manipulés par plusieurs méthodes standard. Pour plus d'informations, reportez-vous à Méthodes String. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Time Une valeur qui indique une heure spécifique. Les valeurs Time doivent toujours être créées avec une méthode statique système. Reportez-vous à Méthodes Time à la page 409. De plus, deux types de données primitifs non standard ne peuvent pas être utilisés en tant que type de variable ou de méthode, mais figurent dans des méthodes statiques système : • • AnyType. La méthode statique valueOf convertit un champ sObject de type AnyType en primitif standard. AnyType est utilisé dans la base de données de la plate-forme Force.com exclusivement pour des champs sObject dans des tableaux de suivi d'historique des champs. Currency. La méthode statique Currency.newInstance crée un type Currency littéral. Cette méthode est utilisée exclusivement dans des clauses SOQL et SOSL WHERE pour filtrer par des champs de devise sObject. Vous ne pouvez pas instancier Currency dans un autre type Apex. Pour plus d'informations sur le type de données AnyType, reportez-vous à Field Types dans le guide Object Reference for Salesforce and Force.com. Types sObject Dans le guide du développeur, le terme sObject fait référence à un objet qui peut être stocké dans la base de données de la plate-forme Force.com. Une variable sObject représente une ligne de données et peut être déclarée dans Apex uniquement en utilisant le nom d'API SOAP de l'objet. Par exemple : Account a = new Account(); MyCustomObject__c co = new MyCustomObject__c(); Semblable à l'API SOAP, le langage Apex permet d'utiliser le type sObject abstrait générique pour représenter n'importe quel objet. Le type de données sObject peut être utilisé dans du code qui traite différents types de sObject. L'opérateur new requiert toutefois un type sObject concret. Par conséquent, toutes les instances sont des sObjects spécifiques. Par exemple : sObject s = new Account(); Constructions du langage salesforce | Types sObject | 33 Vous pouvez également utiliser une conversion (casting) entre le type sObject générique et le type sObject spécifique. Par exemple : // Cast the generic variable s from the example above // into a specific account and account variable a Account a = (Account)s; // The following generates a runtime error Contact c = (Contact)s; Puisque les sObjects fonctionnent comme des objects, vous pouvez également avoir : Object obj = s; // and a = (Account)obj; Les opérations DML fonctionnent dans des variables déclarées en tant que type de données sObject générique ainsi que dans des sObjects réguliers. Les variables sObject sont initialisées sur null, mais une référence d'objet valide peut leur être attribuée avec l'opérateur new. Par exemple : Account a = new Account(); Les développeurs peuvent également spécifier des valeurs de champs initiales avec des paires nom = valeur, séparées par des virgules, en instanciant un nouveau sObject. Par exemple : Account a = new Account(name = 'Acme', billingcity = 'San Francisco'); Pour plus d'informations sur l'accès à des sObjects existants à partir de la base de données de la plate-forme Force.com, reportez-vous à Requêtes SOQL et SOSL à la page 77. Remarque: L'ID d'un sObject est une valeur en lecture seule qui ne peut jamais être modifiée explicitement dans Apex, sauf si elle est effacée pendant une opération de clonage ou attribuée avec un constructeur. La plate-forme Force.com attribue des valeurs ID automatiquement lors de l'insertion initiale d'un enregistrement d'objet dans la base de données. Pour plus informations, reportez-vous à Listes à la page 38. Étiquettes personnalisées Les étiquettes personnalisées ne sont pas des sObjects standard. Vous ne pouvez pas créer une instance d'une étiquette personnalisée. Vous pouvez accéder à la valeur d'une étiquette personnalisée uniquement en utilisant system.label.label_name. Par exemple : String errorMsg = System.Label.generic_error; Pour plus d'informations sur les étiquettes personnalisées, reportez-vous à Présentation des étiquettes personnalisées. Constructions du langage salesforce | Types sObject | 34 Accès aux champs sObject Comme dans Java, les champs sObject peuvent être accédés ou modifiés avec une simple notation pointée. Par exemple : Account a = new Account(); a.Name = 'Acme'; // Access the account name field and assign it 'Acme' Les champs générés par le système, tels que Created By (Créé par) ou Last Modified Date (Date de dernière modification), ne sont pas modifiables. Si vous essayez de les modifier, le moteur d'exécution Apex génère une erreur. En outre, les valeurs de champs de formule et les valeurs d'autres champs en lecture seule pour l'utilisateur contextuel, ne sont pas modifiables. Si vous utilisez un type sObject générique à la place d'un objet spécifique, tel que Compte, vous pouvez récupérer uniquement le champ Id en utilisant une notation pointée. Vous pouvez définir le champ Id pour le code Apex enregistré en utilisant l'API Salesforce.com (versions 27.0 et supérieures). Vous pouvez également utiliser les méthodes sObject génériques put et get. Reportez-vous à Méthodes sObject. Cet exemple montre comment accéder au champ Id et présente les opérations qui ne sont pas autorisées dans des sObjects génériques. Account a = new Account(Name = 'Acme', BillingCity = 'San Francisco'); insert a; sObject s = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1]; // This is allowed ID id = s.Id; // The following line results in an error when you try to save String x = s.Name; // This line results in an error when you try to save using API version 26.0 or earlier s.Id = [SELECT Id FROM Account WHERE Name = 'Acme' LIMIT 1].Id; Remarque: Si votre organisation a des comptes personnels activés, vous disposez de deux types de compte : comptes professionnels et comptes personnels. Si votre code crée un compte en utilisant name, un compte professionnel est créé. Si votre code utilise LastName, un compte personnel est créé. Si vous souhaitez effectuer des opérations avec un sObject, nous recommandons de le convertir au préalable en objet spécifique. Par exemple : Account a = new Account(Name = 'Acme', BillingCity = 'San Francisco'); insert a; sObject s = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1]; ID id = s.ID; Account convertedAccount = (Account)s; convertedAccount.name = 'Acme2'; Constructions du langage salesforce | Types sObject | 35 update convertedAccount; Contact sal = new Contact(FirstName = 'Sal', Account = convertedAccount); L'exemple suivant montre comment utiliser SOSL sur un ensemble d'enregistrements afin de déterminer leur type d'objet. Une fois l'enregistrement sObject générique converti en Contact, Piste ou Compte, vous pouvez modifier ses champs en conséquence : public class convertToCLA { List<Contact> contacts; List<Lead> leads; List<Account> accounts; public void convertType(Integer phoneNumber) { List<List<sObject>> results = [FIND '4155557000' IN Phone FIELDS RETURNING Contact(Id, Phone, FirstName, LastName), Lead(Id, Phone, FirstName, LastName), Account(Id, Phone, Name)]; sObject[] records = ((List<sObject>)results[0]); if (!records.isEmpty()) { for (Integer i = 0; i < records.size(); i++) { sObject record = records[i]; if (record.getSObjectType() == Contact.sObjectType) { contacts.add((Contact) record); } else if (record.getSObjectType() == Lead.sObjectType){ leads.add((Lead) record); } else if (record.getSObjectType() == Account.sObjectType) { accounts.add((Account) record); } } } } } Constructions du langage salesforce | Types sObject | 36 Accès aux champs sObject via des relations Les enregistrements sObject représentent des relations à d'autres enregistrements avec deux champs : un ID et une adresse qui pointe vers une représentation du sObject associé. Par exemple, le sObject Contact contient un champ AccountId de type ID et un champ Account de type Compte qui pointent vers l'enregistrement sObject associé lui-même. Le champ ID peut être utilisé pour modifier le compte auquel le contact est associé, alors que le champ de référence sObject peut être utilisé pour accéder aux données à partir du compte. Le champ de référence est renseigné uniquement suite à une requête SOQL ou SOSL (voir la remarque ci-dessous). Par exemple, le code Apex ci-dessous montre comment un compte et un contact peuvent être associés entre eux, et comment utiliser le contact pour modifier un champ dans le compte : Remarque: Pour offrir un exemple le plus complet possible, ce code utilise quelques éléments présentés plus loin dans ce guide : • • Pour plus d'informations sur insert et update, reportez-vous à Opération Insert à la page 334 et Opération Update à la page 334. Pour plus informations sur SOQL et SOSL, reportez-vous à Requêtes SOQL et SOSL à la page 77. Account a = new Account(Name = 'Acme'); insert a; // Inserting the record automatically assigns a // value to its ID field Contact c = new Contact(LastName = 'Weissman'); c.AccountId = a.Id; // The new contact now points at the new account insert c; // A SOQL query accesses data for the inserted contact, // including a populated c.account field c = [SELECT Account.Name FROM Contact WHERE Id = :c.Id]; // Now fields in both records can be changed through the contact c.Account.Name = 'salesforce.com'; c.LastName = 'Roth'; // To update the database, the two types of records must be // updated separately update c; // This only changes the contact's last name update c.Account; // This updates the account name Constructions du langage salesforce | Types sObject | 37 Remarque: L'expression c.Account.Name, ainsi que toute autre expression qui traverse une relation, affiche des caractéristiques légèrement différentes lorsqu'elle est lue en tant que valeur que lorsqu'elle est modifiée : • • Lors de sa lecture en tant que valeur, si c.Account est null, c.Account.Name évalue en null, mais ne génère pas une exception NullPointerException. Cette conception permet aux développeurs de naviguer entre plusieurs relations sans avoir à vérifier la présence de valeurs nulles. Lors de sa modification, si c.Account est null, c.Account.Name génère une exception NullPointerException. En outre, la clé du champ sObject peut être utilisée avec insert, update ou upsert pour résoudre des clés étrangères par ID externe. Par exemple : Account refAcct = new Account(externalId__c = '12345'); Contact c = new Contact(Account = refAcct, LastName = 'Kay'); insert c; Un nouveau contact est inséré avec AccountId égal au compte avecexternal_id égal à 12345. Si ce compte n'existe pas, l'insertion échoue. Conseil: Le code ci-dessous est équivalent au code ci-dessus. Cependant, comme il utilise une requête SOQL, il n'est pas aussi efficace. Si ce code était appelé plusieurs fois, il risquerait d'atteindre la limite d'exécution en nombre maximal de requêtes SOQL. Pour plus d'informations sur les limites d'exécution, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Account refAcct = [SELECT Id FROM Account WHERE externalId__c='12345']; Contact c = new Contact(Account = refAcct.Id); insert c; Validation de sObjects et de champs Lors de l'analyse et de la validation du code Apex, toutes les références de sObject et de champ sont validées par rapport aux noms d'objet et de champ, et une exception est générée si un nom incorrect est utilisé. En outre, l'analyseur Apex surveille les objets et les champs personnalisés utilisés, dans la syntaxe du code ainsi que dans les instructions SOQL et SOSL incorporées. La plate-forme empêche les utilisateurs d'effectuer les modifications de type ci-dessous, si elles entraînent l'invalidation du code Apex : • Modifier le nom d'un champ ou d'un objet Constructions du langage • • • salesforce | Collections | 38 Convertir un type de données vers un autre Supprimer un champ ou un objet Modifier certains éléments à l'échelle de l'organisation, tels que le partage d'enregistrements, le suivi d'historique de champs ou des types d'enregistrement Collections Le langage Apex inclut les types de collection suivants : • • • Lists Maps Sets Remarque: Le nombre d'éléments qu'une collection peut contenir est illimité. Il existe toutefois une limite générale en taille de segment mémoire. Listes Une liste est une collection ordonnée de d'objets primitifs typés, sObjects, définis par l'utilisateur, Apex ou de collections qui se différencient par leur index. Par exemple, le tableau suivant est une représentation visuelle d'une liste de chaînes (String) : Index 0 Index 1 Index 2 Index 3 Index 4 Index 5 'Rouge' 'Orange' 'Jaune' 'Vert' 'Bleu' 'Violet' La position d'index du premier élément dans une liste est toujours 0. Puisque les listes peuvent contenir n'importe qu'elle collection, elles peuvent être imbriquées les unes dans les autres et devenir multidimensionnelles. Par exemple, vous pouvez avoir une liste de listes d'ensembles de nombres entiers. Une liste peut contenir jusqu'à quatre niveaux de collections imbriquées. Pour déclarer une liste, utilisez le mot clé List suivi du type de données primitif, sObject, liste imbriquée, carte ou ensemble entre crochets <>. Par exemple : // Create an empty list of String List<String> my_list = new List<String>(); // Create a nested list List<List<Set<Integer>>> my_list_2 = new List<List<Set<Integer>>>(); // Create a list of account records from a SOQL query List<Account> accs = [SELECT Id, Name FROM Account LIMIT 1000]; Pour accéder aux éléments d'une liste, utilisez les méthodes système fournies par Apex. Par exemple : List<Integer> MyList = new List<Integer>(); // Define a new list MyList.add(47); // Adds a second element of value 47 to the end Constructions du langage salesforce | Collections | 39 // of the list MyList.get(0); // Retrieves the element at index 0 MyList.set(0, 1); // Adds the integer 1 to the list at index 0 MyList.clear(); // Removes all elements from the list Pour plus d'informations, notamment une liste complète de toutes les méthodes prises en charge, reportez-vous à Méthodes List à la page 411. Utilisation d'une notation tabulaire pour des listes unidimensionnelles de primitifs ou de sObjects Lors de l'utilisation d'une liste unidimensionnelle de primitifs ou de sObjects, vous pouvez utiliser une notation tabulaire plus traditionnelle pour déclarer et référencer les éléments de liste. Par exemple, vous pouvez déclarer une liste unidimensionnelle de primitifs ou de sObjects en suivant les données ou le nom du type sObject entre les crochets droits [] : String[] colors = new List<String>(); Pour référencer un élément d'une liste unidimensionnelle de primitifs ou de sObjects, vous pouvez également suivre le nom de la liste avec la position d'index de l'élément entre crochets droits. Par exemple : colors[3] = 'Green'; Toutes les listes sont initialisées sur null. Des valeurs peuvent être attribuées et de la mémoire peut être allouée aux listes en utilisant une notation littérale. Par exemple : Exemple Description Définit une liste Integer sans élément List<Integer> ints = new Integer[0]; Définit une liste Account sans élément List<Account> accts = new Account[]{}; List<Integer> ints = new Integer[6]; List<Account> accts = new Account[] {new Account(), null, new Account()}; Définit une liste Integer avec de la mémoire allouée pour six nombres entiers Définit une liste Account avec de la mémoire allouée pour trois comptes, qui comprend un nouvel objet Account en première position, null en deuxième position et un autre objet Account en troisième position Définit la liste Contact avec une nouvelle liste List<Contact> contacts = new List<Contact> (otherList); Constructions du langage salesforce | Collections | 40 Listes de sObjects Le langage Apex génère automatiquement des ID pour chaque objet dans une liste de sObjects lors de l'insertion ou de la mise à jour/insertion dans la base de données avec une instruction DML (langage de manipulation de données). Par conséquent, une liste de sObjects ne peut pas être insérée ou mise à jour/insérée si elle contient plusieurs fois le même sObject, même avec un ID null. Cette situation nécessiterait que deux ID soient écrits en mémoire avec la même structure, ce qui est illégal. Par exemple, l'instruction insert dans le bloc de code ci-dessous génère une exception ListException, car elle tente d'insérer une liste avec deux références au même sObject (a) : try { // Create a list with two references to the same sObject element Account a = new Account(); Account[] accs = new Account[]{a, a}; // Attempt to insert it... insert accs; // Will not get here System.assert(false); } catch (ListException e) { // But will get here } Pour plus d'informations sur les instructions DML, reportez-vous à Opérations DML (langage de manipulation de données) Apex à la page 327. Vous pouvez utiliser des types de données sObject génériques avec des listes. Vous pouvez également créer une instance générique d'une liste. Tri des listes En utilisant la méthode List.sort, vous pouvez trier des listes de types de données primitifs, des types personnalisés (vos classes Apex) qui implémentent l'interface Comparable, des éléments SelectOption et des sObjects (objets standard et objets personnalisés). Le tri des types de données primitifs est ascendant. Pour des types personnalisés, les critères de tri et l'ordre de tri dépendent de l'implémentation que vous fournissez pour la méthode compareTo de l'interface Comparable. Pour plus d'informations sur l'implémentation de l'interface Comparable pour vos propres classes, reportez-vous à Interface Comparable. Pour des sObjects, l'ordre de tri est ascendant et utilise une séquence d'étapes présentées dans la section suivante. Cependant, vous pouvez également implémenter un ordre de tri personnalisé pour des sObjects en encapsulant votre sObject dans une classe Apex et en implémentant l'interface Comparable, comme indiqué dans Ordre de tri personnalisé pour des sObjects. Constructions du langage salesforce | Collections | 41 Pour SelectOption, l'ordre de tri est ascendant, basé sur les champs de valeur et d'étiquette. Pour consulter la séquence d'étapes de comparaison de SelectOption, reportez-vous à Ordre de tri par défaut pour SelectOption. Ordre de tri par défaut des sObjects La méthode List.sort trie les sObjects dans l'ordre ascendant, et compare les sObjects en utilisant une séquence triées d'étapes qui spécifient les étiquettes ou les champs utilisés. La comparaison commence à la première étape de la séquence et se termine lorsque les deux sObjects sont triés en utilisant les étiquettes ou les champs spécifiés. La séquence de comparaison utilisée est la suivante : 1. L'étiquette du type sObject. Par exemple, un sObject Account précède un Contact. 2. Le champ Name, si applicable. Par exemple, si la liste inclut deux comptes nommés respectivement A et B, le compte A précède le compte B. 3. Les champs standard, en commençant par les premiers champs dans l'ordre alphabétique, à l'exception des champs ID et Name. Par exemple, si deux comptes portent le même nom, le premier champ standard utilisé pour le tri est AccountNumber. 4. Les champs personnalisés, en commençant par les premiers champs dans l'ordre alphabétique. Par exemple, supposons que deux comptes portent le même nom, ont des champs standard identiques et que deux champs personnalisés existent, FieldA et FieldB. La valeur de FieldA est utilisée en premier pour le tri. Les étapes de cette séquence ne sont pas nécessairement toutes exécutées. Par exemple, si une liste contient deux sObjects du même type avec des valeurs Name uniques, ils sont triés en fonction du champ Name et le tri s'arrête à l'étape 2. Sinon, si les noms sont identiques ou si le sObject n'a pas de champ Name, le tri passe à l'étape 3 pour trier par champ standard. Pour les champs de texte, l'algorithme de tri utilise l'ordre de tri Unicode. De plus, les champs vides précèdent les champs non vides dans l'ordre de tri. L'exemple ci-dessous présente le tri d'une liste de sObject Account. Cet exemple montre comment le champ Name est utilisé pour placer le compte Acme devant les deux comptes sForce dans la liste. Comme il existe deux comptes nommés sForce, le champ Industry est utilisé pour trier les comptes restants, car Industry précède le champ Site dans l'ordre alphabétique. Account[] acctList = new List<Account>(); acctList.add( new Account( Name='sForce', Industry='Biotechnology', Site='Austin')); acctList.add(new Account( Name='sForce', Industry='Agriculture', Site='New York')); acctList.add(new Account( Name='Acme')); System.debug(acctList); Constructions du langage salesforce | Collections | 42 acctList.sort(); System.assertEquals('Acme', acctList[0].Name); System.assertEquals('sForce', acctList[1].Name); System.assertEquals('Agriculture', acctList[1].Industry); System.assertEquals('sForce', acctList[2].Name); System.assertEquals('Biotechnology', acctList[2].Industry); System.debug(acctList); Cet exemple est similaire au précédent, mais il utilise l'objet personnalisé Merchandise__c. Il montre comment le champ Name est utilisé pour placer les articles Notebooks avant les Pens dans la liste. Comme il existe deux articles sObjects dont le champ Name inclut la valeur Pens, le champ Description est utilisé pour trier les articles restants, car Description précède les champs Price et Total_Inventory dans l'ordre alphabétique. Merchandise__c[] merchList = new List<Merchandise__c>(); merchList.add( new Merchandise__c( Name='Pens', Description__c='Red pens', Price__c=2, Total_Inventory__c=1000)); merchList.add( new Merchandise__c( Name='Notebooks', Description__c='Cool notebooks', Price__c=3.50, Total_Inventory__c=2000)); merchList.add( new Merchandise__c( Name='Pens', Description__c='Blue pens', Price__c=1.75, Total_Inventory__c=800)); System.debug(merchList); merchList.sort(); System.assertEquals('Notebooks', merchList[0].Name); System.assertEquals('Pens', merchList[1].Name); System.assertEquals('Blue pens', merchList[1].Description__c); System.assertEquals('Pens', merchList[2].Name); Constructions du langage salesforce | Collections | 43 System.assertEquals('Red pens', merchList[2].Description__c); System.debug(merchList); Ordre de tri personnalisé des sObjects Pour implémenter un ordre de tri personnalisé pour des sObjects dans des listes, créez une classe wrapper pour le sObject et implémentez l'interface Comparable. La classe wrapper contient le sObject en question et implémente la méthode compareTo, dans laquelle vous spécifiez la logique de tri. Cet exemple montre comment créer une classe wrapper pour Opportunité. L'implémentation de la méthode compareTo dans cette classe compare deux opportunités basées sur le champ Amount : la variable membre de la classe contenue dans cette instance, et l'objet opportunité transmis dans la méthode. global class OpportunityWrapper implements Comparable { public Opportunity oppy; // Constructor public OpportunityWrapper(Opportunity op) { oppy = op; } // Compare opportunities based on the opportunity amount. global Integer compareTo(Object compareTo) { // Cast argument to OpportunityWrapper OpportunityWrapper compareToOppy = (OpportunityWrapper)compareTo; // The return value of 0 indicates that both elements are equal. Integer returnValue = 0; if (oppy.Amount > compareToOppy.oppy.Amount) { // Set return value to a positive value. returnValue = 1; } else if (oppy.Amount < compareToOppy.oppy.Amount) { // Set return value to a negative value. returnValue = -1; } return returnValue; Constructions du langage salesforce | Collections | 44 } } Cet exemple fournit un test pour la classe OpportunityWrapper. Il trie une liste d'objets OpportunityWrapper et vérifie que les éléments de la liste sont triés par le montant de l'opportunité. @isTest private class OpportunityWrapperTest { static testmethod void test1() { // Add the opportunity wrapper objects to a list. OpportunityWrapper[] oppyList = new List<OpportunityWrapper>(); Date closeDate = Date.today().addDays(10); oppyList.add( new OpportunityWrapper(new Opportunity( Name='Edge Installation', CloseDate=closeDate, StageName='Prospecting', Amount=50000))); oppyList.add( new OpportunityWrapper(new Opportunity( Name='United Oil Installations', CloseDate=closeDate, StageName='Needs Analysis', Amount=100000))); oppyList.add( new OpportunityWrapper(new Opportunity( Name='Grand Hotels SLA', CloseDate=closeDate, StageName='Prospecting', Amount=25000))); // Sort the wrapper objects using the implementation of the // compareTo method. oppyList.sort(); // Verify the sort order System.assertEquals('Grand Hotels SLA', oppyList[0].oppy.Name); System.assertEquals(25000, oppyList[0].oppy.Amount); Constructions du langage salesforce | Collections | 45 System.assertEquals('Edge Installation', oppyList[1].oppy.Name); System.assertEquals(50000, oppyList[1].oppy.Amount); System.assertEquals('United Oil Installations', oppyList[2].oppy.Name); System.assertEquals(100000, oppyList[2].oppy.Amount); // Write the sorted list contents to the debug log. System.debug(oppyList); } } Ordre de tri par défaut pour SelectOption La méthode List.sort trie les éléments SelectOption dans l'ordre ascendant en utilisant les champs de valeur et d'étiquette, et elle est basée sur une séquence de comparaison. 1. Le champ de valeur est utilisé pour trier le premier. 2. Si deux champ de valeur contiennent la même valeur ou sont vides, le champ d'étiquette est utilisé. Notez que le champ désactivé n'est pas utilisé pour le tri. Pour les champs de texte, l'algorithme de tri utilise l'ordre de tri Unicode. De plus, les champs vides précèdent les champs non vides dans l'ordre de tri. Dans cet exemple, une liste contient trois éléments SelectOption. Deux éléments, United States et Mexico, ont le même champ de valeur ('A'). La méthode List.sort trie ces deux éléments par le champ d'étiquette, et place Mexico avant United States, comme indiqué dans la sortie. Le dernier élément de la liste triée est Canada, trié par son champ de valeur 'C', qui précède 'A'. List<SelectOption> options = new List<SelectOption>(); options.add(new SelectOption('A','United States')); options.add(new SelectOption('C','Canada')); options.add(new SelectOption('A','Mexico')); System.debug('Before sorting: ' + options); options.sort(); System.debug('After sorting: ' + options); La sortie des instructions de débogage est la suivante. Elle affiche le contenu de la liste avant et après le tri. DEBUG|Before sorting: (System.SelectOption[value="A", label="United States", disabled="false"], System.SelectOption[value="C", label="Canada", disabled="false"], System.SelectOption[value="A", label="Mexico", disabled="false"]) DEBUG|After sorting: (System.SelectOption[value="A", label="Mexico", disabled="false"], System.SelectOption[value="A", label="United States", disabled="false"], Constructions du langage salesforce | Collections | 46 System.SelectOption[value="C", label="Canada", disabled="false"]) Sets Un ensemble (set) est une collection non triée d'éléments qui ne contient aucun doublon. Les éléments Set peuvent avoir n'importe quel type : primitif, collection, sObjects, défini par l'utilisateur et Apex intégré. Par exemple, le tableau suivant représente un ensemble de chaînes qui utilise des noms de villes : 'San Francisco' 'New York' 'Paris' 'Tokyo' Des ensembles Set peuvent contenir des collections imbriquées les unes dans les autres. Par exemple, vous pouvez avoir un ensemble de listes d'ensembles de nombres entiers. Un ensemble peut contenir jusqu'à quatre niveaux de collections imbriquées. Pour déclarer un ensemble, utilisez le mot clé Set suivi du nom du type de données primitif entre crochets <>. Par exemple : new Set<String>() Les méthodes de déclaration et de renseignement d'un ensemble sont les suivantes : Set<String> s1 = new Set<String>{'a', 'b + c'}; // Defines a new set with two elements Set<String> s2 = new Set<String>(s1); // Defines a new set that contains the // elements of the set created in the previous step Pour accéder aux éléments d'un ensemble, utilisez les méthodes système fournies par Apex. Par exemple : Set<Integer> s = new Set<Integer>(); // Define a new set s.add(1); // Add an element to the set System.assert(s.contains(1)); // Assert that the set contains an element s.remove(1); // Remove the element from the set Pour déterminer si des objets non primitifs sont uniques, les champs des objets sont comparés, à l'exception des objets de type défini par l'utilisateur. Par exemple si vous essayez d'ajouter deux comptes portant le même nom dans un ensemble, un seul est ajouté. // Create two accounts, a1 and a2 Account a1 = new account(name='MyAccount'); Account a2 = new account(name='MyAccount'); // Add both accounts to the new set Set<Account> accountSet = new Set<Account>{a1, a2}; // Verify that the set only contains one item Constructions du langage salesforce | Collections | 47 System.assertEquals(accountSet.size(), 1); Cependant, si vous ajoutez une description à l'un des comptes, il est considéré comme unique : // Create two accounts, a1 and a2, and add a description to a2 Account a1 = new account(name='MyAccount'); Account a2 = new account(name='MyAccount', description='My test account'); // Add both accounts to the new set Set<Account> accountSet = new Set<Account>{a1, a2}; // Verify that the set contains two items System.assertEquals(accountSet.size(), 2); Pour déterminer si les objets de type défini par l'utilisateur sont uniques, les méthodes equals et hashCode sont utilisées, que vous fournissez dans vos classes. Pour plus d'informations, notamment une liste complète de toutes les méthodes système Set prises en charge, reportez-vous à Méthodes Set à la page 427. Notez les limitations suivantes des ensembles : • • Contrairement au langage Java, les développeurs Apex n'ont pas besoin de référencer l'algorithme utilisé pour implémenter un ensemble dans leurs déclarations (par exemple, HashSet ou TreeSet). Le langage Apex utilise une structure hash pour tous les ensembles. Un ensemble est une collection non triée. Ne vous appuyez pas sur l'ordre dans lequel les résultats de l'ensemble sont renvoyés. L'ordre des objets renvoyés par les ensembles peut changer sans avertissement. Maps Un map (mappage) est une collection de paires clé-valeur dans lesquelles chaque clé unique est mappée avec une valeur unique. Les clés et les valeurs peuvent avoir n'importe quel type : primitif, collection, sObjects, défini par l'utilisateur et Apex intégré. Par exemple, le tableau suivant présente un mappage de pays et de devises : Pays (clé) 'États-Unis' 'Japon' 'France' 'Angleterre' 'Inde' Devise (valeur) 'Dollar' 'Yen' 'Euro' 'Livre Sterling' 'Roupie' Les clés et les valeurs Map peuvent contenir n'importent quelle collection, ainsi que des collections imbriquées. Par exemple, vous pouvez avoir un mappage de nombres entiers avec des mappages, qui à leur tour mappent des chaînes avec des listes. Les clés Map ne peuvent contenir que quatre niveaux de collections imbriquées. Pour déclarer un mappage, utilisez le mot clé Map suivi des types de données de la clé et de la valeur entre crochets <>. Par exemple : Map<String, String> country_currencies = new Map<String, String>(); Constructions du langage salesforce | Collections | 48 Map<ID, Set<String>> m = new Map<ID, Set<String>>(); Map<ID, Map<ID, Account[]>> m2 = new Map<ID, Map<ID, Account[]>>(); Vous pouvez utiliser des types de données sObject génériques avec des mappages. Vous pouvez également créer une instance générique d'un mappage. Comme avec les listes, vous pouvez renseigner des paires clé-valeur de mappage lors de la déclaration du mappage en utilisant une syntaxe en accolades ({}). Entre les accolades, spécifiez d'abord la clé, puis la valeur de cette clé en utilisant =>. Par exemple : Map<String, String> MyStrings = new Map<String, String>{'a' => 'b', 'c' => 'd'.toUpperCase()}; Account[] accs = new Account[5]; // Account[] is synonymous with List<Account> Map<Integer, List<Account>> m4 = new Map<Integer, List<Account>>{1 => accs}; Dans le premier exemple, la valeur de la clé a est b, et la valeur de la clé c est d. Dans le deuxième exemple, la clé 1 a la valeur de la liste accs. L'exemple ci-dessous présente l'utilisation de sObjects en tant que clés de mappage. Map<Account, String> mapKeyExample = new Map<Account, String>{ new Account(Name='Account1') => '[email protected]', new Account(Name='Account2') => '[email protected]' }; Pour accéder aux éléments d'un mappage, utilisez les méthodes Map fournies par Apex. Par exemple : Account myAcct = new Account(); //Define a new account Map<Integer, Account> m = new Map<Integer, Account>(); // Define a new map m.put(1, myAcct); // Insert a new key-value pair in the map System.assert(!m.containsKey(3)); // Assert that the map contains a key Account a = m.get(1); Set<Integer> s = m.keySet(); // Retrieve a value, given a particular key // Return a set that contains all of the keys in the map Pour plus d'informations, notamment une liste complète de toutes les méthodes Map prises en charge, reportez-vous à Méthodes Map à la page 420. Notez les points suivants sur les mappages : • • Contrairement au langage Java, les développeurs Apex n'ont pas besoin de référencer l'algorithme utilisé pour implémenter un mappage dans leurs déclarations (par exemple, HashMap ou TreeMap). Le langage Apex utilise une structure hash pour tous les mappages. Ne vous appuyez pas sur l'ordre dans lequel les résultats de mappage sont renvoyés. L'ordre des objets renvoyés par les mappages peut changer sans avertissement. Accédez toujours aux éléments de mappage par clé. Constructions du langage • • • salesforce | Collections | 49 Une clé de mappage peut inclure la valeur null. L'ajout d'une entrée de mappage avec cette clé, qui correspond à une clé existante dans le mappage, remplace l'entrée existante de cette clé avec la nouvelle entrée. Pour déterminer si les clés de mappage de types définis par l'utilisateur sont uniques, les méthodes equals et hashCode sont utilisées, que vous fournissez dans vos classes. Pour déterminer si tous les autres types non primitifs sont uniques, les champs des objets sont comparés. Mappages de tableaux SObject Des mappages d'un ID ou d'un type de données String avec un sObject peuvent être initialisés à partir d'une liste de sObjects. Les ID des objets (qui doivent être non nuls et distincts) sont utilisés en tant que clés. L'une des utilisations courantes de ce type de mappage concerne les « liaisons » en mémoire entre deux tableaux. Par exemple, l'exemple ci-dessous charge un mappage d'ID et de contacts : Map<ID, Contact> m = new Map<ID, Contact>([SELECT Id, LastName FROM Contact]); Dans l'exemple, la requête SOQL renvoie une liste de contacts avec leur champ Id et LastName. L'opérateur new utilise la liste pour créer un mappage. Pour plus informations, reportez-vous à Requêtes SOQL et SOSL à la page 77. Type paramétré En règle générale, Apex est un langage de programmation typé de façon statique, ce qui signifie que les utilisateurs peuvent spécifier le type de données d'une variable avant son utilisation. L'exemple ci-dessous est légal dans le langage Apex : Integer x = 1; L'exemple ci-dessous n'est pas légal si x n'a pas été au préalable défini : x = 1; Les lists, maps et sets (listes, mappages et ensembles) sont paramétrés dans Apex : ils peuvent accepter n'importe quel type de données que le langage Apex prend en charge en tant qu'argument. Le type de données doit être remplacé par un type de données existant lors de la construction de la liste, du mappage ou de l'ensemble. Par exemple : List<String> myList = new List<String>(); Sous-type avec des listes paramétrées Dans le langage Apex, si le type T est un sous-type de U, List<T> est un sous-type de List<U>. L'exemple ci-dessous est légal : List<String> slst = new List<String> {'foo', 'bar'}; List<Object> olst = slst; Utilisation de types personnalisés dans des clés et ensembles de mappages Lors de l'utilisation d'un type personnalisé (votre classe Apex) pour les éléments clés de mappage ou ensemble, fournissez les méthodes equals et hashCode dans votre classe. Le langage Apex utilise ces deux méthodes pour déterminer si les clés de vos objets sont égales et uniques. Constructions du langage salesforce | Collections | 50 Ajout des méthodes equals et hashCode à votre classe Pour vous assurer que les clés de mappage de votre type personnalisé sont correctement comparées et qu'il est possible de déterminer de façon cohérente si elles sont uniques, fournissez une implémentation des deux méthodes suivantes dans votre classe : La méthode equals avec cette signature : • public Boolean equals(Object obj) { // Your implementation } Notez les points suivants lors de l'implémentation de la méthode equals. En supposant que x, y et z sont des instances non nulles de votre classe, la méthode equals doit être : Réfléchie : x.equals(x) Symétrique : x.equals(y) doit renvoyer true si et uniquement si y.equals(x) renvoie true Transitive : si x.equals(y) renvoie true et y.equals(z) renvoie true, x.equals(z) doit renvoyer true Cohérente : les invocations multiples de x.equals(y) renvoient toujours true ou renvoient toujours false Pour toute valeur de référence non nulle x, x.equals(null) doit renvoyer false ◊ ◊ ◊ ◊ ◊ La méthode equals dans Apex est basée sur la méthode equals dans Java. La méthode hashCode avec cette signature : • public Integer hashCode() { // Your implementation } Notez les points suivants lors de l'implémentation de la méthode hashCode. ◊ Si la méthode hashCode est invoquée plusieurs fois sur le même objet durant l'exécution d'une requête Apex, elle doit renvoyer la même valeur. ◊ Si deux objets sont égaux, selon la méthode equals, hashCode doit renvoyer la même valeur. ◊ Si deux objets sont différents, selon les résultats de la méthode equals, il n'est pas nécessaire que hashCode renvoie des valeurs distinctes. La méthode hashCode dans Apex est basée sur la méthode hashCode dans Java. Un autre avantage de la fourniture de la méthode equals dans votre classe est la simplification de la comparaison de vos objets. Vous pouvez utiliser l'opérateur == pour comparer des objets, ou la méthode equals. Par exemple : // obj1 and obj2 are instances of MyClass if (obj1 == obj2) { // Do something } if (obj1.equals(obj2)) { // Do something Constructions du langage salesforce | Collections | 51 } Exemple Cet exemple présente l'implémentation des méthodes equals et hashCode. La classe fournie par ces méthodes est répertoriée en premier. Elle contient également un constructeur qui prend deux nombres entiers. Le deuxième exemple est un extrait de code qui crée trois objets de la classe, dont deux ont la même valeur. Ensuite, les entrées de mappage sont ajoutées en utilisant les objets semblables en tant que clés. L'exemple vérifie que le mappage ne contient que deux entrées, car la première entrée a été remplacée par la dernière entrée ajoutée qui a la même clé. L'exemple utilise ensuite l'opérateur == qui fonctionne comme prévu, car la classe implémente equals. De plus, quelques opérations de mappage supplémentaires sont effectuées, notamment pour vérifier si le mappage contient certaines clés, et pour écrire toutes les clés et de toutes les valeurs dans le journal de débogage. Pour terminer, l'exemple crée un ensemble et lui ajoute les mêmes objets. Il vérifie que la taille de l'ensemble est égal à deux, car deux objets seulement sur les trois sont uniques. public class PairNumbers { Integer x,y; public PairNumbers(Integer a, Integer b) { x=a; y=b; } public Boolean equals(Object obj) { if (obj instanceof PairNumbers) { PairNumbers p = (PairNumbers)obj; return ((x==p.x) && (y==p.y)); } return false; } public Integer hashCode() { return (31 * x) ^ y; } } Cet extrait de code utilise la classe PairNumbers. Map<PairNumbers, String> m = new Map<PairNumbers, String>(); PairNumbers p1 = new PairNumbers(1,2); PairNumbers p2 = new PairNumbers(3,4); Constructions du langage // Duplicate key PairNumbers p3 = new PairNumbers(1,2); m.put(p1, 'first'); m.put(p2, 'second'); m.put(p3, 'third'); // Map size is 2 because the entry with // the duplicate key overwrote the first entry. System.assertEquals(2, m.size()); // Use the == operator if (p1 == p3) { System.debug('p1 and p3 are equal.'); } // Perform some other operations System.assertEquals(true, m.containsKey(p1)); System.assertEquals(true, m.containsKey(p2)); System.assertEquals(false, m.containsKey(new PairNumbers(5,6))); for(PairNumbers pn : m.keySet()) { System.debug('Key: ' + pn); } List<String> mValues = m.values(); System.debug('m.values: ' + mValues); // Create a set Set<PairNumbers> s1 = new Set<PairNumbers>(); s1.add(p1); s1.add(p2); s1.add(p3); salesforce | Collections | 52 Constructions du langage salesforce | Enumérations | 53 // Verify that we have only two elements // since the p3 is equal to p1. System.assertEquals(2, s1.size()); Itération de collections Les collections peuvent correspondre à des listes, des ensembles ou des mappages. La modification des éléments d'une collection pendant une itération à travers cette collection n'est pas prise en charge et entraîne une erreur. N'ajoutez pas et ne supprimez pas directement des éléments pendant une itération à travers une collection qui contient ces éléments. Ajout d'éléments pendant une itération Pour ajouter des éléments pendant une itération dans une liste, un ensemble ou un mappage, conservez ces nouveaux éléments dans une liste, un ensemble ou un mappage temporaire, puis ajoutez-les à l'original une fois l'itération de la collection terminée. Suppression d'éléments pendant une itération Pour supprimer des éléments pendant une itération dans une liste, créez une liste, puis copiez les éléments que vous souhaitez conserver. Vous pouvez également ajouter les éléments que vous souhaitez supprimer à une liste temporaire, puis les supprimer une fois l'itération de la collection terminée. Remarque: La méthode List.remove est exécutée de façon linéaire. Utilisez-la pour supprimer les éléments qui ont des implications en termes de temps et de ressources. Pour supprimer des éléments pendant une itération dans un mappage ou un ensemble, conservez les clés que vous souhaitez supprimer dans une liste temporaire, puis supprimez-les une fois l'itération de la collection terminée. Enumérations Une énumération (enum) est un type de données abstrait avec des valeurs dont chacune accepte exactement un ensemble fini d'identifiants que vous spécifiez. Les énumérations sont généralement utilisées pour définir un ensemble de valeurs possibles qui n'ont pas d'autre ordre numérique, par exemple une suite de cartes ou une saison particulière de l'année. Bien que chaque valeur corresponde à une valeur entière distincte, les énumérations masquent cette implémentation afin d'éviter toute utilisation erronée de ces valeurs, par exemple pour effectuer les opérations arithmétiques. Lorsque vous avez créé une énumération, des variables, des arguments de méthode et des renvois de type enum peuvent être déclarés. Remarque: Contrairement à Java, le type enum lui-même n'a pas de syntaxe de constructeur. Pour définir une enum, utilisez le mot clé enum dans votre déclaration et insérez la liste des valeurs possibles entre accolades. Par exemple, le code suivant crée une enum appelée Season : public enum Season {WINTER, SPRING, SUMMER, FALL} En créant l'énumération Season, vous créez également un type de données appelé Season. Vous pouvez l'utiliser comme tout autre type de données. Par exemple : Season e = Season.WINTER; Constructions du langage salesforce | Enumérations | 54 Season m(Integer x, Season e) { If (e == Season.SUMMER) return e; //... } Vous pouvez également définir une classe en tant qu'énumération. Notez que lorsque vous créez une classe enum, vous n'utilisez pas le mot clé class dans la définition. public enum MyEnumClass { X, Y } Vous pouvez utiliser une énumération partout où vous pouvez utiliser un autre nom de type de données. Si vous définissez une variable dont le type est une énumération, tout objet que vous lui attribuez doit être une instance de cette classe enum. Toutes les méthodes webService peuvent utiliser des types enum dans leur signature. Dans ce cas, le fichier WSDL associé inclut les définitions de l'énumération et ses valeurs, qui peuvent ensuite être utilisées par le client API. Le langage Apex fournit les énumérations définies par le système suivantes : • System.StatusCode Cette énumération correspond au code d'erreur de l'API qui est indiqué dans le document WSDL pour toutes les opérations de l'API. Par exemple : StatusCode.CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY StatusCode.INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY La liste complète des codes de statut est disponible dans le fichier WSDL de votre organisation. Pour plus d'informations sur l'accès au fichier WSDL pour votre organisation, reportez-vous à « Téléchargement des certificats d'authentification client et WSDL de Salesforce » dans l'aide en ligne de Salesforce. • System.XmlTag : Cette énumération renvoie une liste de balises XML utilisées pour analyser le résultat XML à partir d'une méthode webService. Pour plus d'informations, reportez-vous à Classe XmlStreamReader à la page 658. • System.LoggingLevel : Cette énumération est utilisée avec la méthode system.debug pour spécifier le niveau de consignation de tous les appels debug. Pour plus d'informations, reportez-vous à Méthodes System à la page 529. • System.RoundingMode : Cette énumération est utilisée par des méthodes qui effectuent des opérations mathématiques pour spécifier le comportement d'arrondi de l'opérations, telles que la méthode Decimal divide et la méthode Double round. Pour plus d'informations, reportez-vous à Mode d'arrondi à la page 371. • System.SoapType : Cette énumération est renvoyée par la méthode getSoapType de résultat de description du champ. Pour plus informations, reportez-vous à Valeurs enum Schema.SOAPType à la page 455. • System.DisplayType : Constructions du langage salesforce | Compréhension des règles de conversion | 55 Cette énumération est renvoyée par la méthode getType de résultat de description du champ. Pour plus informations, reportez-vous à Valeurs enum Schema.DisplayType à la page 450. • System.JSONToken : Cette énumération est utilisée pour l'analyse du contenu JSON. Pour plus d'informations, reportez-vous à Enum System.JSONToken à la page 512. • ApexPages.Severity : Cette énumération spécifie la sévérité d'un message Visualforce. Pour plus d'informations, reportez-vous à Enum ApexPages.Severity à la page 605. • Dom.XmlNodeType : Cette énumération spécifie le type de noeud d'un document DOM. Pour plus d'informations, reportez-vous à Types de noeud à la page 670. Remarque: Les énumérations définies par le système ne peuvent pas être utilisées dans des méthodes de service Web. Toutes les valeurs d'énumération, y compris les énumérations système, ont des méthodes communes associées. Pour plus d'informations, reportez-vous à Méthodes Enum à la page 431. Vous ne pouvez pas ajouter des méthodes définies par les utilisateurs à des valeurs d'énumération. Compréhension des règles de conversion Généralement, le langage Apex nécessite de convertir explicitement un type de données vers un autre. par exemple, une variable du type de données Integer ne peut pas être implicitement converti en String. Vous devez utiliser la méthode string.format. Cependant, quelques types de données peuvent être implicitement convertis, sans utiliser de méthode. Les nombres forment une hiérarchie de types. Les variables de types numériques inférieurs peuvent toujours être attribuées à des types supérieurs sans conversion explicite. La liste suivante est une hiérarchie de nombres, du plus faible au plus élevé : 1. 2. 3. 4. Integer Long Double Decimal Remarque: Lorsqu'une valeur a été transmise d'un nombre de type inférieur à un nombre de type supérieur, la valeur est convertie vers le type supérieur du nombre. Notez que la hiérarchie et la conversion implicite sont différentes de la hiérarchie de nombres Java, langage dans lequel le nombre d'interface de base est utilisé et la conversion d'objet implicite n'est jamais autorisée. Outre les nombres, d'autres types de données peuvent être implicitement convertis. Les règles suivantes s'appliquent : • • • Les ID peuvent toujours être attribués à des chaînes. Les chaînes peuvent être attribuées à des ID. Cependant, lors de l'exécution, la légitimité de l'ID de la valeur est vérifiée. Dans la négative, une exception d'exécution est levée. Le mot-clé instanceOf peut toujours être utilisé afin de tester si une chaîne est un ID. Constructions du langage salesforce | Variables | 56 Considérations supplémentaires sur les types de données Types de données de valeurs numériques Les valeurs numériques représentent des valeurs Integer, sauf si L (pour un type Long) ou .0 (pour un type Double ou Decimal) est ajouté. Par exemple, l'expression Long d = 123; déclare une variable Long nommée d et l'attribue à une valeur numérique Integer (123), qui est implicitement convertie en type Long. La valeur Integer côté droit est comprise dans la plage de nombres entiers et l'attribution réussit. Cependant, si la valeur numérique côté droit dépasse la valeur maximale d'un nombre entier (Integer), vous obtenez une erreur de compilation. Dans ce cas, la solution consiste à ajouter L à la valeur numérique afin qu'elle représente une valeur Long dont la portée est plus large, comme dans l'exemple suivant : Long d = 2147483648L;. Dépassement des valeurs de type de données Les calculs arithmétiques qui produisent des valeurs supérieures à la valeur maximale du type actuel sont considérés comme un dépassement. Par exemple, Integer i = 2147483647 + 1; produit une valeur –2147483648, car 2147483647 est la valeur maximale d'un Integer. Par conséquent, l'ajout de 1 arrondit à la valeur négative minimale pour des nombres entiers, soit –2147483648. Si les calculs arithmétiques génèrent des résultats supérieurs à la valeur maximale du type actuel, le résultat final est incorrect, car les valeurs calculées supérieures au maximum génèrent un dépassement. Par exemple, l'expression Long MillsPerYear = 365 * 24 * 60 * 60 * 1000; génère un résultat incorrect, car le produit des nombres entiers côté droit est supérieur à la valeur Integer maximale, et ils dépassent. Par conséquent, le produit final n'est pas celui attendu. Pour l'éviter, assurez-vous que le type des valeurs numériques ou des variables que vous utilisez dans des opérations arithmétiques est suffisamment grand pour contenir les résultats. Dans cet exemple, ajoutez L aux valeurs numériques pour les rendre Long afin que les produits intermédiaires soient Long et éviter ainsi tout dépassement. L'exemple ci-dessous montre comment calculer correctement le nombre de millisecondes dans une année en multipliant des valeurs numériques Long. Long MillsPerYear = 365L * 24L * 60L * 60L * 1000L; Long ExpectedValue = 31536000000L; System.assertEquals(MillsPerYear, ExpectedValue); Perte de fractions dans des divisions Lors de la division de valeurs numériques Integer ou Long, la partie fractionnaire du résultat, le cas échéant, est supprimée avant toute conversion implicite en Double ou Decimal. Par exemple, Double d = 5/3; renvoie 1.0, car le résultat (1.666...) est un Integer arrondi à 1 avant d'être implicitement converti en Double. Pour conserver la valeur fractionnaire, assurez-vous d'utiliser des valeurs numériques Double ou Decimal dans la division. Par exemple, Double d = 5.0/3.0; renvoie 1.6666666666666667, car 5.0 et 3.0 représentent des valeurs Double, qui génèrent un quotient Double sans perte de valeur fractionnaire. Variables Les variables locales sont déclarées avec une syntaxe de style Java. Par exemple : Integer i = 0; String str; Account a; Account[] accts; Constructions du langage salesforce | Sensibilité à la casse | 57 Set<String> s; Map<ID, Account> m; Comme dans Java, les variables multiples peuvent être déclarées et initialisées dans une instruction unique, qui utilise la virgule comme séparateur. Par exemple : Integer i, j, k; Toutes les variables autorisent null en tant que valeur et sont initialisées sur null si elles ne sont pas attribuées à une autre valeur. Par exemple, dans l'exemple ci-dessous, i et k sont des valeurs attribuées, alors que j est définie sur null, car elle n'est pas attribuée : Integer i = 0, j, k = 1; Les variables peuvent être définies à partir de n'importe quel point d'un bloc, et appliquent la portée à partir de ce point. Les sous-blocs ne peuvent pas être redéfinis en nom de variable qui a déjà été utilisé dans un bloc parent, mais les blocs parallèles peuvent réutiliser un nom de variable. Par exemple : Integer i; { // Integer i; This declaration is not allowed } for (Integer j = 0; j < 10; j++); for (Integer j = 0; j < 10; j++); Sensibilité à la casse Pour éviter toute confusion avec les requêtes SOQL et SOSL insensibles à la casse, le code Apex est également insensible à la casse. Cela signifie : • Les noms de variables et de méthodes ne sont pas sensibles à la casse. Par exemple : Integer I; //Integer i; • This would be an error. Les références à des noms d'objet et de champ ne sont pas sensibles à la casse. Par exemple : Account a1; ACCOUNT a2; • Les instructions SOQL et SOSL ne sont pas sensibles à la casse. Par exemple : Account[] accts = [sELect ID From ACCouNT where nAme = 'fred']; Constructions du langage salesforce | Constantes | 58 Notez également que le langage Apex utilise les mêmes sémantiques de filtrage que SOQL, qui représente la base des comparaisons dans l'API SOAP et dans l'interface utilisateur de Salesforce. L'utilisation de ces sémantiques peut générer des comportements intéressants. Par exemple, si un utilisateur final génère un rapport basé sur un filtre pour des valeurs précédant 'm' dans l'alphabet (c.-à-d. des valeurs < 'm'), des champs nuls sont renvoyés dans les résultats. Les utilisateurs considèrent généralement un champ sans valeur comme un caractère « espace », plutôt qu'une valeur « nulle », ce qui justifie ce comportement. Par conséquent, dans le langage Apex, les expressions suivantes sont toutes évaluées sur true : String s; System.assert('a' == 'A'); System.assert(s < 'b'); System.assert(!(s > 'b')); Remarque: Bien que s < 'b' évalue à true dans l'exemple ci-dessus, 'b.'compareTo(s) génère une erreur, car vous essayez de comparer une lettre à une valeur nulle. Constantes Les constantes peuvent être définies en utilisant le mot clé final, ce qui signifie que la variable peut être attribuée au maximum une fois, dans la déclaration elle-même ou dans une méthode d'initialisation statique si la constante est définie dans une classe. Par exemple : public class myCls { static final Integer PRIVATE_INT_CONST; static final Integer PRIVATE_INT_CONST2 = 200; public static Integer calculate() { return 2 + 7; } static { PRIVATE_INT_CONST = calculate(); } } Pour plus d'informations, reportez-vous à Utilisation du mot clé final à la page 152. Constructions du langage salesforce | Expressions | 59 Expressions Une expression est une construction formée d'invocations de variables, d'opérateurs et de méthodes, qui évalue une valeur unique. Cette section présente les expressions du langage Apex et inclut les rubriques suivantes : • • • • • Compréhension des expressions Compréhension des opérateurs d'expression Compréhension des précédences d'opérateur Extension d'expressions sObject et List Utilisation de commentaires Compréhension des expressions Une expression est une construction formée d'invocations de variables, d'opérateurs et de méthodes, qui évalue une valeur unique. Dans le langage Apex, une expression correspond toujours à l'un des types suivants : • Une expression littérale. Par exemple : 1 + 1 • Un sObject, objet Apex, liste, ensemble ou mappage nouveau. Par exemple : new Account(<field_initializers>) new Integer[<n>] new Account[]{<elements>} new List<Account>() new Set<String>{} new Map<String, Integer>() new myRenamingClass(string oldName, string newName) • Toute valeur qui peut agir à gauche d'un opérateur d'attribution (valeurs L), y compris des variables, des positions de listes unidimensionnelles et la plupart des références de champ sObject ou d'objet Apex. Par exemple : Integer i myList[3] myContact.name myRenamingClass.oldName • Toute référence de champ sObject qui n'est pas une valeur L, y compris : ◊ L'ID d'un sObject dans une liste (reportez-vous à Listes) ◊ Un ensemble d'enregistrements enfants associé à un sObject (par exemple, l'ensemble de contacts associé à un compte particulier). Ce type d'expression génère un résultat de requête, comme les requêtes SOQL et SOSL. Constructions du langage salesforce | Compréhension des opérateurs d'expression | 60 Une requête SOQL ou SOSL entre crochets droits, qui permet une évaluation instantanée dans Apex. Par exemple : • Account[] aa = [SELECT Id, Name FROM Account WHERE Name ='Acme']; Integer i = [SELECT COUNT() FROM Contact WHERE LastName ='Weissman']; List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead]; Pour plus informations, reportez-vous à Requêtes SOQL et SOSL à la page 77. Une invocation de méthode statique ou d'instance. Par exemple : • System.assert(true) myRenamingClass.replaceNames() changePoint(new Point(x, y)); Compréhension des opérateurs d'expression Les expressions peuvent être liées entre elles avec des opérateurs pour créer des expressions composées. Le langage Apex prend en charge les opérateurs suivants : Opérateur Syntaxe Description = x = y Opérateur d'affectation (associatif à droite). Attribue la valeur de y à la valeur L de x. Notez que le type de données x doit correspondre au type de données y, et ne peut pas être null. += x += y Opérateur d'affectation d'addition (associatif à droite). Ajoute la valeur de y à la valeur d'origine de x, puis réattribue la nouvelle valeur à x. Pour plus d'informations, reportez-vous à +. x et y ne peuvent pas être null. *= x *= y Opérateur d'affectation de multiplication (associatif à droite). Multiplie la valeur de y par la valeur d'origine de x, puis réattribue la nouvelle valeur à x. Notez que x et y doivent être des Integer ou des Double, ou une combinaison des deux. x et y ne peuvent pas être null. -= x -= y Opérateur d'affectation de soustraction (associatif à droite). Soustrait la valeur de y de la valeur d'origine de x, puis réattribue la nouvelle valeur à x. Notez que x et y doivent être des Integer ou des Double, ou une combinaison des deux. x et y ne peuvent pas être null. /= x /= y Opérateur d'affectation de division (associatif à droite). Divise la valeur d'origine de x par la valeur de y, puis réattribue la nouvelle valeur à x. Notez que x et y doivent être des Integer ou des Double, ou une combinaison des deux. x et y ne peuvent pas être null. Constructions du langage salesforce | Compréhension des opérateurs d'expression | 61 Opérateur Syntaxe Description |= x |= y Opérateur d'affectation OR (associatif à droite). Si x, booléen, et y, booléen, sont tous les deux false, x reste false. Sinon, la valeur true est attribuée à x. Remarque : • • &= x &= y Cet opérateur exhibe un comportement « court-circuit », ce qui signifie que y est évalué uniquement si x est false. x et y ne peuvent pas être null. Opérateur d'affectation AND (associatif à droite). Si x, booléen, et y, booléen, sont tous les deux true, x reste true. Sinon, la valeur false est attribuée à x. Remarque : • • Cet opérateur exhibe un comportement « court-circuit », ce qui signifie que y est évalué uniquement si x est true. x et y ne peuvent pas être null. <<= x <<= y Opérateur d'affectation décalage gauche au niveau du bit. Décale chaque bit dans x vers la gauche de y bits, de sorte que les bits de poids fort sont perdus, et les nouveaux bits droits sont définis sur 0. Cette valeur est ensuite réattribuée à x. >>= x >>= y Opérateur d'affectation décalage droit au niveau du bit. Décale chaque bit dans x vers la droite de y bits, de sorte que les bits de poids faible sont perdus, et les nouveaux bits gauches sont définis sur 0 pour des valeurs positives de y et sur 1 pour des valeurs négatives de y. Cette valeur est ensuite réattribuée à x. >>>= x >>>= y Opérateur d'affectation décalage droit non signé au niveau du bit. Décale chaque bit dans x vers la droite de y bits, de sorte que les bits de poids faible sont perdus, et les nouveaux bits gauches sont définis sur 0 pour toutes les valeurs de y. Cette valeur est ensuite réattribuée à x. ? : x ? y : z Opérateur ternaire (associatif à droite). Cet opérateur agit en tant que paramètre abrégé pour les instructions if-then-else. Si x, booléen, est true, y est le résultat. Sinon, z est le résultat. Notez que x ne peut pas être null. && x && y Opérateur logique AND (associatif à gauche). Si x, booléen, et y, booléen, sont tous les deux true, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • && est prioritaire sur || Cet opérateur exhibe un comportement « court-circuit », ce qui signifie que y est évalué uniquement si x est true. x et y ne peuvent pas être null. Constructions du langage salesforce | Compréhension des opérateurs d'expression | 62 Opérateur Syntaxe Description || x || y Opérateur logique OR (associatif à gauche). Si x, booléen, et y, booléen, sont tous les deux false, l'expression évalue à false. Sinon, l'expression évalue à true. Remarque : • • • == x == y && est prioritaire sur || Cet opérateur exhibe un comportement « court-circuit », ce qui signifie que y est évalué uniquement si x est false. x et y ne peuvent pas être null. Opérateur d'égalité. Si la valeur de x est égale à la valeur de y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • Contrairement au langage Java, == dans Apex compare l'égalité de la valeur de l'objet, pas l'égalité de la référence. Par conséquent : ◊ La comparaison de chaînes en utilisant == n'est pas sensible à la casse ◊ La comparaison d'ID en utilisant == est sensible à la casse, et ne fait pas la distinction entre les formats à 15 caractères et à 18 caractères • • • • • === x === y Pour les sObject et les tableaux sObject, == effectue une vérification approfondie de toutes les valeurs de champ sObject avant de renvoyer son résultat. De la même façon pour les collections, les types Apex intégrés et les types définis par l'utilisateur. Pour des enregistrements, chaque champ doit avoir la même valeur pour == pour évaluer sur true. x ou y peuvent être le null littéral. La comparaison de deux valeurs quelconques ne peut jamais donner null. SOQL et SOSL utilisent = pour leur opérateur d'égalité, pas ==. Bien que le langage Apex et SOQL et SOSL soient étroitement liés, cette différence de syntaxe regrettable existe, car la plupart des langages modernes utilisent = pour l'affectation et == pour l'égalité. Les concepteurs du langage Apex ont considéré qu'il était préférable de conserver ce modèle que de forcer les développeurs à apprendre un nouvel opérateur d'affectation. Par conséquent, les développeurs Apex doivent utiliser == pour des tests d'égalité dans le corps principal du code Apex, et = pour l'égalité dans les requêtes SOQL et SOSL. Opérateur d'égalité identique. Si x et y référencent un emplacement identique dans la mémoire, l'expression évalue à true. Sinon, l'expression évalue à false. Notez que cet opérateur fonctionne uniquement pour les sObjects ou les collections (telles que mappage ou liste). Pour un objet Apex (tel qu'une Exception ou l'instanciation d'une classe), l'opérateur d'égalité identique est le même que l'opérateur d'égalité. Constructions du langage salesforce | Compréhension des opérateurs d'expression | 63 Opérateur Syntaxe Description < x < y Opérateur inférieur à. Si x est inférieur à y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • • • • • > x > y Contrairement à d'autres procédures stockées dans la base de données, Apex ne prend pas en charge la logique booléenne à trois états, et la comparaison de deux valeurs quelconques ne peut jamais donner null. Si x ou y est égal à null et est un Integer, Double, Date ou Datetime, l'expression est false. Une valeur String ou ID non null est toujours supérieure à une valeur null. Si x et y sont des ID, ils doivent référencer le même type d'objet. Sinon, une erreur d'exécution est générée. Si x ou y est un ID et que l'autre valeur est une String, la valeur String est validée et traitée en tant qu'ID. x et y ne peuvent pas être booléens. La comparaison de deux chaînes est effectuée conformément aux paramètres régionaux de l'utilisateur contextuel. Opérateur supérieur à. Si x est supérieur à y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • • • • • <= x <= y La comparaison de deux valeurs quelconques ne peut jamais donner null. Si x ou y est égal à null et est un Integer, Double, Date ou Datetime, l'expression est false. Une valeur String ou ID non null est toujours supérieure à une valeur null. Si x et y sont des ID, ils doivent référencer le même type d'objet. Sinon, une erreur d'exécution est générée. Si x ou y est un ID et que l'autre valeur est une String, la valeur String est validée et traitée en tant qu'ID. x et y ne peuvent pas être booléens. La comparaison de deux chaînes est effectuée conformément aux paramètres régionaux de l'utilisateur contextuel. Opérateur inférieur ou égal à. Si x est inférieur ou égal à y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • La comparaison de deux valeurs quelconques ne peut jamais donner null. Si x ou y est égal à null et est un Integer, Double, Date ou Datetime, l'expression est false. Une valeur String ou ID non null est toujours supérieure à une valeur null. Constructions du langage Opérateur Syntaxe salesforce | Compréhension des opérateurs d'expression | 64 Description • • • • >= x >= y Si x et y sont des ID, ils doivent référencer le même type d'objet. Sinon, une erreur d'exécution est générée. Si x ou y est un ID et que l'autre valeur est une String, la valeur String est validée et traitée en tant qu'ID. x et y ne peuvent pas être booléens. La comparaison de deux chaînes est effectuée conformément aux paramètres régionaux de l'utilisateur contextuel. Opérateur supérieur ou égal à. Si x est supérieur ou égal à y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • • • • • != x != y La comparaison de deux valeurs quelconques ne peut jamais donner null. Si x ou y est égal à null et est un Integer, Double, Date ou Datetime, l'expression est false. Une valeur String ou ID non null est toujours supérieure à une valeur null. Si x et y sont des ID, ils doivent référencer le même type d'objet. Sinon, une erreur d'exécution est générée. Si x ou y est un ID et que l'autre valeur est une String, la valeur String est validée et traitée en tant qu'ID. x et y ne peuvent pas être booléens. La comparaison de deux chaînes est effectuée conformément aux paramètres régionaux de l'utilisateur contextuel. Opérateur d'inégalité. Si la valeur de x est différente de la valeur de y, l'expression évalue à true. Sinon, l'expression évalue à false. Remarque : • • • • • !== x !== y Contrairement au langage Java, != dans Apex compare l'égalité de la valeur de l'objet, pas l'égalité de la référence. Pour les sObject et les tableaux sObject, != effectue une vérification approfondie de toutes les valeurs de champ sObject avant de renvoyer son résultat. Pour des enregistrements, != évalue à true si les enregistrements ont des valeurs différentes pour un champ quelconque. x ou y peuvent être le null littéral. La comparaison de deux valeurs quelconques ne peut jamais donner null. Opérateur d'inégalité identique. Si x et y ne référencent pas un emplacement identique dans la mémoire, l'expression évalue à true. Sinon, l'expression évalue à false. Notez que cet opérateur fonctionne uniquement pour des sObjects, des collections (telles que le mappage ou liste) ou un objet Apex (tel qu'une Exception ou l'instanciation d'une classe). Constructions du langage salesforce | Compréhension des opérateurs d'expression | 65 Opérateur Syntaxe Description + x + y Opérateur d'addition. Ajoute la valeur de x à la valeur de y en fonction des règles suivantes : • Si x et y sont des Integer ou des Double, il ajoute la valeur de x à la valeur de y. Si un Double est utilisé, le résultat est un Double. • Si x est une Date et y un Integer, il renvoie une nouvelle Date qui est incrémentée par le nombre de jours spécifié. • Si x est une Datetime et y est un Integer ou un Double, il renvoie une nouvelle Date qui est incrémentée par le nombre de jours spécifié, avec la partie fractionnaire correspondant à une partie du jour. • Si x est une String et y est une String et n'importe quel autre type d'argument non null, il concatène y à la fin de x. - x - y Opérateur de soustraction. Soustrait la valeur de y de la valeur de x en fonction des règles suivantes : • Si x et y sont des Integer ou des Double, il soustrait la valeur de x de la valeur de y. Si un Double est utilisé, le résultat est un Double. • Si x est une Date et y un Integer, il renvoie une nouvelle Date qui est décrémentée par le nombre de jours spécifié. • Si x est une Datetime et y est un Integer ou un Double, il renvoie une nouvelle Date qui est décrémentée par le nombre de jours spécifié, avec la partie fractionnaire correspondant à une partie du jour. * x * y Opérateur de multiplication. Multiplie x, un Integer ou un Double, avec y, un autre Integer ou Double. Notez que si un double est utilisé, le résultat est un Double. / x / y Opérateur de division. Divise x, un Integer ou un Double, par y, un autre Integer ou Double. Notez que si un double est utilisé, le résultat est un Double. ! !x Opérateur de complément logique. Il inverse la valeur d'un booléen, de sorte que true devient false, et false devient true. - -x Opérateur de négation unaire. Multiplie la valeur de x, un Integer ou un Double, par -1. Notez que l'équivalent positif + est également valide en termes de syntaxe, mais n'a aucun effet mathématique. ++ x++ Opérateur d'incrément. Ajoute 1 à la valeur de x, une variable de type numérique. Si elle a un préfixe (++x), l'expression évalue à la valeur de x après l'incrément. Si elle a un suffixe (x++), l'expression évalue à la valeur de x avant l'incrément. ++x -- x---x & x & y Opérateur de décrément. Soustrait 1 de la valeur de x, une variable de type numérique. Si elle a un préfixe (--x), l'expression évalue à la valeur de x après le décrément. Si elle a un suffixe (x--), l'expression évalue à la valeur de x avant le décrément. Opérateur AND au niveau du bit. Associe avec des AND chaque bit dans x au bit correspondant dans y de sorte que le bit résultat est défini sur 1 si les Constructions du langage Opérateur salesforce | Compréhension des précédences d'opérateur | 66 Syntaxe Description deux bits sont définis sur 1. Cet opérateur n'est pas valide pour les types Long ou Integer. | x | y Opérateur OR au niveau du bit. Associe avec des OR chaque bit dans x au bit correspondant dans y de sorte que le bit résultat est défini sur 1 si un bit au moins est défini sur 1. Cet opérateur n'est pas valide pour les types Long ou Integer. ^ x ^ y Opérateur OR exclusif au niveau du bit. Associe avec des OR exclusifs chaque bit dans x au bit correspondant dans y de sorte que le bit résultat est défini sur 1 si exactement un bit est défini sur 1 et l'autre bit est défini sur 0. ^= x ^= y Opérateur OR exclusif au niveau du bit. Associe avec des OR exclusifs chaque bit dans x au bit correspondant dans y de sorte que le bit résultat est défini sur 1 si exactement un bit est défini sur 1 et l'autre bit est défini sur 0. << x << y Opérateur de décalage gauche au niveau du bit. Décale chaque bit dans x vers la gauche de y bits, de sorte que les bits de poids fort sont perdus, et les nouveaux bits droits sont définis sur 0. >> x >> y Opérateur de décalage droit signé au niveau du bit. Décale chaque bit dans x vers la droite de y bits, de sorte que les bits de poids faible sont perdus, et les nouveaux bits gauches sont définis sur 0 pour des valeurs positives de y et sur 1 pour des valeurs négatives de y. >>> x >>> y Opérateur de décalage droit non signé au niveau du bit. Décale chaque bit dans x vers la droite de y bits, de sorte que les bits de poids faible sont perdus, et les nouveaux bits gauches sont définis sur 0 pour toutes les valeurs de y. () (x) Parenthèses. Élève la priorité d'une expression x de sorte qu'elle est évaluée en premier dans une expression composée. Compréhension des précédences d'opérateur Le langage Apex utilisez les règles précédences d'opérateur suivantes : Précédence Opérateurs Description 1 {} () ++ -- Regroupement, et incréments et décréments de préfixe 2 ! -x +x (type) new Négation unaire, conversion du type et création d'objet 3 * / Multiplication et division 4 + - Addition et soustraction 5 < <= > >= instanceof Comparaisons supérieur à et inférieur à, tests de référence Constructions du langage salesforce | Extension d'expressions sObject et List | 67 Précédence Opérateurs Description 6 == != Comparaisons : égal à et différent de 7 && AND logique 8 || OR logique 9 = += -= *= /= &= Opérateurs d'affectation Extension d'expressions sObject et List Comme dans le langage Java, les expressions sObject et List peuvent être étendues avec respectivement des références de méthode et des expressions de liste pour former de nouvelles expressions. Dans l'exemple suivant, une nouvelle variable contenant la longueur du nom du nouveau compte est attribuée à acctNameLength. Integer acctNameLength = new Account[]{new Account(Name='Acme')}[0].Name.length(); Ci-dessus, new Account[] génère une liste. La liste est renseignée par l'instruction SOQL {new Account(name='Acme')}. L'élément 0, le premier de la liste, est ensuite accédé par la partie suivante de la chaîne [0]. Le nom du sObject dans la liste est accédé, suivi par la méthode renvoyant la longueur name.length(). Dans l'exemple suivant, un nom changé en lettres minuscules est renvoyé. String nameChange = [SELECT Name FROM Account][0].Name.toLowerCase(); Utilisation de commentaires Les commentaires à une et à plusieurs lignes sont prises en charge dans le code Apex : • Pour créer un commentaire sur une ligne, utilisez //. Tous les caractères sur la même ligne à droite de // sont ignorés par l'analyseur. Par exemple : Integer i = 1; // This comment is ignored by the parser • Pour créer un commentaire sur plusieurs lignes, utilisez /* et */ pour marquer le début et la fin du bloc de commentaires. Par exemple : Integer i = 1; /* This comment can wrap over multiple lines without getting interpreted by the parser. */ Constructions du langage salesforce | Instructions d'attribution | 68 Instructions d'attribution Une instruction d'attribution place une valeur dans une variable, généralement sous l'un des deux formats suivants : [LValue] = [new_value_expression]; [LValue] = [[inline_soql_query]]; Dans les formats ci-dessus, [LValue] représente une expression qui peut être placée à gauche d'un opérateur d'affectation. Elles comprennent : • Une simple variable. Par exemple : Integer i = 1; Account a = new Account(); Account[] accts = [SELECT Id FROM Account]; • Un élément de liste déréférencé : Par exemple : ints[0] = 1; accts[0].Name = 'Acme'; • Une référence de champ sObject que l'utilisateur contextuel est autorisé à modifier. Par exemple : Account a = new Account(Name = 'Acme', BillingCity = 'San Francisco'); // IDs cannot be set prior to an insert call // a.Id = '00300000003T2PGAA0'; // Instead, insert the record. The system automatically assigns it an ID. insert a; // Fields also must be writeable for the context user // a.CreatedDate = System.today(); This code is invalid because // createdDate is read-only! // Since the account a has been inserted, it is now possible to // create a new contact that is related to it Contact c = new Contact(LastName = 'Roth', Account = a); Constructions du langage salesforce | Instructions conditionnelles (If-Else) | 69 // Notice that you can write to the account name directly through the contact c.Account.Name = 'salesforce.com'; L'attribution est toujours effectuée par référence. Par exemple : Account a = new Account(); Account b; Account[] c = new Account[]{}; a.Name = 'Acme'; b = a; c.add(a); // These asserts should now be true. You can reference the data // originally allocated to account a through account b and account list c. System.assertEquals(b.Name, 'Acme'); System.assertEquals(c[0].Name, 'Acme'); De la même façon, deux listes peuvent pointer vers la même valeur dans la mémoire. Par exemple : Account[] a = new Account[]{new Account()}; Account[] b = a; a[0].Name = 'Acme'; System.assert(b[0].Name == 'Acme'); En plus de =, les autres opérateurs d'affectation incluent +=, *=, /=, |=, &=, ++ et --. Reportez-vous à Compréhension des opérateurs d'expression à la page 60. Instructions conditionnelles (If-Else) L'instruction conditionnelle dans Apex fonctionne de façon similaire à Java : if ([Boolean_condition]) // Statement 1 else // Statement 2 La partie else est toujours facultative, et toujours groupée avec le if le plus proche. Par exemple : Integer x, sign; Constructions du langage // Your code if (x <= 0) if (x == 0) sign = 0; else sign = -1; est équivalent à : Integer x, sign; // Your code if (x <= 0) { if (x == 0) { sign = 0; } else { sign = -1; } } Les instructions else if répétées sont également autorisées. Par exemple : if (place == 1) { medal_color = 'gold'; } else if (place == 2) { medal_color = 'silver'; } else if (place == 3) { medal_color = 'bronze'; } else { medal_color = null; } Boucles Le langage Apex prend en charge les types de boucle procédurale suivants : • • • • • do {statement} while (Boolean_condition); while (Boolean_condition) statement; for (initialization; Boolean_exit_condition; increment) statement; for (variable : array_or_set) statement; for (variable : [inline_soql_query]) statement; Toutes les boucles autorisent les structures de contrôle de boucles : salesforce | Boucles | 70 Constructions du langage salesforce | Boucles Do-While | 71 break; existe dans la boucle entière • • continue; passe à l'itération suivante de la boucle Boucles Do-While La boucle do-while du langage Apex exécute à répétition un bloc de code tant qu'une condition booléenne particulière reste vraie. Sa syntaxe est la suivante : do { code_block } while (condition); Remarque: Des Accolades ({}) sont toujours requises autour d'un code_block. Comme dans Java, la boucle Apex do-while vérifie l'instruction de condition booléenne après l'exécution de la première boucle. Par conséquent, le bloc de code est toujours exécuté au moins une fois. Par exemple, le code suivant sort les chiffres de 1 à 10 dans le journal de débogage : Integer count = 1; do { System.debug(count); count++; } while (count < 11); Boucles While La boucle while du langage Apex exécute à répétition un bloc de code tant qu'une condition booléenne particulière reste vraie. Sa syntaxe est la suivante : while (condition) { code_block } Remarque: Des accolades ({}) sont requises autour d'un code_block uniquement si le bloc contient plusieurs instructions. Contrairement à la boucle do-while, la boucle while vérifie l'instruction de condition booléenne avant l'exécution de la première boucle. Par conséquent, il est possible que le bloc de code ne soit jamais exécuté. Constructions du langage salesforce | Boucles For | 72 Par exemple, le code suivant sort les chiffres de 1 à 10 dans le journal de débogage : Integer count = 1; while (count < 11) { System.debug(count); count++; } Boucles For Le langage Apex prend en charge trois variantes de la boucle for : • La boucle traditionnelle for : for (init_stmt; exit_condition; increment_stmt) { code_block } • La boucle for d'itération de liste ou d'ensemble : for (variable : list_or_set) { code_block } où variable doit avoir un type primitif ou sObject identique à celui de list_or_set. • La boucle for SOQL : for (variable : [soql_query]) { code_block } ou for (variable_list : [soql_query]) { code_block } où variable et variable_list doivent avoir un type sObject identique à celui renvoyé par la requête soql_query. Remarque: Des accolades ({}) sont requises autour d'un code_block uniquement si le bloc contient plusieurs instructions. Constructions du langage salesforce | Boucles For | 73 Chaque type est présenté dans les sections suivantes. Boucles For traditionnelles La boucle traditionnelle for dans Apex correspond à la syntaxe traditionnelle utilisée dans Java et d'autres langages. Sa syntaxe est la suivante : for (init_stmt; exit_condition; increment_stmt) { code_block } Lors de l'exécution de ce type de boucle for, le moteur d'exécution Apex exécute les étapes suivantes, dans l'ordre : 1. Exécution du composant init_stmt de la boucle. Notez que plusieurs variantes peuvent être déclarées et/ou initialisées dans cette instruction. 2. Exécution de la vérification exit_condition. Si true, la boucle continue. Si false, la boucle s'arrête. 3. Exécution du code_block. 4. Exécution de l'instruction increment_stmt. 5. Retour à l'étape 2. Par exemple, le code suivant sort les chiffres de 1 à 10 dans le journal de débogage. Notez qu'une variable d'initialisation supplémentaire, j, est incluse pour démontrer la syntaxe : for (Integer i = 0, j = 0; i < 10; i++) { System.debug(i+1); } Boucles For d'itération de List ou de Set La boucle for d'itération de list ou de set est répétée sur tous les éléments d'une liste ou d'un ensemble. Sa syntaxe est la suivante : for (variable : list_or_set) { code_block } où variable doit avoir un type primitif ou sObject identique à celui de list_or_set. Lors de l'exécution de ce type de boucle for, le moteur d'exécution Apex attribue une variable à chaque élément dans list_or_set, puis exécute le code_block pour chaque valeur. Par exemple, le code suivant sort les chiffres de 1 à 10 dans le journal de débogage : Integer[] myInts = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for (Integer i : myInts) { Constructions du langage salesforce | Boucles For | 74 System.debug(i); } Boucles For SOQL Les boucles for SOQL sont répétées sur tous les enregistrements sObject renvoyés par une requête SOQL. La syntaxe d'une boucle for SOQL est : for (variable : [soql_query]) { code_block } ou for (variable_list : [soql_query]) { code_block } où variable et variable_list doivent être du même type que les sObjects renvoyés par la requête soql_query. Comme dans les requêtes SOQL standard, l'instruction [soql_query] peut référencer des expressions de code dans leur clause WHERE en utilisant la syntaxe :. Par exemple : String s = 'Acme'; for (Account a : [SELECT Id, Name from Account where Name LIKE :(s+'%')]) { // Your code } L'exemple suivant combine la création d'une liste à partir d'une requête SOQL avec la méthode DML update. // Create a list of account records from a SOQL query List<Account> accs = [SELECT Id, Name FROM Account WHERE Name = 'Siebel']; // Loop through the list and update the Name field for(Account a : accs){ a.Name = 'Oracle'; } // Update the database update accs; Constructions du langage salesforce | Boucles For | 75 Boucles For SOQL et requêtes SOQL standard Les boucles for SOQL diffèrent des instructions SOQL standard par la méthode qu'elles utilisent pour récupérer les sObjects. Alors que les requêtes standard présentées dans Requêtes SOQL et SOSL peuvent récupérer le count d'une requête ou un nombre d'enregistrements d'objets, les boucles for SOQL récupèrent tous les sObjects, en utilisant une segmentation efficace avec des appels aux méthodes query et queryMore de l'API SOAP. Les développeurs doivent toujours utiliser une boucle for SOQL pour traiter les résultats de requête qui renvoient de nombreux enregistrements, afin d'éviter les limites en taille de segment mémoire. Notez que les requêtes qui incluent une fonction aggregate ne prennent pas en charge queryMore. Une exception d'exécution est levée si vous utilisez une requête contenant une fonction aggregate qui renvoie plus de 2000 lignes dans une boucle for. Format des boucles For SOQL Les boucles for SOQL peuvent traiter des enregistrements individuels en utilisant une variable sObject unique, ou par lots de 200 sObjects à la fois en utilisant une liste sObject : Le format sObject unique exécute le <code_block> de la boucle for une fois par enregistrement sObject. Il est par conséquent facile à comprendre et à utiliser, mais très inefficace si vous souhaitez utiliser des instructions DML (langage de manipulation de données) dans le corps de la boucle for. Chaque instruction DML traite finalement un seul sObject à la fois. Le format de liste sObject exécute le <code_block> de la boucle for une fois par liste de 200 sObjects. Par conséquent, il est plus difficile à comprendre et à utiliser, mais représente le format idéal pour utiliser des instructions DML dans le corps de la boucle for. Chaque instruction DML peut traiter en masse une liste de sObjects à la fois. • • Par exemple, le code suivant illustre la différence entre les deux types de boucle for de requête SOQL : // Create a savepoint because the data should not be committed to the database Savepoint sp = Database.setSavepoint(); insert new Account[]{new Account(Name = 'yyy'), new Account(Name = 'yyy'), new Account(Name = 'yyy')}; // The single sObject format executes the for loop once per returned record Integer i = 0; for (Account tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) { i++; } System.assert(i == 3); // Since there were three accounts named 'yyy' in the // database, the loop executed three times // The sObject list format executes the for loop once per returned batch // of records Constructions du langage salesforce | Boucles For | 76 i = 0; Integer j; for (Account[] tmp : [SELECT Id FROM Account WHERE Name = 'yyy']) { j = tmp.size(); i++; } System.assert(j == 3); // The list should have contained the three accounts // named 'yyy' System.assert(i == 1); // Since a single batch can hold up to 100 records and, // only three records should have been returned, the // loop should have executed only once // Revert the database to the original state Database.rollback(sp); Remarque: • • • Les mots clés break et continue peuvent être utilisés dans les deux types de format de boucle for des requêtes en ligne. Lors de l'utilisation du format de liste sObject, continue passe à la liste suivante de sObjects. Les instructions DML peuvent traiter uniquement 10 000 enregistrements à la fois, et les boucles for de liste sObject traitent les enregistrements par lots de 200. Par conséquent, si vous insérez, mettez à jour ou supprimez plusieurs enregistrements dans les enregistrements renvoyés dans une boucle for de liste sObject, vous pouvez rencontrer des erreurs de limite lors de l'exécution. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Vous pouvez obtenir une exception QueryException dans une boucle for SOQL avec le message Aggregate query has too many rows for direct assignment, use FOR loop. Cette exception est parfois générée en accédant à un vaste ensemble d'enregistrements enfants d'un sObject récupéré dans la boucle, ou en obtenant la taille d'un tel ensemble d'enregistrements. Pour éviter cette exception, utilisez une boucle for pour itérer sur les enregistrements enfants, comme suit : Integer count=0; for (Contact c : returnedAccount.Contacts) { count++; // Do some other processing } Constructions du langage salesforce | Requêtes SOQL et SOSL | 77 Requêtes SOQL et SOSL Vous pouvez évaluer sur le champ les instructions SOQL (Object Query Language) de Salesforce ou SOSL (Object Search Language) de Salesforce dans le langage Apex en les plaçant entre crochets droits. Instructions SOQL Les instructions SOQL évaluent une liste de sObjects, un sObject unique ou un Integer pour les requêtes de méthode count. Par exemple, vous pouvez récupérer une liste de comptes nommés Acme : List<Account> aa = [SELECT Id, Name FROM Account WHERE Name = 'Acme']; Dans cette liste, vous pouvez accéder à des éléments individuels : if (!aa.isEmpty()) { // Execute commands } Vous pouvez également créer des objets à partir des requêtes SOQL dans les objets existants. L'exemple suivant crée un contact à partir du premier compte avec un nombre d'employés supérieur à 10 : Contact c = new Contact(Account = [SELECT Name FROM Account WHERE NumberOfEmployees > 10 LIMIT 1]); c.FirstName = 'James'; c.LastName = 'Yoyce'; Notez que l'objet créé contient des valeurs nulles pour ses champs, qui doivent être définies. La méthode count peut être utilisée pour envoyer le nombre de lignes retournées par une requête. L'exemple suivant renvoie le nombre total de contacts qui incluent le nom Weissman : Integer i = [SELECT COUNT() FROM Contact WHERE LastName = 'Weissman']; Vous pouvez également effectuer des opérations sur les résultats à l'aide d'arithmétique standard : Integer j = 5 * [SELECT COUNT() FROM Account]; Pour une description complète de la syntaxe de la requête SOQL, reportez-vous au guide Salesforce SOQL and SOSL Reference Guide. Instructions SOSL Les instructions SOSL évaluent à une liste de listes de sObjects, dans laquelle chaque liste contient les résultats de la recherche d'un type sObject particulier. Les listes de résultats sont toujours renvoyées dans le même ordre, telles que spécifiées dans la requête SOSL. Si une requête SOSL ne renvoie aucun enregistrement pour un type sObject spécifié, les résultats de recherche incluent une liste vide pour cet sObject. Constructions du langage salesforce | Utilisation des résultats de requêtes SOQL et SOSL | 78 Par exemple, vous pouvez renvoyer une liste de comptes, de contacts, d'opportunités et de pistes qui commencent par le mappage de phrases : List<List<SObject>> searchList = [FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead]; Remarque: La syntaxe de la clause FIND dans Apex diffère de la syntaxe de la clause FIND dans l'API SOAP : • Dans Apex, la valeur de la clause FIND est comprise entre apostrophes. Par exemple : FIND 'map*' IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead • Dans l'API Force.com, la valeur de la clause FIND est comprise entre accolades. Par exemple : FIND {map*} IN ALL FIELDS RETURNING Account (Id, Name), Contact, Opportunity, Lead Dans searchList, vous pouvez créer des tableaux pour chaque objet renvoyé : Account [] accounts = ((List<Account>)searchList[0]); Contact [] contacts = ((List<Contact>)searchList[1]); Opportunity [] opportunities = ((List<Opportunity>)searchList[2]); Lead [] leads = ((List<Lead>)searchList[3]); Pour une description complète de la syntaxe de la requête SOSL, reportez-vous au guide Salesforce SOQL and SOSL Reference Guide. Utilisation des résultats de requêtes SOQL et SOSL Les requêtes SOQL et SOSL renvoi des données uniquement pour les champs sObject sélectionnés dans la requête d'origine. Si vous essayez d'accéder à un champ qui n'a pas été sélectionné dans la requête SOQL ou SOSL (autre qu'un ID), vous recevez une erreur d'exécution, même si le champ contient une valeur dans la base de données. L'exemple de code suivant génère une erreur d'exécution : insert new Account(Name = 'Singha'); Account acc = [SELECT Id FROM Account WHERE Name = 'Singha' LIMIT 1]; // Note that name is not selected String name = [SELECT Id FROM Account WHERE Name = 'Singha' LIMIT 1].Name; L'exemple suivant est le même code réécrit afin de ne pas produire l'erreur d'exécution. Notez que Name a été ajouté à l'instruction select, après Id. insert new Account(Name = 'Singha'); Account acc = [SELECT Id FROM Account WHERE Name = 'Singha' LIMIT 1]; // Note that name is now selected Constructions du langage salesforce | Utilisation de fonctions Aggregate SOQL | 79 String name = [SELECT Id, Name FROM Account WHERE Name = 'Singha' LIMIT 1].Name; Même si un seul champ sObject est sélectionné, une requête SOQL ou SOSL renvoie toujours les données sous la forme d'enregistrements complets. Par conséquent, vous devez déréférencer le champ pour pouvoir y accéder. Par exemple, le code ci-dessous récupère une liste de sObject dans la base de données avec une requête SOQL, accède au premier enregistrement de compte de la liste, puis déréférence le champ AnnualRevenue de l'enregistrement : Double rev = [SELECT AnnualRevenue FROM Account WHERE Name = 'Acme'][0].AnnualRevenue; // When only one result is returned in a SOQL query, it is not necessary // to include the list's index. Double rev2 = [SELECT AnnualRevenue FROM Account WHERE Name = 'Acme' LIMIT 1].AnnualRevenue; Il n'est pas nécessaire de déréférencer un champ sObject dans le résultat d'une requête SOQL uniquement lorsque la requête renvoie un Integer suite à une opération COUNT : Integer i = [SELECT COUNT() FROM Account]; Les champs dans les enregistrements renvoyés par des requêtes SOSL doivent toujours être déréférencés. Notez également que les champs sObject qui contiennent des formules renvoient la valeur du champ lors de l'émission de la requête SOQL ou SOSL. Toute modification apportée à d'autres champs utilisés dans la formule s'applique à la valeur du champ de formule uniquement après la sauvegarde de l'enregistrement et une nouvelle requête créée pour cet enregistrement dans Apex. Comme d'autres champs sObject en lecture seule, les valeurs des champs de formule ne sont pas modifiables dans Apex. Utilisation de fonctions Aggregate SOQL Les fonctions aggregate dans SOQL, telles que SUM() et MAX(), permettent de regrouper et de récapituler vos données dans une requête. Pour plus d'informations sur les fonctions aggregate, reportez-vous à « Aggregate Functions » dans le guide Salesforce SOQL and SOSL Reference Guide. Vous pouvez utiliser des fonctions aggregate sans utiliser de clause GROUP BY. Par exemple vous pouvez utiliser la fonction aggregate AVG() pour rechercher le montant (Amount) moyen de toutes les opportunités. AggregateResult[] groupedResults = [SELECT AVG(Amount)aver FROM Opportunity]; Object avgAmount = groupedResults[0].get('aver'); Notez que toute requête qui inclut une fonction aggregate renvoie ses résultats dans un tableau d'objets AggregateResult. AggregateResult est un sObject en lecture seule, utilisé uniquement pour des résultats de requête. Constructions du langage salesforce | Utilisation de très grandes requêtes SOQL | 80 Les fonctions aggregate forment un outil plus puissant pour générer des rapports lorsque vous les utilisez avec une clause GROUP BY. Par exemple, vous pouvez rechercher le Amount moyen de toutes vos opportunités par campagne. AggregateResult[] groupedResults = [SELECT CampaignId, AVG(Amount) FROM Opportunity GROUP BY CampaignId]; for (AggregateResult ar : groupedResults) { System.debug('Campaign ID' + ar.get('CampaignId')); System.debug('Average amount' + ar.get('expr0')); } Tout champ agrégé dans une liste SELECT qui n'a pas d'alias obtient automatiquement un alias implicite avec un format expri, où i indique l'ordre des champs agrégés sans alias explicite. La valeur de i commence à 0 et est incrémentée pour chaque champ agrégé sans alias explicite. Pour plus d'informations, reportez-vous à « Using Aliases with GROUP BY » dans le guide Salesforce SOQL and SOSL Reference Guide. Remarque: Les requêtes qui incluent des fonctions aggregate sont soumises aux mêmes limitations du gouverneur que les autres requêtes SOQL concernant le nombre total d'enregistrements renvoyés. Cette limite inclut tous les enregistrements que contient l'agrégation, pas seulement le nombre de lignes renvoyées par la requête. Si vous atteignez cette limite, ajoutez une condition à la clause WHERE pour réduire le nombre d'enregistrements traités par la requête. Utilisation de très grandes requêtes SOQL Votre requête SOQL peut renvoyer de nombreux sObjects qui dépassent la limite de la taille de segment mémoire et génèrent une erreur. Pour la résoudre, utilisez une boucle for de requête SOQL, qui peut traiter plusieurs lots d'enregistrements en utilisant des appels internes à query et à queryMore. Par exemple, si les résultats sont trop grands, la syntaxe ci-dessous peut entraîner une exception d'exécution. Account[] accts = [SELECT Id FROM Account]; À la place, utilisez une boucle for de requête SOQL dans l'un des exemples ci-dessous : // Use this format if you are not executing DML statements // within the for loop for (Account a : [SELECT Id, Name FROM Account WHERE Name LIKE 'Acme%']) { // Your code without DML statements here } // Use this format for efficiency if you are executing DML statements Constructions du langage salesforce | Utilisation de très grandes requêtes SOQL | 81 // within the for loop for (List<Account> accts : [SELECT Id, Name FROM Account WHERE Name LIKE 'Acme%']) { // Your code here update accts; } L'exemple suivant montre une boucle for de requête SOQL utilisée pour mettre à jour en masse les enregistrements. Supposons que vous souhaitez modifier le nom de contact dans tous les enregistrements de contact dont le nom et le prénom correspondent à un critère spécifique : public void massUpdate() { for (List<Contact> contacts: [SELECT FirstName, LastName FROM Contact]) { for(Contact c : contacts) { if (c.FirstName == 'Barbara' && c.LastName == 'Gordon') { c.LastName = 'Wayne'; } } update contacts; } } Au lieu d'utiliser une requête SOQL dans une boucle for, la méthode recommandée de mise à jour en masse des enregistrements consiste à utiliser Apex par lot, qui réduit les risques d'atteindre les limitations du gouverneur. Pour plus informations, reportez-vous à Boucles For SOQL à la page 74. Requêtes SOQL plus efficaces Pour de meilleures performances, les requêtes SOQL doivent être sélectives, notamment pour les requêtes dans des déclencheurs. Pour éviter les délais d'exécution importants, les requêtes SOQL non sélectives peuvent être terminées par le système. Les développeurs reçoivent un message d'erreur lorsqu'une requête non sélective dans un déclencheur est exécutée sur un objet qui contient plus de 100 000 enregistrements. Pour éviter cette erreur, assurez-vous d'utiliser une requête sélective. Critères de requête SOQL sélective • • Une requête est sélective lorsque l'un des filtres de requête se trouve dans un champ indexé et que le filtre de requête maintient le nombre de lignes généré sous le seuil défini par le système. Les performances de la requête SOQL augmentent lorsque deux filtres ou plus utilisés dans la clause WHERE remplissent les conditions spécifiées. Le seuil de sélectivité est de 10 % des enregistrements pour le premier million d'enregistrements, et moins de 5 % des enregistrements à compter du premier million d'enregistrements, jusqu'à un maximum de 333 000 enregistrements. Constructions du langage salesforce | Utilisation de très grandes requêtes SOQL | 82 Dans certains cas, par exemple lorsqu'un filtre de requête se trouve dans un champ standard indexé, le seuil peut être plus élevé. En outre, le seuil de sélectivité peut être modifié. Considérations sur les index personnalisés pour des requêtes SOQL sélectives • • • • • Les champs suivants sont indexés par défaut : clés primaires (champs ID, Nom et Propriétaire), clés étrangères (champs de référence ou de relation principal-details), dates d'audit (telles que LastModifiedDate) et champs personnalisés marqués en tant qu'ID externe ou Unique. Les champs non indexés par défaut peuvent être automatiquement indexés ultérieurement si l'optimiseur Salesforce détermine qu'un index peut améliorer les performances des requêtes fréquemment exécutées. Le support Salesforce.com peut ajouter des index personnalisés aux requêtes pour les clients. Il n'est pas possible de créer un index personnalisé dans les types de champ suivants : listes de sélection multiple, champs de devise dans une organisation multidevises, champs de texte longs, certains champs de formule et champs binaires (champs de type blob, fichier ou texte crypté). Notez que de nouveaux types de données, généralement complexes, peuvent être ajoutés à Salesforce et leurs champs peuvent ne pas autoriser l'indexation personnalisée. Généralement, un index personnalisé n'est pas utilisé dans les cas suivants : ◊ La ou les valeurs demandées dépassent le seuil défini par le système évoqué plus haut. ◊ L'opérateur de filtrage est un opérateur négatif, tel que NOT EQUAL TO (ou !=), NOT CONTAINS et NOT STARTS WITH. ◊ L'opérateur CONTAINS est utilisé dans le filtre et le nombre de lignes à scanner dépasse 333 000, car l'opérateur CONTAINS nécessite un scan complet de l'index. Notez que ce seuil peut être modifié. ◊ Lors d'une comparaison avec une valeur vide (Name != ''). Cependant, il existe d'autres scénarios complexes dans lesquels les index personnalisés ne sont pas utilisés. Si votre cas n'est pas mentionné ici ou si vous souhaitez obtenir une aide supplémentaire sur les requêtes non sélectives, contactez votre représentant salesforce.com. Exemples de requêtes SOQL sélectives Pour mieux comprendre si une requête sur un objet volumineux est sélective ou non, analysons quelques requêtes. Pour ces requêtes, supposons qu'il existe 100 000 enregistrements (qui comprennent les enregistrements supprimés provisoirement, c.-à-d. présents dans la Corbeille) pour le sObject Account. Requête 1 : SELECT Id FROM Account WHERE Id IN (<list of account IDs>) La clause WHERE se trouve dans un champ indexé (ID). Si SELECT COUNT() FROM Account WHERE Id IN (<list of account IDs>) renvoie un nombre d'enregistrements inférieur au seuil de sélectivité, l'index dans ID est utilisé. Ce cas est le plus fréquent, car la liste d'ID contient une faible quantité d'enregistrements. Requête 2 : SELECT Id FROM Account WHERE Name != '' Puisque Account est un objet volumineux, même si Name est indexé (clé primaire), ce filtre renvoie la plupart des enregistrements, rendant la requête non sélective. Requête 3 : SELECT Id FROM Account WHERE Name != '' AND CustomField__c = 'ValueA' Constructions du langage salesforce | Utilisation de requêtes SOQL renvoyant un enregistrement | 83 Ici nous devons déterminer si chaque filtre, considéré individuellement, est sélectif. Comme nous l'avons vu dans l'exemple précédent, le premier filtre n'est pas sélectif. Examinons le deuxième. Si le nombre d'enregistrements renvoyé par SELECT COUNT() FROM Account WHERE CustomField__c = 'ValueA' est inférieur au seuil de sélectivité, et que CustomField__c est indexé, la requête est sélective. Utilisation de requêtes SOQL renvoyant un enregistrement Des requêtes SOQL peuvent être utilisées pour attribuer une valeur sObject unique lorsque la liste de résultats contient un seul élément. Lorsque la valeur L d'une expression est un type sObject unique, Apex attribue automatiquement l'enregistrement sObject unique dans la liste des résultats de requêtes à la valeur L. Une exception d'exécution est levée si aucun sObject ou plusieurs sObjects sont détectés dans la liste. Par exemple : List<Account> accts = [SELECT Id FROM Account]; // These lines of code are only valid if one row is returned from // the query. Notice that the second line dereferences the field from the // query without assigning it to an intermediary sObject variable. Account acct = [SELECT Id FROM Account]; String name = [SELECT Name FROM Account].Name; Amélioration des performances en annulant la recherche des valeurs nulles Dans vos requêtes SOQL et SOSL, évitez de rechercher les enregistrements qui contiennent des valeurs nulles. Commencez par exclure ces valeurs afin d'améliorer les performances. Dans l'exemple suivant, tous les enregistrements dont la valeur treadID est Null sont exclus des valeurs renvoyées. Public class TagWS { /* getThreadTags * * a quick method to pull tags not in the existing list * */ public static webservice List<String> getThreadTags(String threadId, List<String> tags) { system.debug(LoggingLevel.Debug,tags); List<String> retVals = new List<String>(); Set<String> tagSet = new Set<String>(); Constructions du langage salesforce | Compréhension des requêtes SOQL de clé étrangère et de relations parent-enfant | 84 Set<String> origTagSet = new Set<String>(); origTagSet.addAll(tags); // Note WHERE clause verifies that threadId is not null for(CSO_CaseThread_Tag__c t : [SELECT Name FROM CSO_CaseThread_Tag__c WHERE Thread__c = :threadId AND WHERE threadID != null]) { tagSet.add(t.Name); } for(String x : origTagSet) { // return a minus version of it so the UI knows to clear it if(!tagSet.contains(x)) retVals.add('-' + x); } for(String x : tagSet) { // return a plus version so the UI knows it's new if(!origTagSet.contains(x)) retvals.add('+' + x); } return retVals; } Compréhension des requêtes SOQL de clé étrangère et de relations parent-enfant L'instruction SELECT d'une requête SOQL peut correspondre à n'importe quelle instruction SOQL valide, y compris des liaisons de clé étrangère et d'enregistrements parent-enfant. Si des liaisons de clé étrangère sont incluses, les sObjects qui en résultent peuvent être référencés en utilisant une notation de champ normale. Par exemple : System.debug([SELECT Account.Name FROM Contact WHERE FirstName = 'Caroline'].Account.Name); Constructions du langage salesforce | Utilisation de relations polymorphiques dans des requêtes SOQL | 85 De plus, les relations parent-enfant dans les sObjects agissent aussi comme des requêtes SOQL. Par exemple : for (Account a : [SELECT Id, Name, (SELECT LastName FROM Contacts) FROM Account WHERE Name = 'Acme']) { Contact[] cons = a.Contacts; } //The following example also works because we limit to only 1 contact for (Account a : [SELECT Id, Name, (SELECT LastName FROM Contacts LIMIT 1) FROM Account WHERE Name = 'testAgg']) { Contact c = a.Contacts; } Utilisation de relations polymorphiques dans des requêtes SOQL Une relation polymorphique est une relation entre des objets, dans laquelle les objets référencés peuvent avoir plusieurs types différents. Par exemple, le champ de relation What d'un Événement peut être un Compte, une Campagne ou une Opportunité. Cette section présente l'utilisation de requêtes SOQL avec des relations polymorphiques dans le langage Apex. Pour plus d'informations générales sur les relations polymorphiques, reportez-vous à Understanding Polymorphic Keys and Relationships dans le guide Force.com SOQL and SOSL Reference. Vous pouvez utiliser des requêtes SOQL qui référencent des champs polymorphiques dans le langage Apex, pour obtenir des résultats qui dépendent du type d'objet référencé par le champ polymorphique. Une approche consiste à filtrer vos résultats en utilisant le qualificateur Type. Cet exemple demande les événements (Events) qui sont associés à un compte (Account) ou une opportunité (Opportunity) via le champ What. List<Event> = [SELECT Description FROM Event WHERE What.Type IN ('Account', 'Opportunity')]; Une autre approche consiste à utiliser la clause TYPEOF dans l'instruction SOQL SELECT. Cet exemple demande également les événements (Events) qui sont associés à un compte (Account) ou une opportunité (Opportunity) via le champ What. List<Event> = [SELECT TYPEOF What WHEN Account THEN Phone WHEN Opportunity THEN Amount END FROM Event]; Remarque: Actuellement, TYPEOF est disponible en tant qu'aperçu du développeur dans la fonctionnalité Polymorphisme SOQL. Pour plus d'informations sur l'activation de TYPEOF pour votre organisation, contactez salesforce.com. Ces requêtes renvoient une liste de sObjects dont le champ de relation référence les types d'objet souhaités. Constructions du langage salesforce | Utilisation de relations polymorphiques dans des requêtes SOQL | 86 Pour accéder à l'objet référencé dans une relation polymorphique, vous pouvez utiliser le mot clé instanceof afin de déterminer le type d'objet. L'exemple suivant utilise instanceof pour déterminer si un Account ou une Opportunity est associé à un Event. Event myEvent = eventFromQuery; if (myEvent.What instanceof Account) { // myEvent.What references an Account, so process accordingly } else if (myEvent.What instanceof Opportunity) { // myEvent.What references an Opportunity, so process accordingly } Notez que vous devez attribuer le sObject référencé, que la requête renvoie, à une variable du type approprié avant de la transmettre à une autre méthode. L'exemple suivant demande l'utilisateur ou groupe propriétaire des objets personnalisés Merchandise__c en utilisant une requête SOQL avec une clause TYPEOF, utilise instanceof pour déterminer le type de propriétaire, puis attribue les objets du propriétaire à des variables de type User ou Group avant de les transmettre à des méthodes de marchandises. public class PolymorphismExampleClass { // Utility method for a User public static void processUser(User theUser) { System.debug('Processed User'); } // Utility method for a Group public static void processGroup(Group theGroup) { System.debug('Processed Group'); } public static void processOwnersOfMerchandise() { // Select records based on the Owner polymorphic relationship field List<Merchandise__c> merchandiseList = [SELECT TYPEOF Owner WHEN User THEN LastName WHEN Group THEN Email END FROM Merchandise__c]; // We now have a list of Merchandise__c records owned by either a User or Group for (Merchandise__c merch: merchandiseList) { // We can use instanceof to check the polymorphic relationship type // Note that we have to assign the polymorphic reference to the appropriate // sObject type before passing to a method Constructions du langage salesforce | Utilisation de variables Apex dans des requêtes SOQL et SOSL | 87 if (merch.Owner instanceof User) { User userOwner = merch.Owner; processUser(userOwner); } else if (merch.Owner instanceof Group) { Group groupOwner = merch.Owner; processGroup(groupOwner); } } } } Utilisation de variables Apex dans des requêtes SOQL et SOSL Dans le langage Apex, les instructions SOQL et SOSL peuvent référencer des variables et des expressions de code Apex si elles sont précédées de deux points (:). Cette utilisation de variable de code local dans une instruction SOQL ou SOSL est appelée liaison. L'analyseur Apex commence par évaluer la variable locale dans le contexte du code avant d'exécuter l'instruction SOQL ou SOSL. Les expressions de liaison peuvent être utilisées en tant que : • • • • • • Chaîne de recherche dans des clauses FIND. Littéraux de filtrage dans des clauses WHERE. Valeur de l'opérateur IN ou NOT IN dans des clauses WHERE, permettant le filtrage dans un ensemble de valeurs dynamique. Notez que cette utilisation est spécifique à une liste d'ID ou de String, bien qu'elle fonctionne avec des listes de n'importe quel type. Noms de division dans des clauses WITH DIVISION. Valeur numérique dans des clauses LIMIT. Valeur numérique dans des clauses OFFSET. L'expression de liaison ne peuvent pas être utilisées avec d'autres clauses, telles que INCLUDES. Par exemple : Account A = new Account(Name='xxx'); insert A; Account B; // A simple bind B = [SELECT Id FROM Account WHERE Id = :A.Id]; // A bind with arithmetic B = [SELECT Id FROM Account Constructions du langage salesforce | Utilisation de variables Apex dans des requêtes SOQL et SOSL | 88 WHERE Name = :('x' + 'xx')]; String s = 'XXX'; // A bind with expressions B = [SELECT Id FROM Account WHERE Name = :'XXXX'.substring(0,3)]; // A bind with an expression that is itself a query result B = [SELECT Id FROM Account WHERE Name = :[SELECT Name FROM Account WHERE Id = :A.Id].Name]; Contact C = new Contact(LastName='xxx', AccountId=A.Id); insert new Contact[]{C, new Contact(LastName='yyy', accountId=A.id)}; // Binds in both the parent and aggregate queries B = [SELECT Id, (SELECT Id FROM Contacts WHERE Id = :C.Id) FROM Account WHERE Id = :A.Id]; // One contact returned Contact D = B.Contacts; // A limit bind Integer i = 1; B = [SELECT Id FROM Account LIMIT :i]; // An OFFSET bind Integer offsetVal = 10; List<Account> offsetList = [SELECT Id FROM Account OFFSET :offsetVal]; Constructions du langage salesforce | Utilisation de variables Apex dans des requêtes SOQL et SOSL | 89 // An IN-bind with an Id list. Note that a list of sObjects // can also be used--the Ids of the objects are used for // the bind Contact[] cc = [SELECT Id FROM Contact LIMIT 2]; Task[] tt = [SELECT Id FROM Task WHERE WhoId IN :cc]; // An IN-bind with a String list String[] ss = new String[]{'a', 'b'}; Account[] aa = [SELECT Id FROM Account WHERE AccountNumber IN :ss]; // A SOSL query with binds in all possible clauses String myString1 = 'aaa'; String myString2 = 'bbb'; Integer myInt3 = 11; String myString4 = 'ccc'; Integer myInt5 = 22; List<List<SObject>> searchList = [FIND :myString1 IN ALL FIELDS RETURNING Account (Id, Name WHERE Name LIKE :myString2 LIMIT :myInt3), Contact, Opportunity, Lead WITH DIVISION =:myString4 LIMIT :myInt5]; Constructions du langage salesforce | Demande de tous les enregistrements avec une instruction SOQL | 90 Demande de tous les enregistrements avec une instruction SOQL Les instructions SOQL peuvent utiliser les mots clés ALL ROWS pour interroger tous les enregistrements dans une organisation, y compris les enregistrements supprimés et les activités archivées. Par exemple : System.assertEquals(2, [SELECT COUNT() FROM Contact WHERE AccountId = a.Id ALL ROWS]); Vous pouvez utiliser ALL ROWS pour interroger des enregistrements dans la Corbeille de votre organisation. Vous ne pouvez pas utiliser les mots clés ALL ROWS avec les mots clés FOR UPDATE. Instructions de verrouillage Apex permet aux développeurs de verrouiller des enregistrements sObject pendant leur mise à jour afin d'éviter les conditions « race » et d'autres menaces de sécurité. Lorsqu'un enregistrement sObject est verrouillé, aucun autre programme ni utilisateur n'est autorisé à effectuer des mises à jour. Pour verrouiller un ensemble d'enregistrements sObject dans le langage Apex, incorporez les mots clés FOR UPDATE après n'importe quelle instruction SOQL en ligne. Par exemple, l'instruction suivante, en plus d'interroger deux comptes, verrouille également les comptes qui sont renvoyés : Account [] accts = [SELECT Id FROM Account LIMIT 2 FOR UPDATE]; Remarque: Vous ne pouvez pas utiliser les mots clés ORDER BY dans une requête SOQL qui utilise le verrouillage. Lorsque les comptes sont verrouillés par cet appel, les instructions DML (langage de manipulation de données) peuvent modifier leur valeurs de champ dans la base de données dans la transaction. ATTENTION: Définissez avec prudence des verrous dans votre code Apex. Reportez-vous à Éviter les impasses, ci-dessous. Verrouillage d'une boucle For SOQL Les mots clés FOR UPDATE peuvent également être utilisés dans des boucles SOQL for. Par exemple : for (Account[] accts : [SELECT Id FROM Account FOR UPDATE]) { // Your code } Comme indiqué dans Boucles For SOQL, l'exemple ci-dessus correspond en interne à des appels aux méthodes query() et queryMore() dans l'API SOAP. Constructions du langage salesforce | Éviter les impasses | 91 Notez qui n'existe aucune instruction commit. Si votre déclencheur Apex réussit, toute modification apportée à la base de données s'applique automatiquement. Si votre déclencheur Apex échoue, les modifications apportées à la base de données sont annulées. Éviter les impasses Notez que le langage Apex peut conduire à des impasses, comme tout autre langage logique de procédural qui nécessite la mise à jour de multiples tableaux ou lignes de base de données. Pour éviter ces impasses, le moteur d'exécution Apex commence par verrouiller les enregistrements parent sObject, puis les enfants. En tant que développeur, soyez prudent(e) en verrouillant des lignes pour vous assurer qu'elles créent pas d'impasses. Pour éviter les impasses, utilisez les techniques de contournement standard en accédant aux tableaux et aux lignes dans le même ordre à partir de n'importe quel emplacement dans l'application. Contrôle des transactions Toutes les requêtes sont délimitées par le déclencheur, une méthode de classe, un service Web, une page Visualforce ou un bloc anonyme qui exécute le code Apex. Si la requête complète réussit, toutes les modifications sont appliquées dans la base de données. Par exemple, supposons qu'une page Visualforce a appelé un contrôleur Apex, qui à son tour a appelé une classe Apex supplémentaire. Les modifications sont appliquées dans la base de données lorsque tout le code Apex et la page Visualforce ont été exécutés. Si la requête échoue, toutes les modifications apportées à la base de données sont annulées. Cependant, durant le traitement des enregistrements, il arrive parfois que vos règles métier nécessitent l'annulation d'un travail partiel (instructions DML déjà exécutées) afin de poursuivre le traitement dans une autre direction. Le langage Apex permet de générer un point d'enregistrement, c.-à-d. un point dans la requête qui précise l'état actuel de la base de données. Toute instruction DML qui se produit après le point d'enregistrement peut être abandonnée et les conditions de la base de données préalables à la génération du point d'enregistrement peuvent être restaurées. Les limitations suivantes s'appliquent à la génération des variables du point d'enregistrement et à la restauration de la base de données : • • • • • • Si vous définissez plusieurs points d'enregistrement, puis restaurez un point d'enregistrement qui ne correspond pas au dernier point d'enregistrement généré, les variables des points d'enregistrement ultérieurs ne sont plus valides. Par exemple, si vous générez un premier point d'enregistrement SP1, puis un deuxième point d'enregistrement SP2, et que vous restaurez vers le point d'enregistrement SP1, la variable SP2 n'est plus valide. Vous recevez une erreur d'exécution lorsque vous essayez de l'utiliser. Les références à des points d'enregistrements ne peuvent pas croiser des invocations de déclencheur, car chaque invocation de déclencheur correspond à un nouveau contexte d'exécution. Si vous déclarez un point d'enregistrement en tant que variable statique, puis essayez de l'utiliser à travers des contextes de déclencheur, vous obtenez une erreur d'exécution. Chaque point d'enregistrement que vous définissez est prise en compte dans les limitations du gouverneur pour les instructions DML. Les variables statiques ne sont pas restaurées lors d'une annulation. Si vous essayez de réexécuter le déclencheur, les variables statiques conservent les valeurs de la première exécution. Chaque annulation est prise en compte dans les limitations du gouverneur pour les instructions DML. Vous obtenez une erreur d'exécution si vous essayez de restaurer la base de données un nombre de fois excessif. L'ID dans un sObject inséré après la définition d'un point enregistrement n'est pas effacé après une annulation. Créez un nouveau sObject à insérer après une annulation. Une tentative d'insertion du sObject en utilisant la même variable créée avant l'annulation échoue, car la variable du sObject inclut un ID. La mise à jour ou la mise à jour/insertion du sObject en Constructions du langage salesforce | Instructions d'exception | 92 utilisant la même variable échoue également, car le sObject ne figure pas dans la base de données et, par conséquent, ne peut pas être mis à jour. L'exemple suivant utilise les méthodes Database setSavepoint et rollback. Account a = new Account(Name = 'xxx'); insert a; System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber); // Create a savepoint while AccountNumber is null Savepoint sp = Database.setSavepoint(); // Change the account number a.AccountNumber = '123'; update a; System.assertEquals('123', [SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber); // Rollback to the previous null value Database.rollback(sp); System.assertEquals(null, [SELECT AccountNumber FROM Account WHERE Id = :a.Id]. AccountNumber); Instructions d'exception Le langage Apex utilise des exceptions pour consigner des erreurs et d'autres événements qui gênent l'exécution normale du code. Des instructions throw peuvent être utilisées pour générer des exceptions, et try, catch et finally permettent d'effectuer une restauration sans heurt suite à une exception. Vous pouvez également créer vos propres exceptions en utilisant la classe Exception. Pour plus d'informations, reportez-vous à Classe Exception à la page 585. Instructions Throw Une instruction throw permet de signaler une erreur qui s'est produite. Pour signaler une exception, utilisez l'instruction throw en lui ajoutant un objet d'exception pour fournir des informations sur l'erreur spécifique. Par exemple : throw exceptionObject; Constructions du langage salesforce | Instructions Try-Catch-Finally | 93 Instructions Try-Catch-Finally Les instructions try, catch et finally peuvent être utilisées pour restaurer sans heurt suite au signalement d'une exception : L'instruction try identifie un bloc de code dans lequel une exception peut se produire. L'instruction catch identifie un bloc de code qui peut traiter un type particulier d'exception. Une instruction try unique peut être associé à plusieurs instructions catch. Cependant, chaque instruction catch doit avoir un type d'exception unique. L'instruction facultative finally identifie un bloc de code dont l'exécution est garantie et permet de nettoyer le code inclus dans le bloc try. Une instruction try unique peut être associée à une instruction finally. • • • Syntaxe La syntaxe de ces instructions est la suivante : try { code_block } catch (exceptionType) { code_block } // Optional catch statements for other exception types. // Note that the general exception type, 'Exception', // must be the last catch block when it is used. } catch (Exception e) { code_block } // Optional finally statement } finally { code_block } Exemple Par exemple : try { // Your code here } catch (ListException e) { // List Exception handling code here Constructions du langage salesforce | Instructions Try-Catch-Finally | 94 } catch (Exception e) { // Generic exception handling code here } Remarque: Les exceptions de limitation générées par un gouverneur d'exécution ne peuvent pas être détectées. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Chapitre 3 Invocation de code Apex Sujets : • • • • Déclencheurs Planificateur Apex Blocs anonymes Apex dans AJAX Vous pouvez invoquer votre code Apex à l'aide de plusieurs mécanismes. Vous pouvez écrire un déclencheur Apex et invoquer le code de votre déclencheur pour les événements qu'il spécifie, avant ou après une opération donnée pour un type sObject spécifique. Vous pouvez également écrire une classe Apex et planifier son exécution à intervalles prédéfinis, ou exécuter des extraits de code dans un bloc anonyme. Pour terminer, vous pouvez utiliser les outils Ajax pour invoquer des méthodes de services Web mises en oeuvre dans Apex. Ce chapitre inclut les sections suivantes : • • • • Déclencheurs Planificateur Apex (pour les classes Apex uniquement) Blocs anonymes Outils AJAX Invocation de code Apex salesforce | Déclencheurs | 96 Déclencheurs Un code Apex peut être invoqué en utilisant des déclencheurs. Un déclencheur est un code Apex qui est exécuté avant ou après les types d'opération suivants : • • • • • • insérer mettre à jour supprimer fusionner mettre à jour/insérer annuler la suppression Par exemple, un déclencheur peut être exécuté avant l'insertion d'enregistrements d'un objet dans la base de données, après la suppression d'enregistrements ou même après la restauration d'un enregistrement à partir de la Corbeille. Vous pouvez définir des déclencheurs pour n'importe quel objet standard de niveau supérieur, tel que Contact ou Compte, pas pour des objets standard enfant, tels que Rôle du contact. • • Pour des commentaires de requête, cliquez sur Votre nom > Configuration > Personnaliser > Requêtes > Commentaires sur la requête > Déclencheurs. Pour des e-mails, cliquez sur Votre nom > Configuration > Personnaliser > Requêtes > E-mails > Déclencheurs. Il existe deux types de déclencheur : • • Les déclencheurs Before peuvent être utilisés pour mettre à jour ou valider des valeurs d'enregistrement avant leur enregistrement dans la base de données. Les déclencheurs After peuvent être utilisés pour accéder à des valeurs de champ définies par la base de données (par exemple le champ Id ou lastUpdated d'un enregistrement), et pour affecter les modifications dans d'autres enregistrements, par exemple la consignation dans un tableau d'audit ou le déclenchement d'événements asynchrones avec une file d'attente. Les déclencheurs peuvent également modifier d'autres enregistrements du même type que ceux qui ont initialement activé le déclencheur. Par exemple, si un déclencheur est activé après une mise à jour du contact A, il peut également modifier les contacts B, C et D. Puisque les déclencheurs peuvent entraîner la modification d'autres enregistrements, et que ces modifications peuvent activer d'autres déclencheurs, le moteur d'exécution Apex considère toutes ces opérations comme une seule unité de travail et limite les opérations qui peuvent être exécutées afin d'empêcher une récursion infinie. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. De plus, si vous mettez à jour ou supprimez un enregistrement dans son déclencheur Before, ou supprimez un enregistrement dans son déclencheur After, vous obtenez erreur d'exécution. Cela inclut les opérations directes et indirectes. Par exemple, si vous mettez à jour le compte A, et que le déclencheur before update du compte A insère le contact B, et le déclencheur after insert du contact B interroge le compte A et le met à jour en utilisant l'instruction DML update ou la méthode database, vous mettez indirectement à jour le compte A dans son déclencheur before, et vous obtenez une erreur d'exécution. Considérations sur la mise en oeuvre Avant de créer des déclencheurs, tenez compte des points suivants : • • Les déclencheurs upsert sont activés avant et après les déclencheurs insert ou avant et après les déclencheurs update selon les cas. Les déclencheurs merge sont activés avant et après les déclencheurs delete pour les enregistrements perdus et les déclencheurs before update pour l'enregistrement gagnant uniquement. Reportez-vous à Déclencheurs et instructions Merge à la page 106. Invocation de code Apex salesforce | Déclencheurs en masse | 97 Les déclencheurs exécutés après l'annulation de la suppression d'un enregistrement fonctionnent uniquement avec des objets spécifiques. Reportez-vous à Déclencheurs et enregistrements restaurés à la page 107. Historique des champs n'est pas enregistré avant la fin d'un déclencheur. Si vous interrogez l'historique des champs dans un déclencheur, aucun historique ne correspond à la transaction actuelle. Pour un code Apex enregistré en utilisant l'API version 20.0 ou antérieure de Salesforce.com, si un appel API active un déclencheur, le lot de 200 enregistrements à traiter est divisé en lots de 100 enregistrements. Pour un code Apex enregistré en utilisant l'API versions 21.0 et supérieures de Salesforce.com, les lots d'API ne sont pas divisés. Notez que les valeurs de variable statique sont réinitialisées entre les lots, contrairement aux limitations du gouverneur. N'utilisez pas des variables statiques pour suivre les informations d'état entre les lots. • • • Déclencheurs en masse Tous des déclencheurs sont par défaut des déclencheurs en masse qui peuvent traiter plusieurs enregistrements à la fois. Vous devez toujours planifier le traitement de plusieurs enregistrements à la fois. Remarque: Un objet Event défini comme récurrent n'est pas traité en masse pour les déclencheurs insert, delete ou update. Les déclencheurs en masse peuvent traiter des mises à jour d'enregistrements uniques et des opérations en masse telles que : Importation de données Appels API en masse Force.com Actions en masse, telles que des modifications et des suppressions de propriétaires d'enregistrement Méthodes et déclencheurs Apex récursifs qui invoquent des traitements DML en masse • • • • Syntaxe de déclencheur Pour définir un déclencheur, utilisez la syntaxe suivante : trigger triggerName on ObjectName (trigger_events) { code_block } où trigger_events peut être une liste de valeurs séparées par une virgule comportant un ou plusieurs des événements suivants : • • • • • • • before insert before update before delete after insert after update after delete after undelete Remarque: • Vous pouvez utiliser le mot clé webService uniquement dans un déclencheur au sein d'une méthode définie comme asynchrone, c.-à-d. définie avec le mot clé @future. Invocation de code Apex • salesforce | Variables de contexte de déclencheur | 98 Un déclencheur appelé par une action insert, delete ou update pour une tâche ou un événement récurrent entraîne une erreur d'exécution si le déclencheur est appelé en masse à partir de l'API Force.com. Par exemple, le code suivant définit un déclencheur pour les événements before insert et before update dans l'objet Account : trigger myAccountTrigger on Account (before insert, before update) { // Your code here } Le bloc de code d'un déclencheur ne peut pas contenir le mot clé static. Les déclencheurs peuvent contenir uniquement des mots clés applicables à une classe interne. De plus, il n'est pas nécessaire d'appliquer manuellement les modifications de base de données effectuées par un déclencheur. Si votre déclencheur Apex réussit, toute modification apportée à la base de données s'applique automatiquement. Si votre déclencheur Apex échoue, les modifications apportées à la base de données sont annulées. Variables de contexte de déclencheur Tous les déclencheurs définissent des variables implicites qui permettent aux développeurs d'accéder au contexte d'exécution. Ces variables sont contenues dans la classe System.Trigger : Variable Utilisation isExecuting Renvoie true si le contexte actuel du code Apex est un déclencheur, pas une page Visualforce, un service Web ou un appel d'API executeanonymous(). isInsert Renvoie true si ce déclencheur a été activé suite à une opération insert, à partir de l'interface utilisateur de Salesforce, un code Apex ou l'API. isUpdate Renvoie true si ce déclencheur a été activé suite à une opération update, à partir de l'interface utilisateur de Salesforce, un code Apex ou l'API. isDelete Renvoie true si ce déclencheur a été activé suite à une opération delete, à partir de l'interface utilisateur de Salesforce, un code Apex ou l'API. isBefore Renvoie true si ce déclencheur a été activé avant la sauvegarde d'un enregistrement. isAfter Renvoie true si ce déclencheur a été activé après la sauvegarde de tous les enregistrements. isUndelete Renvoie true si ce déclencheur a été activé après la restauration de l'enregistrement à partir de la Corbeille (c.-à-d. après une opération undelete à partir de l'interface utilisateur de Salesforce, un code Apex ou l'API). new Renvoie une liste de toutes les nouvelles versions des enregistrements sObject. Notez que cette liste de sObject est disponible uniquement dans les déclencheurs insert et update, et que les enregistrements peuvent être modifiés uniquement dans les déclencheurs before. Invocation de code Apex salesforce | Variables de contexte de déclencheur | 99 Variable Utilisation newMap Un mappage d'ID avec les nouvelles versions des enregistrements sObject. Notez que ce mappage est disponible uniquement dans les déclencheurs before update, after insert et after update. Renvoie une liste des anciennes versions des enregistrements sObject. old Notez que cette liste de sObject est disponible uniquement dans les déclencheurs update et delete. oldMap Un mappage d'ID avec les anciennes versions des enregistrements sObject. Notez que ce mappage est disponible uniquement dans les déclencheurs update et delete. Le nombre total d'enregistrements dans une invocation de déclencheur, anciens et nouveaux. size Remarque: Si un enregistrement qui active un déclencheur inclut une valeur de champ non valide (par exemple une formule qui divise par zéro), cette valeur est définie sur null dans les variables de contexte de déclencheur new, newMap, old et oldMap. Par exemple, dans ce simple déclencheur, Trigger.new est une liste de sObjects et peut être répétée dans une boucle for, ou utilisée en tant que variable de liaison dans la clause IN d'une requête SOQL : Trigger t on Account (after insert) { for (Account a : Trigger.new) { // Iterate over each sObject } // This single query finds every contact that is associated with any of the // triggering accounts. Note that although Trigger.new is a collection of // records, when used as a bind variable in a SOQL query, Apex automatically // transforms the list of records into a list of corresponding Ids. Contact[] cons = [SELECT LastName FROM Contact WHERE AccountId IN :Trigger.new]; } Ce déclencheur utilise les variables de contexte booléennes Trigger.isBefore et Trigger.isDelete pour définir un code qui s'exécute uniquement dans des conditions de déclencheurs spécifiques : trigger myAccountTrigger on Account(before delete, before insert, before update, after delete, after insert, after update) { if (Trigger.isBefore) { Invocation de code Apex salesforce | Variables de contexte de déclencheur | 100 if (Trigger.isDelete) { // In a before delete trigger, the trigger accesses the records that will be // deleted with the Trigger.old list. for (Account a : Trigger.old) { if (a.name != 'okToDelete') { a.addError('You can\'t delete this record!'); } } } else { // In before insert or before update triggers, the trigger accesses the new records // with the Trigger.new list. for (Account a : Trigger.new) { if (a.name == 'bad') { a.name.addError('Bad name'); } } if (Trigger.isInsert) { for (Account a : Trigger.new) { System.assertEquals('xxx', a.accountNumber); System.assertEquals('industry', a.industry); System.assertEquals(100, a.numberofemployees); System.assertEquals(100.0, a.annualrevenue); a.accountNumber = 'yyy'; } // If the trigger is not a before trigger, it must be an after trigger. } else { if (Trigger.isInsert) { List<Contact> contacts = new List<Contact>(); for (Account a : Trigger.new) { if(a.Name == 'makeContact') { contacts.add(new Contact (LastName = a.Name, Invocation de code Apex salesforce | Considérations sur les variables de contexte | 101 AccountId = a.Id)); } } insert contacts; } } }}} Considérations sur les variables de contexte Tenez compte des considérations suivantes sur les variables de contexte de déclencheur : • • • • Les variables trigger.new et trigger.old ne peuvent pas être utilisées dans des opérations DML Apex. Vous pouvez utiliser un objet pour modifier ses propres valeurs de champs à l'aide de la variable trigger.new, mais uniquement dans des déclencheurs before. Dans tous les déclencheurs after, la variable trigger.new n'est pas enregistrée, par conséquent une exception d'exécution est levée. La variable trigger.old est toujours en lecture seule. Vous ne pouvez pas supprimer la variable trigger.new. Le tableau suivant présente des considérations sur certaines actions dans différents événements de déclencheur : Événement de déclencheur Peut modifier des champs en Peut mettre à jour l'objet utilisant trigger.new d'origine en utilisant une opération DML update Peut supprimer l'objet d'origine en utilisant une opération DML delete before insert Autorisé. Non applicable. L'objet d'origine n'a pas été créé, rien ne peut le référencer, par conséquent, rien ne peut le mettre à jour. after insert Non autorisé. Une erreur Autorisé. d'exécution est générée, car la variable trigger.new est déjà enregistrée. Autorisé, mais pas nécessaire. L'objet est supprimé immédiatement après son insertion. before update Autorisé. Non autorisé. Une erreur d'exécution est générée. Non autorisé. Une erreur d'exécution est générée. after update Non autorisé. Une erreur d'exécution est générée, car la variable trigger.new est déjà enregistrée. Autorisé. Bien qu'un code incorrect puisse entraîner une récursion infinie, l'erreur serait détectée par les limitations du gouverneur. Autorisé. Les mises à jour sont enregistrées avant la suppression de l'objet, par conséquent si la suppression de l'objet est annulée, les mises à jour sont visibles. Non applicable. L'objet d'origine n'a pas été créé, rien ne peut le référencer, par conséquent, rien ne peut le mettre à jour. Invocation de code Apex salesforce | Idiomes de déclencheur en masse courants | 102 Événement de déclencheur Peut modifier des champs en Peut mettre à jour l'objet utilisant trigger.new d'origine en utilisant une opération DML update Peut supprimer l'objet d'origine en utilisant une opération DML delete before delete Non autorisé. Une erreur d'exécution est générée. La variable trigger.new n'est pas disponible dans les déclencheurs before delete. after delete Non autorisé. Une erreur Non applicable. L'objet a déjà Non applicable. L'objet a déjà d'exécution est générée. La été supprimé. été supprimé. variable trigger.new n'est pas disponible dans les déclencheurs after delete. after undelete Non autorisé. Une erreur Autorisé. d'exécution est générée. La variable trigger.old n'est pas disponible dans les déclencheurs undelete. Autorisé. Les mises à jour sont Non autorisé. Une erreur enregistrées avant la d'exécution est générée. La suppression de l'objet, par suppression est déjà en cours. conséquent si la suppression de l'objet est annulée, les mises à jour sont visibles. Autorisé, mais pas nécessaire. L'objet est supprimé immédiatement après son insertion. Idiomes de déclencheur en masse courants Bien que les déclencheurs en masse permettent aux développeurs à traiter davantage d'enregistrements sans dépasser les limitations du gouverneur en termes d'exécution, ils peuvent être plus difficiles à comprendre et à coder, car ils nécessitent le traitement de lots de plusieurs enregistrements à la fois. Les sections suivantes présentent des exemples d'idiomes à utiliser fréquemment en écrivant en masse. Utilisation de mappages et d'ensembles dans des déclencheurs en masse Les structures de données d'ensembles et de mappages sont essentielles pour réussir le codage de déclencheurs en masse. Les ensembles permettent d'isoler des enregistrements distincts, alors que les mappages permettent de contenir les résultats de requêtes organisés par ID d'enregistrement. Par exemple, le déclencheur en masse de l'exemple d'application de devis ci-dessous commence par ajouter chaque entrée de catalogue de prix associée à des enregistrements OpportunityLineItem dans Trigger.new à un ensemble, pour s'assurer que l'ensemble contient uniquement des éléments distincts. Il interroge ensuite les PricebookEntries pour obtenir leurs couleurs produit associées, et place les résultats dans un mappage. Une fois le mappage créé, le déclencheur parcourt les OpportunityLineItems dans Trigger.new et utilise le mappage pour attribuer les couleurs appropriées. // When a new line item is added to an opportunity, this trigger copies the value of the // associated product's color to the new record. trigger oppLineTrigger on OpportunityLineItem (before insert) { // For every OpportunityLineItem record, add its associated pricebook entry Invocation de code Apex salesforce | Idiomes de déclencheur en masse courants | 103 // to a set so there are no duplicates. Set<Id> pbeIds = new Set<Id>(); for (OpportunityLineItem oli : Trigger.new) pbeIds.add(oli.pricebookentryid); // Query the PricebookEntries for their associated product color and place the results // in a map. Map<Id, PricebookEntry> entries = new Map<Id, PricebookEntry>( [select product2.color__c from pricebookentry where id in :pbeIds]); // Now use the map to set the appropriate color on every OpportunityLineItem processed // by the trigger. for (OpportunityLineItem oli : Trigger.new) oli.color__c = entries.get(oli.pricebookEntryId).product2.color__c; } Corrélation d'enregistrements avec des résultats de requête dans des déclencheurs en masse Utilisez des mappages ID vers sObject Trigger.newMap et Trigger.oldMap pour corréler les enregistrements avec les résultats de requête. Par exemple, le déclencheur de l'exemple d'application de devis ci-dessous utilise Trigger.oldMap pour créer un ensemble d'ID uniques (Trigger.oldMap.keySet()). L'ensemble est ensuite utilisé dans une requête pour créer une liste des devis associés aux opportunités en cours de traitement par le déclencheur. Pour chaque devis renvoyé par la requête, l'opportunité associée est récupérée à partir de Trigger.oldMap et sa suppression est empêchée : trigger oppTrigger on Opportunity (before delete) { for (Quote__c q : [SELECT opportunity__c FROM quote__c WHERE opportunity__c IN :Trigger.oldMap.keySet()]) { Trigger.oldMap.get(q.opportunity__c).addError('Cannot delete opportunity with a quote'); } } Utilisation de déclencheurs pour insérer ou mettre à jour des enregistrements avec des champs uniques Suite à un événement insert ou upsert, si un enregistrement duplique la valeur d'un champ unique dans un autre enregistrement de ce lot, le message d'erreur sur l'enregistrement dupliqué inclut l'ID du premier enregistrement. Cependant, le message d'erreur peut être erroné d'ici la fin de la requête. Invocation de code Apex salesforce | Définition de déclencheurs | 104 Lorsque des déclencheurs existent, la logique retry dans des opérations en masse entraîne l'exécution d'un cycle restaurer/réessayer. Ce cycle retry attribue de nouvelles clés aux nouveaux enregistrements. Par exemple, si deux enregistrements sont insérés avec la même valeur pour un champ unique, et que vous avez un événement insert défini pour un déclencheur, le deuxième enregistrement dupliqué échoue, et l'ID du premier enregistrement est indiqué. Cependant, lorsque le système restaure les modifications et réintroduit le premier enregistrement, un nouvel ID est attribué à l'enregistrement. Ainsi, le message d'erreur généré par le deuxième enregistrement n'est plus valide. Définition de déclencheurs Le code d'un déclencheur est stocké sous forme de métadonnées sous l'objet auquel il est associé. Pour définir un déclencheur dans Salesforce : 1. Pour un objet standard, cliquez sur Votre nom > Configuration > Personnaliser, cliquez sur le nom de l'objet, puis sur Déclencheurs. Pour un objet personnalisé, cliquez sur Votre nom > Configuration > Créer > Objets, puis cliquez sur le nom de l'objet. Pour des membres de campagne, cliquez sur Votre nom > Configuration > Personnaliser > Campagnes > Membre de campagne > Déclencheurs. Pour des commentaires de requête, cliquez sur Votre nom > Configuration > Personnaliser > Requêtes > Commentaires sur la requête > Déclencheurs. Pour des e-mails, cliquez sur Votre nom > Configuration > Personnaliser > Requêtes > E-mails > Déclencheurs. Pour des objets standard Pièce jointe, Contenu de document et Note, vous ne pouvez pas créer de déclencheur dans l'interface utilisateur de Salesforce. Pour ces objets, créez un déclencheur en utilisant les outils de développement tels que la Console du développeur ou l'IDE Force.com. Vous pouvez également utiliser l'API métadonnées. 2. Dans la liste associée Déclencheurs, cliquez sur Nouveau. 3. Cliquez sur Paramètres de version pour spécifier la version l'Apex et l'API utilisée dans ce déclencheur. Si votre organisation a des packages gérés installés depuis AppExchange, vous pouvez également spécifier la version de chaque package géré à utiliser avec ce déclencheur. Utilisez les valeurs par défaut pour toutes les versions. Elles associent le déclencheur à la version la plus récente du code Apex et de l'API, ainsi que de chaque package géré. Vous pouvez spécifier une version antérieure d'un package géré si vous souhaitez accéder à des composants ou des fonctionnalités qui diffèrent de la version la plus récente du package. 4. Cliquez sur Déclencheur Apex, puis sélectionnez la case Est actif si le déclencheur doit être compilé et activé. N'activez pas cette case si vous souhaitez seulement stocker le code dans les métadonnées de votre organisation. Cette case est activée par défaut. 5. Dans la zone de texte Corps, saisissez le code Apex du déclencheur. Un seul déclencheur peut contenir jusqu'à 1 million de caractères. Pour définir un déclencheur, utilisez la syntaxe suivante : trigger triggerName on ObjectName (trigger_events) { code_block } où trigger_events peut être une liste de valeurs séparées par une virgule comportant un ou plusieurs des événements suivants : • before insert Invocation de code Apex • • • • • • salesforce | Définition de déclencheurs | 105 before update before delete after insert after update after delete after undelete Remarque: • • Vous pouvez utiliser le mot clé webService uniquement dans un déclencheur au sein d'une méthode définie comme asynchrone, c.-à-d. définie avec le mot clé @future. Un déclencheur appelé par une action insert, delete ou update pour une tâche ou un événement récurrent entraîne une erreur d'exécution si le déclencheur est appelé en masse à partir de l'API Force.com. 6. Cliquez sur Enregistrer. Remarque: Les déclencheurs sont stockés avec un indicateur isValid défini sur true si les métadonnées dépendantes n'ont pas changé depuis la dernière compilation du déclencheur. Si des modifications sont apportées aux noms ou aux champs d'objet utilisés dans le déclencheur, même si elles sont mineures (par exemple, dans la description d'un champ ou d'un objet), l'indicateur isValid est défini sur false jusqu'à ce que le compilateur Apex réexécute le code. La recompilation est exécutée à l'exécution suivante du déclencheur ou lorsqu'un utilisateur réenregistre le déclencheur dans les métadonnées. Si un champ référence un enregistrement supprimé, Salesforce efface la valeur du champ de référence par défaut. Alternativement, vous pouvez empêcher la suppression d'enregistrements s'ils font partie d'une relation de référence. L'éditeur de déclencheur Apex Lors d'une modification dans Visualforce ou Apex, dans le pied de page du mode de développement Visualforce ou via Configuration, un éditeur est disponible avec les fonctionnalités suivantes : Mise en évidence de la syntaxe L'éditeur applique automatiquement la mise évidence de la syntaxe pour les mots clés, et l'ensemble des fonctions et opérateurs. Rechercher ( ) Permet de rechercher du texte dans la page, la classe ou le déclencheur actuel. Pour utiliser la recherche, saisissez une chaîne dans la zone de texte Rechercher, puis cliquez sur Rechercher suivant. • Pour remplacer une chaîne de recherche trouvée, saisissez la nouvelle chaîne dans la zone de texte Remplacer, puis cliquez sur remplacer pour remplacer uniquement cette instance, ou sur Remplacer tout pour remplacer cette instance et toutes les autres instances de la chaîne de recherche dans la page, la classe ou le déclencheur. • Pour rendre l'opération de recherche sensible à la casse, sélectionnez l'option Respecter la casse. • Pour utiliser une expression régulière comme chaîne de recherche, sélectionnez l'option Expressions régulières. Les expressions régulières suivent les règles d'expression standard de JavaScript. Une recherche utilisant des expressions régulières peut trouver des chaînes qui occupent plusieurs lignes. Si vous utilisez l'opération de remplacement avec une chaîne trouvée par une expression régulière, l'opération de remplacement peut aussi relier des variables de groupe d'expressions régulières ($1, $2, etc.) à partir de la chaîne de recherche trouvée. Par exemple, pour remplacer une balise <h1> par <h2> et conserver intacts tous les attributs de la <h1> d'origine, recherchez <h1(\s+)(.*)> et remplacez-la par <h2$1$2>. Invocation de code Apex salesforce | Déclencheurs et instructions Merge | 106 Accéder à la ligne ( ) Ce bouton permet de mettre en évidence un numéro de ligne spécifié. Si la ligne n'est pas affichée, l'éditeur navigue jusqu'à la ligne. Annuler ( ) et Répéter ( ) Utilisez Annuler pour inverser une action de modification et Répéter pour recréer une action de modification précédemment annulée. Taille de la police Sélectionnez une taille de police dans la liste déroulante pour contrôler la taille des caractères affichés dans l'éditeur. Position de ligne et de colonne La position de ligne et de colonne du curseur est affichée dans la barre d'état en bas de l'éditeur. Vous pouvez l'utiliser avec la fonction Accéder à la ligne ( ) pour accélérer la navigation dans l'éditeur. Nombre de lignes et de caractères Le nombre total de lignes et de caractères est affiché dans la barre d'état en bas de l'éditeur. Déclencheurs et instructions Merge Les événements de fusion n'activent par leurs propres événements de déclencheur. À la place, ils déclenchent des événements de suppression et de mise à jour, comme suit : Suppression des enregistrements perdus Une opération de fusion unique déclenche un événement de suppression unique pour tous les enregistrements supprimés dans la fusion. Pour déterminer les enregistrements supprimés suite à une opération de fusion, utilisez le champ MasterRecordId dans Trigger.old. Lorsqu'un enregistrement est supprimé après avoir perdu dans une opération de fusion, son champ MasterRecordId est défini sur l'ID de l'enregistrement gagnant. Le champ MasterRecordId est défini uniquement dans les événements de déclencheur after delete. Si votre application nécessite un traitement spécial pour les enregistrements supprimés suite à une fusion, vous devez utiliser l'événement de déclencheur after delete. Mise à jour de l'enregistrement gagnant Une opération de fusion unique déclenche un événement de mise à jour unique seulement pour l'enregistrement gagnant. Tout enregistrement enfant associé à un nouveau parent suite à l'opération de fusion n'active pas de déclencheur. Par exemple, si deux contacts sont fusionnés, seuls les déclencheurs delete contact et update contact sont activés. Aucun déclencheur n'est activé pour les enregistrements associés au contact, tels que les comptes ou les opportunités. Lors d'une fusion, la séquence des événements est la suivante : 1. Le déclencheur before delete est activé. 2. Le système supprime les enregistrements nécessaires pour la fusion, attribue de nouveaux enregistrements parents aux enregistrements enfants, puis définit le champ MasterRecordId dans les enregistrements supprimés. 3. Le déclencheur after delete est activé. 4. Le système effectue les mises à jour spécifiques requises pour l'enregistrement principal. Les déclencheurs update normaux s'appliquent. Invocation de code Apex salesforce | Déclencheurs et enregistrements restaurés | 107 Déclencheurs et enregistrements restaurés Le déclencheur after undelete fonctionne uniquement avec des enregistrements restaurés, c.-à-d. qui ont été supprimés puis restaurés à partir de la Corbeille via l'instruction DML undelete. Ils sont également appelés enregistrements undeleted. Les événements de déclencheur after undelete sont exécutés uniquement sur des objets de niveau supérieur. Par exemple, si vous supprimez un compte, une opportunité peut également être supprimée. Lorsque vous restaurez le compte à partir de la Corbeille, l'opportunité est également restaurée. Si un événement de déclencheur after undelete est associé au compte et à l'opportunité, seul l'événement de déclencheur after undelete du compte est exécuté. L'événement de déclencheur after undelete est activé uniquement pour les objets suivants : • • • • • • • • • • • • • • Compte Ressource Campagne Requête Contact Document de contact Contrat Objets personnalisés Événement Piste Opportunité Produit Solution Tâche Déclencheurs et séquence d'exécution Lorsque vous sauvegardez un enregistrement avec une instruction insert, update ou upsert, Salesforce exécute les événements suivants dans l'ordre. Remarque: Avant que Salesforce n'exécute ces événements sur le serveur, le navigateur exécute une validation JavaScript si l'enregistrement contient des champs de liste de sélection dépendante. La validation limite chaque champ de la liste à ses valeurs disponibles. Aucune validation n'est exécutée côté client. Sur le serveur, Salesforce effectue les opérations suivantes : 1. Chargement de l'enregistrement d'origine à partir de la base de données ou initialisation de l'enregistrement pour une instruction upsert. 2. Chargement des nouvelles valeurs de champ de l'enregistrement à partir de la requête et remplacement des anciennes valeurs. Si la requête provient d'une page de modification de l'interface utilisateur standard, Salesforce exécute une validation système pour vérifier les points suivants dans l'enregistrement : • • • Conformité avec les règles spécifiques à la présentation Présence des valeurs requises au niveau de la présentation et au niveau de la définition du champ Validité des formats de champ Invocation de code Apex • salesforce | Déclencheurs et séquence d'exécution | 108 Longueur de champ maximale Salesforce n'effectue pas de validation système à cette étape lorsque la requête provient d'autres sources, notamment d'une application Apex ou d'un appel API SOAP. 3. Exécute tous les déclencheurs before. 4. Réexécute la plupart des étapes de validation système, notamment pour vérifier que tous les champs obligatoires contiennent une valeur non null, et exécute toute règle de validation définie par utilisateur. La seule validation système que Salesforce n'exécute pas une deuxième fois (lorsque la requête provient d'une page de modification de l'interface utilisateur standard) est l'application des règles spécifiques à la présentation. 5. Sauvegarde l'enregistrement dans la base de données, mais sans l'appliquer. 6. Exécute tous les déclencheurs after. 7. Exécute les règles d'attribution. 8. Exécute les règles de réponse automatique. 9. Exécute les règles de workflow. 10. Si des mises à jour de champ de workflow existent, met de nouveau à jour l'enregistrement. 11. Si l'enregistrement a été actualisé avec des mises à jour de champ de workflow, active une nouvelle fois (une nouvelle fois seulement) les déclencheurs before et after, en plus des validations standard. Les règles de validation personnalisées ne sont pas réexécutées. Remarque: Les déclencheurs before et after sont activés une nouvelle fois uniquement si des éléments doivent être mis à jour. Si les champs ont déjà été définis sur une valeur, les déclencheurs ne sont pas activés de nouveau. 12. Exécute les règles d'escalade. 13. Si l'enregistrement contient un champ récapitulatif de cumul ou fait partie d'un workflow inter-objets, il effectue des calculs et met à jour le champ récapitulatif de cumul dans l'enregistrement parent. L'enregistrement parent passe par la procédure de sauvegarde. 14. Si l'enregistrement parent est mis à jour, et qu'un enregistrement grand-parent contient un champ récapitulatif de cumul ou fait partie d'un workflow inter-objets, il effectue des calculs et met à jour le champ récapitulatif de cumul dans l'enregistrement parent. L'enregistrement grand-parent passe par la procédure de sauvegarde. 15. Exécute l'évaluation Partage basé sur des critères. 16. Applique toutes les opérations DML à la base de données. 17. Exécute une logique post-application, par exemple envoi d'e-mails. Remarque: Durant un enregistrement récursif, Salesforce ignore les étapes 7 à 14. Considérations supplémentaires Notez les points suivants lors de l'utilisation de déclencheurs : • • Si l'option Activer la validation et les déclencheurs à partir de la conversion de piste est sélectionnée, que la conversion de piste crée une opportunité et que cette opportunité a des déclencheurs Apex before associés, les déclencheurs sont exécutés immédiatement après la création de l'opportunité et avant la création du rôle de contact de l'opportunité. Pour plus d'informations, reportez-vous à « Personnalisation des paramètres de piste » dans l'aide en ligne de Salesforce. Si vous utilisez des déclencheurs before pour définir les valeurs Stage et Forecast Category d'un enregistrement d'opportunité, le comportement est le suivant : Invocation de code Apex salesforce | Opérations n'invoquant pas de déclencheurs | 109 ◊ Si vous définissez les valeurs Stage et Forecast Category, l'enregistrement d'opportunité contient ces valeurs exactes. ◊ Si vous définissez la valeur Stage, mais pas Forecast Category, la valeur Forecast Category de l'enregistrement d'opportunité applique par défaut la valeur associée à Stage dans le déclencheur. ◊ Si vous réinitialisez la valeur Stage sur une valeur spécifiée dans un appel API ou entrante provenant de l'interface utilisateur, la valeur Forecast Category doit également provenir de l'appel API ou de l'interface utilisateur. Si aucune valeur n'est spécifiée pour Forecast Category et que la valeur entrante Stage est différente de la valeur Stage du déclencheur, Forecast Category applique par défaut la valeur associée à Stage dans le déclencheur. Si la valeur Stage du déclencheur et la valeur Stage entrante sont identiques, Forecast Category n'est pas appliquée par défaut. • Si vous clonez une opportunité avec des produits, les événements suivants se produisent dans l'ordre : 1. L'opportunité parent est enregistrée conformément à la liste des événements indiqués ci-dessus. 2. Les produits d'opportunité sont enregistrés conformément à la liste des événements indiqués ci-dessus. Remarque: Si des erreurs se produisent dans un produit d'opportunité, vous devez renvoyer l'opportunité et corriger les erreurs avant le clonage. Si des produits d'opportunité contiennent des champs personnalisés uniques, vous devez les renseigner avec des valeurs nulles avant de cloner l'opportunité. • Trigger.old contient une version des objets antérieure à la mise à jour spécifique qui a activé le déclencheur. Il existe cependant une exception : Lorsqu'un enregistrement est mis à jour, déclenchant ainsi la mise à jour d'un champ de règle de workflow, dans le dernier déclencheur update, Trigger.old ne contient pas la version de l'objet immédiatement antérieur à la mise à jour du workflow, mais l'objet antérieur à la mise à jour initiale effectuée. Par exemple, supposons qu'un enregistrement existant contient un champ numérique avec une valeur initiale de 1. Un utilisateur met à jour ce champ sur 10, une mise à jour de champ de workflow est exécutée et incrémente la valeur à 11. Dans le déclencheur update qui est exécuté après la mise à jour du champ de workflow, la valeur du champ de l'objet obtenue par Trigger.old correspond à la valeur d'origine 1, plutôt que 10, comme ce serait normalement le cas. Opérations n'invoquant pas de déclencheurs Les déclencheurs sont invoqués uniquement pour les opérations DML (langage de manipulation de données) qui sont initiées ou traitées par le serveur d'applications Java. Par conséquent, actuellement certaines opérations en masse système n'invoquent pas de déclencheurs. Voici quelques exemples : • • • • • • • • • • • • Opérations de suppression en cascade. Les enregistrements qui n'ont pas initié une suppression delete n'entraînent pas d'évaluation de déclencheur. Mise à jour en cascade d'enregistrements enfants associés à un nouveau parent suite à une opération de fusion Modifications en masse de statuts de campagne Transferts en masse de services Mises à jour en masse d'adresses Transferts en masse de requêtes d'approbation Actions en masse de messagerie Modification des types de données de champ personnalisé Renommage ou remplacement de listes de sélection Gestion de catalogues Modification du service par défaut d'un utilisateur avec l'option de transfert de service activée Modifications des objets suivants : Invocation de code Apex salesforce | Considérations sur les entités et les champs dans les déclencheurs | 110 ◊ BrandTemplate ◊ MassEmailTemplate ◊ Folder • Les déclencheurs update account ne sont pas activés avant ou après la modification d'un type d'enregistrement compte professionnel en compte personnel (ou compte personnel en compte professionnel). Remarque: Les opérations d'insertion, de mise à jour et de suppression sur des comptes personnels activent des déclencheurs de compte, pas des déclencheurs de contact. Les déclencheurs before associés aux opérations suivantes sont activés uniquement lors d'une conversion de piste si la validation et les déclencheurs sont activés pour la conversion de piste dans l'organisation : • • insert de comptes, contacts et opportunités update de comptes et contacts Les déclencheurs d'opportunité ne sont pas activés lorsque le propriétaire du compte change suite au changement du propriétaire de l'opportunité associée. Lorsque vous modifiez un produit d'opportunité dans une opportunité, ou lorsqu'une planification de produit d'opportunité change un produit d'opportunité, même si le produit d'opportunité change l'opportunité, les déclencheurs before et after et les règles de validation ne sont pas activés pour l'opportunité. Cependant, les champs récapitulatifs de cumul sont mis à jour et les règles de workflow associées à l'opportunité sont exécutées. Les méthodes PageReference getContent et getContentAsPDF ne sont pas autorisées dans des déclencheurs. Notez les points suivants concernant l'objet ContentVersion : • Les opérations de pack de contenu qui impliquent l'objet ContentVersion, y compris slides et slide autorevision, n'invoquent pas de déclencheurs. Remarque: Un pack de contenu est révisé lors de la révision l'une de ses diapositives. • • Les valeurs des champs TagCsv et VersionData sont disponibles dans les déclencheurs uniquement si la requête de création ou de mise à jour d'enregistrements ContentVersion provient de l'API. Vous ne pouvez pas utiliser les déclencheurs before ou after delete avec l'objet ContentVersion. Considérations sur les entités et les champs dans les déclencheurs Entité QuestionDataCategorySelection non disponible dans les déclencheurs After Insert Le déclencheur after insert, qui est activé après l'insertion d'un ou de plusieurs enregistrements Question, n'a pas accès aux enregistrements QuestionDataCategorySelection associé aux Questions insérées. Par exemple, la requête suivante ne renvoie aucun résultat dans un déclencheur after insert : QuestionDataCategorySelection[] dcList = [select Id,DataCategoryName from QuestionDataCategorySelection where ParentId IN :questions]; Invocation de code Apex salesforce | Considérations sur les entités et les champs dans les déclencheurs | 111 Champs non mis à jour dans les déclencheurs Before Certaines valeurs sont définies pendant l'opération de sauvegarde système qui est exécutée après l'activation des déclencheurs before. Par conséquent, ces champs ne peuvent pas être modifiés ni détectés avec précision dans un déclencheur before insert ou before update. Voici quelques exemples : • • • • • • • • • • • • Task.isClosed Opportunity.amount* Opportunity.ForecastCategory Opportunity.isWon Opportunity.isClosed Contract.activatedDate Contract.activatedById Case.isClosed Solution.isReviewed Id (pour tous les enregistrements)** createdDate (pour tous les enregistrements)** lastUpdated (pour tous les enregistrements) * Lorsque Opportunity ne contient pas de lineitems, Amount peut être modifié par un déclencheur before. ** Id et createdDate peuvent être détectés dans des déclencheurs before update, mais pas modifiés. Entités non prises en charge dans les déclencheurs Update Certains objets ne peuvent pas être mis à jour et, par conséquent, ne doivent pas avoir de déclencheurs before update et after update. • • FeedItem FeedComment Entités non prises en charge dans les déclencheurs After Undelete Certains objets ne peuvent pas être restaurés et, par conséquent, ne doivent pas avoir de déclencheurs after undelete. • • • • CollaborationGroup CollaborationGroupMember FeedItem FeedComment Considérations supplémentaires sur les objets Chatter Notez les points suivants sur les déclencheurs FeedItem et FeedComment : • • • Seuls les FeedItems de Type TextPost, LinkPost et ContentPost peuvent être insérés et, par conséquent, invoquer le déclencheur before ou after insert. Les mises à jour de statut d'utilisateur n'entraînent pas l'activation des déclencheurs FeedItem. Bien que les objets FeedPost soient pris en charge par l'API versions 18.0, 19.0 et 20.0, n'utilisez pas les déclencheurs insert ou delete enregistrés dans des versions antérieures à 21.0. Pour FeedItem, les champs suivants ne sont pas disponibles dans le déclencheur before insert : ◊ ContentSize ◊ ContentType De plus, le champ ContentData n'est disponible dans aucun déclencheur. Invocation de code Apex salesforce | Exceptions de déclencheur | 112 Pour les déclencheurs before insert et after insert FeedComment, les champs de ContentVersion associés à FeedComment (obtenu via FeedComment.RelatedRecordId) ne sont pas disponibles. Le code Apex utilise une sécurité supplémentaire lors de l'exécution dans un contexte Chatter. Pour publier dans un groupe privé, l'utilisateur qui exécute le code doit être membre de ce groupe. Si l'utilisateur n'est pas membre, vous pouvez définir le champ CreatedById en tant que membre du groupe dans l'enregistrement FeedItem. • • Notez les points suivants pour les objets CollaborationGroup et CollaborationGroupMember : Lors de la mise à jour de CollaborationGroupMember, CollaborationGroup est également mis à jour automatiquement pour s'assurer que le nombre de membres est correct. Ainsi, lorsque les déclencheurs update ou delete CollaborationGroupMember sont exécutés, les déclencheurs update CollaborationGroup sont également exécutés. • Exceptions de déclencheur Des déclencheurs peuvent être utilisés pour empêcher l'exécution d'opérations DML en appelant la méthode addError() dans un enregistrement ou un champ. Lors d'une utilisation sur des enregistrements Trigger.new dans des déclencheurs insert et update, et sur des enregistrements Trigger.old dans des déclencheurs delete, le message d'erreur personnalisé s'affiche dans l'interface de l'application et est consigné. Remarque: Pour l'utilisateur, le délai de réponse est réduit si les erreurs sont ajoutées aux déclencheurs before. Un sous-ensemble d'enregistrements en cours de traitement peut être marqué avec la méthode addError() : Si le déclencheur a été engendré par une instruction DML dans Apex, toute erreur entraîne l'annulation de l'opération complète. Cependant, le moteur d'exécution traite chaque enregistrement dans l'opération afin de compiler une liste d'erreurs exhaustive. Si le déclencheur a été engendré par un appel DML en masse dans l'API Force.com, le moteur d'exécution laisse de côté les enregistrements incorrects et tente d'effectuer une sauvegarde partielle des enregistrements qui n'ont généré aucune erreur. Reportez-vous à Gestion des exceptions DML en masse à la page 353. • • Si un déclencheur génère une exception non gérée, tous les enregistrements sont marqués avec une erreur et aucun traitement n'est effectué. Meilleures pratiques pour les déclencheurs et les requêtes en masse En développement, il est inexact de considérer que les invocations de déclencheurs ne contiennent jamais plus d'un enregistrement. Les déclencheurs Apex sont optimisés pour fonctionner en masse ce qui, par définition, nécessite que les développeurs écrivent la logique prenant en charge les opérations en masse. Voici un exemple d'un modèle de programmation défectueux. Il suppose qu'un seul enregistrement est extrait pendant une invocation de déclencheur. Bien qu'il puisse prendre en charge la plupart des événements d'interface utilisateur, il ne prend pas en charge les opérations en masse invoquées via l'API SOAP ou Visualforce. trigger MileageTrigger on Mileage__c (before insert, before update) { User c = [SELECT Id FROM User WHERE mileageid__c = Trigger.new[0].id]; } Invocation de code Apex salesforce | Planificateur Apex | 113 Voici un autre exemple d'un modèle de programmation défectueux. Il suppose que moins de 100 enregistrements sont extraits pendant une invocation de déclencheur. Si plus de 20 enregistrements sont extraits dans cette requête, le déclencheur dépasse la limite de requêtes SOQL de instructions 100 SELECT : trigger MileageTrigger on Mileage__c (before insert, before update) { for(mileage__c m : Trigger.new){ User c = [SELECT Id FROM user WHERE mileageid__c = m.Id]; } } Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Cet exemple montre le modèle correct pour prendre en charge la nature en masse des déclencheurs, tout en respectant les limitations du gouverneur : Trigger MileageTrigger on Mileage__c (before insert, before update) { Set<ID> ids = Trigger.new.keySet(); List<User> c = [SELECT Id FROM user WHERE mileageid__c in :ids]; } Ce modèle respecte la nature en masse du déclencheur en transmettant la collection Trigger.new à un ensemble, puis en utilisant l'ensemble dans une requête SOQL unique. Ce modèle capture tous les enregistrements entrants dans la requête, tout en limitant le nombre de requêtes SOQL. Meilleures pratiques pour la conception de programmes en masse Les meilleurs pratiques de conception de ce modèle sont les suivantes : • • Limiter le nombre d'opérations DML (langage de manipulation de données) en ajoutant des enregistrements à des collections et en effectuant les opérations DML sur ces collections. Limiter le nombre d'instructions SOQL en retraitant les enregistrements et en générant des ensembles, qui peuvent être placés dans une seule instruction SOQL utilisée avec la clause IN. Voir aussi : Quelles sont les limitations du langage Apex ? Planificateur Apex Pour invoquer l'exécution de classes Apex à des heures spécifiques, commencez par mettre en oeuvre l'interface Schedulable pour la classe, puis spécifiez la planification en utilisant la page Planifier Apex dans l'interface utilisateur de Salesforce, ou la méthode System.schedule. Invocation de code Apex salesforce | Planificateur Apex | 114 Pour plus d'informations sur la page Planifier Apex, reportez-vous à « Planification de classes Apex » dans l'aide en ligne de Salesforce. Important: Salesforce ajoute le processus à la file d'attente uniquement à l'heure prévue. L'exécution réelle peut être retardée en fonction de la disponibilité du service. Vous pouvez avoir 25 classes planifiées à la fois. Vous pouvez déterminer le nombre actuel en affichant la page Tâches planifiées dans Salesforce ou en programmant l'API SOAP pour interroger l'objet CronTrigger. Soyez très prudent(e) si vous envisagez de planifier une classe à partir d'un déclencheur. Assurez-vous que le déclencheur n'ajoute pas davantage de classes planifiées que les 25 autorisées. En particulier, considérez les mises à jour en masse d'API, les assistants d'importation, les modifications d'enregistrement en masse via l'interface utilisateur et toutes les situations dans lesquelles plusieurs enregistrements peuvent être mis à jour à la fois. Vous ne pouvez pas mettre à jour une classe Apex s'il existe une ou plusieurs tâches planifiées actives pour cette classe. Mise en oeuvre de l'interface Schedulable Pour planifier l'exécution d'une classe Apex à intervalles réguliers, commencez par écrire une classe Apex qui met en oeuvre l'interface Schedulable fournie par Salesforce. Le planificateur s'exécute en tant que système : toutes les classes sont exécutées, que l'utilisateur dispose ou non de l'autorisation d'exécution de la classe. Pour plus d'informations sur la définition d'autorisations de classe, reportez-vous à « Présentation de la sécurité de la classe Apex » dans l'aide en ligne de Salesforce. Pour surveiller ou arrêter l'exécution d'une tâche Apex planifiée en utilisant l'interface utilisateur de Salesforce, cliquez sur Votre nom > Configuration > Surveillance > Tâches planifiées. Pour plus d'informations, reportez-vous à « Surveillance des tâches prévues » dans l'aide en ligne de Salesforce. L'interface Schedulable inclut une méthode qui doit être mise en oeuvre, execute. global void execute(SchedulableContext sc){} La méthode mise en oeuvre doit être déclarée global ou public. Utilisez cette méthode pour instancier la classe que vous souhaitez planifier. Conseil: Bien qu'il soit possible d'effectuer un traitement supplémentaire dans la méthode execute, nous recommandons d'exécuter tout le traitement dans une classe séparée. L'exemple ci-dessous met en oeuvre l'interface Schedulable pour une classe appelée mergeNumbers : global class scheduledMerge implements Schedulable{ global void execute(SchedulableContext SC) { mergeNumbers M = new mergeNumbers(); } } L'exemple suivant utilise la méthode System.Schedule pour mettre en oeuvre la classe ci-dessus. scheduledMerge m = new scheduledMerge(); Invocation de code Apex salesforce | Planificateur Apex | 115 String sch = '20 30 8 10 2 ?'; system.schedule('Merge Job', sch, m); Vous pouvez également utiliser l'interface Schedulable avec des classes Apex par lot. L'exemple suivant met en oeuvre l'interface Schedulable pour une classe Apex appelée batchable : global class scheduledBatchable implements Schedulable{ global void execute(SchedulableContext sc) { batchable b = new batchable(); database.executebatch(b); } } Utilisez l'objet SchedulableContext poursuivre la tâche lorsqu'elle est planifiée. La méthode SchedulableContext getTriggerID renvoie l'ID de l'objet CronTrigger associé à cette tâche planifiée en tant que chaîne. Utilisez cette méthode pour suivre la progression de la tâche planifiée. Pour arrêter l'exécution d'une tâche qui a été planifiée, utilisez la méthode System.abortJob avec l'ID renvoyé par la méthode getTriggerID. Test du planificateur Apex L'exemple ci-dessous montre comment tester à l'aide du planificateur Apex. La méthode System.schedule lance un processus asynchrone. Cela signifie que lorsque vous testez un code Apex planifié, vous devez vous assurer que la tâche planifiée est terminée avant de tester les résultats. Utilisez les méthodes de Test startTest et stopTest sur la méthode System.schedule pour vous assurer qu'elle est terminée avant de poursuivre votre test. Tous les appels asynchrones effectués après la méthode startTest sont collectés par le système. Lors de l'exécution de stopTest, tous les processus asynchrones sont exécutés de façon synchrone. Si vous n'incluez pas la méthode System.schedule dans les méthodes startTest et stopTest, la tâche planifiée est exécutée à la fin de votre méthode de test pour le code Apex enregistré à l'aide de l'API Salesforce.com versions 25.0 et supérieures, mais pas dans les versions antérieures. Voici la classe à tester : global class TestScheduledApexFromTestMethod implements Schedulable { // This test runs a scheduled job at midnight Sept. 3rd. 2022 public static String CRON_EXP = '0 0 0 3 9 ? 2022'; global void execute(SchedulableContext ctx) { CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id = :ctx.getTriggerId()]; Invocation de code Apex salesforce | Planificateur Apex | 116 System.assertEquals(CRON_EXP, ct.CronExpression); System.assertEquals(0, ct.TimesTriggered); System.assertEquals('2022-09-03 00:00:00', String.valueOf(ct.NextFireTime)); Account a = [SELECT Id, Name FROM Account WHERE Name = 'testScheduledApexFromTestMethod']; a.name = 'testScheduledApexFromTestMethodUpdated'; update a; } } Le code suivant teste la classe ci-dessus : @istest class TestClass { static testmethod void test() { Test.startTest(); Account a = new Account(); a.Name = 'testScheduledApexFromTestMethod'; insert a; // Schedule the test job String jobId = System.schedule('testBasicScheduledApex', TestScheduledApexFromTestMethod.CRON_EXP, new TestScheduledApexFromTestMethod()); // Get the information from the CronTrigger API object CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime FROM CronTrigger WHERE id = :jobId]; Invocation de code Apex salesforce | Planificateur Apex | 117 // Verify the expressions are the same System.assertEquals(TestScheduledApexFromTestMethod.CRON_EXP, ct.CronExpression); // Verify the job has not run System.assertEquals(0, ct.TimesTriggered); // Verify the next time the job will run System.assertEquals('2022-09-03 00:00:00', String.valueOf(ct.NextFireTime)); System.assertNotEquals('testScheduledApexFromTestMethodUpdated', [SELECT id, name FROM account WHERE id = :a.id].name); Test.stopTest(); System.assertEquals('testScheduledApexFromTestMethodUpdated', [SELECT Id, Name FROM Account WHERE Id = :a.Id].Name); } } Utilisation de la méthode System.Schedule Après avoir mis en oeuvre une classe avec l'interface Schedulable, utilisez la méthode System.Schedule pour l'exécuter. Le planificateur s'exécute en tant que système : toutes les classes sont exécutées, que l'utilisateur dispose ou non de l'autorisation d'exécution de la classe. Remarque: Soyez très prudent(e) si vous envisagez de planifier une classe à partir d'un déclencheur. Assurez-vous que le déclencheur n'ajoute pas davantage de classes planifiées que les 25 autorisées. En particulier, considérez les mises à jour en masse d'API, les assistants d'importation, les modifications d'enregistrement en masse via l'interface utilisateur et toutes les situations dans lesquelles plusieurs enregistrements peuvent être mis à jour à la fois. La méthode System.Schedule utilise trois arguments : un nom pour la tâche, une expression utilisée pour représenter l'heure et la date d'exécution planifiée de la tâche, ainsi que le nom de la classe La syntaxe de cette expression est la suivante : Seconds Minutes Hours Day_of_month Month Day_of_week optional_year Remarque: Salesforce ajoute le processus à la file d'attente uniquement à l'heure prévue. L'exécution réelle peut être retardée en fonction de la disponibilité du service. La méthode System.Schedule utilise le fuseau horaire de l'utilisateur comme base pour toutes les planifications. Invocation de code Apex salesforce | Planificateur Apex | 118 Les valeurs de l'expression sont les suivantes : Nom Valeurs Caractères spéciaux Seconds 0 à 59 Aucun Minutes 0 à 59 Aucun Hours 0 à 23 , - * / Day_of_month 1 à 31 , - * ? / L W Month 1 à 12 ou comme suit : • JAN • FEB • MAR • APR • MAY • JUN • JUL • AUG • SEP • OCT • NOV • DEC , - * / Day_of_week 1 à 7 ou comme suit : • SUN • MON • TUE • WED • THU • FRI • SAT , - * ? / L # optional_year null ou 1970 à 2099 , - * / Les caractères spéciaux sont définis comme suit : Caractère spécial Description , Délimite les valeurs. Par exemple, utilisez JAN, MAR, APR pour spécifier plusieurs mois. - Spécifie une plage. Par exemple, utilisez JAN-MAR pour spécifier plusieurs mois. * Spécifie toutes les valeurs. Par exemple, si Month est spécifié en tant que *, la tâche est planifiée tous les mois. ? Spécifie aucune valeur précise. Disponible uniquement pour Day_of_month et Day_of_week, et généralement utilisé afin de spécifier une valeur pour l'un et pas pour l'autre. Invocation de code Apex salesforce | Planificateur Apex | 119 Caractère spécial Description / Spécifie des incréments. Le chiffre qui précède la barre oblique spécifie le début de l'intervalle et le chiffre qui suit la barre oblique indique la valeur de l'intervalle. Par exemple, si vous spécifiez 1/5 pour Day_of_month, la classe Apex est exécutée le cinquième jour de chaque mois, à compter du premier du mois. L Spécifie la fin d'une plage (dernier). Disponible uniquement pour Day_of_month et Day_of_week. Utilisé avec Day of month, L indique toujours le dernier jour du mois, par exemple le 31 janvier, le 29 février pour les années bissextiles, etc. Utilisé seul avec Day_of_week, signifie toujours 7 ou SAT (si samedi est le dernier jour de la semaine). Utilisé avec une valeur Day_of_week, signifie le dernier jour de ce type dans le mois. Par exemple, si vous spécifiez 2L, vous désignez le dernier lundi du mois. N'utilisez pas une plage de valeurs avec L, car vous risquez d'obtenir des valeurs inattendues. W Spécifie le jour de la semaine le plus proche (lundi à vendredi) d'un jour donné. Disponible uniquement pour Day_of_month. Par exemple, si vous spécifiez 20W et que le 20 est un samedi, la classe est exécutée le 19. Si vous spécifiez 1W et que le 1er est un samedi, la classe n'est pas exécutée le mois précédent, mais le 3, c.-à-d. le lundi suivant. Conseil: Utilisez L et W ensemble pour spécifier le dernier jour de la semaine du mois. Spécifie le énième jour du mois, sous le format weekday#day_of_month. Disponible uniquement pour Day_of_week. Le chiffre qui précède # spécifie le jour de la semaine (SUN-SAT, si dimanche est le premier jour de la semaine). Le nombre qui suit # spécifie le jour du mois. Par exemple, 2#2 signifie que la classe est exécutée de deuxième lundi de chaque mois. # Le tableau suivant présente des exemples d'utilisation de l'expression. Expression Description 0 0 13 * * ? La classe est exécutée tous les jours à 13 heures. 0 0 22 ? * 6L La classe est exécutée le dernier vendredi de chaque mois à 22 heures. 0 0 10 ? * MON-FRI La classe est exécutée du lundi au vendredi à 10 heures. 0 0 20 * * ? 2013 La classe est exécutée tous les jours à 8 heures pendant l'année 2013. Dans l'exemple suivant, la classe proschedule met en oeuvre l'interface Schedulable. L'exécution de la classe est planifiée à 8 heures le 13 février proschedule p = new proschedule(); String sch = '0 0 8 13 2 ?'; system.schedule('One Time Pro', sch, p); Invocation de code Apex salesforce | Blocs anonymes | 120 Meilleures pratiques et limitations du planificateur Apex • • • • • • Salesforce ajoute le processus à la file d'attente uniquement à l'heure prévue. L'exécution réelle peut être retardée en fonction de la disponibilité du service. Soyez très prudent(e) si vous envisagez de planifier une classe à partir d'un déclencheur. Assurez-vous que le déclencheur n'ajoute pas davantage de classes planifiées que les 25 autorisées. En particulier, considérez les mises à jour en masse d'API, les assistants d'importation, les modifications d'enregistrement en masse via l'interface utilisateur et toutes les situations dans lesquelles plusieurs enregistrements peuvent être mis à jour à la fois. Bien qu'il soit possible d'effectuer un traitement supplémentaire dans la méthode execute, nous recommandons d'exécuter tout le traitement dans une classe séparée. Vous pouvez avoir 25 classes planifiées à la fois. Vous pouvez déterminer le nombre actuel en affichant la page Tâches planifiées dans Salesforce ou en programmant l'API SOAP pour interroger l'objet CronTrigger. Vous ne pouvez pas utiliser les méthodes getContent et getContentAsPDF PageReference dans un code Apex planifié. Les appels de services Web synchrones ne sont pas pris en charge dans un code Apex planifié. Pour pouvoir effectuer des appels, créez en appel asynchrone en le plaçant dans une méthode annotée avec @future(callout=true), puis appelez cette méthode à partir d'un code Apex planifié. Cependant, si votre code Apex planifié exécute une tâche par lot, les appels sont pris en charge par la classe par lot. Reportez-vous à Utilisation d'un code Apex par lot. Blocs anonymes Un bloc anonyme est un code Apex qui n'est pas stocké dans les métadonnées, mais qui peut être compilé et exécuté en utilisant l'un des éléments suivants : • • • Console du développeur IDE Force.com L'appel de l'API SOAP executeAnonymous : ExecuteAnonymousResult executeAnonymous(String code) Vous pouvez utiliser des blocs anonymes pour évaluer rapidement et sur le champ un code Apex, par exemple dans la Console du développeur ou l'IDE Force.com, ou pour écrire un code qui change dynamiquement à l'exécution. Par exemple, vous pouvez écrire une application Web cliente qui récupère la saisie d'un utilisateur, par exemple un nom ou une adresse, et utilise un bloc anonyme de code Apex pour insérer un contact avec ce nom et cette adresse dans la base de données. Notez les points suivants sur le contenu d'un bloc anonyme (pour executeAnonymous, la chaîne code) : • • • • • • Il peut inclure des méthodes et des exceptions définies par l'utilisateur. Les méthodes définies par l'utilisateur ne peuvent pas inclure le mot clé static. Il n'est pas nécessaire d'appliquer manuellement des modifications dans la base de données. Si votre déclencheur Apex réussit, toute modification apportée à la base de données s'applique automatiquement. Si votre déclencheur Apex échoue, les modifications apportées à la base de données sont annulées. Contrairement aux classes et aux déclencheurs, les blocs anonymes s'exécutent sous l'utilisateur actuel et peuvent échouer lors de la compilation si le code viole les autorisations au niveau du champ et de l'objet de l'utilisateur. Il n'a pas d'autre portée que locale. Par exemple, bien qu'il soit illégal d'utiliser le modificateur d'accès global, il n'a aucune signification. La portée de la méthode est limitée au bloc anonyme. Invocation de code Apex salesforce | Apex dans AJAX | 121 Bien qu'une méthode définie par l'utilisateur puisse se référencer elle-même ou référencer des méthodes ultérieures sans déclaration forward (anticipée), les variables ne peuvent pas être référencées avant leur déclaration. Dans l'exemple suivant, le nombre entier int doit être déclaré, alors que myProcedure1 ne l'est pas : Integer int1 = 0; void myProcedure1() { myProcedure2(); } void myProcedure2() { int1++; } myProcedure1(); Le résultat renvoyé pour des blocs anonymes comprend : • • • Des informations de statut pour les phases de compilation et d'exécution de l'appel, qui incluent les erreurs générées. Le contenu du journal de débogage, qui inclut la sortie de tous les appels à la méthode System.debug (reportez-vous à Compréhension du journal de débogage à la page 256) La trace de pile Apex de toutes les exceptions d'exécution de code non détectées, qui inclut la classe, la méthode et le numéro de ligne de chaque élément de pile d'appel. Pour plus d'informations sur executeAnonymous(), reportez-vous à API SOAP et en-têtes SOAP pour Apex. Reportez-vous également à Utilisation de journaux dans la Console du développeur et à IDE Force.com. Apex dans AJAX Les outils AJAX comprennent une prise en charge intégrée de l'invocation de code Apex via des blocs anonymes ou des méthodes webService publiques. Pour cela, insérez les lignes suivantes dans votre code AJAX : <script src="/soap/ajax/15.0/connection.js" type="text/javascript"></script> <script src="/soap/ajax/15.0/apex.js" type="text/javascript"></script> Remarque: Pour des boutons AJAX, utilisez les formes alternatives. Pour invoquer un code Apex, utilisez l'une des deux méthodes suivantes : • Exécuter anonymement via sforce.apex.executeAnonymous (script). Cette méthode renvoie un résultat similaire au type de résultat de l'API, mais en tant que structure JavaScript. Invocation de code Apex • salesforce | Apex dans AJAX | 122 Utiliser un WSDL de classe. Par exemple, vous pouvez appeler la classe Apex suivante : global class myClass { webService static Id makeContact(String lastName, Account a) { Contact c = new Contact(LastName = lastName, AccountId = a.Id); return c.id; } } En utilisant le code JavaScript suivant : var account = sforce.sObject("Account"); var id = sforce.apex.execute("myClass","makeContact", {lastName:"Smith", a:account}); La méthode execute prend des types de données primitifs, des sObjects et des listes de primitifs ou de sObjects. Pour appeler une méthode webService sans paramètre, utilisez {} comme troisième paramètre pour sforce.apex.execute. Par exemple, pour appeler la classe Apex suivante : global class myClass{ webService static String getContextUserName() { return UserInfo.getFirstName(); } } Utilisez le code JavaScript suivant : var contextUser = sforce.apex.execute("myClass", "getContextUserName", {}); Remarque: Si un espace de noms a été défini pour votre organisation, vous devez l'inclure dans le code JavaScript lorsque vous invoquez la classe. Par exemple, pour appeler la classe ci-dessus, le code JavaScript doit être réécrit comme suit : var contextUser = sforce.apex.execute("myNamespace.myClass", "getContextUserName", {}); Pour vérifier si votre organisation a un espace de noms, connectez-vous à votre organisation Salesforce, puis accédez à Votre nom > Configuration > Créer > Packages. Si un espace de noms est défini, il est indiqué sous Paramètres du développeur. Les deux exemples produisent des valeurs JavaScript natives qui représentent le type de renvoi des méthodes. Invocation de code Apex salesforce | Apex dans AJAX | 123 Utilisez la ligne suivante pour afficher une fenêtre contextuelle contenant les informations de débogage. sforce.debug.trace=true; Chapitre 4 Classes, objets et interfaces Sujets : • • • • • • • • • • • Compréhension des classes Interfaces et extension de classes Mots clés Annotations Classes et conversion Différences entre les classes Apex et les classes Java Création d'une définition de classe Sécurité des classes Application des autorisations d'objet et de champ Préfixe d'espace de noms Paramètres de version Une classe est un modèle ou plan directeur à partir duquel les objets Apex sont créés. Les classes sont formées d'autres classes, de méthodes définies par l'utilisateur, de variables, de types d'exception et d'un code d'initialisation statique. Elles sont stockées dans l'application sous Votre nom > Configuration > Développer > Classes Apex. Une fois enregistrées, les méthodes de classe ou les variables peuvent être appelées par un autre code Apex, ou via l'API SOAP (ou outils AJAX) pour les méthodes désignées par le mot clé webService. Dans la plupart des cas, les concepts de classe présentés ici sont basés sur leurs semblables dans Java et sont faciles à comprendre pour les personnes qui connaissent Java. • • • • • • • Compréhension des classes : informations supplémentaires sur la création de classes dans Apex Interfaces et extension de classes : informations sur les interfaces Mot clés et Annotations : modificateurs supplémentaires pour des classes, des méthodes ou des variables Classes et conversions : attribution d'une classe d'un type de données à un autre Différences entre les classes Apex et les classes Java : en quoi Apex et Java diffèrent Création d'une définition de classe et Sécurité des classes : création d'une classe dans l'interface utilisateur de Salesforce et autoriser les utilisateurs à accéder aux classes Préfixe d'espace de noms et Paramètres de version : utilisation d'un préfixe d'espace de noms et de classes Apex de version Classes, objets et interfaces salesforce | Compréhension des classes | 125 Compréhension des classes Comme dans Java, vous pouvez créer des classes dans le langage Apex. Une classe est un modèle ou plan directeur à partir duquel les objets sont créés. Un object est une instance d'une classe. Par exemple, la classe PurchaseOrder décrit un bon de commande complet ainsi que toutes les actions possibles avec un bon de commande. Une instance de la classe PurchaseOrder est un bon de commande spécifique que vous envoyez ou recevez. Tous les objets ont un état et un comportement, c.-à-d. des informations qu'un objet connaît sur lui-même et des actions qu'il peut exécuter. L'état d'un objet BonCommande (qu'il connaît) inclut l'utilisateur qui l'a envoyé, la date et l'heure de création et s'il est marqué comme important. Le comportement d'un objet BonCommande (ce qu'il peut exécuter) inclut le contrôle d'inventaire, l'expédition d'un produit ou la notification un client. Une classe peut contenir des variables et des méthodes. Les variables sont utilisées pour spécifier l'état d'un objet, tel que le Name ou le Type de l'objet. Ces variables sont associées à une classe dont ils sont membres. Par conséquent, ils sont communément référencés en tant que variables de membre. Les méthodes sont utilisées pour contrôler le comportement, tel que getOtherQuotes ou copyLineItems. Une interface est semblable à une classe dans laquelle aucune méthode n'a été mise en oeuvre. Les signatures des méthodes sont présentes, mais le corps de chaque méthode est vide. Pour utiliser une interface, une autre classe doit la mettre en oeuvre en fournissant un corps pour toutes les méthodes incluses dans l'interface. Pour des informations générales sur les classes, les objets et les interfaces, reportez-vous à http://java.sun.com/docs/books/tutorial/java/concepts/index.html Définition de classes Apex Dans le langage Apex, vous pouvez définir des classes de niveau supérieur (également appelées classes externes), ainsi que des classes internes, c.-à-d. une classe définie dans une autre classe. Vous pouvez avoir des classes internes sur un seul niveau. Par exemple : public class myOuterClass { // Additional myOuterClass code here class myInnerClass { // myInnerClass code here } } Pour définir une classe, spécifiez les éléments suivants : 1. Modificateurs d'accès : • • Vous devez utiliser l'un des modificateurs d'accès (tels que public ou global) dans la déclaration en tant que classe de niveau supérieur. Il n'est pas nécessaire d'utiliser un modificateur d'accès dans la déclaration d'une classe interne. 2. Des modificateurs de définition facultatifs (tels que virtual, abstract, etc.). 3. Obligatoire : Le mot clé class suivi du nom de la classe. 4. Des extensions et/ou des mises en oeuvre facultatives. Classes, objets et interfaces salesforce | Définition de classes Apex | 126 Utilisez la syntaxe suivante pour définir des classes : private | public | global [virtual | abstract | with sharing | without sharing | (none)] class ClassName [implements InterfaceNameList | (none)] [extends ClassName | (none)] { // The body of the class } • • • • • • Le modificateur d'accès private déclare si cette classe est connue uniquement localement, c.-à-d. par cette section du code seulement. Il correspond à l'accès par défaut pour les classes internes. Par conséquent, si vous ne spécifiez aucun modificateur d'accès pour une classe interne, il est considéré comme private. Ce mot clé peut être utilisé uniquement avec des classes internes. Le modificateur d'accès public déclare si cette classe est visible dans votre application ou espace de noms. Le modificateur d'accès global déclare si cette classe est connue partout par l'ensemble du code Apex. Toutes les classes qui contiennent des méthodes définies avec le mot clé webService doivent être déclarées global. Si une méthode ou une classe interne est déclarée global, la classe externe de niveau supérieur doit également être définie comme global. Les mots clés with sharing et without sharing spécifient le mode de partage de cette classe. Pour plus d'informations, reportez-vous à Utilisation des mots clés with sharing ou without sharing à la page 156. Le modificateur de définition virtual déclare si cette classe autorise l'extension et les remplacements. Vous ne pouvez pas remplacer une méthode par le mot clé override si la classe n'a pas été définie comme virtual. Le modificateur de définition abstract déclare si cette classe contient des méthodes abstraites, c.-à-d. des méthodes dont seule la signature est déclarée et sans corps défini. Remarque: • • • Vous ne pouvez pas ajouter une méthode abstraite à une classe globale une fois la classe chargée dans une version de package Géré-Publié. Si la classe du package Géré-Publié est virtuelle, la méthode que vous pouvez lui ajouter doit également être virtuelle et avoir une mise en oeuvre. Vous ne pouvez pas remplacer une méthode virtuelle publique ou protégée d'une classe globale dans un package géré-installé. Pour plus d'informations sur les packages gérés, reportez-vous à Développement de code Apex dans des packages gérés à la page 280. Une classe peut mettre en oeuvre plusieurs interfaces, mais uniquement étendre une classe existante. Cette restriction signifie que le langage Apex ne prend pas en charge l'héritage multiple. Les noms d'interface dans les listes sont séparés par des virgules. Pour plus d'informations sur les interfaces, reportez-vous à Interfaces et extension de classes à la page 147. Pour plus d'informations sur les modificateurs d'accès de méthode et de variable, reportez-vous à Modificateurs d'accès à la page 138. Classes, objets et interfaces salesforce | Exemple de classe étendue | 127 Exemple de classe étendue L'exemple ci-dessous présente une classe étendue, avec toutes les fonctionnalités des classes Apex. Les mots clés et les concepts présentés dans l'exemple sont expliqués en détail dans ce chapitre. // Top-level (outer) class must be public or global (usually public unless they contain // a Web Service, then they must be global) public class OuterClass { // Static final variable (constant) – outer class level only private static final Integer MY_INT; // Non-final static variable - use this to communicate state across triggers // within a single request) public static String sharedState; // Static method - outer class level only public static Integer getInt() { return MY_INT; } // Static initialization (can be included where the variable is defined) static { MY_INT = 2; } // Member variable for outer class private final String m; // Instance initialization block - can be done where the variable is declared, // or in a constructor { m = 'a'; } // Because no constructor is explicitly defined in this outer class, an implicit, // no-argument, public constructor exists Classes, objets et interfaces salesforce | Exemple de classe étendue | 128 // Inner interface public virtual interface MyInterface { // No access modifier is necessary for interface methods - these are always // public or global depending on the interface visibility void myMethod(); } // Interface extension interface MySecondInterface extends MyInterface { Integer method2(Integer i); } // Inner class - because it is virtual it can be extended. // This class implements an interface that, in turn, extends another interface. // Consequently the class must implement all methods. public virtual class InnerClass implements MySecondInterface { // Inner member variables private final String s; private final String s2; // Inner instance initialization block (this code could be located above) { this.s = 'x'; } // Inline initialization (happens after the block above executes) private final Integer i = s.length(); // Explicit no argument constructor InnerClass() { Classes, objets et interfaces salesforce | Exemple de classe étendue | 129 // This invokes another constructor that is defined later this('none'); } // Constructor that assigns a final variable value public InnerClass(String s2) { this.s2 = s2; } // Instance method that implements a method from MyInterface. // Because it is declared virtual it can be overridden by a subclass. public virtual void myMethod() { /* does nothing */ } // Implementation of the second interface method above. // This method references member variables (with and without the "this" prefix) public Integer method2(Integer i) { return this.i + s.length(); } } // Abstract class (that subclasses the class above). No constructor is needed since // parent class has a no-argument constructor public abstract class AbstractChildClass extends InnerClass { // Override the parent class method with this signature. // Must use the override keyword public override void myMethod() { /* do something else */ } // Same name as parent class method, but different signature. // This is a different method (displaying polymorphism) so it does not need // to use the override keyword protected void method2() {} // Abstract method - subclasses of this class must implement this method abstract Integer abstractMethod(); } Classes, objets et interfaces salesforce | Exemple de classe étendue | 130 // Complete the abstract class by implementing its abstract method public class ConcreteChildClass extends AbstractChildClass { // Here we expand the visibility of the parent method - note that visibility // cannot be restricted by a sub-class public override Integer abstractMethod() { return 5; } } // A second sub-class of the original InnerClass public class AnotherChildClass extends InnerClass { AnotherChildClass(String s) { // Explicitly invoke a different super constructor than one with no arguments super(s); } } // Exception inner class public virtual class MyException extends Exception { // Exception class member variable public Double d; // Exception class constructor MyException(Double d) { this.d = d; } // Exception class method, marked as protected protected void doIt() {} } // Exception classes can be abstract and implement interfaces public abstract class MySecondException extends Exception implements MyInterface { } Classes, objets et interfaces salesforce | Exemple de classe étendue | 131 } Cet exemple de code illustre : • • • • • • • • • • • • • • • • • • La définition d'une classe de niveau supérieur (également appelée classe externe) Des variables statiques et des méthodes statiques dans la classe de niveau supérieur, ainsi que des blocs de code d'initialisation statiques Des variables et des méthodes de membre pour la classe de niveau supérieur Des classes sans constructeur défini par l'utilisateur ; elles ont un constructeur implicite, sans argument Une définition d'interface dans la classe de niveau supérieur Une interface qui étend une autre interface Des définitions de classe interne (d'un niveau) avec une classe de niveau supérieur Une classe qui met en oeuvre une interface (et par conséquent sa sous-interface associée) en implémentant des versions publiques de méthodes de signature Une définition et une invocation de constructeur de classe interne Une variable de membre de classe interne et une référence à cette classe en utilisant le mot clé this (sans argument) Un constructeur de classe interne qui utilise le mot clé this (sans argument) pour invoquer un constructeur différent Un code d'initialisation hors des constructeurs ; les deux variables where sont définies, ainsi que des blocs anonymes entre accolades ({}). Notez qu'ils exécutent chaque construction dans l'ordre dans lequel ils s'affichent dans le fichier, comme avec Java. Extension de classe et une classe abstraite Des méthodes qui remplacent des méthodes de classe de base (qui doivent être déclarées sur virtual) Le mot clé override pour des méthodes qui remplacent des méthodes de sous-classe Des méthodes abstraites et leur mise en oeuvre par des sous-classes concrètes Le modificateur d'accès protected Des exceptions en temps qu'objets de première classe, avec des membres, des méthodes et des constructeurs Cet exemple montre comment la classe ci-dessus peut être appelée par un autre code Apex : // Construct an instance of an inner concrete class, with a user-defined constructor OuterClass.InnerClass ic = new OuterClass.InnerClass('x'); // Call user-defined methods in the class System.assertEquals(2, ic.method2(1)); // Define a variable with an interface data type, and assign it a value that is of // a type that implements that interface OuterClass.MyInterface mi = ic; // Use instanceof and casting as usual OuterClass.InnerClass ic2 = mi instanceof OuterClass.InnerClass ? Classes, objets et interfaces salesforce | Déclaration de variables de classe | 132 (OuterClass.InnerClass)mi : null; System.assert(ic2 != null); // Construct the outer type OuterClass o = new OuterClass(); System.assertEquals(2, OuterClass.getInt()); // Construct instances of abstract class children System.assertEquals(5, new OuterClass.ConcreteChildClass().abstractMethod()); // Illegal - cannot construct an abstract class // new OuterClass.AbstractChildClass(); // Illegal – cannot access a static method through an instance // o.getInt(); // Illegal - cannot call protected method externally // new OuterClass.ConcreteChildClass().method2(); Cet exemple de code illustre : • • • • La construction de la classe externe La construction d'une classe interne et la déclaration d'un type d'interface interne Une variable déclarée en tant que type d'interface peut se voir attribuer une instance d'une classe qui met en oeuvre cette interface La conversion d'une variable d'interface en un type de classe qui met en oeuvre cette interface (après vérification en utilisant l'opérateur instanceof) Déclaration de variables de classe Pour déclarer une variable, spécifiez les éléments suivants : • • • • Facultatif : Des modificateurs, tels que public ou final, ainsi que static. Obligatoire : Le type de données de la variable, tel que String ou Boolean. Obligatoire : Le nom de la variable. Facultatif : La valeur de la variable. Classes, objets et interfaces salesforce | Définition de méthodes de classe | 133 Utilisez la syntaxe suivante lors de la définition d'une variable : [public | private | protected | global | final] [static] data_type variable_name [= value] Par exemple : private static final Integer MY_INT; private final Integer i = 1; Définition de méthodes de classe Pour définir une méthode, spécifiez les éléments suivants : Facultatif : Des modificateurs, tels que public ou protected. Obligatoire : Le type de données de la valeur renvoyé par la méthode, tel que String ou Integer. Utilisez void si la méthode ne renvoie aucune valeur. Obligatoire : Une liste de paramètres d'entrée pour la méthode, séparés par des virgules, chacun précédé de son type de données, et entre parenthèses (). En l'absence de paramètre, utilisez un ensemble de parenthèses vides. Une méthode ne peut inclure que 32 paramètres d'entrée. Obligatoire : Le corps de la méthode, entre accolades {}. Tout le code de la méthode, y compris les déclarations de variables locales, est inclus ici. • • • • Utilisez la syntaxe suivante lors de la définition d'une méthode : (public | private | protected | global ) [override] [static] data_type method_name (input parameters) { // The body of the method } Remarque: Vous pouvez utiliser override uniquement pour remplacer des méthodes dans des classes qui ont été définies comme virtual. Par exemple : public static Integer getInt() { return MY_INT; } Comme dans Java, les méthodes qui renvoient des valeurs peuvent également être exécutées en tant qu'instruction si leurs résultats ne sont pas attribués à une autre variable. Notez les points suivants pour les méthodes définies par les utilisateurs : Classes, objets et interfaces • • • • • salesforce | Définition de méthodes de classe | 134 Elles peuvent être utilisées partout où les méthodes système sont utilisées. Elles peuvent être récursives. Elles peuvent entraîner des effets secondaires, tels que des instructions DML insert qui initialisent des ID d'enregistrements sObject. Reportez-vous à Opérations DML (langage de manipulation de données) Apex à la page 327. Elles peuvent se référencer elles-mêmes ou référencer des méthodes définies ultérieurement dans la même classe ou un bloc anonyme. Le code Apex analyse les méthodes en deux phases, par conséquent les méthodes anticipées ne sont pas requises. Elles peuvent être polymorphiques. Par exemple une méthode nommée foo peut être mise en oeuvre de deux façons, une avec un seul paramètre Integer et une autre avec deux paramètres Integer. Selon que la méthode est appelée avec un ou deux paramètres Integer, l'analyseur Apex sélectionne la mise en oeuvre appropriée à exécuter. Si l'analyseur ne trouve pas de correspondance exacte, il recherche une correspondance approximative en utilisant des règles de coercition de type. Pour plus d'informations sur la conversion des données, reportez-vous à Compréhension des règles de conversion à la page 55. Remarque: Si l'analyseur trouve plusieurs correspondances approximatives, une erreur d'analyse est générée. • Lors de l'utilisation de méthodes void avec des effets secondaires, les méthodes définies par l'utilisateur sont généralement exécutées en tant qu'instructions de procédure autonomes dans un code Apex. Par exemple : System.debug('Here is a note for the log.'); • Elles peuvent inclure des instructions dans lesquelles les valeurs renvoyées sont exécutées en tant qu'instruction si leurs résultats ne sont pas attribués à une autre variable. Ce comportement est identique dans Java. Transmission d'arguments de méthode par valeur Dans le langage Apex, tous les arguments de type de données primitifs, tels que Nombre entier ou Chaîne, sont transmis en méthodes par valeur. Cela signifie que toute modification apportée aux arguments existe uniquement dans la portée de la méthode. Lorsque la méthode retourne, les modifications de l'argument sont perdues. Les arguments de type de données non primitifs, tels que sObjects, sont également transmis en méthodes par valeur. Cela signifie que lorsque la méthode retourne, l'argument passé référence toujours le même objet qu'avant l'appel de la méthode et ne peut pas être modifié pour pointer vers un autre objet. Cependant, les valeurs des champs de l'objet peuvent être modifiées dans la méthode. Dans les exemples suivants, des arguments de type de données primitif et non primitif sont transmis dans des méthodes. Exemple : Transmission d'arguments de type de données primitif Cet exemple montre comment un argument primitif de type String est transmis par valeur à une autre méthode. Dans cet exemple, la méthode debugStatusMessage crée une variable String, msg et lui attribue une valeur. Elle transmet ensuite cette variable en tant qu'argument à une autre méthode, qui modifie la valeur String. Cependant, comme String est un type primitif, il est transmis par valeur. Lors du renvoi de la méthode, la valeur de la variable d'origine msg est inchangée. Une instruction d'assertion vérifie que la valeur de msg correspond toujours à l'ancienne valeur. public class PassPrimitiveTypeExample { public static void debugStatusMessage() { String msg = 'Original value'; processString(msg); // The value of the msg variable didn't Classes, objets et interfaces salesforce | Définition de méthodes de classe | 135 // change; it is still the old value. System.assertEquals(msg, 'Original value'); } public static void processString(String s) { s = 'Modified value'; } } Exemple : Transmission d'arguments de type de données non primitif Cet exemple montre comment un argument List est transmis par valeur à une autre méthode et peut être modifié. Il indique également que l'argument List ne peut pas être modifié pour pointer vers un autre objet List. Pour commencer, la méthode createTemperatureHistory crée une variable, fillMe, qui est une liste de nombres entiers, et la transmet à une méthode. La méthode appelée renseigne cette liste avec des Integer qui représentent des valeurs de température arrondies. Lors du renvoi de la méthode, une assertion vérifie que le contenu de la variable List d'origine a changé et qu'elle contient maintenant cinq valeurs. Ensuite, l'exemple crée une deuxième variable List, createMe, et la transmet à une autre méthode. La méthode appelée attribue l'argument transmis à une nouvelle liste qui contient de nouvelles valeurs Integer. Lors du renvoi de la méthode, la variable createMe d'origine ne pointe pas vers la nouvelle liste, mais pointe toujours vers la liste d'origine, qui est vide. Une assertion vérifie que createMe ne contient aucune valeur. public class PassNonPrimitiveTypeExample { public static void createTemperatureHistory() { List<Integer> fillMe = new List<Integer>(); reference(fillMe); // The list is modified and contains five items // as expected. System.assertEquals(fillMe.size(),5); List<Integer> createMe = new List<Integer>(); referenceNew(createMe); // The list is not modified because it still points // to the original list, not the new list // that the method created. System.assertEquals(createMe.size(),0); } public static void reference(List<Integer> m) { Classes, objets et interfaces salesforce | Utilisation de constructeurs | 136 // Add rounded temperatures for the last five days. m.add(70); m.add(68); m.add(75); m.add(80); m.add(82); } public static void referenceNew(List<Integer> m) { // Assign argument to a new List of // five temperature values. m = new List<Integer>{55, 59, 62, 60, 63}; } } Utilisation de constructeurs Un constructeur est un code invoqué lors de la création d'un objet à partir du plan directeur de la classe. Il n'est pas nécessaire d'écrire un constructeur pour chaque classe. Si une classe n'a pas de constructeur défini par l'utilisateur, un constructeur public, implicite et sans argument est utilisé. La syntaxe d'un constructeur est similaire à une méthode, mais elle est différente de la définition d'une méthode, car elle n'a jamais de type de renvoi explicite et n'est pas héritée par l'objet créé à partir d'elle. Après avoir écrit le constructeur pour une classe, vous devez utiliser le mot clé new pour instancier un objet à partir de cette classe en utilisant ce constructeur. Par exemple, en utilisant la classe suivante : public class TestObject { // The no argument constructor public TestObject() { // more code here } } Un nouvel objet de ce type peut être instancié avec le code suivant : TestObject myTest = new TestObject(); Classes, objets et interfaces salesforce | Utilisation de constructeurs | 137 Si vous écrivez un constructeur qui prend des arguments, vous pouvez ensuite l'utiliser pour créer un objet utilisant ces arguments. Si vous créez un constructeur qui prend des arguments, mais souhaitez toutefois utiliser un constructeur sans argument, vous devez en inclure un dans votre code. Une fois un constructeur créé pour une classe, vous n'avez plus accès au constructeur public sans argument par défaut. Vous ne devez créer le vôtre. Dans le langage Apex, un constructeur peut être surchargé, c.-à-d. que plusieurs constructeurs peuvent exister pour une classe, chacun avec des paramètres différents. L'exemple suivant illustre une classe avec deux constructeurs : un sans argument et un autre qui prend un simple argument Integer. Il illustre également comment un constructeur appelle l'autre constructeur en utilisant la syntaxe this(...), également appelée chaînage des constructeurs. public class TestObject2 { private static final Integer DEFAULT_SIZE = 10; Integer size; //Constructor with no arguments public TestObject2() { this(DEFAULT_SIZE); // Using this(...) calls the one argument constructor } // Constructor with one argument public TestObject2(Integer ObjectSize) { size = ObjectSize; } } Les nouveaux objets de ce type peuvent être instanciés avec le code suivant : TestObject2 myObject1 = new TestObject2(42); TestObject2 myObject2 = new TestObject2(); Chaque constructeur que vous créez pour une classe doit avoir une liste d'arguments différente. Dans l'exemple suivant, tous les constructeurs sont possibles : public class Leads { // First a no-argument constructor public Leads () {} Classes, objets et interfaces salesforce | Modificateurs d'accès | 138 // A constructor with one argument public Leads (Boolean call) {} // A constructor with two arguments public Leads (String email, Boolean call) {} // Though this constructor has the same arguments as the // one above, they are in a different order, so this is legal public Leads (Boolean call, String email) {} } Lorsque vous définissez une nouvelle classe, vous définissez un nouveau type de données. Vous pouvez utiliser un nom de classe partout où vous pouvez utiliser des noms de type de données, tels que String, Boolean ou Account. Si vous définissez une variable dont le type est une classe, tout objet que vous lui attribuez doit être une instance de cette classe ou sous-classe. Modificateurs d'accès Le langage Apex permet d'utiliser les modificateurs d'accès private, protected, public et global lors de la définition de méthodes et de variables. Des déclencheurs et des blocs anonymes peuvent également utiliser ces modificateurs d'accès, mais ils ne sont pas aussi utiles dans des parties de code Apex plus petites. Par exemple, la déclaration d'une méthode en tant que global dans un bloc anonyme ne vous permet pas de l'appeler de l'extérieur de ce code. Pour plus d'informations sur les modificateurs d'accès de classe, reportez-vous à Définition de classes Apex à la page 125. Remarque: Les méthodes d'interface n'ont pas de modificateur d'accès. Elles sont toujours globales. Pour plus d'informations, reportez-vous à Interfaces et extension de classes à la page 147. Par défaut, une méthode ou une variable est visible uniquement par le code Apex dans la classe de définition. Vous devez explicitement spécifier une méthode ou une variable en tant que publique afin qu'elle soit disponible pour d'autres classes dans le même espace de noms d'application (reportez-vous à Préfixe d'espace de noms). Vous pouvez modifier le niveau de visibilité en utilisant les modificateurs d'accès suivants : private Correspond au modificateur par défaut, ce qui signifie que la méthode ou variable est accessible uniquement dans la classe Apex dans laquelle elle est définie. Si vous ne spécifiez pas de modificateur d'accès, la méthode ou la variable est private. protected Ce modificateur signifie que la méthode ou la variable est visible de toutes les classes internes dans la classe Apex de définition. Vous pouvez utiliser ce modificateur d'accès uniquement pour des méthodes d'instance et des variables de membre. Notez qu'il est plus permissif que le paramètre (privé) par défaut, semblable à Java. Classes, objets et interfaces salesforce | Statique et instance | 139 public Ce modificateur signifie que la méthode ou la variable peut être utilisée par n'importe quel code Apex dans cette application ou cet espace de noms. Remarque: Dans le langage Apex, le modificateur d'accès public est différent de celui dans Java. Il est destiné à décourager la liaison d'applications, afin de conserver le code de chaque application séparé. Dans le langage Apex, si vous souhaitez rendre un élément public comme dans Java, utilisez le modificateur d'accès global. global Ce modificateur signifie que la méthode ou la variable peut être utilisée par n'importe quel code Apex qui a accès à la classe, pas seulement par le code Apex dans la même application. Ce modificateur d'accès doit être utilisé pour n'importe quelle méthode qui doit être référencée hors de l'application, dans l'API SOAP ou par un autre code Apex. Si vous déclarez une méthode ou une variable comme global, vous devez également déclarer la classe qui le contient comme global. Remarque: Nous recommandons de n'utiliser le modificateur d'accès global que rarement, ou jamais. Les dépendances inter-applications sont difficiles à gérer. Pour utiliser le modificateur d'accès private, protected, public ou global, appliquez la syntaxe suivante : [(none)|private|protected|public|global] declaration Par exemple : private string s1 = '1'; public string gets1() { return this.s1; } Statique et instance Dans le langage Apex, vous pouvez avoir des méthodes, des variables et un code d'initialisation statiques. Les classes Apex ne peuvent pas être statiques. Vous pouvez également avoir des méthodes, des membres de variables et un code d'initialisation d'instance (sans modificateur), les variables locales : • • • Les méthodes, les variables ou le code d'initialisation statiques sont associés à une classe et sont autorisés dans des classes externes. Lorsque vous déclarez une méthode ou une variable en tant que static, elle est initialisée une seule fois au chargement de la classe. Les variables statiques ne sont pas transmises avec l'état de la vue pour une page Visualforce. Les méthodes, les variables de membre et le code d'initialisation d'instance sont associés à un objet particulier et n'ont pas de modificateur de définition. Lorsque vous déclarez des méthodes, des variables de membre ou un code d'initialisation d'instance, une instance de cet élément est créée avec chaque objet instancié à partir de la classe. Les variables locales sont associées au bloc de code dans lequel elles sont déclarées. Toutes les variables locales doivent être initialisées avant leur utilisation. Classes, objets et interfaces salesforce | Statique et instance | 140 L'exemple suivant présente une variable locale dont la portée est la durée du bloc de code if : Boolean myCondition = true; if (myCondition) { integer localVariable = 10; } Utilisation de méthodes et de variables statiques Vous pouvez utiliser des méthodes et des variables statiques uniquement avec des classes externes. Les classes internes n'ont pas de méthodes ou de variables statiques. Une méthode ou une variable statique ne nécessite pas une instance de la classe pour être exécutée. Toutes les variables de membre statiques dans une classe sont initialisées avant la création de n'importe quel objet de la classe. Cela inclut tous les blocs de code d'initialisation statiques. Elles sont toutes exécutées dans l'ordre dans lequel elles s'affichent dans la classe. Les méthodes statiques sont généralement utilisées en tant que méthodes utilitaires et ne dépendent jamais d'une valeur de variable de membre d'instance particulière. Une méthode statique est associée uniquement à une classe. Par conséquent, elle ne peut accéder à aucune valeur de variable de membre d'instance de cette classe. Les variables statiques sont statiques uniquement dans la portée de la requête. Elles ne sont pas statiques dans l'ensemble du serveur ni dans l'organisation entière. Utilisez des variables statiques pour stocker les informations qui sont partagées aux confins de la classe. Toutes les instances de la même classe partagent une copie unique de variables statiques. Par exemple, tous les déclencheurs qui sont engendrés par la même requête peuvent communiquer ensemble en affichant et en mettant à jour des variables statiques dans une classe associée. Un déclencheur récursif peut utiliser la valeur d'une variable de classe afin de déterminer quand il doit quitter la récursion. Prenons la classe suivante : public class p { public static boolean firstRun = true; } Un déclencheur qui utilise cette classe peut ensuite échouer de façon sélective à sa première exécution : trigger t1 on Account (before delete, after delete, after undelete) { if(Trigger.isBefore){ if(Trigger.isDelete){ if(p.firstRun){ Trigger.old[0].addError('Before Account Delete Error'); p.firstRun=false; } Classes, objets et interfaces salesforce | Statique et instance | 141 } } } Les variables statiques de classe ne peuvent pas être accédées via une instance de cette classe. Par conséquent, si la classe C a une variable statique S et que x est une instance de C, x.S n'est pas une expression légale. Cela est vrai pour les méthodes d'instance : si M() est une méthode statique, x.M() n'est pas légal. À la place, votre code doit se référer à ces identificateurs statiques en utilisant la classe : C.S et C.M(). Si une variable locale porte le même nom que la classe, ces méthodes et variables statiques sont masquées. Les classes internes se comportent comme des classes internes Java statiques, mais ne nécessitent pas le mot clé static. Les classes internes peuvent avoir des variables de membre d'instance comme des classes externes, mais il n'existe aucun pointeur implicite vers une instance de la classe externe (utilisant le mot clé this). Remarque: Pour un code Apex enregistré en utilisant l'API version 20.0 ou antérieure de Salesforce.com, si un appel API active un déclencheur, le lot de 200 enregistrements à traiter est divisé en lots de 100 enregistrements. Pour un code Apex enregistré en utilisant l'API versions 21.0 et supérieures de Salesforce.com, les lots d'API ne sont pas divisés. Notez que les valeurs de variable statique sont réinitialisées entre les lots, contrairement aux limitations du gouverneur. N'utilisez pas des variables statiques pour suivre les informations d'état entre les lots. Utilisation de méthodes et de variables d'instance Les méthodes et les variables de membre d'instance sont utilisées par une instance d'une classe, c.-à-d. par un objet. Les variables de membre d'instance sont déclarées dans une classe, mais pas dans une méthode. Généralement, les méthodes d'instance utilisent des variables de membre d'instance pour affecter le comportement de la méthode. Supposons que vous souhaitez une classe qui collecte deux points dimensionnels pour les tracer sur un graphique. Elle est illustrée dans la classe squelette ci-dessous, qui utilise des variables de membre pour contenir la liste des points et une classe interne afin de gérer la liste de points bidimensionnelle. public class Plotter { // This inner class manages the points class Point { Double x; Double y; Point(Double x, Double y) { this.x = x; this.y = y; } Double getXCoordinate() { Classes, objets et interfaces salesforce | Statique et instance | 142 return x; } Double getYCoordinate() { return y; } } List<Point> points = new List<Point>(); public void plot(Double x, Double y) { points.add(new Point(x, y)); } // The following method takes the list of points and does something with them public void render() { } } Utilisation d'un code d'initialisation Un code d'initialisation d'instance est un bloc de code sous le format suivant qui est défini dans une classe : { //code body } Le code d'initialisation d'instance est une classe exécutée chaque fois qu'un objet est instancié à partir de cette classe. Ces blocs de code sont exécutés avant le constructeur. Si vous ne souhaitez pas écrire votre propre constructeur pour une classe, vous pouvez utiliser un bloc de code d'initialisation d'instance afin d'initialiser des variables d'instance. Dans la plupart des cas, vous devez cependant appliquer à la variable une valeur par défaut ou utiliser le corps d'un constructeur pour effectuer l'initialisation et ne pas utiliser le code d'initialisation d'instance. Classes, objets et interfaces salesforce | Statique et instance | 143 Un code d'initialisation statique est un bloc de code précédé du mot clé static: static { //code body } Semblable à un autre code statique, un bloc de code d'initialisation statique est initialisé une seule fois lors de la première utilisation de la classe. Une classe peut inclure n'importe quel nombre de blocs de code d'initialisation d'instance ou statique. Il peuvent figurer n'importe où dans le corps du code. Les blocs de code sont exécutés dans l'ordre dans lequel ils s'affichent dans le fichier, de la même façon que dans Java. Vous pouvez utiliser un code d'initialisation statique pour initialiser des variables finales statiques et déclarer toute information statique, notamment un mappage de valeurs. Par exemple : public class MyClass { class RGB { Integer red; Integer green; Integer blue; RGB(Integer red, Integer green, Integer blue) { this.red = red; this.green = green; this.blue = blue; } } static Map<String, RGB> colorMap = new Map<String, RGB>(); static { colorMap.put('red', new RGB(255, 0, 0)); colorMap.put('cyan', new RGB(0, 255, 255)); Classes, objets et interfaces salesforce | Propriétés Apex | 144 colorMap.put('magenta', new RGB(255, 0, 255)); } } Propriétés Apex Une propriété Apex est semblable à une variable. Vous pouvez toutefois effectuer des opérations supplémentaires dans votre code sur une valeur de propriété avant qu'elle soit accédée ou renvoyée. Les propriétés peuvent être utilisées de différentes façons : elles peuvent valider des données avant d'effectuer une modification, entraîner une action lors de la modification de données (par exemple modifier la valeur d'autres variables de membre) ou exposer des données récupérées à partir d'autres sources (par exemple une autre classe). Les définitions de propriété comprennent un ou deux blocs de code représentant un accesseur get et un accesseur set : Le code d'un accesseur get est exécuté lors de la lecture de la propriété. Le code d'un accesseur set est exécuté lors de l'attribution d'une nouvelle valeur à la propriété. • • Une propriété contenant uniquement un accesseur get est considérée en lecture seule. Une propriété contenant uniquement un accesseur set est considérée en écriture seule. Une propriété avec les deux accesseurs est en lecture-écriture. Pour déclarer une propriété, utilisez la syntaxe suivante dans le corps d'une classe : Public class BasicClass { // Property declaration access_modifier return_type property_name { get { //Get accessor code block } set { //Set accessor code block } } } Où : • • • access_modifier correspond au modificateur d'accès de la propriété. Tous les modificateurs applicables à des variables peuvent également être appliqués à des propriétés. Ils comprennent : public, private, global, protected, static, virtual, abstract, override et transient. Pour plus d'informations sur les modificateurs d'accès, reportez-vous à Modificateurs d'accès à la page 138. return_type correspond au type de la propriété, tel que Integer, Double, sObject, etc. Pour plus d'informations, reportez-vous à Types de données à la page 29. property_name correspond au nom de la propriété. Classes, objets et interfaces salesforce | Propriétés Apex | 145 Par exemple, la classe suivante définit une propriété nommée prop. La propriété est publique. Elle renvoie un type de données Integer. public class BasicProperty { public integer prop { get { return prop; } set { prop = value; } } } Le segment de code suivant appelle la classe ci-dessus, en exerçant les accesseurs get et set : BasicProperty bp = new BasicProperty(); bp.prop = 5; // Calls set accessor System.assert(bp.prop == 5); // Calls get accessor Notez les points suivants : • • • • • • • • Le corps de l'accesseur get est semblable à celui d'une méthode. Il doit renvoyer une valeur de type de propriété. L'exécution de l'accesseur get est semblable à la lecture de la valeur de la variable. L'accesseur get doit se terminer par une instruction de renvoi. Il est préférable que votre accesseur get ne modifie pas l'état de l'objet sur lequel il est défini. L'accesseur set est semblable à une méthode dont le type de renvoi est void. Lorsque vous attribuez une valeur à la propriété, l'accesseur set est invoqué avec un argument qui fournit la nouvelle valeur. Lorsque l'accesseur set est invoqué, le système transmet un argument implicite au setter, appelé value, du même type de données que la propriété. Les propriétés ne peuvent pas être définies sur interface. Les propriétés Apex sont basées sur leurs semblables dans le langage C#, avec les différences suivantes : ◊ Les propriétés fournissent directement un stockage des valeurs. Il n'est pas nécessaire de créer des membres de support pour stocker les valeurs. ◊ Il est possible de créer des propriétés automatiques dans le langage Apex. Pour plus d'informations, reportez-vous à Utilisation de propriétés automatiques à la page 145. Utilisation de propriétés automatiques Aucun code supplémentaire n'est requis pour les propriétés dans leurs blocs de code d'accesseur get ou set. Vous pouvez à la place laisser les blocs de code d'accesseur get ou set vides afin de définir une propriété automatique. Les propriétés automatiques permettent d'écrire un code plus compact, moins difficile à déboguer et à gérer. Elles peuvent être déclarées en lecture seule, en lecture-écriture ou en écriture seule. L'exemple suivant créé trois propriétés automatiques : public class AutomaticProperty { public integer MyReadOnlyProp { get; } public double MyReadWriteProp { get; set; } Classes, objets et interfaces salesforce | Propriétés Apex | 146 public string MyWriteOnlyProp { set; } } Le segment de code suivant exerce ces propriétés : AutomaticProperty ap = new AutomaticProperty(); ap.MyReadOnlyProp = 5; // This produces a compile error: not writable ap.MyReadWriteProp = 5; // No error System.assert(MyWriteOnlyProp == 5); // This produces a compile error: not readable Utilisation de propriétés statiques Lorsqu'une propriété est déclarée static, les méthodes d'accesseur de la propriété sont exécutées dans un contexte statique. Cela signifie que les accesseurs n'ont pas accès aux variables de membre non statiques définies dans la classe. L'exemple suivant crée une classe avec des propriétés statiques et d'instance : public class StaticProperty { public static integer StaticMember; public integer NonStaticMember; public static integer MyGoodStaticProp { get{return MyGoodStaticProp;} } // The following produces a system error // public static integer MyBadStaticProp { return NonStaticMember; } public integer MyGoodNonStaticProp { get{return NonStaticMember;} } } Le segment de code suivant appelle les propriétés statiques et d'instance : StaticProperty sp = new StaticProperty(); // The following produces a system error: a static variable cannot be // accessed through an object instance // sp.MyGoodStaticProp = 5; // The following does not produce an error Classes, objets et interfaces salesforce | Interfaces et extension de classes | 147 StaticProperty.MyGoodStaticProp = 5; Utilisation de modificateurs d'accès dans des accesseurs de propriété Les accesseurs de propriété peuvent être définis avec leurs propres modificateurs d'accès. Si un accesseur inclut son propre modificateur d'accès, il remplace celui de la propriété. Le modificateur d'accès d'un accesseur individuel doit être plus restrictif que celui de la propriété. Par exemple, si la propriété est définie comme public, l'accesseur individuel ne peut pas être défini comme global. La définition de classe suivante présente des exemples supplémentaires : global virtual class PropertyVisibility { // X is private for read and public for write public integer X { private get; set; } // Y can be globally read but only written within a class global integer Y { get; public set; } // Z can be read within the class but only subclasses can set it public integer Z { get; protected set; } } Interfaces et extension de classes Une interface est semblable à une classe dans laquelle aucune méthode n'a été mise en oeuvre. Les signatures des méthodes sont présentes, mais le corps de chaque méthode est vide. Pour utiliser une interface, une autre classe doit la mettre en oeuvre en fournissant un corps pour toutes les méthodes incluses dans l'interface. Les interfaces peuvent fournir une couche d'abstraction à votre code. Elles séparent les mises en oeuvre spécifiques d'une méthode de la déclaration de cette méthode. Vous pouvez ainsi avoir plusieurs mises en oeuvre d'une méthode basées sur votre application spécifique. La définition d'une interface est semblable à la définition d'une nouvelle classe. Par exemple, une société peut avoir deux types de bon de commande, un type provenant des clients et un autre provenant des employés. Les deux correspondent à un type de bon de commande. Supposons que vous avez besoin d'une méthode pour accorder une remise. Le montant de la remise peut dépendre du type du bon de commande. Vous pouvez modéliser le concept général d'un bon de commande dans une interface, et avoir des mises en oeuvre spécifiques pour les clients et pour les employés. L'exemple suivant s'intéresse uniquement à l'aspect de remise d'un bon de commande. public class PurchaseOrders { // An interface that defines what a purchase order looks like in general public interface PurchaseOrder { // All other functionality excluded Double discount(); Classes, objets et interfaces salesforce | Interfaces et extension de classes | 148 } // One implementation of the interface for customers public virtual class CustomerPurchaseOrder implements PurchaseOrder { public virtual Double discount() { return .05; // Flat 5% discount } } // Employee purchase order extends Customer purchase order, but with a // different discount public class EmployeePurchaseOrder extends CustomerPurchaseOrder{ public override Double discount() { return .10; // It’s worth it being an employee! 10% discount } } } Notez les points suivants relatifs à l'exemple ci-dessus : • • • L'interface PurchaseOrder est définie comme prototype général. Les méthodes définies dans une interface n'ont pas de modificateur d'accès et contiennent uniquement leur signature. La classe CustomerPurchaseOrder met en oeuvre cette interface. Elle doit par conséquent fournir une définition pour la méthode discount. Comme dans le langage Java, toute classe qui met en oeuvre une interface doit définir toutes les méthodes contenues dans l'interface. La version employé du bon de commande élargit la version client. Une classe étend une autre classe en utilisant le mot clé extends. Une classe ne peut étendre qu'une seule autre classe, mais elle peut mettre en oeuvre plusieurs interfaces. Lorsque vous définissez une nouvelle interface, vous définissez un nouveau type de données. Vous pouvez utiliser un nom d'interface partout où vous pouvez utiliser un autre nom de type de données. Si vous définissez une variable dont le type est une interface, tout objet que vous lui attribuez doit être une instance de cette classe qui met en oeuvre l'interface, ou un type de données sous-interface. Une interface peut étendre une autre interface. Comme pour les classes, lorsqu'une interface étend une autre interface, toutes les méthodes et les propriétés de l'interface étendue sont disponibles pour l'interface qui étend. Reportez-vous également à Classes et conversion à la page 170. Remarque: Vous ne pouvez pas ajouter une méthode globale une fois la classe chargée dans une version de package Géré-Publié. Classes, objets et interfaces salesforce | Itérateurs personnalisés | 149 Itérateurs personnalisés Un itérateur traverse chaque élément d'une collection. Par exemple, dans une boucle while dans Apex, vous définissez une condition pour la boucle existante et vous devez fournir le moyen de traverser la collection, c.-à-d. un itérateur. Dans l'exemple suivant, count est incrémenté de 1 à chaque exécution de la boucle (count++) : while (count < 11) { System.debug(count); count++; } En utilisant l'interface Iterator, vous pouvez créer un ensemble d'instructions personnalisées permettant de traverser une liste via une boucle. Cela est utile pour des données existantes dans des sources extérieures à Salesforce, dont vous définissez généralement la portée en utilisant une instruction SELECT. Des opérateurs peuvent également être utilisés si vous avez plusieurs instructions SELECT. Utilisation d'itérateurs personnalisés Pour utiliser des itérateurs, vous devez créer une classe Apex qui met en oeuvre l'interface Iterator. L'interface Iterator inclut les méthodes d'instance suivantes : Nom Arguments Renvoie Description hasNext Boolean Renvoie true si la collection traversée inclut un autre élément, sinon renvoie false. next N'importe quel type Renvoie l'élément suivant dans la collection. Toutes les méthodes dans l'interface Iterator doivent être déclarées comme global ou public. Vous pouvez utiliser un itérateur personnalisé uniquement dans boucle while. Par exemple : IterableString x = new IterableString('This is a really cool test.'); while(x.hasNext()){ system.debug(x.next()); } Actuellement, les itérateurs ne sont pas pris en charge dans les boucles for. Utilisation d'itérateurs personnalisés avec Iterable Si vous ne souhaitez pas utiliser un itérateur personnalisé avec une liste, mais créer votre propre structure de données, vous pouvez utiliser l'interface Iterable pour générer la structure. L'interface Iterator inclut la méthode suivante : Classes, objets et interfaces Nom salesforce | Itérateurs personnalisés | 150 Arguments iterator Renvoie Description Classe d'itérateur Renvoie une référence à l'itérateur pour cette interface. La méthode iterator doit être déclarée global ou public. Elle crée une référence à l'itérateur que vous pouvez ensuite utiliser pour traverser la structure de données. Dans l'exemple suivant, un itérateur personnalisé itère via une collection : global class CustomIterable implements Iterator<Account>{ List<Account> accs {get; set;} Integer i {get; set;} public CustomIterable(){ accs = [SELECT Id, Name, NumberOfEmployees FROM Account WHERE Name = 'false']; i = 0; } global boolean hasNext(){ if(i >= accs.size()) { return false; } else { return true; } } global Account next(){ // 8 is an arbitrary // constant in this example // that represents the // maximum size of the list. Classes, objets et interfaces salesforce | Itérateurs personnalisés | 151 if(i == 8){return null;} i++; return accs[i-1]; } } Le code ci-dessus est appelé par : global class foo implements iterable<Account>{ global Iterator<Account> Iterator(){ return new CustomIterable(); } } Le code ci-dessous est une tâche par lot qui utilise un itérateur : global class batchClass implements Database.batchable<Account>{ global Iterable<Account> start(Database.batchableContext info){ return new foo(); } global void execute(Database.batchableContext info, List<Account> scope){ List<Account> accsToUpdate = new List<Account>(); for(Account a : scope){ a.Name = 'true'; a.NumberOfEmployees = 69; accsToUpdate.add(a); } update accsToUpdate; } global void finish(Database.batchableContext info){ } } Classes, objets et interfaces salesforce | Mots clés | 152 Mots clés Le langage Apex inclut les mots clés suivants : • • • • • • final instanceof super this transient with sharing et without sharing Utilisation du mot clé final Vous pouvez utiliser le mot clé final pour modifier des variables. Une valeur peut être attribuée une seule fois à des variables finales, dans leur déclaration ou dans le code d'initialisation. Vous devez attribuer une valeur dans l'un de ces emplacements. Les variables finales statiques peuvent être modifiées dans le code d'initialisation statique ou dans leur définition. Les variables finales de membre peuvent être modifiées dans des blocs de code d'initialisation, des constructeurs ou avec d'autres déclarations de variables. Pour définir une constante, marquez une variable comme static et final (reportez-vous à Constantes à la page 58). Les variables statiques non finales sont utilisées pour communiquer l'état au niveau de la classe (tel que l'état entre des déclencheurs). Toutefois, elles ne sont pas partagées entre les requêtes. Les méthodes et les classes sont finales par défaut. Vous ne pouvez pas utiliser le mot clé final dans la déclaration d'une classe ou d'une méthode. Cela signifie qu'elles ne peuvent pas être remplacées. Si vous souhaitez remplacer une méthode ou une classe, utilisez le mot clé virtual. • • • • • • Utilisation du mot clé instanceof Si vous souhaitez vérifier à l'exécution si un objet est une instance d'une classe particulière, utilisez le mot clé instanceof. Le mot clé instanceof peut être utilisé uniquement pour vérifier si le type de cible dans l'expression, à droite du mot clé, est une alternative viable pour le type d'expression déclaré, à gauche. Vous pouvez ajouter la vérification ci-dessous à la classe Report dans classes et exemple de conversion avant de reconvertir l'élément en objet CustomReport. If (Reports.get(0) instanceof CustomReport) { // Can safely cast it back to a custom report object CustomReport c = (CustomReport) Reports.get(0); } Else { // Do something with the non-custom-report. } Classes, objets et interfaces salesforce | Utilisation du mot clé super | 153 Utilisation du mot clé super Le mot clé super peut être utilisé par des classes qui sont étendues par des classes virtuelles ou abstraites. En utilisant super, vous pouvez remplacer des constructeurs et des méthodes de la classe parente. Par exemple, si vous avez la classe virtuelle suivante : public virtual class SuperClass { public String mySalutation; public String myFirstName; public String myLastName; public SuperClass() { mySalutation = 'Mr.'; myFirstName = 'Carl'; myLastName = 'Vonderburg'; } public SuperClass(String salutation, String firstName, String lastName) { mySalutation = salutation; myFirstName = firstName; myLastName = lastName; } public virtual void printName() { System.debug('My name is ' + mySalutation + myLastName); } public virtual String getFirstName() { return myFirstName; } } Classes, objets et interfaces salesforce | Utilisation du mot clé this | 154 Vous pouvez créer la classe suivante qui étend Superclass et remplace sa méthode printName : public class Subclass extends Superclass { public override void printName() { super.printName(); System.debug('But you can call me ' + super.getFirstName()); } } La sortie attendue lors de l'appel de Subclass.printName est My name is Mr. Vonderburg. But you can call me Carl. Vous pouvez également utiliser super pour appeler des constructeurs. Ajoutez le constructeur suivant à SubClass : public Subclass() { super('Madam', 'Brenda', 'Clapentrap'); } La sortie attendue de Subclass.printName est maintenant My name is Madam Clapentrap. But you can call me Brenda. Meilleures pratiques d'utilisation du mot clé super Seules les classes étendues à partir de classes virtual ou abstract peuvent utiliser super. Vous pouvez utiliser super uniquement dans des méthodes désignées par le mot-clé override. • • Utilisation du mot clé this Il existe deux façons d'utiliser le mot clé this. Vous pouvez utiliser le mot clé this en notation pointée, sans parenthèse, pour représenter l'instance actuelle de la classe dans laquelle il s'affiche. Utilisez ce format du mot clé this pour accéder à des variables et des méthodes d'instance. Par exemple : public class myTestThis { string s; { this.s = 'TestString'; } } Dans l'exemple ci-dessus, la classe myTestThis déclare une variable d'instance s. Le code d'initialisation renseigne la variable en utilisant le mot clé this. Classes, objets et interfaces salesforce | Utilisation du mot clé transient | 155 Vous pouvez également utiliser le mot clé this pour effectuer un chaînage de constructeurs, c.-à-d. appeler un autre constructeur dans un constructeur. Sous ce format, utilisez le mot clé this entre parenthèses. Par exemple : public class testThis { // First constructor for the class. It requires a string parameter. public testThis(string s2) { } // Second constructor for the class. It does not require a parameter. // This constructor calls the first constructor using the this keyword. public testThis() { this('None'); } } Lorsque vous utilisez le mot clé this dans un constructeur pour effectuer un chaînage de constructeurs, il doit correspondre à la première instruction dans le constructeur. Utilisation du mot clé transient Utilisez le mot clé transient pour déclarer des variables d'instance qui ne peuvent pas être enregistrées, et qui ne doivent pas être transmises dans l'état de la vue pour une page Visualforce. Par exemple : Transient Integer currentTotal; Vous pouvez également utiliser le mot clé transient dans des classes Apex qui peuvent être sérialisées, à savoir dans des contrôleurs, des extensions de contrôleur ou des classes qui mettent en oeuvre l'interface Batchable ou Schedulable. De plus, vous pouvez utiliser transient dans des classes qui définissent des types de champ déclarés dans les classes sérialisables. La déclaration de variables comme transient réduit la taille de l'état de la vue. Un cas d'utilisation courant du mot clé transient est un champ dans une page Visualforce requis uniquement pour la durée d'une requête de page, mais qui ne doit pas faire partie de l'état de la vue de la page et dont le calcul répété pendant une requête utiliserait trop de ressources système. Certains objets Apex sont automatiquement considérés comme transient, c.-à-d. que leurs valeurs ne sont pas enregistrées dans l'état de la vue de la page. Ces objets incluent : • • • • • PageReferences Classes XmlStream Collections automatiquement marquées comme transient uniquement si le type d'objet qu'elles contiennent est automatiquement marqué comme transient, par exemple une collection de points d'enregistrement La plupart des objets générés par des méthodes système, tels que Schema.getGlobalDescribe Instances de classe JSONParser Pour plus d'informations, reportez-vous à Prise en charge JSON à la page 490. Les variables statiques ne sont pas non plus transmises via l'état de la vue. Classes, objets et interfaces salesforce | Utilisation des mots clés with sharing ou without sharing | 156 L'exemple suivant contient une page Visualforce et un contrôleur personnalisé. Un clic sur le bouton Actualiser de la page entraîne la mise à jour de la date transient, car elle est recrée à chaque actualisation de la page. La date non transient conserve sa valeur d'origine, qui a été désérialisée de l'état de la vue et reste par conséquent inchangée. <apex:page controller="ExampleController"> T1: {!t1} <br/> T2: {!t2} <br/> <apex:form> <apex:commandLink value="refresh"/> </apex:form> </apex:page> public class ExampleController { DateTime t1; transient DateTime t2; public String getT1() { if (t1 == null) t1 = System.now(); return '' + t1; } public String getT2() { if (t2 == null) t2 = System.now(); return '' + t2; } } Utilisation des mots clés with sharing ou without sharing Le code Apex est généralement exécuté dans le contexte système, c.-à-d. que les autorisations de l'utilisateur actuel, la sécurité au niveau du champ et les règles de partage ne sont pas prises en compte pendant son exécution. Remarque: La seule exception à cette règle est un code Apex qui est exécuté avec l'appel executeAnonymous. executeAnonymous est toujours exécuté en utilisant les autorisations complètes de l'utilisateur actuel. Pour plus d'informations sur executeAnonymous, reportez-vous à Blocs anonymes à la page 120. Classes, objets et interfaces salesforce | Utilisation des mots clés with sharing ou without sharing | 157 Puisque ces règles ne s'appliquent pas, les développeurs qui utilisent le langage Apex doivent s'assurer de ne pas exposer accidentellement des données sensibles qui sont habituellement masquées par des autorisations utilisateur, une sécurité au niveau du champ ou des paramètres par défaut à l'échelle de l'organisation. Ils doivent se montrer particulièrement prudents avec les services Web, qui peuvent être restreints par des autorisations, mais être exécutés dans un contexte système une fois initié. La plupart du temps, le contexte système fournit le comportement approprié pour les opérations au niveau du système, telles que les déclencheurs et les services Web qui doivent accéder à toutes les données d'une organisation. Cependant, vous pouvez également spécifier que des classes Apex particulières doivent faire respecter les règles de partage applicables à l'utilisateur actuel (pour plus d'informations sur les règles de partage, reportez-vous à l'aide en ligne de Salesforce.com). Remarque: L'application de règles de partage en utilisant le mot clé with sharing n'impose pas les autorisations et la sécurité au niveau du champ de l'utilisateur. Le code Apex a toujours accès à l'ensemble des champs et des objets d'une organisation, pour s'assurer que l'exécution du code n'échoue pas en raison de champs ou d'objets masqués pour un utilisateur. Utilisez les mots clés with sharing lors de la déclaration d'une classe, pour imposer les règles de partage applicables à l'utilisateur actuel. Par exemple : public with sharing class sharingClass { // Code here } Utilisez les mots clés without sharing lors de la déclaration d'une classe, pour s'assurer que les règles de partage de l'utilisateur actuel ne sont pas appliquées. Par exemple : public without sharing class noSharing { // Code here } Si une classe n'est pas déclarée « with » ou « without sharing », les règles de partage actuelles restent en vigueur. Cela signifie que si la classe est appelée par une classe qui impose le partage, le partage s'applique pour la classe appelée. Les classes internes et les classes externes peuvent être déclarées with sharing. Le paramètre de partage s'applique à l'ensemble du code inclus dans la classe, y compris le code d'initialisation, les constructeurs et les méthodes. Les classes héritent de ce paramètre d'une classe parente, lorsqu'une classe étend ou met en oeuvre une autre classe, mais les classes internes n'héritent pas du paramètre de partage de leur classe conteneur. Par exemple : public with sharing class CWith { // All code in this class operates with enforced sharing rules. Classes, objets et interfaces salesforce | Utilisation des mots clés with sharing ou without sharing | 158 Account a = [SELECT . . . ]; public static void m() { . . . } static { . . . } { . . . } public c() { . . . } } public without sharing class CWithout { // All code in this class ignores sharing rules and operates // as if the context user has the Modify All Data permission. Account a = [SELECT . . . ]; . . . public static void m() { . . . // This call into CWith operates with enforced sharing rules // for the context user. When the call finishes, the code execution // returns to without sharing mode. CWith.m(); } Classes, objets et interfaces salesforce | Annotations | 159 public class CInner { // All code in this class executes with the same sharing context // as the code that calls it. // Inner classes are separate from outer classes. . . . // Again, this call into CWith operates with enforced sharing rules // for the context user, regardless of the class that initially called this inner class. // When the call finishes, the code execution returns to the sharing mode that was used to call this inner class. CWith.m(); } public class CInnerWithOut exends CWithout { // All code in this class ignores sharing rules because // this class extends a parent class that ignores sharing rules. } } ATTENTION: Rien ne garantit qu'une classe déclarée with sharing n'appelle pas un code qui fonctionne sans partage (without sharing). La sécurité au niveau de la classe reste nécessaire. De plus, toutes les requêtes SOQL ou SOSL qui utilisent PriceBook2 ignorent le mot clé with sharing. Tous les enregistrements PriceBook sont renvoyés, quelles que soient les règles de partage appliquées. L'application des règles de partage de l'utilisateur actuel peut affecter : • • Les requêtes SOQL et SOSL. Une requête peut renvoyer un nombre de lignes inférieur que si elle fonctionnait dans le contexte système. Les opérations DML. Une opération peut échouer si l'utilisateur actuel ne dispose pas des autorisations appropriées. Par exemple, lorsque l'utilisateur spécifie une valeur de clé étrangère qui existe dans l'organisation, mais à laquelle l'utilisateur actuel n'a pas accès. Annotations Une annotation Apex modifie la façon d'utiliser une méthode ou une classe, de la même façon que les annotations dans Java. Classes, objets et interfaces salesforce | Annotation Deprecated | 160 Les annotations sont définies avec le symbole @ initial suivi du mot clé approprié. Pour ajouter une annotation à une méthode, spécifiez-la immédiatement avant la définition de la méthode ou de la classe. Par exemple : global class MyClass { @future Public static void myMethod(String a) { //long-running Apex code } } Le langage Apex prend en charge les annotations suivantes : • • • • • • @Deprecated @Future @IsTest @ReadOnly @RemoteAction Les annotations REST Apex : ◊ ◊ ◊ ◊ ◊ @RestResource(urlMapping='/yourUrl') @HttpDelete @HttpGet @HttpPost @HttpPut Annotation Deprecated Utilisez l'annotation deprecated pour identifier des méthodes, des classes, des exceptions, des énumérations, des interfaces ou des variables qui ne peuvent plus être référencées dans les versions successives du package géré dans lequel elles résident. Elles sont utiles pour refactoriser un code dans des packages gérés dont les exigences évoluent. Les nouveaux abonnés ne peuvent pas afficher les éléments dépréciés, tandis que les éléments continuent à fonctionner pour les abonnés et intégrations d'API existants. L'extrait de code ci-dessous présente une méthode dépréciée. La même syntaxe peut être utilisée pour des classes, des exceptions, des énumérations, des interfaces ou des variables dépréciées. @deprecated // This method is deprecated. Use myOptimizedMethod(String a, String b) instead. global void myMethod(String a) { } Classes, objets et interfaces salesforce | Annotation Future | 161 Notez les règles suivantes lors de la dépréciation d'identificateurs Apex : Les packages non gérés ne peuvent pas contenir un code qui utilise le mot clé deprecated. Lorsqu'un élément Apex est déprécié, tous les modificateurs d'accès global qui référencent l'identificateur déprécié doivent également être dépréciés. Toute méthode globale qui utilise le type déprécié dans sa signature (dans un argument d'entrée ou dans le type de renvoi de la méthode) doit également être dépréciée. Un élément déprécié, tel qu'une méthode ou une classe, peut toujours être référencé en interne par le développeur du package. Les méthodes et les variables webService ne peuvent pas être dépréciées. Vous pouvez déprécier une enum, mais pas des valeurs enum individuelles. Vous pouvez déprécier une interface, mais pas des méthodes individuelles dans une interface Vous pouvez déprécier une classe abstraite, mais pas des méthodes abstraites individuelles dans une classe abstraite. Vous ne pouvez pas supprimer l'annotation deprecated pour annuler la dépréciation d'un élément dans Apex après avoir publié la version d'un package dans lequel cet élément est déprécié dans le code Apex. • • • • • • • Pour plus d'informations sur les versions de packages, reportez-vous à Développement de code Apex dans des packages gérés à la page 280. Annotation Future Utilisez l'annotation future pour identifier les méthodes qui sont exécutées de façon asynchrone. Lorsque vous spécifiez future, la méthode est exécutée lorsque Salesforce a des ressources disponibles. Par exemple, vous pouvez utiliser l'annotation future lors d'un appel de service Web asynchrone vers un service externe. Sans l'annotation, l'appel de service Web est effectué à partir du même fil qui exécute le code Apex, et aucun traitement supplémentaire n'est effectué jusqu'à la fin de l'appel (traitement synchrone). Les méthodes avec l'annotation future doivent être des méthodes statiques et peuvent renvoyer uniquement un type void. Pour effectuer une méthode dans une classe exécutée de façon asynchrone, définissez la méthode avec l'annotation future. Par exemple : global class MyFutureClass { @future static void myMethod(String a, Integer i) { System.debug('Method called with: ' + a + ' and ' + i); //do callout, other long running code } } L'extrait suivant montre comment spécifier qu'une méthode exécute un appel : @future (callout=true) public static void doCalloutFromFuture() { //Add code to perform callout Classes, objets et interfaces salesforce | Annotations IsTest | 162 } Vous pouvez spécifier (callout=false) pour empêcher une méthode d'effectuer des appels. Pour tester des méthodes définies avec l'annotation future, appelez la classe contenant la méthode dans un bloc de code startTest, stopTest. Tous les appels asynchrones effectués après la méthode startTest sont collectés par le système. Lors de l'exécution de stopTest, tous les processus asynchrones sont exécutés de façon synchrone. Les méthodes avec l'annotation future présentent les limitations suivantes : • 10 appels de méthode au maximum peuvent être effectués par invocation Apex Remarque: Les appels asynchrones, tels que @future ou executeBatch, appelés dans un bloc startTest, stopTest, ne sont pas pris en compte dans les limitations du nombre de tâches en file d'attente. • • • • Salesforce impose également une limite sur le nombre d'invocations de méthode future : 200 appels de méthode par licence utilisateur Salesforce complète, licence utilisateur Salesforce Platform ou licence utilisateur Force.com App Subscription, par 24 heures. Cette limitation s'applique à l'échelle de l'organisation. Les licences utilisateur Chatter Only, Utilisateurs clients de Chatter, Customer Portal User et Partner portal ne sont pas incluses dans le calcul de cette limitation. Par exemple, supposons que votre organisation possède trois licences Salesforce complètes, deux licences Salesforce Platform et 100 licences Customer Portal User. Votre organisation entière est limitée à 1000 appels de méthode par 24 heures, calculés par la formule 200 * (3+2), pas 200 * (3+2+100). Les paramètres spécifiés doivent être des types de données primitifs, des tableaux de types de données primitifs ou des collections de types de données primitifs. Les méthodes avec l'annotation future ne peuvent pas prendre des sObjects ou des objets en tant qu'arguments. Les méthodes avec l'annotation future ne peuvent pas être utilisées dans des contrôleurs Visualforce, ni dans des méthodes getMethodName ou setMethodName, ni dans le constructeur. Notez que toute méthode qui utilise l'annotation future requiert une attention particulière, car elle n'est pas nécessairement exécutée dans l'ordre dans lequel elle est appelée. Vous ne pouvez pas appeler une méthode annotée avec future à partir d'une méthode qui contient également l'annotation future. Vous ne pouvez pas non plus appeler un déclencheur à partir d'une méthode annotée qui appelle une autre méthode annotée. Les méthodes getContent et getContentAsPDF PageReference ne peuvent pas être utilisées dans des méthodes avec l'annotation future. Pour plus d'informations sur les appels, reportez-vous à Invocation d'appels en utilisant le langage Apex à la page 306. Voir aussi : Compréhension des limitations et des gouverneurs d'exécution Annotations IsTest Utilisez l'annotation isTest pour définir des classes ou des méthodes individuelles qui contiennent uniquement le code utilisé pour tester votre application. L'annotation isTest est semblable à la création de méthodes déclarées testMethod. Classes, objets et interfaces salesforce | Annotations IsTest | 163 Remarque: Les classes définies avec l'annotation isTest ne sont pas prises en compte dans les limitations de votre organisation de 3 Mo pour l'ensemble du code Apex. Les méthodes de test individuelles dans une classe non annotées avec isTest sont prises en compte dans la limitation de la taille du code de votre organisation. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. À compter du code Apex enregistré en utilisant l'API Salesforce.com version 24.0, les méthodes de test n'ont plus accès par défaut aux données préexistantes dans l'organisation. Néanmoins, le code de test enregistré en utilisant l'API Salesforce.com version 23.0 ou antérieure garde accès à toutes les données de l'organisation et son accès aux données reste inchangé. Reportez-vous à Isolation des données test de celles de l'organisation dans les tests unitaires à la page 187. Les classes et les méthodes définies comme isTest peuvent être private ou public. Les classes définies comme isTest doivent être de niveau supérieur. L'exemple suivant présente une classe de test privé qui contient deux méthodes de test. @isTest private class MyTestClass { // Methods for testing @isTest static void test1() { // Implement test code } @isTest static void test2() { // Implement test code } } L'exemple suivant présente une classe de test public qui contient deux méthodes utilitaires pour la création de données test : @isTest public class TestUtil { public static void createTestAccounts() { // Create some test accounts } public static void createTestContacts() { // Create some test contacts Classes, objets et interfaces salesforce | Annotations IsTest | 164 } } Les classes définies comme isTest ne peuvent pas être des interfaces ou des énumérations. Les méthodes d'une classe de test public peuvent être appelées uniquement à partir d'un test exécuté, c.-à-d. une méthode de test ou un code invoqué par une méthode de test, et ne peuvent pas être appelées par une requête non test. De plus, les méthodes de classe de test peuvent être invoquées en utilisant l'interface utilisateur ou l'API Salesforce. Pour plus d'informations, reportez-vous à Exécution de méthodes de test unitaire. Annotation IsTest(SeeAllData=true) Pour un code Apex enregistré en utilisant l'API Salesforce.com versions 24.0 et supérieures, utilisez l'annotation isTest(SeeAllData=true) pour accorder aux classes de test et aux méthodes de test individuelles l'accès à toutes les données de l'organisation, y compris aux données préexistantes que le test n'a pas créées. À compter du code Apex enregistré en utilisant l'API Salesforce.com version 24.0, les méthodes de test n'ont plus accès par défaut aux données préexistantes dans l'organisation. Néanmoins, le code de test enregistré en utilisant l'API Salesforce.com version 23.0 ou antérieure garde accès à toutes les données de l'organisation et son accès aux données reste inchangé. Reportez-vous à Isolation des données test de celles de l'organisation dans les tests unitaires à la page 187. Considérations sur l'annotation IsTest(SeeAllData=true) • • Si une classe de test est définie avec l'annotation isTest(SeeAllData=true), cette annotation s'applique à toutes ses méthodes de test, qu'elles soient définies avec l'annotation @isTest ou le mot clé testmethod. L'annotation isTest(SeeAllData=true) est utilisée pour ouvrir l'accès aux données lorsqu'elle s'applique au niveau de la classe ou de la méthode. Cependant, l'utilisation de isTest(SeeAllData=false) dans une méthode ne limite pas l'accès aux données de l'organisation pour cette méthode si la classe qu'elle contient a déjà été définie avec l'annotation isTest(SeeAllData=true). Dans ce cas, la méthode garde accès à l'ensemble des données de l'organisation. Cette exemple montre comment définir une classe de test avec l'annotation isTest(SeeAllData=true). Toutes les méthodes de test de cette classe ont accès à l'ensemble des données de l'organisation. // All test methods in this class can access all data. @isTest(SeeAllData=true) public class TestDataAccessClass { // This test accesses an existing account. // It also creates and accesses a new test account. static testmethod void myTestMethod1() { // Query an existing account in the organization. Account a = [SELECT Id, Name FROM Account WHERE Name='Acme' LIMIT 1]; System.assert(a != null); // Create a test account based on the queried account. Classes, objets et interfaces salesforce | Annotations IsTest | 165 Account testAccount = a.clone(); testAccount.Name = 'Acme Test'; insert testAccount; // Query the test account that was inserted. Account testAccount2 = [SELECT Id, Name FROM Account WHERE Name='Acme Test' LIMIT 1]; System.assert(testAccount2 != null); } // Like the previous method, this test method can also access all data // because the containing class is annotated with @isTest(SeeAllData=true). @isTest static void myTestMethod2() { // Can access all data in the organization. } } Ce deuxième exemple montre comment appliquer l'annotation isTest(SeeAllData=true) dans une méthode de test. La classe qui contient cette méthode de test n'est pas définie avec cette annotation. Par conséquent, vous devez appliquer cette annotation dans la méthode de test afin d'accorder l'accès à toutes les données pour la méthode. La deuxième méthode de test ne contient pas cette annotation. Par conséquent, elle peut accéder uniquement aux données qu'elle créée, en plus des objets utilisés pour gérer votre organisation, tels que les utilisateurs. // This class contains test methods with different data access levels. @isTest private class ClassWithDifferentDataAccess { // Test method that has access to all data. @isTest(SeeAllData=true) static void testWithAllDataAccess() { // Can query all data in the organization. } // Test method that has access to only the data it creates Classes, objets et interfaces salesforce | Annotations IsTest | 166 // and organization setup and metadata objects. @isTest static void testWithOwnDataAccess() { // This method can still access the User object. // This query returns the first user object. User u = [SELECT UserName,Email FROM User LIMIT 1]; System.debug('UserName: ' + u.UserName); System.debug('Email: ' + u.Email); // Can access the test account that is created here. Account a = new Account(Name='Test Account'); insert a; // Access the account that was just created. Account insertedAcct = [SELECT Id,Name FROM Account WHERE Name='Test Account']; System.assert(insertedAcct != null); } } Annotation IsTest(OnInstall=true) Utilisez l'annotation IsTest(OnInstall=true) pour spécifier les tests Apex qui sont exécutés durant l'installation d'un package. Cette annotation est utilisée pour des tests dans des packages gérés ou non gérés. Seules les méthodes de test avec cette annotation, ou les méthodes qui font partie d'une classe de test contenant cette annotation, sont exécutées durant l'installation d'un package. Les tests annotés pour être exécutés lors de l'installation d'un package doivent passer pour la réussite de l'installation du package. Il n'est plus possible de contourner un test échoué durant l'installation d'un package. Une méthode ou une classe de test qui ne contient pas cette annotation, ou qui est annotée avec isTest(OnInstall=false) ou isTest, n'est pas exécutée durant l'installation. Cet exemple montre comment annoter une méthode de test qui est exécutée lors de l'installation d'un package. Dans cet exemple, la méthode test1 est exécutée, contrairement à test2 et à test3. public class OnInstallClass { // Implement logic for the class. public void method1(){ // Some code } // This test method will be executed // during the installation of the package. @isTest(OnInstall=true) Classes, objets et interfaces salesforce | Annotation ReadOnly | 167 static void test1() { // Some test code } // Tests excluded from running during the // the installation of a package. @isTest static void test2() { // Some test code } static testmethod void test3() { // Some test code } } Annotation ReadOnly L'annotation @ReadOnly permet d'effectuer des requêtes sans restriction avec la base de données Force.com. D'autres limites restent applicables. Il est important de noter que cette annotation, bien qu'elle supprime la limite en nombre de lignes renvoyées pour une requête, empêche d'effectuer les opérations suivantes dans la requête : des opérations DML, des appels à System.schedule, des appels aux méthodes annotées avec @future et l'envoi d'e-mails. L'annotation @ReadOnly est disponible pour les services Web et l'interface Schedulable. Pour utiliser l'annotation @ReadOnly, la requête de niveau supérieur doit être dans l'exécution de la planification ou dans l'invocation du service Web. Par exemple, si une page Visualforce appelle à un service Web qui contient l'annotation @ReadOnly, la requête échoue, car Visualforce est la requête de niveau supérieur, pas le service Web. Les pages Visualforce peuvent appeler des méthodes de contrôleur avec l'annotation @ReadOnly, et ces méthodes sont exécutées avec les mêmes restrictions assouplies. Pour augmenter d'autres limites spécifiques à Visualforce, notamment la taille d'une collection qui peut être utilisée par un composant d'itération tel que <apex:pageBlockTable>, vous pouvez définir l'attribut readonly de la balise <apex:page> sur true. Pour plus d'informations, reportez-vous à Working with Large Sets of Data dans le guide Visualforce Developer's Guide. Annotation RemoteAction L'annotation RemoteAction fournit la prise en charge des méthodes Apex utilisées dans Visualforce pour être appelées via JavaScript. Ce processus est souvent appelé JavaScript remoting. Classes, objets et interfaces salesforce | Annotations REST Apex | 168 Remarque: Les méthodes avec l'annotation RemoteAction doivent être static et global ou public. Pour utiliser JavaScript remoting dans une page Visualforce, ajoutez la requête en tant qu'invocation JavaScript sous le format suivant : [namespace.]controller.method( [parameters...,] callbackFunction, [configuration] ); • • • • • • namespace correspond à l'espace de noms de la classe de contrôleur. Il est requis si votre organisation à un espace de noms défini ou si la classe provient d'un package installé. controller est le nom de votre contrôleur Apex. method est le nom de la méthode Apex que vous appelez. parameters est la liste de paramètres, séparés par des virgules, que prend votre méthode. callbackFunction est le nom de la fonction JavaScript qui va gérer la réponse du contrôleur. Vous pouvez également déclarer en ligne une fonction anonyme. callbackFunction reçoit le statut de l'appel de la méthode et le résultat sous forme de paramètres. configuration configure le traitement de l'appel et de la réponse distants. Il permet de spécifier l'échappement ou non de la réponse de la méthode Apex. La valeur par défaut est {escape: true}. Dans votre contrôleur, votre déclaration de méthode Apex est précédée de l'annotation @RemoteAction, comme suit : @RemoteAction global static String getItemId(String objectName) { ... } Votre méthode peut prendre des primitifs, collections, typés et sObjects génériques Apex, et des classes et des interfaces Apex définies par l'utilisateur en tant qu'arguments. Les sObjects génériques doivent avoir une valeur ID ou sobjectType pour identifier le type actuel. Les paramètres d'interface doivent avoir un apexType pour identifier le type actuel. Votre méthode peut renvoyer des primitifs, des sObjects, des collections, des classes Apex définies par l'utilisateur et des énumérations Apex, SaveResult, UpsertResult, DeleteResult, SelectOption ou PageReference. Pour plus d'informations, reportez-vous à JavaScript Remoting for Apex Controllers dans le guide Visualforce Developer's Guide. Annotations REST Apex Six nouvelles annotations ont été ajoutées, qui permettent d'exposer une classe Apex en tant que service Web RESTful. • • • • @RestResource(urlMapping='/yourUrl') @HttpDelete @HttpGet @HttpPost Classes, objets et interfaces • salesforce | Annotations REST Apex | 169 @HttpPut Voir aussi : Exemple de code de base REST Apex Annotation RestResource L'annotation @RestResource est utilisée au niveau de la classe et permet d'exposer une classe Apex en tant que ressource REST. Notez les points suivants lors de l'utilisation de cette annotation : • • • • Le mappage d'URL est relatif à https://instance.salesforce.com/services/apexrest/. Un caractère générique (*) peut être utilisé. Le mappage d'URL est sensible à la casse. Un mappage d'URL pour mon_url correspond uniquement à une ressource REST contenant mon_url, pas Mon_Url. Pour utiliser cette annotation, votre classe Apex doit être définie comme globale. Directives URL Les mappages de chemins d'URL sont les suivants : • • Le chemin doit commencer par une barre oblique '/' Si un astérisque '*' est utilisé, il doit être précédé et suivi d'une barre oblique '/', sauf si l'astérisque '*' est le dernier caractère, auquel cas il doit être suivi d'une barre oblique '/' Les règles de mappage d'URL sont les suivantes : • • • Une correspondance exacte gagne toujours. Si aucune correspondance n'est trouvée, recherchez tous les modèles avec un caractère générique qui correspondent, puis sélectionnez le plus long (en taille de chaîne). Si aucune correspondance avec un caractère générique n'est trouvée, un code de statut de réponse HTTP 404 est renvoyé. L'URL d'une classe d'espace de noms contient l'espace de noms. Par exemple, si votre classe est un espace de noms abc et que la classe est mappée avec votre_url, l'URL de l'API est modifiée comme suit : https://instance.salesforce.com/services/apexrest/abc/votre_url/. En cas de collision d'URL, la classe d'espace de noms est toujours utilisée. Annotation HttpDelete L'annotation @HttpDelete est utilisée au niveau de la méthode et permet d'exposer une méthode Apex en tant que ressource REST. Cette méthode est appelée lorsqu'une requête DELETE HTTP est envoyée, et supprime la ressource spécifiée. Pour utiliser cette annotation, votre méthode Apex doit être définie comme statique globale. Annotation HttpGet L'annotation @HttpGet est utilisée au niveau de la méthode et permet d'exposer une méthode Apex en tant que ressource REST. Cette méthode est appelée lorsqu'une requête GET HTTP est envoyée, et renvoie la ressource spécifiée. Notez les points suivants lors de l'utilisation de cette annotation : Classes, objets et interfaces • • salesforce | Classes et conversion | 170 Pour utiliser cette annotation, votre méthode Apex doit être définie comme statique globale. Les méthodes annotées avec @HttpGet sont également appelées si la requête HTTP utilise la méthode de requête HEAD. Annotation HttpPatch L'annotation @HttpPatch est utilisée au niveau de la méthode et permet d'exposer une méthode Apex en tant que ressource REST. Cette méthode est appelée lorsqu'une requête PATCH HTTP est envoyée, et met à jour la ressource spécifiée. Pour utiliser cette annotation, votre méthode Apex doit être définie comme statique globale. Annotation HttpPost L'annotation @HttpPost est utilisée au niveau de la méthode et permet d'exposer une méthode Apex en tant que ressource REST. Cette méthode est appelée lorsqu'une requête POST HTTP est envoyée, et crée une ressource. Pour utiliser cette annotation, votre méthode Apex doit être définie comme statique globale. Annotation HttpPut L'annotation @HttpPut est utilisée au niveau de la méthode et permet d'exposer une méthode Apex en tant que ressource REST. Cette méthode est appelée lorsqu'une requête PUT HTTP est envoyée, et crée ou met à jour la ressource spécifiée. Pour utiliser cette annotation, votre méthode Apex doit être définie comme statique globale. Classes et conversion Généralement, toutes les informations de type sont disponibles à l'exécution. Cela signifie que le langage Apex permet le casting (conversion), c.-à-d. un type de données d'une classe peut être attribué à un type de données d'une autre classe, mais uniquement si une classe est un enfant de l'autre classe. Utilisez le casting pour convertir un objet de type de données vers un autre. Dans l'exemple suivant, CustomReport étend la classe Report. Par conséquent, elle est un enfant de cette classe. Cela signifie que vous pouvez utiliser le casting pour attribuer des objets avec le type de données parent (Report) à des objets avec le type de données enfant (CustomReport). Dans le bloc de code suivant, un objet de rapport personnalisé est tout d'abord ajouté à la liste des objets de rapport. L'objet de rapport personnalisé est ensuite renvoyé en tant qu'objet de rapport, puis reconverti en objet de rapport personnalisé. Public virtual class Report { Public class CustomReport extends Report { // Create a list of report objects Report[] Reports = new Report[5]; // Create a custom report object CustomReport a = new CustomReport(); Classes, objets et interfaces salesforce | Classes et conversion | 171 // Because the custom report is a sub class of the Report class, // you can add the custom report object a to the list of report objects Reports.add(a); // The following is not legal, because the compiler does not know that what you are // returning is a custom report. You must use cast to tell it that you know what // type you are returning // CustomReport c = Reports.get(0); // Instead, get the first item in the list by casting it back to a custom report object CustomReport c = (CustomReport) Reports.get(0); } } Classes, objets et interfaces salesforce | Classes et collections | 172 Figure 4: Exemple de conversion De plus, un type d'interface peut être converti en sous-interface ou en type de classe qui met en oeuvre cette interface. Conseil: Pour vérifier si une classe est un type de classe spécifique, utilisez le mot clé instanceOf. Pour plus d'informations, reportez-vous à Utilisation du mot clé instanceof à la page 152. Classes et collections Des listes et des mappages peuvent être utilisés avec des classes et des interfaces, de la même façon que des listes et des mappages peuvent être utilisés avec des sObjects. Cela signifie, par exemple, que vous pouvez utiliser un type de données défini par l'utilisateur uniquement pour la valeur d'un mappage, pas pour la clé. De la même façon, vous ne pouvez pas créer un ensemble d'objets définis par l'utilisateur. Si vous créez un mappage ou une liste d'interfaces, tout type enfant de l'interface peut être placé dans cette collection. Par exemple, si la liste contient une interface i1, et que MyC met en oeuvre i1, alors MyC peut être placé dans la liste. Conversion de collection Puisque les collections dans le langage Apex ont un type déclaré à l'exécution, Apex permet la conversion de collection. Classes, objets et interfaces salesforce | Différences entre les classes Apex et les classes Java | 173 Les collections peuvent être converties de la même façon que des tableaux peuvent être convertis dans Java. Par exemple, une liste d'objets CustomerPurchaseOrder peut être attribuée à une liste d'objets PurchaseOrder si la classe CustomerPurchaseOrder est un enfant de la classe PurchaseOrder. public virtual class PurchaseOrder { Public class CustomerPurchaseOrder extends PurchaseOrder { } { List<PurchaseOrder> POs = new PurchaseOrder[] {}; List<CustomerPurchaseOrder> CPOs = new CustomerPurchaseOrder[]{}; POs = CPOs;} } Une fois la liste CustomerPurchaseOrder attribuée à la variable de la liste PurchaseOrder, elle peut être reconvertie en liste d'objets CustomerPurchaseOrder, mais uniquement parce que cette instance avait été initialement instanciée en tant que liste CustomerPurchaseOrder. Une liste d'objets PurchaseOrder instanciée ainsi ne peut pas être convertie en liste d'objets CustomerPurchaseOrder, même si la liste d'objets PurchaseOrder contient uniquement des objets CustomerPurchaseOrder. Si l'utilisateur d'une liste PurchaseOrder qui contient uniquement des objets CustomerPurchaseOrders essaie d'insérer une sous-classe non CustomerPurchaseOrder de PurchaseOrder (telle que InternalPurchaseOrder), une exception d'exécution est levée, car les collections Apex ont un type déclaré à l'exécution. Remarque: Les mappages se comportent de la même façon que des listes avec le côté valeur du mappage : si le côté valeur d'un mappage A peut être converti en côté de valeur d'un mappage B, et qu'ils ont le même type de clé, le mappage A peut être converti en mappage B. Une erreur d'exécution se produit si la conversion n'est pas valide avec le mappage spécifique à l'exécution. Différences entre les classes Apex et les classes Java La liste suivante présente les principales différences entre les classes Apex et les classes Java : • • • • Les classes et les interfaces internes peuvent être déclarées à un seul niveau dans une classe externe. Les méthodes et les variables statiques peuvent être déclarées uniquement dans une définition de classe de niveau supérieur, pas dans une classe interne. Les classes internes se comportent comme des classes internes Java statiques, mais ne nécessitent pas le mot clé static. Les classes internes peuvent avoir des variables de membre d'instance comme des classes externes, mais il n'existe aucun pointeur implicite vers une instance de la classe externe (utilisant le mot clé this). Le type private correspond au modificateur d'accès par défaut, ce qui signifie que la méthode ou variable est accessible uniquement dans la classe Apex dans laquelle elle est définie. Si vous ne spécifiez pas de modificateur d'accès, la méthode ou la variable est private. Classes, objets et interfaces • • • • salesforce | Création d'une définition de classe | 174 La spécification d'aucun modificateur d'accès pour une méthode ou une variable et le modificateur d'accès private sont synonymes. Le modificateur d'accès public signifie que la méthode ou la variable peut être utilisée par n'importe quel code Apex dans cette application ou cet espace de noms. Le modificateur d'accès global signifie que la méthode ou la variable peut être utilisée par n'importe quel code Apex qui a accès à la classe, pas seulement par le code Apex dans la même application. Ce modificateur d'accès doit être utilisé pour n'importe quelle méthode qui doit être référencée hors de l'application, dans l'API SOAP ou par un autre code Apex. Si vous déclarez une méthode ou une variable comme global, vous devez également déclarer la classe qui le contient comme global. Les méthodes et les classes sont finales par défaut. ◊ Le modificateur de définition virtual permet l'extension et les remplacements. ◊ Le mot clé override doit être utilisé explicitement dans les méthodes qui remplacent des méthodes de classe de base. • • Les méthodes d'interfaces n'ont aucun modificateur, elles sont toujours globales. Les classes d'exception doivent étendre leur exception ou une autre exception définie par l'utilisateur. ◊ Leur nom doit se terminer par le terme exception. ◊ Les classes d'exception ont quatre constructeurs implicites intégrés, mais vous pouvez en ajouter d'autres. Pour plus d'informations, reportez-vous à Classe Exception à la page 585. • Les classes et les interfaces peuvent être définies dans des déclencheurs et des blocs anonymes, mais uniquement comme locales. Création d'une définition de classe Pour créer une classe dans Salesforce : 1. Cliquez sur Votre nom > Configuration > Développer > Classes Apex. 2. Cliquez sur Nouveau. 3. Cliquez sur Paramètres de version pour spécifier la version de la classe Apex et l'API utilisée dans cette classe. Si votre organisation a des packages gérés installés depuis AppExchange, vous pouvez également spécifier la version de chaque package géré à utiliser avec cette classe. Utilisez les valeurs par défaut pour toutes les versions. Elles associent la classe à la version la plus récente du code Apex et de l'API, ainsi que de chaque package géré. Vous pouvez spécifier une version antérieure d'un package géré si vous souhaitez accéder à des composants ou des fonctionnalités qui diffèrent de la version la plus récente du package. Vous pouvez spécifier une version antérieure d'Apex et de l'API pour conserver un comportement spécifique. 4. Dans l'éditeur de classe, saisissez le code Apex de la classe. Un seule classe peut comporter jusqu'à 1 million de caractères en longueur, sans inclure les commentaires, les méthodes de test ou les classes définies en utilisant @isTest. 5. Cliquez sur Enregistrer pour enregistrer vos modifications et revenir à l'écran de détails de la classe ou cliquez sur Enregistrement rapide pour enregistrer vos modifications et continuer à modifier la classe. Votre classe Apex doit être correctement compilée pour pouvoir l'enregistrer. Les classes peuvent être automatiquement générées à partir d'un WSDL en cliquant sur Générer à partir du WSDL. Reportez-vous à Services SOAP : Définition d'une classe à partir d'un document WSDL à la page 307. Une fois enregistrées, les classes peuvent être appelées à partir de méthodes de classe ou de variables par un autre code Apex, tel qu'un déclencheur. Classes, objets et interfaces salesforce | Création d'une définition de classe | 175 Remarque: Pour faciliter la rétrocompatibilité, les classes sont stockées avec les paramètres d'une version spécifique d'Apex et d'API. Si la classe Apex référence des composants (par exemple un objet personnalisé) dans des packages gérés installés, les paramètres de version de chaque package géré que la classe référence sont également enregistrés. De plus, les classes sont stockées avec un indicateur isValid défini sur true si toutefois les métadonnées dépendantes n'ont pas changé depuis la dernière compilation de la classe. Si des modifications sont apportées aux noms ou aux champs d'objet utilisés dans la classe, même si elles sont mineures (par exemple dans la description d'un champ ou d'un objet), ou si des modifications sont apportées à une classe qui appelle cette classe, l'indicateurisValid est défini sur false. Lorsqu'un déclencheur ou un appel de service Web invoque la classe, le code est recompilé et l'utilisateur est notifié des erreurs éventuelles. En l'absence d'erreur, l'indicateur isValid est réinitialisé sur true. L'éditeur de classe Apex Lors de la modification de Visualforce ou de code Apex, dans le pied de page du mode de développement Visualforce ou via la Configuration, un éditeur est disponible avec les fonctionnalités suivantes : Mise en évidence de la syntaxe L'éditeur applique automatiquement la mise en évidence de la syntaxe pour les mots, et toutes les fonctions et opérateurs. Recherche ( ) La recherche permet de retrouver un texte dans la page, la classe ou le déclencheur actuel. Pour utiliser la recherche, saisissez une chaîne dans la zone de texte Rechercher, puis cliquez sur Rechercher suivant. • Pour remplacer une chaîne de recherche trouvée, saisissez la nouvelle chaîne dans la zone de texte Remplacer, puis cliquez sur Remplacer pour remplacer uniquement cette instance ou sur Remplacer tout pour remplacer cette instance et toutes les autres instances de la chaîne de recherche présentes dans la page, la classe ou le déclencheur. • Pour rendre l'opération de recherche sensible à la casse, sélectionnez l'option Respecter la casse. • Pour utiliser une expression régulière comme chaîne de recherche, sélectionnez l'option Expressions régulières. Les expressions régulières suivent les règles d'expression régulière de JavaScript. Une recherche qui utilise des expressions régulières peut retrouver des chaînes de plusieurs lignes. Si vous utilisez l'opération de remplacement avec une chaîne renvoyée par une expression régulière, l'opération de remplacement peut également lier des variables de groupe d'expressions régulières ($1, $2, etc.) à partir de la chaîne de recherche trouvée. Par exemple, pour remplacer une balise <h1> par <h2>, et laisser inchangés tous les attributs de la <h1> d'origine, recherchez <h1(\s+)(.*)>, puis remplacez-la par <h2$1$2>. Accéder à la ligne ( ) Ce bouton permet de mettre en évidence un numéro de ligne spécifié. Si la ligne n'est pas affichée, l'éditeur navigue jusqu'à la ligne. Annuler ( ) et Répéter ( ) Utilisez Annuler pour inverser une action de modification et Répéter pour recréer une action de modification précédemment annulée. Taille de la police Sélectionnez une taille de police dans la liste déroulante pour contrôler la taille des caractères affichés dans l'éditeur. Position de ligne et de colonne La position de ligne et de colonne du curseur est affichée dans la barre d'état en bas de l'éditeur. Vous pouvez l'utiliser avec la fonction Accéder à la ligne ( ) pour accélérer la navigation dans l'éditeur. Classes, objets et interfaces salesforce | Conventions de nommage | 176 Nombre de lignes et de caractères Le nombre total de lignes et de caractères est affiché dans la barre d'état en bas de l'éditeur. Conventions de nommage Nous recommandons les normes Java suivantes pour le nommage, c.-à-d. des classes qui commencent par une lettre majuscule, des méthodes qui commencent par un verbe en lettres minuscules et des noms de variables explicites. La définition d'une classe et d'une interface sous le même nom dans la même classe est illégale. La définition d'une classe interne sous le même nom que sa classe externe est illégale. Cependant, les méthodes et les variables ont leur propre espace de noms dans la classe. Par conséquent ces trois types de nom ne sont pas en conflit. Notamment, la définition d'une variable, d'une méthode et d'une classe sous le même nom dans une classe est illégale. Occultation de noms Les variables de membre peuvent être occultées par des variables locales, notamment des arguments de fonction. Cela autorise des méthodes et des constructeurs sous le format Java standard : Public Class Shadow { String s; Shadow(String s) { this.s = s; } // Same name ok setS(String s) { this.s = s; } // Same name ok } Des variables de membre dans une classe peuvent occulter des variables de membre portant le même nom dans une classe parente. Cela peut être utile si les deux classes figurent dans des classes de niveau supérieur différentes et sont écrites par différentes équipes. Par exemple, si une classe contient une référence à une classe C et souhaite accéder à une variable de membre M dans une classe parente P (qui porte le même nom qu'une variable de membre dans C), la référence doit d'abord être attribuée à une référence à P. Des variables statiques peuvent être occultées à travers la hiérarchie des classes. Par conséquent, si P définit une variable S statique, une sous-classe C peut également déclarer une variable S statique. Les références à S dans C font référence à cette variable statique. Pour référencer la variable dans P, la syntaxe P.S doit être utilisée. Les variables de classe statiques ne peuvent pas être référencées via une instance de classe. Elles doivent être référencées en utilisant un nom de variable brut seul (dans un fichier de classe de niveau supérieur) ou avec le nom de la classe en préfixe. Par exemple : public class p1 { public static final Integer CLASS_INT = 1; public class c { }; } p1.c c = new p1.c(); // This is illegal // Integer i = c.CLASS_INT; Classes, objets et interfaces salesforce | Sécurité des classes | 177 // This is correct Integer i = p1.CLASS_INT; Sécurité des classes Vous pouvez spécifier les utilisateurs autorisés à exécuter des méthodes dans une classe de niveau supérieur donnée, en fonction de leur profil ou d'ensembles d'autorisations. Vous pouvez définir la sécurité uniquement dans des classes Apex, pas dans des déclencheurs. Pour définir la sécurité d'une classe Apex dans sa page de liste : 1. Cliquez sur Votre nom > Configuration > Développer > Classes Apex. 2. En regard du nom de la classe à restreindre, cliquez sur Sécurité. 3. Sélectionnez les profils que vous souhaitez activer dans la liste Profils disponibles, puis cliquez sur Ajouter, ou sélectionnez les profils que vous souhaitez désactiver de la liste Profil activés, puis cliquez sur Retirer. 4. Cliquez sur Enregistrer. Pour définir la sécurité d'une classe Apex dans sa page de détail : 1. 2. 3. 4. Cliquez sur Votre nom > Configuration > Développer > Classes Apex. Cliquez sur le nom de la classe que vous souhaitez restreindre. Cliquez sur Sécurité. Sélectionnez les profils que vous souhaitez activer dans la liste Profils disponibles, puis cliquez sur Ajouter, ou sélectionnez les profils que vous souhaitez désactiver de la liste Profil activés, puis cliquez sur Retirer. 5. Cliquez sur Enregistrer. Pour définir la sécurité d'une classe Apex à partir d'un ensemble d'autorisations : 1. 2. 3. 4. 5. Cliquez sur Votre nom > Configuration > Gestion des utilisateurs > Ensembles d'autorisations. Sélectionnez un ensemble d'autorisations. Cliquez sur Accès à la classe Apex. Cliquez sur Modifier. Sélectionnez les classes Apex que vous souhaitez activer dans la liste Classes Apex disponibles, puis cliquez sur Ajouter, ou sélectionnez les classes Apex que vous souhaitez désactiver dans la liste de Classes Apex activées, puis cliquez sur Retirer. 6. Cliquez sur Enregistrer. Pour définir la sécurité d'une classe Apex à partir d'un profil : 1. 2. 3. 4. Cliquez sur Votre nom > Configuration > Gérer les utilisateurs > Profils. Sélectionnez un profil. Dans la liste associée ou la page Accès aux classes Apex, cliquez sur Modifier. Sélectionnez les classes Apex que vous souhaitez activer dans la liste Classes Apex disponibles, puis cliquez sur Ajouter, ou sélectionnez les classes Apex que vous souhaitez désactiver dans la liste de Classes Apex activées, puis cliquez sur Retirer. 5. Cliquez sur Enregistrer. Classes, objets et interfaces salesforce | Application des autorisations d'objet et de champ | 178 Application des autorisations d'objet et de champ Le code Apex est généralement exécuté dans le contexte système, c.-à-d. que les autorisations de l'utilisateur actuel, la sécurité au niveau du champ et les règles de partage ne sont pas prises en compte pendant son exécution. La seule exception à cette règle est un code Apex qui est exécuté avec l'appel executeAnonymous. executeAnonymous est toujours exécuté en utilisant les autorisations complètes de l'utilisateur actuel. Pour plus d'informations sur executeAnonymous, reportez-vous à Blocs anonymes à la page 120. Bien que le code Apex n'impose pas les autorisations au niveau de l'objet et au niveau du champ par défaut, vous pouvez les appliquer dans votre code en appelant explicitement les méthodes sObject describe result (de Schema.DescribeSObjectResult) et les méthodes field describe result (de Schema.DescribeFieldResult) qui vérifient les niveaux d'autorisation d'accès de l'utilisateur. Vous pouvez ainsi vérifier si l'utilisateur actuel dispose des autorisations requises et, si ses autorisations sont suffisantes, exécuter une opération DML spécifique ou une requête. Par exemple, vous pouvez appeler la méthode isAccessible, isCreateable ou isUpdateable de Schema.DescribeSObjectResult pour vérifier si l'utilisateur actuel dispose respectivement d'un accès en lecture, en création ou en mise à jour à un sObject. De la même façon, Schema.DescribeFieldResult expose ces méthodes de contrôle d'accès que vous pouvez appeler pour vérifier l'accès en lecture, en création ou en mise à jour à un champ de l'utilisateur actuel. Vous pouvez en outre appeler la méthode isDeletable fournie par Schema.DescribeSObjectResult pour vérifier si l'utilisateur actuel est autorisé à supprimer un sObject spécifique. Les exemples suivants montrent comment appeler les méthodes de contrôle d'accès. Pour vérifier l'autorisation de mise à jour au niveau du champ d'e-mail du contact avant de le mettre à jour : if (Schema.sObjectType.Contact.fields.Email.isUpdateable()) { // Update contact phone number } Pour vérifier l'autorisation de création au niveau du champ d'e-mail du contact avant de créer un contact : if (Schema.sObjectType.Contact.fields.Email.isCreateable()) { // Create new contact } Pour vérifier l'autorisation de lecture au niveau du champ d'e-mail du contact avant d'interroger ce champ : if (Schema.sObjectType.Contact.fields.Email.isAccessible()) { Contact c = [SELECT Email FROM Contact WHERE Id= :Id]; } Pour vérifier l'autorisation au niveau de l'objet pour le contact avant de supprimer le contact : if (Schema.sObjectType.Contact.isDeletable()) { // Delete contact } Classes, objets et interfaces salesforce | Préfixe d'espace de noms | 179 Les règles de partage sont différentes des autorisations au niveau de l'objet et au niveau du champ. Elles peuvent coexister. Si des règles de partage sont définies dans Salesforce, vous pouvez les appliquer au niveau de la classe en déclarant la classe avec le mot clé with sharing. Pour plus d'informations, reportez-vous à Utilisation des mots clés with sharing ou without sharing. Si vous appelez les méthodes de contrôle d'accès sObject describe result et field describe result, l'autorisation de vérification de l'objet et les autorisations au niveau du champ s'appliquent en plus des règles de partage en vigueur. Le niveau d'accès est parfois accordé par une règle de partage qui peut entrer en conflit avec une autorisation au niveau de l'objet ou au niveau du champ. Préfixe d'espace de noms L'application prend en charge l'utilisation de préfixes d'espace de noms. Les préfixes d'espace de noms sont utilisés dans des packages Force.com AppExchange gérés afin de différencier les noms d'objet et de champ personnalisés de ceux utilisés par d'autres organisations. Lorsqu'un développeur a enregistré un préfixe d'espace de noms unique global et l'a ajouté au registre AppExchange, les références externes aux noms d'objet et de champ personnalisés dans les packages gérés du développeur se présentent sous le format long suivant : namespace_prefix__obj_or_field_name__c Ces noms qualifiés complets peuvent être difficiles à mettre à jour lors de l'utilisation d'instructions SOQL, d'instructions SOSL et de code Apex lorsqu'une classe est marquée « géré ». Par conséquent, Apex prend en charge un espace de noms par défaut pour les noms de schéma. Lors de l'utilisation d'identificateurs, l'analyseur évalue l'espace de noms de l'objet actuel, puis considère qu'il correspond à l'espace de noms de tous les autres objets et champs, sauf indication contraire. Par conséquent, une classe stockée doit référencer directement les noms d'objet et de champ personnalisés (en utilisant obj_or_field_name__c) pour les objets qui sont définis dans le même espace de noms d'application. Conseil: Utilisez des préfixes d'espace de noms uniquement pour référencer des objets et des champs personnalisés dans des packages gérés qui ont été installés dans votre organisation à partir d'AppExchange. Utilisation d'espace de noms lors de l'invocation de méthodes Pour invoquer une méthode qui est définie dans un package géré, le langage Apex autorise les identificateurs qualifiés complets sous le format suivant : namespace_prefix.class.method(args) Utilisez l'espace de noms spécial System pour différencier les classes statiques intégrées de celles définies par l'utilisateur (par exemple System.System.debug()). Sans le préfixe d'espace de noms System, les noms de classes statiques système, tels queMath et System, risquent d'être remplacés par des classes définies par l'utilisateur portant le même nom, comme indiqué ci-dessous. Conseil: Utilisez des préfixes d'espace de noms uniquement pour invoquer des méthodes dans des packages gérés qui ont été installés dans votre organisation à partir d'AppExchange. Classes, objets et interfaces salesforce | Précédence des noms d'espace de noms, de classe et de variable | 180 Précédence des noms d'espace de noms, de classe et de variable Les variables locales, les noms de classe et les espaces de noms peuvent utiliser les mêmes identifiants. Par conséquent, l'analyseur Apex évalue les expressions sous le format name1.name2.[...].nameN, comme suit : 1. L'analyseur fait d'abord l'hypothèse que name1 est une variable locale avec name2 - nameN comme références de champ. 2. Si la première hypothèse n'est pas vraie, l'analyseur fait l'hypothèse que name1 est un nom de classe et que name2 est un nom de variable statique avec name3 - nameN comme références de champ. 3. Si la deuxième hypothèse n'est pas vraie, l'analyseur fait l'hypothèse que name1 est un nom d'espace de noms, que name2 est un nom de classe, que name3 est un nom de variable statique et que name4 - nameN sont des références de champ. 4. Si la troisième hypothèse n'est pas vraie, l'analyseur renvoie une erreur. Si l'expression se termine par un ensemble de parenthèses (par exemple name1.name2.[...].nameM.nameN()), l'analyseur Apex évalue l'expression comme suit : 1. L'analyseur fait d'abord l'hypothèse que name1 est une variable locale avec name2 - nameN comme références de champ, et nameN comme invocation de méthode. 2. Si la première hypothèse n'est pas vraie : • • Si l'expression contient uniquement deux identifiants (name1.name2()), l'analyseur fait l'hypothèse que name1 est un nom de classe et que name2 est une invocation de méthode. Si l'expression contient plus de deux identifiants, l'analyseur fait l'hypothèse que name1 est un nom de classe, que name2 est un nom de variable statique avec name3 - nameM comme références de champ, et que nameN est une invocation de méthode. 3. Si la deuxième hypothèse n'est pas vraie, l'analyseur fait l'hypothèse que name1 est un nom d'espace de noms, que name2 est un nom de classe, que name3 est un nom de variable statique, que name4 - nameM sont des références de champ et que nameN est une invocation de méthode. 4. Si la troisième hypothèse n'est pas vraie, l'analyseur renvoie une erreur. Cependant, avec des variables de classe, le langage Apex utilise également une notation pointée pour référencer des variables de membre. Ces variables de membre peuvent référencer d'autres instances de classe, ou référencer un sObject qui possède ses propres règles de notation pointée pour référencer des noms de champ (éventuellement des clés étrangères de navigation). Lorsque vous saisissez un champ sObject dans une expression, la suite de l'expression reste dans le domaine du sObject, c.-à-d. que les champs sObject ne peuvent pas de nouveau référencer des expressions Apex. Par exemple, si vous avez la classe suivante : public class c { c1 c1 = new c1(); class c1 { c2 c2; } class c2 { Account a; } } Les expressions suivantes sont toutes légales : c.c1.c2.a.name c.c1.c2.a.owner.lastName.toLowerCase() Classes, objets et interfaces salesforce | Résolution du type et espace de noms système pour les types | 181 c.c1.c2.a.tasks c.c1.c2.a.contacts.size() Résolution du type et espace de noms système pour les types Puisque que le système type doit résoudre les types définis par l'utilisateur localement ou dans d'autres classes, l'analyseur Apex évalue les types comme suit : 1. 2. 3. 4. Pour une référence de type TypeN, l'analyseur commence par examiner ce type en tant que scalaire. Si TypeN n'est pas trouvé, l'analyseur examine les types définis localement. Si TypeN n'est toujours pas trouvé, l'analyseur examine une classe portant ce nom. Si TypeN n'est toujours pas trouvé, l'analyseur examine les types système en tant que sObjects. Pour le type T1.T2, cela peut correspondre à un type interne T2 dans une classe de niveau supérieur T1, ou une classe de niveau supérieur T2 dans l'espace de noms T1 (dans l'ordre de précédence). Paramètres de version Pour faciliter la rétrocompatibilité, les classes et des déclencheurs sont stockés avec les paramètres d'une version d'API Salesforce.com spécifique. Si une classe ou un déclencheur Apex référence des composants (par exemple un objet personnalisé) dans des packages gérés installés, les paramètres de version de chaque package géré que la classe référence sont également enregistrés. Ainsi, lorsque Apex, l'API et les composants de packages gérés évoluent avec les versions publiées ultérieurement, une classe ou un déclencheur reste lié aux versions avec un comportement spécifique connu. La définition d'une version pour un package installé détermine l'interface et le comportement exposés par n'importe quel code Apex dans le package installé. Cela permet de continuer à référencer un code Apex qui peut être déprécié dans la dernière version d'un package installé, si vous avez installé une version du package avant la dépréciation du code. Généralement, vous référencez la dernière version de l'API Salesforce.com et chaque version de package installé. Si vous enregistrez une classe ou un déclencheur Apex sans spécifier la version de l'API Salesforce.com, la classe ou le déclencheur est associé par défaut à la dernière version installée. Si vous enregistrez une classe ou un déclencheur Apex qui référence un package géré sans spécifier la version du package, la classe ou le déclencheur est associé par défaut à la dernière version installée du package géré. Définition de la version de l'API Salesforce pour des classes et des déclencheurs Pour définir la version de l'API et de l'Apex Salesforce.com pour une classe ou un déclencheur : 1. Modifiez une classe ou un déclencheur, puis cliquez sur Paramètres de version. 2. Sélectionnez la Version de l'API Salesforce.com. Il s'agit également de la version Apex associée à la classe ou au déclencheur. 3. Cliquez sur Enregistrer. Si vous passez un objet en tant que paramètre dans un appel de méthode à partir d'une classe Apex C1 vers une autre classe C2, et que C2 a différents champs exposés en raison du paramètre de version de l'API Salesforce.com, les champs dans les objets sont contrôlés par les paramètres de version de la classe C2. Classes, objets et interfaces salesforce | Définition de la version de l'API Salesforce pour des classes et des déclencheurs | 182 En utilisant l'exemple ci-dessous, le champ Categories est défini sur null après avoir appelé la méthode insertIdea dans la classe C2 à partir d'une méthode dans la classe de test C1, car le champ Categories n'est pas disponible dans la version 13.0 de l'API. La première classe est enregistrée en utilisant l'API Salesforce.com version 13.0 : // This class is saved using Salesforce API version 13.0 // Version 13.0 does not include the Idea.categories field global class C2 { global Idea insertIdea(Idea a) { insert a; // category field set to null on insert // retrieve the new idea Idea insertedIdea = [SELECT title FROM Idea WHERE Id =:a.Id]; return insertedIdea; } } La classe suivantes est enregistrée en utilisant l'API Salesforce.com version 16.0 : @isTest // This class is bound to API version 16.0 by Version Settings private class C1 { static testMethod void testC2Method() { Idea i = new Idea(); i.CommunityId = '09aD000000004YCIAY'; i.Title = 'Testing Version Settings'; i.Body = 'Categories field is included in API version 16.0'; i.Categories = 'test'; C2 c2 = new C2(); Idea returnedIdea = c2.insertIdea(i); // retrieve the new idea Idea ideaMoreFields = [SELECT title, categories FROM Idea Classes, objets et interfaces salesforce | Définition de versions de package pour des classes des déclencheurs Apex | 183 WHERE Id = :returnedIdea.Id]; // assert that the categories field from the object created // in this class is not null System.assert(i.Categories != null); // assert that the categories field created in C2 is null System.assert(ideaMoreFields.Categories == null); } } Définition de versions de package pour des classes des déclencheurs Apex Pour configurer les paramètres de version de package pour une classe ou un déclencheur : 1. Modifiez une classe ou un déclencheur, puis cliquez sur Paramètres de version. 2. Sélectionnez une Version pour chaque package géré référencé par la classe ou le déclencheur. Cette version du package géré continue à être utilisée par la classe ou le déclencheur si des versions ultérieures du package géré sont installées, sauf si vous mettez manuellement à jour le paramètre de version. Pour ajouter un package géré installé à la liste des paramètres, sélectionnez un package dans la liste des packages disponibles. Cette liste s'affiche uniquement si un package géré installé n'est pas déjà associé à la classe ou au déclencheur. 3. Cliquez sur Enregistrer. Notez les points suivants lors de l'utilisation des paramètres de version de package : • • Si vous enregistrez une classe ou un déclencheur Apex qui référence un package géré sans spécifier la version du package, la classe ou le déclencheur Apex est associé par défaut à la dernière version installée du package géré. Vous ne pouvez pas Supprimer le paramètre de version d'une classe ou d'un déclencheur pour un package géré si le package est référencé dans la classe ou dans le déclencheur. Utilisez Afficher les dépendances afin de déterminer où un package géré est référencé par une classe ou un déclencheur. Chapitre 5 Test du code Apex Sujets : • • • • • Compréhension du test dans Apex Test unitaire Apex Exécution de méthodes de test unitaire Meilleures pratiques de test Exemple de test Le langage Apex fournit une infrastructure de test qui permet d'écrire des tests unitaires, d'exécuter vos tests, d'examiner les résultats des tests et d'obtenir les résultats de couverture de code. Ce chapitre présente les tests unitaires, la visibilité des données de tests, ainsi que les outils disponibles sur La plate-forme Force.com pour tester le code Apex. • • • • • Compréhension du test dans Apex Test unitaire Apex Exécution de méthodes de test unitaire Meilleures pratiques de test Exemple de test Test du code Apex salesforce | Compréhension du test dans Apex | 185 Compréhension du test dans Apex Le test est la clé de la réussite du développement à long terme et forme un composant essentiel du processus de développement. Nous recommandons vivement d'utiliser un processus de développement piloté par des tests, c.-à-d. des tests effectués parallèlement au développement du code. Pourquoi tester le code Apex ? La phase de test est primordiale pour la réussite de votre application, notamment lorsque vous envisagez de la déployer chez vos clients. Si vous validez le fonctionnement normal de votre application, sans comportement inattendu, vous allez augmenter la confiance de vos clients dans votre société. Il existe deux façons de tester une application. La première utilise l'interface utilisateur de Salesforce. Elle est importante, mais ne permet pas de détecter tous les cas d'utilisation de votre application. La deuxième consiste à tester les fonctionnalités en masse : jusqu'à 200 enregistrements peuvent être passés par votre code s'il est invoqué en utilisant l'API SOAP ou par un contrôleur set Visualforce standard. Une application est rarement terminée. Vous allez publier des versions supplémentaires, qui permettent de modifier et d'étendre les fonctionnalités. Si vous avez écrit des tests complets, vous pouvez vérifier que les nouvelles fonctionnalités n'entraînent aucune régression. Avant de pouvoir déployer votre code ou l'empaqueter pour Force.com AppExchange, les conditions suivantes doivent être remplies : • Au moins 75 % de votre code Apex doit être couvert par des tests unitaires, et tous ces tests doivent être réussis. Notez les points suivants : ◊ Lors du déploiement d'une organisation de production, chaque test unitaire dans l'espace de noms de votre organisation est exécuté. ◊ Les appels à System.debug ne sont pas pris en compte dans la couverture du code Apex. ◊ Les méthodes de test et les classes de test ne sont pas prises en compte dans la couverture du code Apex. ◊ Alors que seulement 75 % de votre code Apex doit être couvert par des tests, votre attention ne doit pas se porter sur le pourcentage du code couvert. Assurez-vous plutôt que chaque cas d'utilisation de votre application est couvert, y compris les cas positifs et négatifs, ainsi que les enregistrements en masse et uniques. Ils doivent représenter 75 % ou plus de couverture de votre code par des tests unitaires. • • Chaque déclencheur doit avoir une couverture de test. Toutes les classes et tous les déclencheurs doivent être compilés avec succès. Salesforce exécute tous les tests dans l'ensemble des organisations qui ont un code Apex, afin de s'assurer que le comportement n'est pas altéré par les mises à niveau du service. Éléments à tester dans un code Apex Salesforce.com recommande d'écrire des tests pour les éléments suivants : Action unique Un test pour vérifier si un enregistrement unique produit un résultat correct et attendu. Test du code Apex salesforce | Test unitaire Apex | 186 Actions en masse Tout code Apex, que ce soit un déclencheur, une classe ou une extension, peut être invoqué pour 1 à 200 enregistrements. Vous devez tester non seulement le cas d'un enregistrement unique, mais également les cas d'enregistrements multiples. Comportement positif Un test pour vérifier si le comportement attendu se produit dans chaque permutation possible, c.-à-d. lorsque l'utilisateur a renseigné tous les éléments correctement, sans dépasser les limitations. Comportement négatif Vos applications présentent souvent des limitations, par exemple l'impossibilité d'ajouter une date future, de spécifier un montant négatif, etc. Vous devez vérifier que les messages d'erreur s'affichent correctement aussi bien dans les cas négatifs que dans les cas positifs (lorsque les limitations sont respectées). Utilisateur restreint Vérifiez si utilisateur disposant d'un accès limité aux sObjects utilisés dans votre code est soumis à un comportement attendu, c.-à-d. s'il peut exécuter le code ou recevoir des messages d'erreur. Remarque: Les opérateurs conditionnels et ternaires ne sont pas considérés comme exécutés tant que les branches positives et négatives n'ont pas été exécutées. Pour consulter des exemples de ces types de test, reportez-vous à Exemple de test à la page 195. Test unitaire Apex Pour faciliter le développement d'un code robuste et sans erreur, Apex prend en charge la création et l'exécution de tests unitaires. Les test unitaires sont des méthodes de classe qui vérifient le fonctionnement normal d'une portion de code. Les méthodes de test unitaires ne prennent aucun argument, ne soumettent aucune donnée à la base de données, n'envoient aucun e-mail et sont marquées avec le mot clé testMethod dans la définition de la méthode. Par exemple : public class myClass { static testMethod void myTest() { code_block } } Utilisez l'annotation isTest pour définir des classes ou des méthodes individuelles qui contiennent uniquement le code utilisé pour tester votre application. L'annotation isTest est semblable à la création de méthodes déclarées testMethod. Remarque: Les classes définies avec l'annotation isTest ne sont pas prises en compte dans les limitations de votre organisation de 3 Mo pour l'ensemble du code Apex. Les méthodes de test individuelles dans une classe non annotées avec isTest sont prises en compte dans la limitation de la taille du code de votre organisation. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Test du code Apex salesforce | Isolation des données test de celles de l'organisation dans les tests unitaires | 187 L'exemple suivant présente une classe de test privé qui contient deux méthodes de test. @isTest private class MyTestClass { // Methods for testing @isTest static void test1() { // Implement test code } @isTest static void test2() { // Implement test code } } Considérations sur les tests unitaires Voici quelques points à noter concernant les tests unitaires. • • • • • Les méthodes de test ne peuvent pas être utilisées pour tester des appels de services Web. Les appels de services Web sont asynchrones, alors que les tests unitaires sont synchrones. Vous ne pouvez pas envoyer d'e-mail à partir d'une méthode de test. Puisque les méthodes de test n'engagent pas les données créées dans le test, il n'est pas nécessaire de supprimer ces données une fois les tests effectués. Pour certains sObjects qui incluent des champs avec des contraintes uniques, l'insertion d'enregistrements sObject dupliqués génère une erreur. Par exemple, l'insertion de sObjects CollaborationGroup portant le même nom entraîne une erreur, car les enregistrements CollaborationGroup doivent avoir des noms uniques. Le suivi des modifications pour un enregistrement (FeedTrackedChange records) dans les fils Chatter n'est pas disponible lorsque les méthodes de test modifient l'enregistrement associé. Les enregistrements FeedTrackedChange nécessitent que la modification de l'enregistrement parent auquel ils sont associés soit engagée dans la base de données avant leur création. Puisque les méthodes de test n'engagent pas de données, elles n'entraînent pas la création d'enregistrements FeedTrackedChange. De la même façon, les enregistrements de suivi d'historique des champs (tels que Historique de compte) ne peuvent pas être créés dans des méthodes de test, car ils nécessitent l'engagement préalable d'autres enregistrements (par exemple, Compte). Voir aussi : Annotations IsTest Isolation des données test de celles de l'organisation dans les tests unitaires À compter du code Apex enregistré en utilisant l'API Salesforce.com versions 24.0 et supérieures, les méthodes de test n'ont pas accès par défaut aux données préexistantes dans l'organisation, telles que les objets standard, les objets personnalisés et les Test du code Apex salesforce | Isolation des données test de celles de l'organisation dans les tests unitaires | 188 données des paramètres personnalisés, et peuvent accéder uniquement aux données qu'elles créent. Cependant, les objets utilisés pour gérer votre organisation ou les objets de métadonnées peuvent être accédés dans vos tests, tels que : • • • • • • • • User Profile Organization RecordType ApexClass ApexTrigger ApexComponent ApexPage Dès que possible, créez des données test pour chaque test. Vous pouvez désactiver cette restriction en annotant votre classe de test ou méthode de test avec l'annotation IsTest(SeeAllData=true). Pour plus d'informations, reportez-vous à Annotation IsTest(SeeAllData=true). Le code de test enregistré en utilisant l'API Salesforce.com version 23.0 ou antérieure garde accès à toutes les données de l'organisation et son accès aux données reste inchangé. Considérations sur l'accès aux données • • • • Si une nouvelle méthode de test enregistrée en utilisant l'API Salesforce.com version 24.0 ou supérieure appelle une méthode dans une autre classe enregistrée avec la version 23.0 ou antérieure, les restrictions d'accès aux données de l'appelant s'appliquent à la méthode appelée ; c.-à-d. que la méthode appelée n'a pas accès aux données de l'organisation, car l'appelant ne disposent pas de cet accès, même si la méthode a été enregistrée dans une version antérieure. Cette restriction d'accès aux données test s'applique à l'ensemble du code exécuté dans un contexte de test. Par exemple, si une méthode active un déclencheur et que le test n'a pas accès aux données de l'organisation, le déclencheur n'y a pas accès non plus. Si un test effectue une requête Visualforce, le test exécuté reste dans un contexte de test, mais il est exécuté dans un thread différent. Par conséquent, l'isolation des données test ne s'applique plus. Dans ce cas, le test peut accéder à toutes les données de l'organisation après avoir initialisé la requête Visualforce. Cependant, si la requête Visualforce exécute un rappel, par exemple un appel distant JavaScript, les données insérées par le rappel ne sont pas visibles par le test. Dans certains cas, il peut s'avérer impossible de créer des types de données à partir de votre méthode de test en raison de limitations spécifiques. Voici quelques exemples de ces limitations. ◊ Il n'est pas possible d'insérer une entrée de catalogue de prix pour un produit à partir d'un test, car le catalogue de prix standard n'est pas accessible et ne peut pas être créé dans un test en cours d'exécution. En outre, l'insertion d'une entrée de catalogue de prix à partir d'un catalogue personnalisé n'est pas pris en charge, car elle nécessite la définition d'un catalogue de prix standard. Dans ce cas, annotez votre méthode de test avec IsTest(SeeAllData=true) pour permettre à votre test d'accéder aux données de l'organisation. ◊ Certains objets standard ne peuvent pas être créés. Pour plus d'informations sur ces objets, reportez-vous au guide Object Reference for Salesforce and Force.com. ◊ Pour certains sObjects qui incluent des champs avec des contraintes uniques, l'insertion d'enregistrements sObject dupliqués génère une erreur. Par exemple, l'insertion de sObjects CollaborationGroup portant le même nom entraîne une erreur, car les enregistrements CollaborationGroup doivent avoir des noms uniques. Cela se produit que votre test soit annoté ou non avec IsTest(SeeAllData=true). ◊ Les enregistrements créés uniquement après des enregistrements associés sont validés dans la base de données, comme les modifications suivies dans Chatter. Le suivi des modifications pour un enregistrement (FeedTrackedChange records) dans les fils Chatter n'est pas disponible lorsque les méthodes de test modifient Test du code Apex salesforce | Utilisation de la méthode runAs | 189 l'enregistrement associé. Les enregistrements FeedTrackedChange nécessitent que la modification de l'enregistrement parent auquel ils sont associés soit engagée dans la base de données avant leur création. Puisque les méthodes de test n'engagent pas de données, elles n'entraînent pas la création d'enregistrements FeedTrackedChange. De la même façon, les enregistrements de suivi d'historique des champs (tels que Historique de compte) ne peuvent pas être créés dans des méthodes de test, car ils nécessitent l'engagement préalable d'autres enregistrements (par exemple, Compte). Utilisation de la méthode runAs Généralement, l'ensemble du code Apex est exécuté en mode système, et les autorisations et le partage d'enregistrement de l'utilisateur actuel ne sont pas pris en compte. La méthode système runAs permet d'écrire des méthodes de test qui changent le contexte de l'utilisateur en utilisateur existant ou en nouvel utilisateur, ou d'exécuter en utilisant le code d'une version spécifique d'un package géré. Lors de l'exécution en tant qu'utilisateur, l'ensemble du partage d'enregistrement de cet utilisateur s'applique. Vous pouvez également utiliser runAs dans une méthode de test. Le contexte système d'origine est redémarré une fois toutes les méthodes de test runAs terminées. Pour plus d'informations sur l'utilisation de la méthode runAs et la spécification du contexte de version d'un package, reportez-vous à Test du comportement dans des versions de package à la page 284. Remarque: Chaque appel à runAs est pris en compte dans le nombre total d'instructions DML émises dans le processus. Dans l'exemple suivant, un utilisateur test est créé, puis le code est exécuté sous l'identité de cet utilisateur avec ses autorisations et son accès aux enregistrements : public class TestRunAs { public static testMethod void testRunAs() { // Setup test data // This code runs as the system user Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; User u = new User(Alias = 'standt', Email='[email protected]', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName='[email protected]'); System.runAs(u) { // The following code runs as user 'u' System.debug('Current User: ' + UserInfo.getUserName()); System.debug('Current Profile: ' + UserInfo.getProfileId()); } } Test du code Apex salesforce | Utilisation de la méthode runAs | 190 } Vous pouvez imbriquer plusieurs méthodes runAs. Par exemple : public class TestRunAs2 { public static testMethod void test2() { Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; User u2 = new User(Alias = 'newUser', Email='[email protected]', EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US', LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName='[email protected]'); System.runAs(u2) { // The following code runs as user u2. System.debug('Current User: ' + UserInfo.getUserName()); System.debug('Current Profile: ' + UserInfo.getProfileId()); // The following code runs as user u3. User u3 = [SELECT Id FROM User WHERE UserName='[email protected]']; System.runAs(u3) { System.debug('Current User: ' + UserInfo.getUserName()); System.debug('Current Profile: ' + UserInfo.getProfileId()); } // Any additional code here would run as user u2. } } } Meilleures pratiques d'utilisation de runAs Les éléments suivants utilisent les autorisations accordées par l'utilisateur spécifié avec runAs, exécuté en tant qu'utilisateur spécifique : • • Apex dynamique Méthodes utilisant with sharing ou without sharing Test du code Apex • salesforce | Utilisation de limitations, startTest, et stopTest | 191 Enregistrements partagés Les autorisations d'origine sont réinitialisées une fois runAs terminé. La méthode runAs ignore les limites en licences utilisateur. Vous pouvez créer des utilisateurs avec runAs même si votre organisation n'a plus de licences utilisateur supplémentaires. Utilisation de limitations, startTest, et stopTest Les méthodes Limits renvoient la limite spécifique du gouverneur particulier, notamment le nombre d'appels d'une méthode ou la taille du segment mémoire restant. Il existe deux versions de chaque méthode : La première renvoie la quantité de ressources qui ont été utilisées dans le contexte actuel, alors que la deuxième version contient le terme « limit » et renvoie la quantité totale de ressources disponibles pour ce contexte. Par exemple, getCallouts renvoie le nombre d'appels à un service externe qui ont déjà été traités dans le contexte actuel, alors que getLimitCallouts renvoie le nombre total d'appels disponibles dans le contexte donné. En plus des méthodes Limits, utilisez les méthodes startTest et stopTest pour valider la proximité du code des limites du gouverneur. La méthode startTest marque dans votre code test le point auquel le test commence. Chaque testMethod est autorisé à appeler une seule fois cette méthode. Tout le code qui précède cette méthode être utilisé pour initialiser des variables, renseigner des structures de données, etc., permettant de définir tout ce dont vous avez besoin pour exécuter votre test. Tout code exécuté après l'appel à startTest et avant stopTest se voit attribuer un nouvel ensemble de limitations du gouverneur. La méthode startTest n'actualise pas le contexte du test : elle ajoute un contexte à votre test. Par exemple, si votre classe effectue 98 requêtes SOQL avant d'appeler startTest, et que la première instruction significative après startTest est une instruction DML, le programme peut maintenant effectuer 100 requêtes supplémentaires. Toutefois, une fois stopTest appelé, le programme revient dans le contexte d'origine et peut effectuer 2 requêtes SOQL supplémentaires seulement avant d'atteindre la limite de 100. La méthode stopTest marque dans votre code test le point auquel le test se termine. Utilisez cette méthode conjointement à la méthode startTest. Chaque testMethod est autorisé à appeler une seule fois cette méthode. Tout code exécuté après la méthode stopTest se voit attribuer les limitations d'origine qui étaient en vigueur avant l'appel de startTest. Tous les appels asynchrones effectués après la méthode startTest sont collectés par le système. Lors de l'exécution de stopTest, tous les processus asynchrones sont exécutés de façon synchrone. Ajout de requêtes SOSL à des tests unitaires Pour garantir un comportement cohérent pour les méthodes de test, toute requête SOSL (Object Search Language) Salesforce qui est ajoutée à une méthode de test Apex renvoie un ensemble vide de résultats de recherche lors de l'exécution de la méthode. Si vous ne souhaitez pas que la requête renvoie une liste de résultats vide, vous pouvez utiliser la méthode système Test.setFixedSearchResults pour définir une liste d'ID d'enregistrements qui sont renvoyés par la recherche. Toutes les requêtes SOSL exécutées ultérieurement dans la méthode de test renvoient la liste des ID d'enregistrement qui ont été spécifiés par la méthode Test.setFixedSearchResults. De plus, la méthode de test peut appeler Test.setFixedSearchResults plusieurs fois pour définir différents ensembles de résultats pour différentes requêtes SOSL. Si vous n'appelez pas la méthode Test.setFixedSearchResults dans une méthode de test ou si vous appelez cette méthode sans spécifier de liste d'ID d'enregistrement, toutes les requêtes SOSL exécutées ultérieurement dans la méthode de test renvoient une liste de résultats vide. Test du code Apex salesforce | Exécution de méthodes de test unitaire | 192 La liste des ID d'enregistrement spécifiés par la méthode Test.setFixedSearchResults remplace les résultats qui seraient normalement renvoyés par la requête SOSL si elle n'était soumise à aucune clause WHERE ou LIMIT. Si ces clauses existent dans la requête SOSL, elles s'appliquent à la liste des résultats de recherche fixes. Par exemple : public class SoslFixedResultsTest1 { public static testMethod void testSoslFixedResults() { Id [] fixedSearchResults= new Id[1]; fixedSearchResults[0] = '001x0000003G89h'; Test.setFixedSearchResults(fixedSearchResults); List<List<SObject>> searchList = [FIND 'test' IN ALL FIELDS RETURNING Account(id, name WHERE name = 'test' LIMIT 1)]; } } Bien que l'enregistrement Account avec l'ID 001x0000003G89h peut ne pas correspondre à la chaîne de requête dans la clause FIND ('test'), l'enregistrement est passé dans la clause RETURNING de l'instruction SOSL. Si l'enregistrement avec l'ID 001x0000003G89h correspond au filtre de la clause WHERE, l'enregistrement est renvoyé. S'il ne correspond pas à la clause WHERE, aucun n'enregistrement n'est renvoyé. Exécution de méthodes de test unitaire Vous pouvez exécuter des tests unitaires pour : • • • Une classe spécifique Un sous-ensemble de classes Tous les tests unitaires de votre organisation Pour exécuter un test, utilisez l'un des éléments suivants : • • • L'interface utilisateur de Salesforce L'IDE Force.com L'API Exécution de tests via l'interface utilisateur de Salesforce Vous pouvez exécuter des tests unitaires à l'aide de la page Exécution du test Apex. Les tests démarrés sur cette page sont exécutés de façon asynchrone, c.-à-d. qu'ils n'ont pas à attendre la fin de l'exécution d'une classe de test. La page Exécution du test Apex actualise le statut d'un test et affiche les résultats une fois le test terminé. Test du code Apex salesforce | Exécution de méthodes de test unitaire | 193 Pour utiliser la page Exécution du test Apex : 1. Cliquez sur Votre nom > Configuration > Développer > Exécution du test Apex. 2. Cliquez sur Sélectionner des tests. Remarque: Si vous avez des classes Apex installées à partir d'un package géré, commencez par compiler ces classes en cliquant sur Compiler toutes les classes dans la page Classes Apex pour les afficher dans la liste. Reportez-vous à Gestion des classes Apex. 3. Sélectionnez les tests à exécuter. La liste des tests inclut des classes qui contiennent des méthodes de test. • • • Pour sélectionner des tests à partir d'un package géré installé, sélectionnez leur espace de noms correspondant dans la liste déroulante. Seules les classes du package de géré avec l'espace de noms sélectionné sont affichées dans la liste. Pour sélectionner des tests qui existent localement dans votre organisation, sélectionnez [Mon espace de noms] dans la liste déroulante. Seules les classes locales qui ne proviennent pas de packages gérés sont affichées dans la liste. Pour sélectionner un test, sélectionnez [Tous les espaces de noms] dans la liste déroulante. Toutes les classes de l'organisation sont affichées, quelles proviennent ou non d'un package géré. Remarque: Les classes dont les tests sont en cours d'exécution ne sont pas affichées dans la liste. 4. Cliquez sur Exécuter. Après l'exécution de tests à l'aide de la page Exécution du test Apex, vous pouvez afficher le pourcentage de couverture de code par ces tests dans la liste de classes Apex. Cliquez sur Votre nom > Configuration > Développer > Classes Apex, puis cliquez sur Calculer le code de couverture de votre organisation. Remarque: La valeur de la couverture de code calculée par Calculer la couverture du code de votre organisation peut différer de la valeur de la couverture de code calculée par l'exécution de tous les tests unitaires en utilisant Exécuter tous les tests. En effet, l'option Calculer la couverture du code de votre organisation exclut les classes qui font partie de packages gérés installés, contrairement à Exécuter tous les tests. Vous pouvez également déterminer quelles lignes de code sont couvertes par des tests pour une classe individuelle. Cliquez sur Votre nom > Configuration > Développer > Classes Apex, puis sur la valeur de pourcentage dans la colonne Couverture du code d'une classe. Cliquez sur Votre nom > Configuration > Développer > Exécution du testApex > Afficher l'historique des tests pour afficher tous les résultats des tests de votre organisation, pas seulement les tests que vous avez exécutés. Une fois les tests exécutés, les résultats sont conservés pendant 30 jours, sauf s'ils sont supprimés. Test du code Apex salesforce | Meilleures pratiques de test | 194 Exécution de tests en utilisant l'IDE Force.com De plus, vous pouvez exécuter des tests avec l'IDE Force.com (reportez-vous à https://wiki.developerforce.com/index.php/Apex_Toolkit_for_Eclipse). Meilleures pratiques de test Des tests de qualité doivent respecter les points suivants : • Couvrir autant de lignes de code que possible. Avant de pouvoir déployer votre code Apex ou de l'empaqueter pour Force.com AppExchange, les conditions suivantes doivent être remplies. Important: ◊ Au moins 75 % de votre code Apex doit être couvert par des tests unitaires, et tous ces tests doivent être réussis. Notez les points suivants : - Lors du déploiement d'une organisation de production, chaque test unitaire dans l'espace de noms de votre organisation est exécuté. Les appels à System.debug ne sont pas pris en compte dans la couverture du code Apex. Les méthodes de test et les classes de test ne sont pas prises en compte dans la couverture du code Apex. Alors que seulement 75 % de votre code Apex doit être couvert par des tests, votre attention ne doit pas se porter sur le pourcentage du code couvert. Assurez-vous plutôt que chaque cas d'utilisation de votre application est couvert, y compris les cas positifs et négatifs, ainsi que les enregistrements en masse et uniques. Ils doivent représenter 75 % ou plus de couverture de votre code par des tests unitaires. ◊ Chaque déclencheur doit avoir une couverture de test. ◊ Toutes les classes et tous les déclencheurs doivent être compilés avec succès. • • • • • • • • • • En cas de logique conditionnelle (comprenant des opérateurs ternaires), exécuter chaque branche de logique de code. Effectuer des appels à des méthodes en utilisant des entrées valides et non valides. Réussir sans lever aucune exception, sauf si ces erreurs sont attendues et détectées dans un bloc try…catch. Toujours traiter toutes les exceptions détectées, ne pas se contenter de leur détection. Utiliser des méthodes System.assert pour démontrer que le code ce comporte correctement. Utiliser la méthode runAs pour tester votre application dans différents contextes utilisateur. Utiliser l'annotation isTest. Les classes définies avec l'annotation isTest ne sont pas prises en compte dans les limitations de votre organisation de 3 Mo pour l'ensemble du code Apex. Reportez-vous à Annotation IsTest à la page 162. Pratiquer la fonctionnalité de déclencheur en masse : utiliser au moins 20 enregistrements dans les tests. Utiliser les mots clés ORDER BY pour s'assurer que les enregistrements sont renvoyés dans l'ordre attendu. Ne pas supposer que les ID d'enregistrement sont dans l'ordre séquentiel. Les ID d'enregistrement ne sont pas créés dans l'ordre ascendant, sauf si vous insérez plusieurs enregistrements dans la même requête. Par exemple, si vous créez un compte A et recevez l'ID 001D000000IEEmT, puis créez le compte B, l'ID du compte B peut être ou non plus haut dans la séquence. • La liste des classes Apex contient une colonne Couverture de code. Si vous cliquez sur la valeur du pourcentage de couverture, une page s'ouvre. Elle indique en bleu toutes les lignes de code de cette classe ou de ce déclencheur qui sont couvertes par Test du code Apex • salesforce | Exemple de test | 195 des tests, et en rouge toutes les lignes de code qui ne sont pas couvertes par des tests. Elle indique également le nombre de fois qu'une ligne donnée dans la classe ou le déclencheur a été exécutée par le test. Configurer des données test : ◊ Créez les données nécessaires dans des classes de test afin d'éviter que les tests dépendent des données d'une organisation particulière. ◊ Créez toutes les données test avant d'appeler la méthode starttest. ◊ Puisque les tests ne sont pas engagés, il n'est pas nécessaire de supprimer les données. • • Écrire des commentaires précisant non seulement les éléments à tester, mais également les hypothèses du testeur concernant les données, le résultat attendu, etc. Tester les classes individuellement dans votre application. Ne jamais tester l'application entière dans un test unique. Si vous exécutez de nombreux tests, notez les points suivants : • • Dans l'IDE Force.com, il peut être nécessaire d'augmenter la valeur Read timeout pour votre projet Apex. Pour plus d'informations, reportez-vous à https://wiki.developerforce.com/index.php/Apex_Toolkit_for_Eclipse. Dans l'interface utilisateur de Salesforce, il peut être nécessaire de tester individuellement les classes de votre organisation, au lieu d'essayer d'exécuter tous les tests à la fois via le bouton Exécuter tous les tests. Exemple de test L'exemple suivant inclut des requêtes pour les types de test suivants : • • • Cas positif avec des enregistrements uniques et multiples Cas négatif avec des enregistrements uniques et multiples Test avec d'autres utilisateurs Le test est utilisé dans une simple application de suivi de la distance parcourue (en miles). Le code existant de l'application vérifie que la distance maximale saisie pour une seule journée ne dépasse pas 500 miles. L'objet principal est un objet personnalisé appelé Mileage__c. Voici la classe de test complète. Les sections suivantes présentent pas à pas des portions spécifiques du code. @isTest private class MileageTrackerTestSuite { static testMethod void runPositiveTestCases() { Double totalMiles = 0; final Double maxtotalMiles = 500; final Double singletotalMiles = 300; final Double u2Miles = 100; Test du code Apex salesforce | Exemple de test | 196 //Set up user User u1 = [SELECT Id FROM User WHERE Alias='auser']; //Run As U1 System.RunAs(u1){ System.debug('Inserting 300 miles... (single record validation)'); Mileage__c testMiles1 = new Mileage__c(Miles__c = 300, Date__c = System.today()); insert testMiles1; //Validate single insert for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY and CreatedById = :u1.id and miles__c != null]) { totalMiles += m.miles__c; } System.assertEquals(singletotalMiles, totalMiles); //Bulk validation totalMiles = 0; System.debug('Inserting 200 mileage records... (bulk validation)'); List<Mileage__c> testMiles2 = new List<Mileage__c>(); for(integer i=0; i<200; i++) { testMiles2.add( new Mileage__c(Miles__c = 1, Date__c = System.today()) ); } insert testMiles2; Test du code Apex salesforce | Exemple de test | 197 for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY and CreatedById = :u1.Id and miles__c != null]) { totalMiles += m.miles__c; } System.assertEquals(maxtotalMiles, totalMiles); }//end RunAs(u1) //Validate additional user: totalMiles = 0; //Setup RunAs User u2 = [SELECT Id FROM User WHERE Alias='tuser']; System.RunAs(u2){ Mileage__c testMiles3 = new Mileage__c(Miles__c = 100, Date__c = System.today()); insert testMiles3; for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY and CreatedById = :u2.Id and miles__c != null]) { totalMiles += m.miles__c; } //Validate System.assertEquals(u2Miles, totalMiles); } //System.RunAs(u2) } // runPositiveTestCases() Test du code Apex salesforce | Exemple de test | 198 static testMethod void runNegativeTestCases() { User u3 = [SELECT Id FROM User WHERE Alias='tuser']; System.RunAs(u3){ System.debug('Inserting a record with 501 miles... (negative test case)'); Mileage__c testMiles3 = new Mileage__c( Miles__c = 501, Date__c = System.today() ); try { insert testMiles3; } catch (DmlException e) { //Assert Error Message System.assert( e.getMessage().contains('Insert failed. First exception on ' + 'row 0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, ' + 'Mileage request exceeds daily limit(500): [Miles__c]'), e.getMessage() ); //Assert field System.assertEquals(Mileage__c.Miles__c, e.getDmlFields(0)[0]); //Assert Status Code System.assertEquals('FIELD_CUSTOM_VALIDATION_EXCEPTION' , e.getDmlStatusCode(0) ); } //catch } //RunAs(u3) } // runNegativeTestCases() } // class MileageTrackerTestSuite Cas de test positif La procédure suivante présente les étapes du code pour le cas de test positif pour des enregistrements uniques et multiples. Test du code Apex salesforce | Exemple de test | 199 1. Ajoutez un texte au journal de débogage, en indiquant l'étape suivante du code : System.debug('Inserting 300 more miles...single record validation'); 2. Créez un objet Mileage__c object, puis insérez-le dans la base de données. Mileage__c testMiles1 = new Mileage__c(Miles__c = 300, Date__c = System.today() ); insert testMiles1; 3. Validez le code en renvoyant les enregistrements insérés : for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY and CreatedById = :createdbyId and miles__c != null]) { totalMiles += m.miles__c; } 4. Utilisez la méthode system.assertEquals pour vérifier que le résultat attendu est renvoyé : System.assertEquals(singletotalMiles, totalMiles); 5. Avant de passer au test suivant, définissez le nombre total de miles sur 0 : totalMiles = 0; 6. Validez le code en créant une insertion en masse de 200 enregistrements. Commencez par ajouter un texte au journal de débogage, en indiquant l'étape suivante du code : System.debug('Inserting 200 Mileage records...bulk validation'); 7. Insérez ensuite 200 enregistrements Mileage__c : List<Mileage__c> testMiles2 = new List<Mileage__c>(); for(Integer i=0; i<200; i++){ testMiles2.add( new Mileage__c(Miles__c = 1, Date__c = System.today()) ); } insert testMiles2; Test du code Apex salesforce | Exemple de test | 200 8. Utilisez System.assertEquals pour vérifier que le résultat attendu est renvoyé : for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY and CreatedById = :CreatedbyId and miles__c != null]) { totalMiles += m.miles__c; } System.assertEquals(maxtotalMiles, totalMiles); Cas de test négatif La procédure suivante présente les étapes du code pour le cas de test négatif. 1. Créez une méthode de test statique appelée runNegativeTestCases : static testMethod void runNegativeTestCases(){ 2. Ajoutez un texte au journal de débogage, en indiquant l'étape suivante du code : System.debug('Inserting 501 miles... negative test case'); 3. Créez un enregistrement Mileage__c record avec 501 miles. Mileage__c testMiles3 = new Mileage__c(Miles__c = 501, Date__c = System.today()); 4. Placez l'instruction insert dans un bloc try/catch. Cela permet de capturer l'exception de validation et de déclarer le message d'erreur généré. try { insert testMiles3; } catch (DmlException e) { 5. Utilisez maintenant System.assert et System.assertEquals pour effectuer le test. Ajoutez le code suivant au bloc catch créé précédemment : //Assert Error Message System.assert(e.getMessage().contains('Insert failed. First exception '+ 'on row 0; first error: FIELD_CUSTOM_VALIDATION_EXCEPTION, '+ 'Mileage request exceeds daily limit(500): [Miles__c]'), Test du code Apex salesforce | Exemple de test | 201 e.getMessage()); //Assert Field System.assertEquals(Mileage__c.Miles__c, e.getDmlFields(0)[0]); //Assert Status Code System.assertEquals('FIELD_CUSTOM_VALIDATION_EXCEPTION' , e.getDmlStatusCode(0)); } } } Test d'un deuxième utilisateur La procédure suivante présente les étapes du code pour l'exécution d'un deuxième utilisateur. 1. Avant de passer au test suivant, définissez le nombre total de miles sur 0 : totalMiles = 0; 2. Configurez l'utilisateur suivant. User u2 = [SELECT Id FROM User WHERE Alias='tuser']; System.RunAs(u2){ 3. Ajoutez un texte au journal de débogage, en indiquant l'étape suivante du code : System.debug('Setting up testing - deleting any mileage records for ' + UserInfo.getUserName() + ' from today'); 4. Insérez ensuite un enregistrement Mileage__c : Mileage__c testMiles3 = new Mileage__c(Miles__c = 100, Date__c = System.today()); insert testMiles3; 5. Validez le code en renvoyant les enregistrements insérés : for(Mileage__c m:[SELECT miles__c FROM Mileage__c WHERE CreatedDate = TODAY Test du code Apex salesforce | Exemple de test | 202 and CreatedById = :u2.Id and miles__c != null]) { totalMiles += m.miles__c; } 6. Utilisez la méthode system.assertEquals pour vérifier que le résultat attendu est renvoyé : System.assertEquals(u2Miles, totalMiles); Chapitre 6 Apex dynamique Sujets : • • • • Compréhension de l'information Describe Apex Requête SOQL dynamique Requête SOSL dynamique Requêtes DML dynamiques Un code Apex dynamique permet aux développeurs de créer des applications plus flexibles en leur permettant d'effectuer les opérations suivantes : • Accéder à l'information describe de sObject et de champ L'information Describe fournit des détails sur les propriétés de sObject et de champ. Par exemple, l'information describe d'un sObject indique si le type de sObject prend en charge des opérations telles que la création ou l'annulation de la suppression, le nom et l'étiquette du sObject, les champs et les objets enfant du sObject, etc. L'information describe d'un champ indique s'il a une valeur par défaut, que ce soit un champ calculé, le type du champ, etc. Notez que l'information describe fournit des détails sur les objets d'une organisation, pas sur des enregistrements individuels. • Écrire des requêtes SOQL dynamiques, des requêtes SOSL dynamiques et des requêtes DML dynamiques Les requêtes SOQL et SOSL dynamiques permettent d'exécuter SOQL ou SOSL en tant que chaîne à l'exécution, alors que le DML dynamique permet de créer un enregistrement de façon dynamique, puis de l'insérer dans la base de données à l'aide du langage de manipulation de données DML. En utilisant des SOQL, SOSL et DML dynamiques, une application peut être adaptée avec précision à l'organisation ainsi qu'aux autorisations de l'utilisateur. Elles peuvent s'avérer utiles pour les applications qui ont été installées à partir de Force.com AppExchange. Apex dynamique salesforce | Compréhension de l'information Describe Apex | 204 Compréhension de l'information Describe Apex Le langage Apex fournit deux structures de données pour l'information describe d'un sObject et d'un champ : • • Jeton : une référence, légère et qui peut être sérialisée, et à un sObject ou à un champ qui est validée lors de la compilation. Résultat Describe : un objet qui contient toutes les propriétés describe du sObject ou du champ. Les objets de résultat Describe ne peuvent pas être sérialisés, et ils sont validés à l'exécution. Le passage d'un jeton à son résultat describe, et inversement, est aisé. Les deux jetons de sObject et de champ incluent la méthode getDescribe qui renvoie le résultat describe pour ce jeton. Dans le résultat describe, les méthodes getSObjectType et getSObjectField renvoient les jetons de sObject et de champ, respectivement. Les jetons étant légers, leur utilisation peut accélérer votre code et le rendre plus efficace. Par exemple, utilisez la version du jeton d'un sObject ou d'un champ pour déterminer le type d'un sObject ou d'un champ que votre code doit utiliser. Le jeton peut être comparé en utilisant l'opérateur d'égalité (==) afin de déterminer si un sObject est l'objet Compte, par exemple, ou si un champ est le champ Nom ou un champ calculé personnalisé. L'exemple de code suivant montre comment utiliser des jetons et des résultats describe pour accéder à des informations sur les propriétés de sObject et de champ : // Create a new account as the generic type sObject sObject s = new Account(); // Verify that the generic sObject is an Account sObject System.assert(s.getsObjectType() == Account.sObjectType); // Get the sObject describe result for the Account object Schema.DescribeSObjectResult r = Account.sObjectType.getDescribe(); // Get the field describe result for the Name field on the Account object Schema.DescribeFieldResult f = Schema.sObjectType.Account.fields.Name; // Verify that the field token is the token for the Name field on an Account object System.assert(f.getSObjectField() == Account.Name); // Get the field describe result from the token f = f.getSObjectField().getDescribe(); L'algorithme suivant montre comment utiliser l'information describe dans le langage Apex : 1. Générez une liste ou un mappage de jetons pour les sObjects de votre organisation (reportez-vous à Accès à tous les sObjects à la page 207). 2. Déterminez le sObject auquel vous devez accéder. Apex dynamique salesforce | Compréhension de l'information Describe Apex | 205 3. Générez le résultat describe du sObject. 4. Si nécessaire, générez un mappage des jetons de champ pour le sObject (reportez-vous à Accès à tous les résultats Describe de champ d'un sObject à la page 209). 5. Générez le résultat describe pour le champ auquel le code doit accéder. Compréhension des autorisations de l'information Describe Un code Apex est généralement exécuté en mode système. Toutes les classes et tous les déclencheurs qui ne sont pas inclus dans un package, c.-à-d. natifs pour votre organisation, n'ont aucune restriction sur les sObjects qu'ils peuvent référencer dynamiquement. Cela signifie qu'avec un code natif, vous pouvez générer un mappage de tous les sObjects de votre organisation, quelles que soient les autorisations de l'utilisateur actuel. Le code Apex dynamique, inclus dans des packages gérés créés par des partenaires FAI de salesforce.com et installés à partir de Force.com AppExchange, a un accès restreint à tout sObject extérieur au package géré. Les partenaires de peuvent définir la valeur Accès API au sein du package pour accorder l'accès aux sObjects standard qui ne sont pas inclus dans le package géré. Alors que les partenaires peuvent demander l'accès aux objets standard, les objets personnalisés ne sont pas inclus dans le package géré et ne peuvent jamais être référencés ni accédés par un code Apex dynamique d'un package. Pour plus d'informations, reportez-vous à « À propos de l'accès API et Apex dynamique dans les packages » dans l'aide en ligne de Salesforce. Utilisation des jetons d'un sObject Les sObject, tels que Compte et MyCustomObject__c, agissent en tant que classes statiques avec des méthodes statiques spéciales et des variables de membre pour accéder aux informations de jeton et de résultat describe. Vous devez référencer explicitement un sObject et un nom de champ lors de la compilation pour avoir accès au résultat describe. Pour accéder au jeton d'un sObject, utilisez l'une des méthodes suivantes : Accédez au membre de variable sObjectType dans un type sObject, tel que Compte. Appelez la méthode getSObjectType dans un résultat describe sObject, une variable sObject, une liste ou un mappage. • • Schema.SObjectType est le type de données d'un jeton sObject. Dans l'exemple suivant, le jeton du sObject Account est renvoyé : Schema.sObjectType t = Account.sObjectType; Le code suivant renvoie également un jeton pour le sObject Account : Account A = new Account(); Schema.sObjectType T = A.getSObjectType(); Cet exemple peut être utilisé pour déterminer si un sObject ou une liste de sObject correspond à un type particulier : public class sObjectTest { { // Create a generic sObject variable s SObject s = Database.query('SELECT Id FROM Account LIMIT 1'); Apex dynamique salesforce | Compréhension de l'information Describe Apex | 206 // Verify if that sObject variable is an Account token System.assertEquals(s.getSObjectType(), Account.sObjectType); // Create a list of generic sObjects List<sObject> l = new Account[]{}; // Verify if the list of sObjects contains Account tokens System.assertEquals(l.getSObjectType(), Account.sObjectType); } } Certains sObject standard ont un champ appelé sObjectType, par exemple AssignmentRule, QueueSObject et RecordType. Pour ces types de sObject, utilisez toujours la méthode getSObjectType pour récupérer le jeton. Si vous utilisez la propriété, par exemple RecordType.sObjectType, le champ est renvoyé. Utilisation des résultats describe de sObject Pour accéder au résultat describe d'un sObject, utilisez l'une des méthodes suivantes : • • Appelez la méthode getDescribe dans un jeton sObject. Utilisez la variable statique Schema sObjectType avec le nom du sObject. Par exemple, Schema.sObjectType.Lead. Schema.DescribeSObjectResult est le type de données du résultat describe d'un sObject. L'exemple suivant utilise la méthode getDescribe dans un jeton sObject : Schema.DescribeSObjectResult D = Account.sObjectType.getDescribe(); L'exemple suivant utilise la variable de membre statique Schema sObjectType : Schema.DescribeSObjectResult D = Schema.SObjectType.Account; Pour plus d'informations sur les méthodes disponibles avec des résultats describe d'un sObject, reportez-vous à Méthodes de résultat Describe d'un sObject à la page 442. Utilisation de jetons de champ Pour accéder au jeton d'un champ, utilisez l'une des méthodes suivantes : • • Accédez au nom de la variable membre statique d'un type statique de sObject, par exemple Account.Name. Appelez la méthode getSObjectField dans le résultat describe d'un champ. Le jeton du champ utilise le type de données Schema.SObjectField. Dans l'exemple suivant, le jeton du champ est renvoyé pour le champ AccountNumber de l'objet Account : Schema.SObjectField F = Account.AccountNumber; Apex dynamique salesforce | Compréhension de l'information Describe Apex | 207 Dans l'exemple suivant, le jeton du champ est renvoyé à partir du résultat describe du champ : // Get the describe result for the Name field on the Account object Schema.DescribeFieldResult f = Schema.sObjectType.Account.fields.Name; // Verify that the field token is the token for the Name field on an Account object System.assert(f.getSObjectField() == Account.Name); // Get the describe result from the token f = f.getSObjectField().getDescribe(); Utilisation des résultats describe de champ Pour accéder au résultat describe d'un champ, utilisez l'une des méthodes suivantes : • • Appelez la méthode getDescribe dans un jeton de champ. Accédez à la variable de membre fields d'un jeton de sObject avec une variable de membre de champ (telle que Name, BillingCity, etc.). Le résultat describe de champ utilise le type de données Schema.DescribeFieldResult. L'exemple suivant utilise la méthode getDescribe : Schema.DescribeFieldResult F = Account.AccountNumber.getDescribe(); Cet exemple utilise la méthode de variable de membre fields : Schema.DescribeFieldResult F = Schema.SObjectType.Account.fields.Name; Dans l'exemple ci-dessus, le système utilise une analyse spéciale pour confirmer lors de la compilation que la variable de membre finale (Name) est valide pour le sObject spécifié. Lorsque l'analyseur trouve la variable de membre fields, il recherche en arrière pour trouver le nom du sObject (Account), et confirme que le nom du champ qui suit la variable de membre fields est légitime. La variable de membre fields fonctionne uniquement lorsqu'elle est utilisée de cette façon. Vous pouvez avoir 100 instructions de variable de membre fields dans une classe ou un déclencheur Apex. Remarque: Vous ne devez pas utiliser la variable de membre fields sans utiliser également un nom de variable de membre de champ ou la méthode getMap. Pour plus d'informations sur getMap, reportez-vous à Accès à tous les résultats Describe de champ d'un sObject à la page 209. Pour plus d'informations sur les méthodes disponibles avec le résultat describe d'un champ, reportez-vous à Méthodes de résultat Describe d'un champ à la page 446. Accès à tous les sObjects Utilisez la méthode Schema getGlobalDescribe pour renvoyer un mappage qui représente la relation entre tous les noms de sObject (clés) et les jetons de sObject (valeurs). Par exemple : Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe(); Apex dynamique salesforce | Compréhension de l'information Describe Apex | 208 Le mappage a les caractéristiques suivantes : • • • • Il est dynamique, c.-à-d. qu'il est généré à l'exécution dans les sObjects actuellement disponibles pour l'organisation, en fonction d'autorisations. Les noms de sObject ne sont pas sensibles à la casse. Les clés utilisent les espaces de noms requis. Les clés indiquent si le sObject est un objet personnalisé. Par exemple, si le bloc de code qui génère le mappage se trouve dans un espace de noms N1 et qu'un sObject est également dans N1, la clé est représentée par MyObject__c dans le mappage. Cependant, si le bloc de code se trouve dans un espace de noms N1 et que le sObject est dans un espace de noms N2, la clé est N2__MyObject__c. De plus, les sObjects standard n'ont pas de préfixe d'espace de noms. Remarque: Si la méthode getGlobalDescribe est appelée dans un package géré installé, elle renvoie les noms de sObject et les jetons de sObjects Chatter, tels que NewsFeed et UserProfileFeed, même si Chatter n'est pas activé dans l'organisation d'installation. Cela n'est pas vrai si la méthode getGlobalDescribe est appelée à partir d'une classe qui ne figure pas dans un package géré installé. Création de sObjects de façon dynamique Vous pouvez créer des sObjects dont les types sont déterminés à l'exécution en appelant la méthode newSObject de la classe de jeton sObject Schema.sObjectType. L'exemple suivant montre comment obtenir un jeton sObject qui correspond à un nom de type de sObject en utilisant la méthode Schema.getGlobalDescribe. Une instance de sObject est ensuite créée via la méthode newSObject de Schema.sObjectType. Cet exemple contient également une méthode de test qui vérifie la création dynamique d'un compte. public class DynamicSObjectCreation { public static sObject createObject(String typeName) { Schema.SObjectType targetType = Schema.getGlobalDescribe().get(typeName); if (targetType == null) { // throw an exception } // Instantiate an sObject with the type passed in as an argument // at run time. return targetType.newSObject(); } static testmethod void testObjectCreation() { String typeName = 'Account'; String acctName = 'Acme'; // Create a new sObject by passing the sObject type as an argument. Apex dynamique salesforce | Compréhension de l'information Describe Apex | 209 Account a = (Account)createObject(typeName); System.assertEquals(typeName, String.valueOf(a.getSobjectType())); // Set the account name and insert the account. a.Name = acctName; insert a; // Verify the new sObject got inserted. Account[] b = [SELECT Name from Account WHERE Name = :acctName]; system.assert(b.size() > 0); } } Accès à tous les résultats Describe de champ pour un sObject Utilisez la méthode getMap de résultat describe de champ pour renvoyer un mappage qui représente la relation entre tous les noms de champ (clés) et jetons de champ (valeurs) d'un sObject. L'exemple suivant génère un mappage qui peut être utilisé pour accéder à un champ par nom : Map<String, Schema.SObjectField> M = Schema.SObjectType.Account.fields.getMap(); Remarque: Le type de valeur de ce mappage n'est pas un résultat describe de champ. L'utilisation des résultats describe consommerait trop de ressources système. À la place, il s'agit d'un mappage de jetons que vous pouvez utiliser pour rechercher le champ approprié. Une fois le champ déterminé, générez pour lui le résultat describe. Le mappage a les caractéristiques suivantes : • • • • Il est dynamique, c.-à-d. qu'il est généré à l'exécution dans les champs de cet sObject. Tous les noms de champ ne sont pas sensibles à la casse. Les clés utilisent les espaces de noms requis. Les clés indiquent si le champ est un objet personnalisé. Par exemple, si le bloc de code qui génère le mappage se trouve dans un espace de noms N1 et qu'un champ est également dans N1, la clé est représentée par MyField__c dans le mappage. Cependant, si le bloc de code se trouve dans un espace de noms N1 et que le champ est dans un espace de noms N2, la clé est N2__MyField__c. De plus, les champs standard n'ont pas de préfixe d'espace de noms. Remarque: Un résultat describe de champ exécuté à partir d'un package géré installé renvoie des champs Chatter, même si Chatter n'est pas activé dans l'organisation de l'installation. Cela n'est pas vrai si le résultat describe de champ est exécuté à partir d'une classe qui ne figure pas dans un package géré installé. Accès à toutes les catégories de données associées à un sObject Utilisez les méthodes describeDataCategory Groups et describeDataCategory GroupStructures pour renvoyer les catégories associées à un objet spécifique : 1. Renvoyez tous les groupes de catégories associés aux objets de votre choix (reportez-vous à describeDataCategory Groups à la page 433). Apex dynamique salesforce | Compréhension de l'information Describe Apex | 210 2. Pour le mappage renvoyé, obtenez le nom du groupe de catégories et le nom du sObject que vous souhaitez interroger (reportez-vous à Schema.DescribeDataCategoryGroupResult à la page 435). 3. Spécifiez le groupe de catégories et l'objet associé, puis récupérez les catégories disponibles pour cet objet (reportez-vous à describeDataCategory GroupStructures à la page 433). La méthode describeDataCategory GroupStructures renvoie les catégories disponibles pour l'objet dans le groupe catégorie que vous spécifiez. Pour plus d'informations sur les catégories de données, reportez-vous à « Que sont les catégories de données ? » dans l'aide en ligne de Salesforce. Dans l'exemple suivant, la méthode describeDataCategoryGroupSample renvoie tous les groupes de catégories associés aux objets Article et Question. La méthode describeDataCategoryGroupStructures renvoie toutes les catégories disponibles pour des articles et des questions dans le groupe de catégories Regions. Pour plus d'informations sur les articles et les questions, reportez-vous à « Gestion des articles et des traductions » et à « Présentation des réponses » dans l'aide en ligne de Salesforce. Pour utiliser l'exemple suivant, vous devez : • • • • • Activer Salesforce Knowledge. Activer la fonctionnalité des réponses. Créer un groupe de catégories de données appelé Regions. Attribuer Regions en tant que groupe de catégories de données qu'utilise Answers. S'assurer que le groupe de catégories de données Regions est attribué à Salesforce Knowledge. Pour plus d'informations sur la création de groupes de catégories de données, reportez-vous à « Création et modification de groupes de catégories » dans l'aide en ligne de Salesforce. Pour plus d'informations sur les réponses, reportez-vous à « Présentation des réponses » dans l'aide en ligne de Salesforce. public class DescribeDataCategoryGroupSample { public static List<DescribeDataCategoryGroupResult> describeDataCategoryGroupSample(){ List<DescribeDataCategoryGroupResult> describeCategoryResult; try { //Creating the list of sobjects to use for the describe //call List<String> objType = new List<String>(); objType.add('KnowledgeArticleVersion'); objType.add('Question'); //Describe Call describeCategoryResult = Schema.describeDataCategoryGroups(objType); //Using the results and retrieving the information for(DescribeDataCategoryGroupResult singleResult : describeCategoryResult){ Apex dynamique salesforce | Compréhension de l'information Describe Apex | 211 //Getting the name of the category singleResult.getName(); //Getting the name of label singleResult.getLabel(); //Getting description singleResult.getDescription(); //Getting the sobject singleResult.getSobject(); } } catch(Exception e){ } return describeCategoryResult; } } public class DescribeDataCategoryGroupStructures { public static List<DescribeDataCategoryGroupStructureResult> getDescribeDataCategoryGroupStructureResults(){ List<DescribeDataCategoryGroupResult> describeCategoryResult; List<DescribeDataCategoryGroupStructureResult> describeCategoryStructureResult; try { //Making the call to the describeDataCategoryGroups to //get the list of category groups associated List<String> objType = new List<String>(); objType.add('KnowledgeArticleVersion'); objType.add('Question'); describeCategoryResult = Schema.describeDataCategoryGroups(objType); Apex dynamique salesforce | Compréhension de l'information Describe Apex | 212 //Creating a list of pair objects to use as a parameter //for the describe call List<DataCategoryGroupSobjectTypePair> pairs = new List<DataCategoryGroupSobjectTypePair>(); //Looping throught the first describe result to create //the list of pairs for the second describe call for(DescribeDataCategoryGroupResult singleResult : describeCategoryResult){ DataCategoryGroupSobjectTypePair p = new DataCategoryGroupSobjectTypePair(); p.setSobject(singleResult.getSobject()); p.setDataCategoryGroupName(singleResult.getName()); pairs.add(p); } //describeDataCategoryGroupStructures() describeCategoryStructureResult = Schema.describeDataCategoryGroupStructures(pairs, false); //Getting data from the result for(DescribeDataCategoryGroupStructureResult singleResult : describeCategoryStructureResult){ //Get name of the associated Sobject singleResult.getSobject(); //Get the name of the data category group singleResult.getName(); //Get the name of the data category group singleResult.getLabel(); //Get the description of the data category group singleResult.getDescription(); Apex dynamique salesforce | Compréhension de l'information Describe Apex | 213 //Get the top level categories DataCategory [] toplevelCategories = singleResult.getTopCategories(); //Recursively get all the categories List<DataCategory> allCategories = getAllCategories(toplevelCategories); for(DataCategory category : allCategories) { //Get the name of the category category.getName(); //Get the label of the category category.getLabel(); //Get the list of sub categories in the category DataCategory [] childCategories = category.getChildCategories(); } } } catch (Exception e){ } return describeCategoryStructureResult; } private static DataCategory[] getAllCategories(DataCategory [] categories){ if(categories.isEmpty()){ return new DataCategory[]{}; } else { DataCategory [] categoriesClone = categories.clone(); DataCategory category = categoriesClone[0]; DataCategory[] allCategories = new DataCategory[]{category}; Apex dynamique salesforce | Compréhension de l'information Describe Apex | 214 categoriesClone.remove(0); categoriesClone.addAll(category.getChildCategories()); allCategories.addAll(getAllCategories(categoriesClone)); return allCategories; } } } Test de l'accès à toutes les catégories de données associées à un sObject L'exemple suivant teste la méthode describeDataCategoryGroupSample présentée dans Accès à toutes les catégories de données associées à un sObject. Il s'assure que le groupe de catégories et les objets associés renvoyés sont corrects. @isTest private class DescribeDataCategoryGroupSampleTest { public static testMethod void describeDataCategoryGroupSampleTest(){ List<DescribeDataCategoryGroupResult>describeResult = DescribeDataCategoryGroupSample.describeDataCategoryGroupSample(); //Assuming that you have KnowledgeArticleVersion and Questions //associated with only one category group 'Regions'. System.assert(describeResult.size() == 2, 'The results should only contain two results: ' + describeResult.size()); for(DescribeDataCategoryGroupResult result : describeResult) { //Storing the results String name = result.getName(); String label = result.getLabel(); String description = result.getDescription(); String objectNames = result.getSobject(); //asserting the values to make sure System.assert(name == 'Regions', 'Incorrect name was returned: ' + name); System.assert(label == 'Regions of the World', Apex dynamique salesforce | Compréhension de l'information Describe Apex | 215 'Incorrect label was returned: ' + label); System.assert(description == 'This is the category group for all the regions', 'Incorrect description was returned: ' + description); System.assert(objectNames.contains('KnowledgeArticleVersion') || objectNames.contains('Question'), 'Incorrect sObject was returned: ' + objectNames); } } } Cet exemple teste la méthode describeDataCategoryGroupStructures présentée dans Accès à toutes les catégories de données associées à un sObject. Il s'assure que le groupe de catégories, les catégories et les objets associés renvoyés sont corrects. @isTest private class DescribeDataCategoryGroupStructuresTest { public static testMethod void getDescribeDataCategoryGroupStructureResultsTest(){ List<Schema.DescribeDataCategoryGroupStructureResult> describeResult = DescribeDataCategoryGroupStructures.getDescribeDataCategoryGroupStructureResults(); System.assert(describeResult.size() == 2, 'The results should only contain 2 results: ' + describeResult.size()); //Creating category info CategoryInfo world = new CategoryInfo('World', 'World'); CategoryInfo asia = new CategoryInfo('Asia', 'Asia'); CategoryInfo northAmerica = new CategoryInfo('NorthAmerica', 'North America'); CategoryInfo southAmerica = new CategoryInfo('SouthAmerica', 'South America'); CategoryInfo europe = new CategoryInfo('Europe', 'Europe'); List<CategoryInfo> info = new CategoryInfo[] { asia, northAmerica, southAmerica, europe }; Apex dynamique salesforce | Compréhension de l'information Describe Apex | 216 for (Schema.DescribeDataCategoryGroupStructureResult result : describeResult) { String name = result.getName(); String label = result.getLabel(); String description = result.getDescription(); String objectNames = result.getSobject(); //asserting the values to make sure System.assert(name == 'Regions', 'Incorrect name was returned: ' + name); System.assert(label == 'Regions of the World', 'Incorrect label was returned: ' + label); System.assert(description == 'This is the category group for all the regions', 'Incorrect description was returned: ' + description); System.assert(objectNames.contains('KnowledgeArticleVersion') || objectNames.contains('Question'), 'Incorrect sObject was returned: ' + objectNames); DataCategory [] topLevelCategories = result.getTopCategories(); System.assert(topLevelCategories.size() == 1, 'Incorrect number of top level categories returned: ' + topLevelCategories.size()); System.assert(topLevelCategories[0].getLabel() == world.getLabel() && topLevelCategories[0].getName() == world.getName()); //checking if the correct children are returned DataCategory [] children = topLevelCategories[0].getChildCategories(); System.assert(children.size() == 4, 'Incorrect number of children returned: ' + children.size()); for(Integer i=0; i < children.size(); i++){ System.assert(children[i].getLabel() == info[i].getLabel() && children[i].getName() == info[i].getName()); } } Apex dynamique salesforce | Requête SOQL dynamique | 217 } private class CategoryInfo { private final String name; private final String label; private CategoryInfo(String n, String l){ this.name = n; this.label = l; } public String getName(){ return this.name; } public String getLabel(){ return this.label; } } } Requête SOQL dynamique Une requête SOQL dynamique correspond à la création d'une chaîne SOQL à l'exécution avec un code Apex. Une requête SOQL dynamique permet de créer des applications plus flexibles. Par exemple, vous pouvez créer une recherche basée sur la saisie d'un utilisateur final ou mettre à jour des enregistrements des noms de champ variables. Pour créer une requête SOQL dynamique à l'exécution, utilisez la méthode de base de données query dans l'une des méthodes suivantes : • Renvoyez un sObject unique lorsque la requête renvoie un enregistrement unique : sObject S = Database.query(string_limit_1); Apex dynamique • salesforce | Requête SOSL dynamique | 218 Renvoyez une liste de sObject lorsque la requête renvoie plusieurs enregistrements : List<sObject> L = Database.query(string); La méthode de base de données query peut être utilisée partout où une requête SOQL en ligne est utilisable, notamment dans des instructions d'attribution régulières et des boucles for. Les résultats sont traités de façon similaire aux requêtes SOQL statiques. Les résultats de requêtes SOQL dynamiques peuvent être spécifiés en tant que sObjects concrets, tels que Account ou MyCustomObject__c, ou en tant que type de données sObject générique. À l'exécution, le système confirme que le type de la requête correspond au type déclaré de la variable. Si la requête ne renvoie pas le type sObject correct, une erreur d'exécution est levée. Cela signifie qu'il n'est pas nécessaire de convertir un sObject générique en sObject concret. Les requêtes SOQL dynamiques ont les mêmes limitations du gouverneur que les requêtes statiques. Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Pour une description complète de la syntaxe d'une requête SOQL, reportez-vous à Salesforce Object Query Language (SOQL) dans le guide Force.com SOQL and SOSL Reference. Injection SOQL L'injection SOQL est une technique par laquelle un utilisateur conduit votre application à exécuter des méthodes de base de données que vous n'aviez pas prévues en passant des instructions SOQL dans votre code. Cela peut se produire dans un code Apex lorsque votre application dépend de la construction par un utilisateur final d'une instruction SOQL dynamique et que vous n'avez pas géré correctement l'entrée. Pour empêcher l'injection SOQL, utilisez la méthode escapeSingleQuotes. Cette méthode ajoute le caractère d'échappement (\) à tous les guillemets simples d'une chaîne transmise par un utilisateur. Cette méthode permet de traiter tous les guillemets simples en tant que chaînes englobantes, au lieu de commandes de base de données. Requête SOSL dynamique Une requête SOSL dynamique correspond à la création d'une chaîne SOSL à l'exécution avec un code Apex. Une requête SOSL dynamique permet de créer des applications plus flexibles. Par exemple, vous pouvez créer une recherche basée sur la saisie d'un utilisateur final ou mettre à jour des enregistrements des noms de champ variables. Pour créer une requête SOSL dynamique à l'exécution, utilisez la méthode de recherche query. Par exemple : List<List <sObject>> myQuery = search.query(SOSL_search_string); L'exemple suivant présente une simple chaîne de requête SOSL. String searchquery='FIND\'Edge*\'IN ALL FIELDS RETURNING Account(id,name),Contact, Lead'; List<List<SObject>>searchList=search.query(searchquery); Les instructions SOSL dynamiques évaluent à une liste de listes de sObjects, dans laquelle chaque liste contient les résultats de la recherche d'un type sObject particulier. Les listes de résultats sont toujours renvoyées dans le même ordre, telles que spécifiées dans la requête SOSL dynamique. Dans l'exemple ci-dessus, les résultats de Account sont les premiers, suivis de Contact, puis de Lead. Apex dynamique salesforce | Requêtes DML dynamiques | 219 La méthode de recherche query peut être utilisée partout où une requête SOSL en ligne est utilisable, notamment dans des instructions d'attribution régulières et des boucles for. Les résultats sont traités de façon similaire aux requêtes SOSL statiques. Les requêtes SOSL dynamiques ont les mêmes limitations du gouverneur que les requêtes statiques. Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Pour une description complète de la syntaxe d'une requête SOSL, reportez-vous à Salesforce Object Search Language (SOSL) dans le guide Force.com SOQL and SOSL Reference. Injection SOSL L'injection SOSL est une technique par laquelle un utilisateur conduit votre application à exécuter des méthodes de base de données que vous n'aviez pas prévues en passant des instructions SOSL dans votre code. Cela peut se produire dans un code Apex lorsque votre application dépend de la construction par un utilisateur final d'une instruction SOSL dynamique et que vous n'avez pas géré correctement l'entrée. Pour empêcher l'injection SOSL, utilisez la méthode escapeSingleQuotes. Cette méthode ajoute le caractère d'échappement (\) à tous les guillemets simples d'une chaîne passée par un utilisateur. Elle permet de traiter tous les guillemets simples en tant que chaînes englobantes, au lieu de commandes de base de données. Requêtes DML dynamiques Outre l'interrogation de l'information describe et de la création de requêtes SOQL à l'exécution, vous pouvez créer dynamiquement des sObjects et les insérer dans la base de données en utilisant le langage de manipulation de données DML. Pour créer un sObject d'un type donné, utilisez la méthode newSObject dans un jeton sObject. Notez que le jeton doit être converti en type sObject concret (tel que Account). Par exemple : // Get a new account Account A = new Account(); // Get the token for the account Schema.sObjectType tokenA = A.getSObjectType(); // The following produces an error because the token is a generic sObject, not an Account // Account B = tokenA.newSObject(); // The following works because the token is cast back into an Account Account B = (Account)tokenA.newSObject(); Bien que le jeton sObject tokenA soit un jeton de Account, il est considéré comme un sObject, car il est accédé séparément. Il doit être reconverti en Account du type de sObject concret pour utiliser la méthode newSObject. Pour plus d'informations sur la conversion, reportez-vous à Classes et conversion à la page 170. Apex dynamique salesforce | Requêtes DML dynamiques | 220 Voici un autre exemple qui présente comment obtenir le jeton sObject via la méthode Schema.getGlobalDescribe, puis créer un nouveau sObject en utilisant la méthode newSObject dans le jeton. Cet exemple contient également une méthode de test qui vérifie la création dynamique d'un compte. public class DynamicSObjectCreation { public static sObject createObject(String typeName) { Schema.SObjectType targetType = Schema.getGlobalDescribe().get(typeName); if (targetType == null) { // throw an exception } // Instantiate an sObject with the type passed in as an argument // at run time. return targetType.newSObject(); } static testmethod void testObjectCreation() { String typeName = 'Account'; String acctName = 'Acme'; // Create a new sObject by passing the sObject type as an argument. Account a = (Account)createObject(typeName); System.assertEquals(typeName, String.valueOf(a.getSobjectType())); // Set the account name and insert the account. a.Name = acctName; insert a; // Verify the new sObject got inserted. Account[] b = [SELECT Name from Account WHERE Name = :acctName]; system.assert(b.size() > 0); } } Apex dynamique salesforce | Requêtes DML dynamiques | 221 Vous pouvez également spécifier un ID avec newSObject pour créer un sObject qui référence un enregistrement existant que vous pouvez mettre à jour ultérieurement. Par exemple : SObject s = Database.query('SELECT Id FROM account LIMIT 1')[0].getSObjectType(). newSObject([SELECT Id FROM Account LIMIT 1][0].Id); Reportez-vous à Schema.sObjectType à la page 452. Définition et récupération de valeurs de champ Utilisez les méthodes get et put dans un objet pour définir et récupérer des valeurs de champs en utilisant le nom d'API des champs, exprimé en tant que chaîne, ou le jeton des champs. Dans l'exemple suivant, le nom d'API du champ AccountNumber est utilisé : SObject s = [SELECT AccountNumber FROM Account LIMIT 1]; Object o = s.get('AccountNumber'); s.put('AccountNumber', 'abc'); L'exemple suivant utilise à la place le jeton du champ AccountNumber : Schema.DescribeFieldResult f = Schema.sObjectType.Account.fields.AccountNumber; Sobject s = Database.query('SELECT AccountNumber FROM Account LIMIT 1'); s.put(f.getsObjectField(), '12345'); Le type de données scalaire Object peut être utilisé en tant que type de données générique pour définir ou récupérer les valeurs de champ dans un sObject. Il équivaut au type de champ anyType. Notez que le type de données Object est différent du type de données sObject, qui peut être utilisé en tant que type générique pour n'importe quel sObject. Remarque: Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Définition et récupération de clés étrangères Le langage Apex prend en charge le renseignement des clés étrangères par le nom (ou l'ID externe) de la même façon que l'API. Pour définir ou récupérer la valeur ID scalaire d'une clé étrangère, utilisez la méthode get ou put. Pour définir ou récupérer l'enregistrement associé à une clé étrangère, utilisez les méthodes getSObject et putSObject. Notez que ces méthodes doivent être utilisées avec le type de données sObject, pas Object. Par exemple : SObject c = Database.query('SELECT Id, FirstName, AccountId, Account.Name FROM Contact LIMIT 1'); SObject a = c.getSObject('Account'); Il n'est pas nécessaire de spécifier un ID externe pour une valeur sObject parente en utilisant des sObject enfants. Si vous fournissez un ID dans le sObject parent, il est ignoré par l'opération DML. Le langage Apex considère que la clé étrangère Apex dynamique salesforce | Requêtes DML dynamiques | 222 est renseignée via une requête relationnelle SOQL, qui renvoie toujours un objet parent avec un ID renseigné. Si vous avez un ID, utilisez-le avec l'objet enfant. Par exemple, supposons que l'objet personnalisé C1 a une clé étrangère c2__c liée à un objet personnalisé enfant C2. Vous souhaitez créer un objet C1 et l'associer à un enregistrement C2 nommé 'xxx' (attribué à la valeur c2__r). Vous n'avez pas besoin de l'ID de l'enregistrement 'xxx', car il est renseigné via la relation du parent à l'enfant. Par exemple : insert new C1__c(name = 'x', c2__r = new C2__c(name = 'xxx')); Si vous aviez attribué une valeur à l'ID pour c2__r, elle serait ignorée. Si vous avez l'ID, attribuez-le à l'objet (c2__c), pas à l'enregistrement. Vous pouvez également accéder à des clés étrangères en utilisant un code Apex dynamique. L'exemple suivant montre comment obtenir les valeurs d'une sous-requête dans une relation parent à enfant, en utilisant un code Apex dynamique : String queryString = 'SELECT Id, Name, ' + '(SELECT FirstName, LastName FROM Contacts LIMIT 1) FROM Account'; SObject[] queryParentObject = Database.query(queryString); for (SObject parentRecord : queryParentObject){ Object ParentFieldValue = parentRecord.get('Name'); // Prevent a null relationship from being accessed SObject[] childRecordsFromParent = parentRecord.getSObjects('Contacts'); if (childRecordsFromParent != null) { for (SObject childRecord : childRecordsFromParent){ Object ChildFieldValue1 = childRecord.get('FirstName'); Object ChildFieldValue2 = childRecord.get('LastName'); System.debug('Account Name: ' + ParentFieldValue + '. Contact Name: '+ ChildFieldValue1 + ' ' + ChildFieldValue2); } } } Chapitre 7 Apex par lot Sujets : • • Utilisation d'une tâche Apex par lot Compréhension du partage géré Apex Un développeur peut désormais utiliser un code Apex par lot pour créer des processus complexes et longs à exécuter sur la plate-forme Force.com. Par exemple, un développeur peut créer une solution d'archivage, exécutée la nuit, qui recherche les enregistrements d'une ancienneté spécifique pour les ajouter à une archive. Un développeur peut également créer une opération de nettoyage des données qui analyse l'ensemble des comptes et des opportunités la nuit, et les met à jour si nécessaire selon des critères personnalisés. Le code Apex par lot est exposé sous la forme d'une interface qui doit être mise en oeuvre par le développeur. Les tâches par lot peuvent être invoquées par programmation à l'exécution en utilisant Apex. Vous pouvez avoir uniquement cinq tâches par lot en file d'attente ou actives en même temps. Vous pouvez déterminer le nombre actuel en affichant la page Tâches planifiées dans Salesforce, ou par programmation en utilisant l'API SOAP pour interroger l'objet AsyncapexJob. ATTENTION: Soyez très prudent(e) si vous envisagez d'invoquer une tâche par lot à partir d'un déclencheur. Vous devez vous assurer que le déclencheur n'ajoute pas d'autres tâches par lot aux cinq autorisées. En particulier, considérez les mises à jour en masse d'API, les assistants d'importation, les modifications d'enregistrement en masse via l'interface utilisateur et toutes les situations dans lesquelles plusieurs enregistrements peuvent être mis à jour à la fois. L'exécution des tâches par lot peut être programmée à des heures spécifiques en utilisant le planificateur Apex ou via la page Planifier Apex dans l'interface utilisateur de Salesforce. Pour plus d'informations sur la page Planifier Apex, reportez-vous à « Planification de classes Apex » dans l'aide en ligne de Salesforce. L'interface Apex par lot est également utilisée pour les recalculs de partage géré Apex. Pour plus d'informations sur les tâches par lot, reportez-vous à Utilisation d'une tâche Apex par lot à la page 224. Pour plus d'informations sur le partage géré Apex, reportez-vous à Compréhension du partage géré Apex à la page 237. Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 224 Utilisation d'une tâche Apex par lot Pour utiliser une tâche Apex par lot, vous devez écrire une classe Apex qui met en oeuvre l'interface Database.Batchable fournie par Salesforce, puis invoquer la classe par programmation. Pour suivre ou arrêter l'exécution de la tâche Apex par lot, cliquez sur Votre nom > Configuration > Surveillance > Tâches Apex. Pour plus d'informations, reportez-vous à « Surveillance de la file d'attente des tâches Apex » dans l'aide en ligne de Salesforce. Mise en oeuvre de l'interface Database.Batchable L'interface Database.Batchable inclut trois méthodes qui doivent être mises en oeuvre : • La méthode start : global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {} La méthode start est appelée au début d'une tâche Apex par lot. Utilisez la méthode start pour collecter les enregistrements ou les objets à passer à la méthode d'interface execute. Cette méthode renvoie un objet Database.QueryLocator, ou un itérable qui contient les enregistrements ou les objets passés dans la tâche. Utilisez l'objet Database.QueryLocator lorsque vous utilisez une requête simple (SELECT) pour générer la portée des objets utilisés dans la tâche par lot. Si vous utilisez l'objet querylocator, les limitations du gouverneur en nombre total d'enregistrements récupérés par des requêtes SOQL sont ignorées. Par exemple, une tâche Apex par lot pour l'objet Account peut renvoyer un QueryLocator pour tous les enregistrements account (jusqu'à 50 millions d'enregistrements) dans une organisation. Un autre exemple est le recalcul du partage pour l'objet Contact qui renvoie un QueryLocator pour tous les enregistrements account de l'organisation. Utilisez l'itérable lorsque vous souhaitez créer une portée complexe pour la tâche par lot. Vous pouvez également l'utiliser pour créer votre propre processus personnalisé afin d'itérer dans la liste. Important: Si vous utilisez un itérable, les limitations du gouverneur en nombre total d'enregistrements récupérés par des requêtes SOQL restent applicables. • La méthode execute : global void execute(Database.BatchableContext BC, list<P>){} La méthode execute est appelée pour chaque lot d'enregistrements transmis à la méthode. Utilisez cette méthode afin d'effectuer tout le traitement requis pour chaque segment de données. Cette méthode prend les éléments suivants : ◊ Une référence à l'objet Database.BatchableContext. ◊ Une liste de sObjects, telle que List<sObject>, ou une liste de types paramétrés. Si vous utilisez un Database.QueryLocator, la liste renvoyée doit être utilisée. L'exécution des lots d'enregistrements dans l'ordre dans lequel ils sont reçus depuis la méthode start n'est pas garantie. • La méthode finish : global void finish(Database.BatchableContext BC){} Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 225 La méthode finish est appelée une fois tous les lots traités. Elle permet d'envoyer des e-mails de confirmation ou d'exécuter des opérations de post-traitement. Chaque exécution d'une tâche Apex par lot est considérée comme une transaction discrète. Par exemple, une tâche Apex par lot qui contient 1000 enregistrements et qui est exécutée sans le paramètre facultatif scope depuis Database.executeBatch, est considérée en cinq transactions de 200 enregistrements chacun. Les limitations du gouverneur Apex sont réinitialisées pour chaque transaction. Si la première transaction réussit et la deuxième échoue, les mises à jour effectuées dans la base de données avec la première transaction ne sont pas annulées. Utilisation de Database.BatchableContext Toutes les méthodes de l'interface Database.Batchable nécessitent une référence à un objet Database.BatchableContext. Utilisez cet objet pour suivre la progression de la tâche par lot. La méthode d'instance avec l'objet Database.BatchableContext est la suivante : Nom Arguments getJobID Renvoie Description ID Renvoie l'ID de l'objet AsyncApexJob associé à cette tâche par lot en tant que chaîne. Utilisez cette méthode pour suivre la progression d'enregistrements dans la tâche par lot. Vous pouvez également utiliser cet ID avec la méthode System.abortJob. L'exemple suivant utilise l'objet Database.BatchableContext pour interroger l'objet AsyncApexJob associé à la tâche par lot. global void finish(Database.BatchableContext BC){ // Get the ID of the AsyncApexJob representing this batch job // from Database.BatchableContext. // Query the AsyncApexJob object to retrieve the current job's information. AsyncApexJob a = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE Id = :BC.getJobId()]; // Send an email to the Apex job's submitter notifying of job completion. Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {a.CreatedBy.Email}; mail.setToAddresses(toAddresses); mail.setSubject('Apex Sharing Recalculation ' + a.Status); mail.setPlainTextBody ('The batch Apex job processed ' + a.TotalJobItems + ' batches with '+ a.NumberOfErrors + ' failures.'); Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 226 } Utilisation de Database.QueryLocator pour définir la portée La méthode start peut renvoyer un objet Database.QueryLocator contenant les enregistrements à utiliser dans la tâche par lot, ou renvoyer un itérable. L'exemple suivant utilise un objet Database.QueryLocator : global class SearchAndReplace implements Database.Batchable<sObject>{ global final String Query; global final String Entity; global final String Field; global final String Value; global SearchAndReplace(String q, String e, String f, String v){ Query=q; Entity=e; Field=f;Value=v; } global Database.QueryLocator start(Database.BatchableContext BC){ return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List<sObject> scope){ for(sobject s : scope){ s.put(Field,Value); } update scope; } global void finish(Database.BatchableContext BC){ } } Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 227 Utilisation d'un itérable dans une tâche Apex par lot pour définir la portée La méthode start peut renvoyer un objet Database.QueryLocator contenant les enregistrements à utiliser dans la tâche par lot, ou renvoyer un itérable. Utilisez un itérable pour parcourir aisément les éléments renvoyés. global class batchClass implements Database.batchable{ global Iterable start(Database.BatchableContext info){ return new CustomAccountIterable(); } global void execute(Database.BatchableContext info, List<Account> scope){ List<Account> accsToUpdate = new List<Account>(); for(Account a : scope){ a.Name = 'true'; a.NumberOfEmployees = 70; accsToUpdate.add(a); } update accsToUpdate; } global void finish(Database.BatchableContext info){ } } Utilisation de la méthode Database.executeBatch Vous pouvez utiliser la méthode Database.executeBatch pour commencer par programmation une tâche par lot. Important: Lorsque vous appelez Database.executeBatch, Salesforce ajoute le processus à la file d'attente uniquement à l'heure planifiée. L'exécution réelle peut être retardée en fonction de la disponibilité du service. La méthode Database.executeBatch prend deux paramètres : • • La classe qui met en oeuvre Database.Batchable. La méthode Database.executeBatch prend un paramètre facultatif scope. Ce paramètre spécifie le nombre d'enregistrements qui doivent être passés dans la méthode execute. Utilisez ce paramètre lorsque vous avez de nombreuses opérations pour chaque enregistrement passé et que vous atteignez les limitations du gouverneur. En limitant le nombre d'enregistrements, vous limitez également les opérations par transaction. Cette valeur doit être supérieure à zéro. Si la méthode start renvoie un QueryLocator, le paramètre de portée facultatif de Database.executeBatch peut avoir une valeur maximale de 2000. Si vous définissez une valeur plus importante, Salesforce segmente les enregistrements renvoyés par QueryLocator en lots plus petits limités à 2000 enregistrements. Si la méthode start renvoie un itérable, la valeur du paramètre de portée n'a pas de limite maximale. Si vous utilisez une valeur très élevée, vous risquez toutefois d'atteindre d'autres limites. Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 228 La méthode Database.executeBatch renvoie l'ID de l'objet AsyncApexJob, qui peut ensuite être utilisé pour suivre la progression de la tâche. Par exemple : ID batchprocessid = Database.executeBatch(reassign); AsyncApexJob aaj = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID =: batchprocessid ]; Pour plus d'informations sur l'objet AsyncApexJob object, reportez-vous à AsyncApexJob dans le guide Object Reference for Salesforce and Force.com. Vous pouvez également utiliser cet ID avec la méthode System.abortJob. Exemples de tâche Apex par lot L'exemple suivant utilise un objet Database.QueryLocator : global class UpdateAccountFields implements Database.Batchable<sObject>{ global final String Query; global final String Entity; global final String Field; global final String Value; global UpdateAccountFields(String q, String e, String f, String v){ Query=q; Entity=e; Field=f;Value=v; } global Database.QueryLocator start(Database.BatchableContext BC){ return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List<sObject> scope){ for(Sobject s : scope){s.put(Field,Value); } update scope; } global void finish(Database.BatchableContext BC){ Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 229 } } Le code suivant peut être utilisé pour appeler la classe ci-dessus : Id batchInstanceId = Database.executeBatch(new UpdateAccountFields(q,e,f,v), 5); La classe suivante utilise une tâche Apex par lot pour réattribuer tous les comptes d'un utilisateur spécifique à un autre utilisateur. global class OwnerReassignment implements Database.Batchable<sObject>{ String query; String email; Id toUserId; Id fromUserId; global Database.querylocator start(Database.BatchableContext BC){ return Database.getQueryLocator(query);} global void execute(Database.BatchableContext BC, List<sObject> scope){ List<Account> accns = new List<Account>(); for(sObject s : scope){Account a = (Account)s; if(a.OwnerId==fromUserId){ a.OwnerId=toUserId; accns.add(a); } } update accns; } global void finish(Database.BatchableContext BC){ Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); mail.setToAddresses(new String[] {email}); Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 230 mail.setReplyTo('[email protected]'); mail.setSenderDisplayName('Batch Processing'); mail.setSubject('Batch Process Completed'); mail.setPlainTextBody('Batch Process has completed'); Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } Utilisez le code suivant pour exécuter la classe OwnerReassignment dans l'exemple précédent : OwnerReassignment reassign = new OwnerReassignment(); reassign.query = 'SELECT Id, Name, Ownerid FROM Account ' + 'WHERE ownerid=\'' + u.id + '\''; reassign.email='[email protected]'; reassign.fromUserId = u; reassign.toUserId = u2; ID batchprocessid = Database.executeBatch(reassign); L'exemple suivant est une classe Apex par lot pour la suppression d'enregistrements. global class BatchDelete implements Database.Batchable<sObject> { public String query; global Database.QueryLocator start(Database.BatchableContext BC){ return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List<sObject> scope){ delete scope; DataBase.emptyRecycleBin(scope); } global void finish(Database.BatchableContext BC){ } } Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 231 Ce code appelle la classe Apex par lot BatchDelete pour supprimer les anciens documents. La requête spécifiée sélectionne dans un dossier tous les documents antérieurs à une date définie pour les supprimer. L'exemple invoque ensuite la tâche par lot. BatchDelete BDel = new BatchDelete(); Datetime d = Datetime.now(); d = d.addDays(-1); // Replace this value with the folder ID that contains // the documents to delete. String folderId = '00lD000000116lD'; // Query for selecting the documents to delete BDel.query = 'SELECT Id FROM Document WHERE FolderId=\'' + folderId + '\' AND CreatedDate < '+d.format('yyyy-MM-dd')+'T'+ d.format('HH:mm')+':00.000Z'; // Invoke the batch job. ID batchprocessid = Database.executeBatch(BDel); System.debug('Returned batch process ID: ' + batchProcessId); Utilisation d'appels dans une tâche Apex par lot Pour utiliser un appel dans une tâche Apexpar lot, vous devez spécifier Database.AllowsCallouts dans la définition de la classe. Par exemple : global class SearchAndReplace implements Database.Batchable<sObject>, Database.AllowsCallouts{ } Les appels incluent des requêtes HTTP, ainsi que les méthodes définies avec le mot clé webService. Utilisation d'un état dans une tâche Apex par lot Chaque exécution d'une tâche Apex par lot est considérée comme une transaction discrète. Par exemple, une tâche Apex par lot qui contient 1000 enregistrements et qui est exécutée sans le paramètre facultatif scope est considérée en cinq transactions de 200 enregistrements chacun. Si vous spécifiez Database.Stateful dans la définition de la classe, vous pouvez maintenir l'état dans ces transactions. Lors de l'utilisation de Database.Stateful, seules les variables de membre d'instance conservent leur valeur entre les transactions, contrairement aux variables de membre statiques qui sont réinitialisées entre les transactions. La conservation de l'état est utile pour compter ou résumer des enregistrements pendant leur traitement. Par exemple, supposons que votre tâche a traité des enregistrements d'opportunité. Vous pouvez définir une méthode dans execute pour regrouper les totaux des montants d'opportunité pendant leur traitement. Si vous ne spécifiez pas Database.Stateful, toutes les variables de membre statiques et d'instance sont réinitialisées à leur valeur d'origine. Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 232 L'exemple suivant récapitule un champ personnalisé total__c pendant le traitement des enregistrements : global class SummarizeAccountTotal implements Database.Batchable<sObject>, Database.Stateful{ global final String Query; global integer Summary; global SummarizeAccountTotal(String q){Query=q; Summary = 0; } global Database.QueryLocator start(Database.BatchableContext BC){ return Database.getQueryLocator(query); } global void execute( Database.BatchableContext BC, List<sObject> scope){ for(sObject s : scope){ Summary = Integer.valueOf(s.get('total__c'))+Summary; } } global void finish(Database.BatchableContext BC){ } } Vous pouvez en outre spécifier une variable pour accéder à l'état initial de la classe. Vous pouvez utiliser cette variable pour partager l'état initial avec toutes les instances les méthodes Database.Batchable. Par exemple : // Implement the interface using a list of Account sObjects // Note that the initialState variable is declared as final global class MyBatchable implements Database.Batchable<sObject> { private final String initialState; Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 233 String query; global MyBatchable(String intialState) { this.initialState = initialState; } global Database.QueryLocator start(Database.BatchableContext BC) { // Access initialState here return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List<sObject> batch) { // Access initialState here } global void finish(Database.BatchableContext BC) { // Access initialState here } } Notez que initialState est l'état initial de la classe. Vous ne pouvez pas l'utiliser pour passer des informations entre des instances de la classe pendant l'exécution de la tâche par lot. Par exemple, si vous changez la valeur de initialState dans execute, le deuxième segment d'enregistrements traités ne peut pas accéder à la nouvelle valeur : seule la valeur initiale est accessible. Test d'une tâche Apex par lot Lors du test de votre Apex par lot, vous pouvez tester une seule exécution de la méthode execute. Vous pouvez utiliser le paramètre scope de la méthode executeBatch afin de limiter le nombre de enregistrements passés dans la méthode execute pour ne pas atteindre les limitations du gouverneur. La méthode executeBatch lance un processus asynchrone. Cela signifie que lorsque vous testez une tâche Apex, vous devez vous assurer que la tâche par lot est terminée avant de tester les résultats. Utilisez les méthodes de Test startTest et stopTest autour de la méthode executeBatch pour vous assurer qu'elle est terminée avant de poursuivre votre test. Tous les appels asynchrones effectués après la méthode startTest sont collectés par le système. Lors de l'exécution de stopTest, tous les processus asynchrones sont exécutés de façon synchrone. Si vous n'incluez pas la méthode executeBatch dans les méthodes Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 234 startTest et stopTest, la tâche par lot est exécutée à la fin de votre méthode de test pour le code Apex enregistré à l'aide de l'API Salesforce.com versions 25.0 et supérieures, mais pas dans les versions antérieures. À compter du code Apex enregistré en utilisant l'API Salesforce.com version 22.0, les exceptions qui se produisent durant l'exécution d'une tâche Apex par lot, qui est invoquée par une méthode de test, sont désormais transmises à la méthode de test d'appel, ce qui entraîne l'échec de la méthode de test. Si vous souhaitez gérer des exceptions dans la méthode de test, insérez votre code dans des instructions try et catch. Vous devez placer le bloc catch après la méthode stopTest. Notez cependant qu'avec le code Apex enregistré en utilisant l'API Salesforce.com versions 21.0 et antérieures, ces exceptions ne sont pas transmises à la méthode de test, qui n'échoue pas. Remarque: Les appels asynchrones, tels que @future ou executeBatch, appelés dans un bloc startTest, stopTest, ne sont pas pris en compte dans les limitations du nombre de tâches en file d'attente. L'exemple ci-dessous teste la classe classe OwnerReassignment. public static testMethod void testBatch() { user u = [SELECT ID, UserName FROM User WHERE username='[email protected]']; user u2 = [SELECT ID, UserName FROM User WHERE username='[email protected]']; String u2id = u2.id; // Create 200 test accounts - this simulates one execute. // Important - the Salesforce.com test framework only allows you to // test one execute. List <Account> accns = new List<Account>(); for(integer i = 0; i<200; i++){ Account a = new Account(Name='testAccount'+'i', Ownerid = u.ID); accns.add(a); } insert accns; Test.StartTest(); OwnerReassignment reassign = new OwnerReassignment(); reassign.query='SELECT ID, Name, Ownerid ' + 'FROM Account ' + 'WHERE OwnerId=\'' + u.Id + '\'' + Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 235 ' LIMIT 200'; reassign.email='[email protected]'; reassign.fromUserId = u.Id; reassign.toUserId = u2.Id; ID batchprocessid = Database.executeBatch(reassign); Test.StopTest(); System.AssertEquals( database.countquery('SELECT COUNT()' +' FROM Account WHERE OwnerId=\'' + u2.Id + '\''), 200); } } Limitations du gouverneur pour une tâche Apex par lot Notez les limitations du gouverneur suivantes pour une tâche Apex par lot : • • Jusqu'à cinq tâches par lot en file d'attente ou actives sont autorisées pour Apex. Un utilisateur peut avoir jusqu'à 50 curseurs de requête ouverts en même temps. Par exemple, si 50 curseurs sont ouverts et qu'une application cliente toujours connectée sous le même utilisateur tente d'en ouvrir un nouveau, le plus ancien des 50 curseurs est libéré. Notez que cette limitation est différente pour la méthode start Apex, qui peut avoir jusqu'à cinq curseurs de requête ouverts à la fois par utilisateur. Les autres méthodes Apex par lot ont une limite plus élevée de 50 curseurs. Les limitations en curseur des différentes fonctionnalités Force.com sont suivies séparément. Par exemple, vous pouvez avoir 50 Apex curseurs de requête, 50 curseur par lot et 50 curseurs Visualforce ouverts en même temps. • • • • • • • Un maximum de 50 millions d'enregistrements peuvent être renvoyés dans l'objet Database.QueryLocator. Si plus de 50 millions d'enregistrements sont renvoyés, la tâche par lot est immédiatement terminée et marquée comme échouée. Si la méthode start renvoie un QueryLocator, le paramètre de portée facultatif de Database.executeBatch peut avoir une valeur maximale de 2000. Si vous définissez une valeur plus importante, Salesforce segmente les enregistrements renvoyés par QueryLocator en lots plus petits limités à 2000 enregistrements. Si la méthode start renvoie un itérable, la valeur du paramètre de portée n'a pas de limite maximale. Si vous utilisez une valeur très élevée, vous risquez toutefois d'atteindre d'autres limites. Si aucune taille n'est spécifiée avec le paramètre facultatif scope de Database.executeBatch, Salesforce segmente les enregistrements renvoyés par la méthode start en lots de 200, puis passe chaque lot à la méthode execute. Les limitations du gouverneur Apex sont réinitialisés à chaque exécution de la méthode execute. Chaque méthode start, execute et finish peut mettre en oeuvre jusqu'à 10 appels. Les exécutions par lot sont limitées à 10 appels par exécution de méthode. Le nombre maximal d'exécutions par lot est de 250 000 par 24 heures. Une seule méthode start d'une tâche Apex par lot peut être exécutée à la fois dans une organisation. Les tâches par lot qui n'ont pas encore commencé restent dans la file d'attente jusqu'à leur démarrage. Notez que cette limite n'entraîne aucun Apex par lot salesforce | Utilisation d'une tâche Apex par lot | 236 échec de tâche par lot, et les méthodes execute des tâches Apex par lot continuent de s'exécuter en parallèle si plusieurs taches simultanées sont en cours. Meilleures pratiques des tâches Apex par lot • • • • • • • • • • • • • • • Soyez très prudent(e) si vous envisagez d'invoquer une tâche par lot à partir d'un déclencheur. Vous devez vous assurer que le déclencheur n'ajoute pas d'autres tâches par lot aux cinq autorisées. En particulier, considérez les mises à jour en masse d'API, les assistants d'importation, les modifications d'enregistrement en masse via l'interface utilisateur et toutes les situations dans lesquelles plusieurs enregistrements peuvent être mis à jour à la fois. Lorsque vous appelez Database.executeBatch, Salesforce place la tâche dans la file d'attente uniquement à l'heure planifiée. L'exécution réelle peut être retardée en fonction de la disponibilité du service. Lors du test de votre Apex par lot, vous pouvez tester une seule exécution de la méthode execute. Vous pouvez utiliser le paramètre scope de la méthode executeBatch afin de limiter le nombre de enregistrements passés dans la méthode execute pour ne pas atteindre les limitations du gouverneur. La méthode executeBatch lance un processus asynchrone. Cela signifie que lorsque vous testez une tâche Apex, vous devez vous assurer que la tâche par lot est terminée avant de tester les résultats. Utilisez les méthodes de Test startTest et stopTest autour de la méthode executeBatch pour vous assurer qu'elle est terminée avant de poursuivre votre test. Utilisez Database.Stateful avec la définition de la classe si vous souhaitez partager des variables de membre d'instance ou des données à travers des transactions de tâche. Sinon, toutes les variables de membre sont réinitialisées à leur état initial au début de chaque transaction. Les méthodes déclarées comme future ne sont pas autorisées dans les classes qui mettent en oeuvre l'interface Database.Batchable. Les méthodes déclarées comme future ne peuvent pas être appelées à partir d'une classe Apex par lot. Pour une classe Apex par lot enregistrée en utilisant l'API Salesforce.com versions 25.0 et antérieures, vous ne pouvez pas appeler la méthode Database.executeBatch à partir d'une méthode Apex par lot. À compter d'une classe Apex enregistrée en utilisant l'API Salesforce.com version 26.0, vous pouvez appeler Database.executeBatch uniquement à partir de la méthode finish. La version utilisée est celle de la classe par lot en cours d'exécution qui lance une autre tâche par lot. Si la méthode finish dans la classe par lot en cours d'exécution appelle une méthode dans une classe helper pour lancer la tâche par lot, la version de l'API Salesforce.com de la classe helper n'a aucune importance. Vous ne pouvez pas utiliser les méthodes PageReference getContent et getContentAsPDF dans une tâche par lot. En cas d'échec grave, par exemple une panne de service, toute opération en cours est marquée comme échouée. Vous devez réexécuter la tâche par lot pour corriger les erreurs éventuelles. Lorsqu'une tâche Apex par lot est exécutée, les notifications par e-mail sont envoyées à l'utilisateur qui a soumis la tâche par lot ou, si le code est inclus dans un package géré et que l'organisation abonnée exécute la tâche par lot, l'e-mail est envoyé au destinataire indiqué dans le champ Destinataire de notification d'exception Apex. Chaque exécution de méthode utilise le bloc anonyme, le contrôleur Visualforce ou la méthode WSDL des limitations du gouverneur standard. Chaque invocation Apex par lot crée un enregistrement AsyncApexJob. Utilisez l'ID de cet enregistrement pour construire une requête SOQL qui récupère l'état de la tâche, le nombre d'erreurs, la progression et l'émetteur de la requête. Pour plus d'informations sur l'objet AsyncApexJob, reportez-vous à AsyncApexJob dans le guide Object Reference for Salesforce and Force.com. Tous les 10 000 enregistrements AsyncApexJob, Apex crée un enregistrement AsyncApexJob supplémentaire de type BatchApexWorker à usage interne. Lors de l'interrogation de tous les enregistrements AsyncApexJob, nous recommandons de filtrer les enregistrements de type BatchApexWorker en utilisant le champ JobType. Sinon, la requête renvoie un enregistrement supplémentaire tous les 10 000 enregistrements AsyncApexJob. Pour plus d'informations sur l'objet AsyncApexJob, reportez-vous à AsyncApexJob dans le guide Object Reference for Salesforce and Force.com. Toutes les méthodes dans la classe doivent être définies comme global ou public. Apex par lot • salesforce | Compréhension du partage géré Apex | 237 Pour un recalcul de partage, nous recommandons que la méthode execute supprime puis recrée tout le partage Apex géré pour les enregistrements du lot. Cela garantit un partage précis et complet. Voir aussi : Instructions d'exception Compréhension des limitations et des gouverneurs d'exécution Compréhension du partage Compréhension du partage géré Apex Le partage est l'acte par lequel des autorisations sont accordées à un utilisateur ou à un groupe d'utilisateurs pour exécuter une série d'actions dans un enregistrement ou un ensemble d'enregistrements. Le partage de l'accès peut être accordé en utilisant l'interface utilisateur de Salesforce et Force.com, ou par programmation en utilisant le langage Apex. Cette section présente le partage en utilisant Apex : • • • Compréhension du partage Partage d'un enregistrement en utilisant Apex Recalcul du partage géré Apex Pour plus d'informations sur le partage, reportez-vous à « Définition de vos paramètres de partage par défaut à l'échelle de l'organisation » dans l'aide en ligne de Salesforce. Compréhension du partage Le partage permet de contrôler l'accès au niveau de l'enregistrement pour tous les objets personnalisés, ainsi que de nombreux objets standard (tels que Compte, Contact, Opportunité et Requête). Les administrateurs commencent par définir le niveau d'accès en partage par défaut à l'échelle d'organisation pour un objet, puis accordent des accès supplémentaires basés sur la propriété des enregistrements, la hiérarchie des rôles, les règles de partage et le partage manuel. Les développeurs peuvent utiliser le partage géré Apex pour accorder par programmation un accès supplémentaire avec un code Apex. La plupart du partage d'un enregistrement est géré dans un objet de partage associé, similaire à une liste de contrôle d'accès (ACL) existant dans d'autres plates-formes. Types de partage Salesforce comprend les types de partage suivants : Partage géré Force.com Le partage géré Force.com nécessite un accès en partage accordé par Force.com basé sur la propriété des enregistrements, la hiérarchie des rôles et des règles de partage : Propriété de l'enregistrement Chaque enregistrement appartient à un utilisateur ou éventuellement à une file d'attente d'objets personnalisés, de requêtes et de pistes. Un accès complet est automatiquement accordé au propriétaire de l'enregistrement, ce qui lui permet d'afficher, de modifier, de transférer, de partager et de supprimer l'enregistrement. Apex par lot salesforce | Compréhension du partage | 238 Hiérarchie des rôles La hiérarchie des rôles permet aux utilisateurs de niveau supérieur à un utilisateur dans la hiérarchie de disposer du même niveau d'accès aux enregistrements appartenant à ou partagés avec les utilisateurs de niveau inférieur. Par conséquent, les utilisateurs de niveau supérieur à un propriétaire d'un enregistrement dans la hiérarchie des rôles disposent implicitement d'un accès complet à l'enregistrement, comportement qui peut toutefois être désactivé pour des objets personnalisés spécifiques. La hiérarchie des rôles n'est pas gérée par le partage d'enregistrements. À la place, l'accès de la hiérarchie des rôles est dérivé à l'exécution. Pour plus d'informations, reportez-vous à « Contrôler l'accès par le biais des hiérarchies » dans l'aide en ligne de Salesforce. Règles de partage Les règles de partage sont utilisés par les administrateurs pour accorder automatiquement aux utilisateurs d'un groupe ou d'un rôle donné l'accès aux enregistrements appartenant à un groupe d'utilisateurs spécifique. Les règles de partage ne peuvent pas être ajoutées à un package et utilisées pour prendre en charge une logique de partage d'applications installées à partir de Force.com AppExchange. Les règles de partage peuvent être basées sur la propriété d'un enregistrement ou d'autres critères. Vous ne pouvez pas utiliser un code Apex pour créer des règles de partage basées sur des critères. De plus, le partage basé sur des critères ne peut pas être testé en utilisant Apex. L'ensemble du partage implicite ajouté par le partage géré Force.com ne peut pas être modifié directement en utilisant l'interface utilisateur de Salesforce, l'API SOAP ou le langage Apex. Partage géré utilisateur, également appelé partage manuel Le partage géré utilisateur permet au propriétaire d'un enregistrement ou à n'importe quel utilisateur disposant d'un accès complet à l'enregistrement de la partager avec un utilisateur ou un groupe d'utilisateurs. Cela est généralement effectué par un utilisateur final pour un seul enregistrement. Seuls le propriétaire de l'enregistrement et les utilisateurs de niveau supérieur au propriétaire dans la hiérarchie des rôles disposent d'un accès complet à l'enregistrement. Il n'est pas possible d'accorder l'accès complet à d'autres utilisateurs. Les utilisateurs qui disposent de l'autorisation au niveau de l'objet « Modifier tout » pour un objet donné ou « Modifier toutes les données » peuvent également partager manuellement un enregistrement. Le partage géré utilisateur est retiré lorsque le propriétaire de l'enregistrement change ou lorsque l'accès octroyé dans le partage n'accorde pas pour l'objet un accès supplémentaire dépassant le niveau d'accès en partage par défaut de l'organisation. Partage géré Apex Le partage géré Apex permet aux développeurs de prendre en charge par programmation les besoins en partage spécifiques à une application via un code Apex ou l'API SOAP. Ce type de partage est similaire au partage géré Force.com. Seuls les utilisateurs qui disposent de l'autorisation « Modifier toutes les données » peuvent ajouter ou modifier le partage géré Apex dans un enregistrement. Le partage géré Apex est conservé en cas de changement de propriété d'un enregistrement. Remarque: Les motifs de partage Apex et le recalcul du partage géré Apex sont disponibles uniquement pour des objets personnalisés. Le champ Motif de partage Dans l'interface utilisateur de Salesforce, le champ Motif d'un objet personnalisé spécifie le type de partage utilisé pour un enregistrement. Ce champ est appelé rowCause dans le langage Apex ou dans l'API Force.com. Chaque élément de la liste suivante est un type de partage utilisé pour des enregistrements. Les tableaux indiquent la valeur du champ Motif et la valeur associée rowCause. • Partage géré Force.com Apex par lot • • salesforce | Compréhension du partage | 239 Valeur du champ Motif Valeur rowCause (utilisée dans Apex ou l'API Force.com) Partage de compte ImplicitChild Propriétaire de l'enregistrement ou partage associé ImplicitParent Propriétaire Owner Équipe d'opportunité Team Règle de partage Rule Règle d'attribution de territoire TerritoryRule Partage géré utilisateur Valeur du champ Motif Valeur rowCause (utilisée dans Apex ou l'API Force.com) Partage manuel Manual Territoire manuel TerritoryManual Partage géré Apex Valeur du champ Motif Valeur rowCause (utilisée dans Apex ou l'API Force.com) Définie par le développeur Définie par le développeur Le motif affiché du partage géré Apex est défini par le développeur. Niveaux d'accès Pour déterminer l'accès d'un utilisateur à un enregistrement, le niveau d'accès le plus permissif est utilisé. La plupart des objets de partage prennent en charge les niveaux d'accès suivants : Niveau d'accès Nom d'API Description Privé Aucun Seuls le propriétaire de l'enregistrement et les utilisateurs de niveau supérieur au propriétaire dans la hiérarchie des rôles peuvent afficher et modifier l'enregistrement. Ce niveau d'accès s'applique uniquement à l'objet AccountShare. Lecture seule Lire L'utilisateur ou le groupe spécifié peut uniquement afficher l'enregistrement. Lecture/Écriture Modifier L'utilisateur ou le groupe spécifié peut afficher et modifier l'enregistrement. Accès complet Tous L'utilisateur ou le groupe spécifié peut afficher, modifier, transférer, partager et supprimer l'enregistrement. Remarque: Ce niveau d'accès peut être accordé uniquement avec le partage géré Force.com. Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 240 Partage d'un enregistrement en utilisant Apex Pour accéder au partage par programmation, vous devez utiliser l'objet de partage associé à l'objet standard ou personnalisé avec lequel vous souhaitez partager. Par exemple, AccountShare est l'objet de partage de l'objet Account, ContactShare est l'objet de partage de l'objet Contact, etc. De plus, tous les objets de partage d'objets personnalisés sont nommés comme suit, MonObjetPersonnalisé correspondant au nom de l'objet personnalisé : MonObjetPersonnalisé__Share Les objets côté détails d'une relation principal-détails n'ont pas d'objet de partage associé. L'accès aux enregistrements de détail est déterminé par l'objet de partage du principal et des paramètres de partage de la relation. Pour plus d'informations, reportez-vous à « Sécurité des objets personnalisés » dans l'aide en ligne de Salesforce. Un objet de partage inclut des enregistrements prenant en charge les trois types de partage : partage géré Force.com, partage géré utilisateur et partage géré Apex. Le partage accordé implicitement aux utilisateurs via les paramètres par défaut de l'organisation, la hiérarchie des rôles et les autorisations, telles que « Afficher tout » et « Modifier tout » pour un objet donné, « Afficher toutes les données » et « Modifier toutes les données », ne sont pas suivis avec cet objet. Chaque objet de partage inclut les propriétés suivantes : Nom de propriété Description objectNameAccessLevel Le niveau d'accès de l'utilisateur ou du groupe spécifié a été accordé pour un sObject de partage. Le nom de propriété est AccessLevel ajouté au nom de l'objet. Par exemple, le nom de propriété de l'objet LeadShare est LeadShareAccessLevel. Les valeurs valides sont : • Edit • Read • All Remarque: Le niveau d'accès All peut être utilisé uniquement par le partage géré Force.com. Ce champ doit être défini sur un niveau d'accès supérieur à celui par défaut de l'organisation pour l'objet parent. Pour plus d'informations, reportez-vous à Niveaux d'accès à la page 239. ParentID L'ID de l'objet. Ce champ ne peut pas être mis à jour. RowCause La raison pour laquelle l'accès est accordé à l'utilisateur ou au groupe. Elle détermine le type de partage, qui contrôle qui peut modifier l'enregistrement de partage. Ce champ ne peut pas être mis à jour. UserOrGroupId L'ID de l'utilisateur ou du groupe auquel vous accordez l'accès. Un groupe peut être un groupe public, un rôle ou un territoire. Ce champ ne peut pas être mis à jour. Vous pouvez partager un objet standard ou personnalisé avec des utilisateurs ou des groupes. Pour plus d'informations sur les types d'utilisateur et de groupe avec lesquels vous pouvez partager un objet, reportez-vous à User et à Group dans le guide Object Reference for Salesforce and Force.com. Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 241 Création d'un partage géré utilisateur en utilisant Apex Il est possible de partager manuellement un enregistrement avec un utilisateur ou un groupe en utilisant le langage Apex ou l'API SOAP. Si le propriétaire de l'enregistrement change, le partage est automatiquement supprimé. L'exemple de classe suivant inclut une méthode qui partage la tâche spécifiée par l'ID de tâche avec l'ID de l'utilisateur ou du groupe spécifié avec un accès en lecture. Il inclut également une méthode de test qui valide cette méthode. Avant d'enregistrer cet exemple de classe, créez un objet personnalisé appelé Job. public class JobSharing { static boolean manualShareRead(Id recordId, Id userOrGroupId){ // Create new sharing object for the custom object Job. Job__Share jobShr = new Job__Share(); // Set the ID of record being shared. jobShr.ParentId = recordId; // Set the ID of user or group being granted access. jobShr.UserOrGroupId = userOrGroupId; // Set the access level. jobShr.AccessLevel = 'Read'; // Set rowCause to 'manual' for manual sharing. // This line can be omitted as 'manual' is the default value for sharing objects. jobShr.RowCause = Schema.Job__Share.RowCause.Manual; // Insert the sharing record and capture the save result. // The false parameter allows for partial processing if multiple records passed // into the operation. Database.SaveResult sr = Database.insert(jobShr,false); // Process the save results. if(sr.isSuccess()){ // Indicates success return true; } Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 242 else { // Get first save result error. Database.Error err = sr.getErrors()[0]; // Check if the error is related to trival access level. // Access levels equal or more permissive than the object's default // access level are not allowed. // These sharing records are not required and thus an insert exception is acceptable. if(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION err.getMessage().contains('AccessLevel')){ // Indicates success. return true; } else{ // Indicates failure. return false; } } } // Test for the manualShareRead method static testMethod void testManualShareRead(){ // Select users for the test. List<User> users = [SELECT Id FROM User WHERE IsActive = true LIMIT 2]; Id User1Id = users[0].Id; Id User2Id = users[1].Id; // Create new job. Job__c j = new Job__c(); j.Name = 'Test Job'; j.OwnerId = user1Id; insert j; && Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 243 // Insert manual share for user who is not record owner. System.assertEquals(manualShareRead(j.Id, user2Id), true); // Query job sharing records. List<Job__Share> jShrs = [SELECT Id, UserOrGroupId, AccessLevel, RowCause FROM job__share WHERE ParentId = :j.Id AND UserOrGroupId= :user2Id]; // Test for only one manual share on job. System.assertEquals(jShrs.size(), 1, 'Set the object\'s sharing model to Private.'); // Test attributes of manual share. System.assertEquals(jShrs[0].AccessLevel, 'Read'); System.assertEquals(jShrs[0].RowCause, 'Manual'); System.assertEquals(jShrs[0].UserOrGroupId, user2Id); // Test invalid job Id. delete j; // Insert manual share for deleted job id. System.assertEquals(manualShareRead(j.Id, user2Id), false); } } Important: Le niveau d'accès par défaut de l'organisation pour l'objet ne doit pas être défini sur le niveau d'accès le plus permissif. Pour des objets personnalisés, il correspond à Accès public en lecture/écriture. Pour plus d'informations, reportez-vous à Niveaux d'accès à la page 239. Création d'un partage géré Apex Le partage géré Apex permet aux développeurs de manipuler par programmation le partage pour prendre en charge le comportement de leur application via un code Apex ou l'API SOAP. Ce type de partage est similaire au partage géré Force.com. Seuls les utilisateurs qui disposent de l'autorisation « Modifier toutes les données » peuvent ajouter ou modifier le partage géré Apex dans un enregistrement. Le partage géré Apex est conservé en cas de changement de propriété d'un enregistrement. Le partage géré Apex doit utiliser un motif de partage Apex. Les motifs de partage Apex permettent aux développeurs de suivre les raisons pour lesquelles ils ont partagé un enregistrement avec un utilisateur ou un groupe d'utilisateurs. L'utilisation de motifs de partage Apex multiples simplifie le code requis pour effectuer des mises à jour et des suppressions d'enregistrements Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 244 de partage. Ils permettent également aux développeurs de partager plusieurs fois avec le même utilisateur ou le même groupe pour différentes raisons. Les motifs de partage Apex sont définies dans la page de détails d'un objet. Chaque motif de partage Apex a une étiquette et un nom : L'étiquette s'affiche dans la colonne Motif lors de la consultation du partage d'un enregistrement dans l'interface utilisateur. Cela permet aux utilisateurs et aux administrateurs de comprendre la source du partage. L'étiquette est également activée pour la traduction via le Système de traduction. Le nom est utilisé lors du référencement du motif dans l'API et Apex. • • Les noms de motif de partage Apex ont tous le format suivant : MyReasonName__c Les motifs de partage Apex peuvent être référencés par programmation comme suit : Schema.CustomObject__Share.rowCause.SharingReason__c Par exemple, un motif de partage Apex appelé Recruiter pour un objet appelé Job peut être référencé comme suit : Schema.Job__Share.rowCause.Recruiter__c Pour plus d'informations, reportez-vous à Méthodes Schema à la page 432. Pour créer un motif de partage Apex : 1. 2. 3. 4. Cliquez sur Votre nom > Configuration > Créer > Objets. Sélectionnez l'objet personnalisé. Cliquez sur Nouveau dans la liste associée Motifs de partage Apex. Saisissez une étiquette pour le motif de partage Apex. L'étiquette s'affiche dans la colonne Motif lors de la consultation du partage d'un enregistrement dans l'interface utilisateur. L'étiquette est également activée pour la traduction via le Système de traduction. 5. Saisissez un nom pour le motif de partage Apex. Le nom est utilisé lors du référencement du motif dans l'API et Apex. Ce nom peut contenir uniquement des traits de soulignement et des caractères alphanumériques, et doit être unique dans votre organisation. Il doit commencer par une lettre, ne pas inclure d’espace, ne pas se terminer pas un trait de soulignement et ne pas contenir deux traits de soulignement consécutifs. 6. Cliquez sur Enregistrer. Remarque: Les motifs de partage Apex et le recalcul du partage géré Apex sont disponibles uniquement pour des objets personnalisés. Exemple de partage géré Apex Pour cet exemple, supposons que nous créons une application de recrutement et que nous avons un objet appelé Job. Nous souhaitons confirmer que le recruteur et le responsable du recrutement répertoriés dans le job ont accès à l'enregistrement. Le déclencheur suivant accorde l'accès au recruteur et au responsable du recrutement lors de la création de l'enregistrement Job. Cet exemple nécessite un objet personnalisé appelé Job, avec deux champs de référence associés aux enregistrements utilisateur appelés Hiring_Manager et Recruiter. De plus, l'objet personnalisé Job doit avoir deux motifs de partage ajoutés appelés Hiring_Manager et Recruiter. trigger JobApexSharing on Job__c (after insert) { Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 245 if(trigger.isInsert){ // Create a new list of sharing objects for Job List<Job__Share> jobShrs = new List<Job__Share>(); // Declare variables for recruiting and hiring manager sharing Job__Share recruiterShr; Job__Share hmShr; for(Job__c job : trigger.new){ // Instantiate the sharing objects recruiterShr = new Job__Share(); hmShr = new Job__Share(); // Set the ID of record being shared recruiterShr.ParentId = job.Id; hmShr.ParentId = job.Id; // Set the ID of user or group being granted access recruiterShr.UserOrGroupId = job.Recruiter__c; hmShr.UserOrGroupId = job.Hiring_Manager__c; // Set the access level recruiterShr.AccessLevel = 'edit'; hmShr.AccessLevel = 'read'; // Set the Apex sharing reason for hiring manager and recruiter recruiterShr.RowCause = Schema.Job__Share.RowCause.Recruiter__c; hmShr.RowCause = Schema.Job__Share.RowCause.Hiring_Manager__c; // Add objects to list for insert jobShrs.add(recruiterShr); jobShrs.add(hmShr); } Apex par lot salesforce | Partage d'un enregistrement en utilisant Apex | 246 // Insert sharing records and capture save result // The false parameter allows for partial processing if multiple records are passed // into the operation Database.SaveResult[] lsr = Database.insert(jobShrs,false); // Create counter Integer i=0; // Process the save results for(Database.SaveResult sr : lsr){ if(!sr.isSuccess()){ // Get the first save result error Database.Error err = sr.getErrors()[0]; // Check if the error is related to a trivial access level // Access levels equal or more permissive than the object's default // access level are not allowed. // These sharing records are not required and thus an insert exception is // acceptable. if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION && err.getMessage().contains('AccessLevel'))){ // Throw an error when the error is not related to trivial access level. trigger.newMap.get(jobShrs[i].ParentId). addError( 'Unable to grant sharing access due to following exception: ' + err.getMessage()); } } i++; } } Apex par lot salesforce | Recalcul du partage géré Apex | 247 } Dans certaines conditions, l'insertion d'une ligne de partage entraîne une mise à jour d'une ligne de partage existante. Notez les exemples suivants : • • Si un niveau d'accès de partage manuel est défini sur Lire et que vous insérez un nouvel accès défini sur Écriture, les lignes de partage d'origine sont mises à jour vers Écriture, indiquant le niveau d'accès supérieur. Si des utilisateurs peuvent accéder à un compte, parce qu'ils ont accès à ses enregistrements enfants (contact, requête, opportunité, etc.), et qu'une règle de partage de compte est créée, le motif de la ligne du partage implicite parent est remplacé par le motif de la ligne de la règle de partage, indiquant le niveau d'accès supérieur. Important: Le niveau d'accès par défaut de l'organisation pour l'objet ne doit pas être défini sur le niveau d'accès le plus permissif. Pour des objets personnalisés, il correspond à Accès public en lecture/écriture. Pour plus d'informations, reportez-vous à Niveaux d'accès à la page 239. Recalcul du partage géré Apex Salesforce recalcule automatiquement le partage pour tous les enregistrements d'un objet lorsque son niveau d'accès en partage par défaut à l'échelle de l'organisation change. Le recalcul ajoute le partage géré Force.com au moment approprié. En outre, tous les types de partage sont supprimés si l'accès qu'ils accordent est considéré comme redondant. Par exemple, le partage manuel qui accorde un accès en lecture seule à un utilisateur est supprimé lorsque le modèle de partage de l'objet change de Privé à Accès public en lecture seule. Pour recalculer le partage géré Apex, vous devez écrire une classe Apex qui met en oeuvre une interface fournie par Salesforce pour effectuer le nouveau calcul. Vous devez ensuite associer la classe à l'objet personnalisé, via sa page de détails, dans la liste associée Recalcul de partage Apex. Remarque: Les motifs de partage Apex et le recalcul du partage géré Apex sont disponibles uniquement pour des objets personnalisés. Vous pouvez exécuter cette classe à partir de la page de détails de l'objet personnalisé, dans laquelle le motif de partage Apex est spécifié. Un administrateur peut souhaiter recalculer le partage géré Apex pour un objet si un problème de verrouillage empêche le code Apex d'accorder l'accès à un utilisateur, selon la définition dans la logique de l'application. Vous pouvez utiliser la méthode Database.executeBatch pour invoquer par programmation un recalcul de partage géré Apex. Remarque: Chaque fois que le niveau d'accès en partage par défaut de l'organisation pour un objet personnalisé est mis à niveau, toutes les classes de recalcul Apex définies pour l'objet personnalisé sont également exécutées. Pour suivre ou arrêter l'exécution du recalcul Apex, cliquez sur Votre nom > Configuration > Surveillance > Tâches Apex. Pour plus d'informations, reportez-vous à « Surveillance de la file d'attente des tâches Apex » dans l'aide en ligne de Salesforce. Création d'une classe Apex pour recalculer le partage Pour recalculer le partage géré Apex, vous devez écrire une classe Apex qui effectue ce calcul. La classe doit mettre en oeuvre l'interface Database.Batchable fournie par Salesforce. L'interface Database.Batchable est utilisée pour tous les processus Apex par lot, notamment le recalcul du partage géré Apex. Vous pouvez mettre en oeuvre cette interface plusieurs fois dans votre organisation. Pour plus d'informations sur les méthodes à mettre en oeuvre, reportez-vous à Utilisation d'une tâche Apex par lot à la page 224. Avant de créer une classe de recalcul de partage géré Apex, consultez les meilleures pratiques. Apex par lot salesforce | Recalcul du partage géré Apex | 248 Important: Le niveau d'accès par défaut de l'organisation pour l'objet ne doit pas être défini sur le niveau d'accès le plus permissif. Pour des objets personnalisés, il correspond à Accès public en lecture/écriture. Pour plus d'informations, reportez-vous à Niveaux d'accès à la page 239. Exemple de recalcul de partage géré Apex Pour cet exemple, supposons que nous créons une application de recrutement et que nous avons un objet appelé Job. Nous souhaitons confirmer que le recruteur et le responsable du recrutement répertoriés dans le job ont accès à l'enregistrement. La classe Apex suivante effectue cette validation. Cet exemple nécessite un objet personnalisé appelé Job, avec deux champs de référence associés aux enregistrements utilisateur appelés Hiring_Manager et Recruiter. De plus, l'objet personnalisé Job doit avoir deux motifs de partage ajoutés appelés Hiring_Manager et Recruiter. Avant d'exécuter cet exemple, remplacez l'adresse e-mail par une adresse valide à laquelle vous souhaitez envoyer les notifications d'erreur et de fin de tâche. global class JobSharingRecalc implements Database.Batchable<sObject> { // String to hold email address that emails will be sent to. // Replace its value with a valid email address. static String emailAddress = '[email protected]'; // The start method is called at the beginning of a sharing recalculation. // This method returns a SOQL query locator containing the records // to be recalculated. global Database.QueryLocator start(Database.BatchableContext BC){ return Database.getQueryLocator([SELECT Id, Hiring_Manager__c, Recruiter__c FROM Job__c]); } // The executeBatch method is called for each chunk of records returned from start. global void execute(Database.BatchableContext BC, List<sObject> scope){ // Create a map for the chunk of records passed into method. Map<ID, Job__c> jobMap = new Map<ID, Job__c>((List<Job__c>)scope); // Create a list of Job__Share objects to be inserted. List<Job__Share> newJobShrs = new List<Job__Share>(); // Locate all existing sharing records for the Job records in the batch. // Only records using an Apex sharing reason for this app should be returned. List<Job__Share> oldJobShrs = [SELECT Id FROM Job__Share WHERE Id IN Apex par lot salesforce | Recalcul du partage géré Apex | 249 :jobMap.keySet() AND (RowCause = :Schema.Job__Share.rowCause.Recruiter__c OR RowCause = :Schema.Job__Share.rowCause.Hiring_Manager__c)]; // Construct new sharing records for the hiring manager and recruiter // on each Job record. for(Job__c job : jobMap.values()){ Job__Share jobHMShr = new Job__Share(); Job__Share jobRecShr = new Job__Share(); // Set the ID of user (hiring manager) on the Job record being granted access. jobHMShr.UserOrGroupId = job.Hiring_Manager__c; // The hiring manager on the job should always have 'Read Only' access. jobHMShr.AccessLevel = 'Read'; // The ID of the record being shared jobHMShr.ParentId = job.Id; // Set the rowCause to the Apex sharing reason for hiring manager. // This establishes the sharing record as Apex managed sharing. jobHMShr.RowCause = Schema.Job__Share.RowCause.Hiring_Manager__c; // Add sharing record to list for insertion. newJobShrs.add(jobHMShr); // Set the ID of user (recruiter) on the Job record being granted access. jobRecShr.UserOrGroupId = job.Recruiter__c; // The recruiter on the job should always have 'Read/Write' access. jobRecShr.AccessLevel = 'Edit'; // The ID of the record being shared jobRecShr.ParentId = job.Id; Apex par lot salesforce | Recalcul du partage géré Apex | 250 // Set the rowCause to the Apex sharing reason for recruiter. // This establishes the sharing record as Apex managed sharing. jobRecShr.RowCause = Schema.Job__Share.RowCause.Recruiter__c; // Add the sharing record to the list for insertion. newJobShrs.add(jobRecShr); } try { // Delete the existing sharing records. // This allows new sharing records to be written from scratch. Delete oldJobShrs; // Insert the new sharing records and capture the save result. // The false parameter allows for partial processing if multiple records are // passed into operation. Database.SaveResult[] lsr = Database.insert(newJobShrs,false); // Process the save results for insert. for(Database.SaveResult sr : lsr){ if(!sr.isSuccess()){ // Get the first save result error. Database.Error err = sr.getErrors()[0]; // Check if the error is related to trivial access level. // Access levels equal or more permissive than the object's default // access level are not allowed. // These sharing records are not required and thus an insert exception // is acceptable. if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION && err.getMessage().contains('AccessLevel'))){ // Error is not related to trivial access level. Apex par lot salesforce | Recalcul du partage géré Apex | 251 // Send an email to the Apex job's submitter. Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {emailAddress}; mail.setToAddresses(toAddresses); mail.setSubject('Apex Sharing Recalculation Exception'); mail.setPlainTextBody( 'The Apex sharing recalculation threw the following exception: ' + err.getMessage()); Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } } } catch(DmlException e) { // Send an email to the Apex job's submitter on failure. Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {emailAddress}; mail.setToAddresses(toAddresses); mail.setSubject('Apex Sharing Recalculation Exception'); mail.setPlainTextBody( 'The Apex sharing recalculation threw the following exception: ' + e.getMessage()); Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } // The finish method is called at the end of a sharing recalculation. global void finish(Database.BatchableContext BC){ // Send an email to the Apex job's submitter notifying of job completion. Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {emailAddress}; mail.setToAddresses(toAddresses); mail.setSubject('Apex Sharing Recalculation Completed.'); mail.setPlainTextBody Apex par lot salesforce | Recalcul du partage géré Apex | 252 ('The Apex sharing recalculation finished processing'); Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); } } Test de recalcul de partage géré Apex Cet exemple insère cinq enregistrements Job et invoque la tâche par lot qui est mise en oeuvre dans la classe par lot de l'exemple précédent. Cet exemple nécessite un objet personnalisé appelé Job, avec deux champs de référence associés aux enregistrements utilisateur appelés Hiring_Manager et Recruiter. De plus, l'objet personnalisé Job doit avoir deux motifs de partage ajoutés appelés Hiring_Manager et Recruiter. Avant d'exécuter ce test, définissez le partage par défaut à l'échelle de l'organisation pour le Job sur Privé. Notez qu'aucun e-mail n'est envoyé à partir de tests et que la classe par lot est invoquée par une méthode de test. Par conséquent, aucune notification par e-mail ne sera envoyée dans le cas présent. @isTest private class JobSharingTester { // Test for the JobSharingRecalc class static testMethod void testApexSharing(){ // Instantiate the class implementing the Database.Batchable interface. JobSharingRecalc recalc = new JobSharingRecalc(); // Select users for the test. List<User> users = [SELECT Id FROM User WHERE IsActive = true LIMIT 2]; ID User1Id = users[0].Id; ID User2Id = users[1].Id; // Insert some test job records. List<Job__c> testJobs = new List<Job__c>(); for (Integer i=0;i<5;i++) { Job__c j = new Job__c(); j.Name = 'Test Job ' + i; j.Recruiter__c = User1Id; j.Hiring_Manager__c = User2Id; testJobs.add(j); } Apex par lot salesforce | Recalcul du partage géré Apex | 253 insert testJobs; Test.startTest(); // Invoke the Batch class. String jobId = Database.executeBatch(recalc); Test.stopTest(); // Get the Apex job and verify there are no errors. AsyncApexJob aaj = [Select JobType, TotalJobItems, JobItemsProcessed, Status, CompletedDate, CreatedDate, NumberOfErrors from AsyncApexJob where Id = :jobId]; System.assertEquals(0, aaj.NumberOfErrors); // This query returns jobs and related sharing records that were inserted // by the batch job's execute method. List<Job__c> jobs = [SELECT Id, Hiring_Manager__c, Recruiter__c, (SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause FROM Shares WHERE (RowCause = :Schema.Job__Share.rowCause.Recruiter__c OR RowCause = :Schema.Job__Share.rowCause.Hiring_Manager__c)) FROM Job__c]; // Validate that Apex managed sharing exists on jobs. for(Job__c job : jobs){ // Two Apex managed sharing records should exist for each job // when using the Private org-wide default. System.assert(job.Shares.size() == 2); for(Job__Share jobShr : job.Shares){ // Test the sharing record for hiring manager on job. if(jobShr.RowCause == Schema.Job__Share.RowCause.Hiring_Manager__c){ System.assertEquals(jobShr.UserOrGroupId,job.Hiring_Manager__c); System.assertEquals(jobShr.AccessLevel,'Read'); Apex par lot salesforce | Recalcul du partage géré Apex | 254 } // Test the sharing record for recruiter on job. else if(jobShr.RowCause == Schema.Job__Share.RowCause.Recruiter__c){ System.assertEquals(jobShr.UserOrGroupId,job.Recruiter__c); System.assertEquals(jobShr.AccessLevel,'Edit'); } } } } } Association d'une classe Apex utilisée pour le recalcul Une classe Apex utilisée pour un nouveau calcul doit être associée à un objet personnalisé. Pour associer une classe de recalcul de partage géré Apex à un objet personnalisé : 1. 2. 3. 4. Cliquez sur Votre nom > Configuration > Créer > Objets. Sélectionnez l'objet personnalisé. Cliquez sur Nouveau dans la liste associée Recalculs de partage Apex. Choisissez la classe Apex qui recalcule le partage Apex de cet objet. La classe que vous choisissez doit mettre en oeuvre l'interface Database.Batchable. Vous ne pouvez pas associer plusieurs fois la même classe Apex au même objet personnalisé. 5. Cliquez sur Enregistrer. Chapitre 8 Débogage du langage Apex Sujets : • • • • Compréhension du journal de débogage Gestion des exceptions non détectées Compréhension des limitations et des gouverneurs d'exécution Utilisation d'avertissements par e-mail pour les limitations du gouverneur Le langage Apex prend en charge le débogage. Vous pouvez déboguer votre code Apex en utilisant la Console du développeur et des journaux de débogage. Pour faciliter le débogage, le langage Apex envoie des e-mails aux développeurs pour les exceptions non gérées. En outre, le langage Apex impose un ensemble spécifique de limitations du gouverneur pour l'exécution du code, afin d'éviter la monopolisation des ressources partagées dans un environnement mutualisé. Dernier point, mais non des moindres, vous pouvez activer l'envoi d'e-mails aux utilisateurs qui exécutent un code dépassant un certain pourcentage des limitations du gouverneur. Ce chapitre inclut les sections suivantes : • • • • Compréhension du journal de débogage Gestion des exceptions non détectées Compréhension des limitations et des gouverneurs d'exécution Utilisation d'avertissements par e-mail pour les limitations du gouverneur Débogage du langage Apex salesforce | Compréhension du journal de débogage | 256 Compréhension du journal de débogage Un journal de débogage enregistre les opérations dans la base de données, les processus système ainsi que les erreurs qui se produisent lors de l'exécution d'une transaction ou de tests unitaires. Le système génère un journal de débogage pour un utilisateur chaque fois qu'il exécute une transaction qui est comprise dans les critères de filtrage. Vous pouvez conserver et gérer les journaux de débogage pour des utilisateurs spécifiques. Pour afficher les journaux de débogage enregistrés, cliquez sur Votre nom > Configuration > Surveillance > Journaux de débogage. Les limites liées aux journaux de débogage sont les suivantes : • • • Une fois ajouté, un utilisateur peut enregistrer jusqu'à 20 journaux de débogage. Lorsqu'un utilisateur atteint cette limite, l'enregistrement des journaux de débogage s'arrête pour cet utilisateur. Cliquez sur Réinitialiser dans la page Surveillance de journaux de débogage afin de réinitialiser le nombre de journaux de cet utilisateur à 20. Les journaux existants ne sont pas écrasés. Chaque journal de débogage est limité à 2 Mo. Les journaux de débogage supérieurs à 2 Mo sont tronqués. Chaque organisation peut conserver jusqu'à 50 Mo de journaux de débogage. Lorsque les journaux de débogage de votre organisation atteignent 50 Mo, les plus anciens journaux sont remplacés. Inspection des sections du journal de débogage Lorsqu'un journal de débogage est généré, le type et la quantité des informations répertoriées dépend des valeurs de filtrage que vous avez définies pour l'utilisateur. Toutefois, le format d'un journal de débogage est toujours le même. Un journal de débogage comprend les sections suivantes : En-tête L'en-tête contient les informations suivantes : • La version de l'API utilisée durant la transaction. • La catégorie du journal et le niveau utilisé pour générer le journal. Par exemple : Voici un exemple d'en-tête : 25.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO; WORKFLOW,INFO Dans cet exemple, la version de l'API est 25.0, et les catégories et niveaux de journal de débogage suivants ont été définis : Code Apex DEBUG Profil Apex INFO Appel INFO Base de données INFO Système DEBUG Validation INFO Débogage du langage Apex salesforce | Compréhension du journal de débogage | 257 Visualforce INFO Workflow INFO Unités d'exécution Une unité d'exécution est équivalente à une transaction. Elle contient tout ce qui s'est passé durant la transaction. L'exécution est délimitée par EXECUTION_STARTED et EXECUTION_FINISHED. Unités de code Une unité de code est une unité de travail discrète dans une transaction. Par exemple, un déclencheur est une unité de code, comme dans la méthode webService, ou une règle de validation. Remarque: Une classe n'est pas une unité de code discrète. Les unités de code sont indiquées par CODE_UNIT_STARTED et CODE_UNIT_FINISHED. Les unités de travail peuvent être incorporées à d'autres unités de travail. Par exemple : EXECUTION_STARTED CODE_UNIT_STARTED|[EXTERNAL]execute_anonymous_apex CODE_UNIT_STARTED|[EXTERNAL]MyTrigger on Account trigger event BeforeInsert for [new] CODE_UNIT_FINISHED <-- The trigger ends CODE_UNIT_FINISHED <-- The executeAnonymous ends EXECUTION_FINISHED Les unités de code comprennent, notamment, les éléments suivants : • Déclencheurs • Invocations de workflow et workflow temporel • Règles de validation • Processus d'approbation • Conversion de pistes par Apex • Invocations de méthodes @future • Invocations de services Web • Appels executeAnonymous • La propriété Visualforce accède aux contrôleurs Apex • Actions Visualforce dans les contrôleurs Apex • Exécution des méthodes Apex par lot start et finish, ainsi que l'exécution de la méthode execute • Exécution de la méthode Apex System.Schedule execute • Traitement des e-mails entrants Lignes de journal Incluses dans les unités de code. Elles indiquent le code ou les règles en cours d'exécution, ou les messages écrits spécialement pour le journal de débogage. Par exemple : Débogage du langage Apex salesforce | Compréhension du journal de débogage | 258 Figure 5: Exemple de ligne d'un journal de débogage Les lignes de journal sont composées d'un ensemble de champs, délimités par un tube (|). Le format est le suivant : • timestamp : comprend l'heure à laquelle se produit l'événement et une valeur entre parenthèses. L'heure est définie dans le fuseau horaire de l'utilisateur, sous le format HH:mm:ss.SSS La valeur représente le temps écoulé en nanosecondes depuis le début de la requête. La valeur de temps écoulé est exclue des journaux révisés dans la Console du développeur. • event identifier : comprend l'événement spécifique qui a déclenché l'écriture dans le journal de débogage, tel que SAVEPOINT_RESET ou VALIDATION_RULE, ainsi que toute information consignée avec cet événement, telle que le nom de la méthode ou le numéro de ligne, et de caractère où le code a été exécuté. Données de journal supplémentaires Le journal contient en outre les informations suivantes : • Utilisation des ressources cumulée : consignée à la fin de nombreuses unités de code, telles que des déclencheurs, executeAnonymous, le traitement de message Apex par lot, les méthodes @future, les méthodes de test Apex, les méthodes de services Web Apex et la conversion de pistes Apex. • Informations de profil cumulées : consignées une seule fois à la fin de la transaction. Contient des informations sur les requêtes les plus coûteuses (les plus exigeantes en ressources), les invocations DML, etc. Voici un exemple de journal de débogage : 22.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO; WORKFLOW,INFO 11:47:46.030 (30064000)|EXECUTION_STARTED 11:47:46.030 (30159000)|CODE_UNIT_STARTED|[EXTERNAL]|TRIGGERS 11:47:46.030 (30271000)|CODE_UNIT_STARTED|[EXTERNAL]|01qD00000004JvP|myAccountTrigger on Account trigger event BeforeUpdate for [001D000000IzMaE] 11:47:46.038 (38296000)|SYSTEM_METHOD_ENTRY|[2]|System.debug(ANY) 11:47:46.038 (38450000)|USER_DEBUG|[2]|DEBUG|Hello World! 11:47:46.038 (38520000)|SYSTEM_METHOD_EXIT|[2]|System.debug(ANY) 11:47:46.546 (38587000)|CUMULATIVE_LIMIT_USAGE 11:47:46.546|LIMIT_USAGE_FOR_NS|(default)| Number of SOQL queries: 0 out of 100 Number of query rows: 0 out of 50000 Débogage du langage Apex salesforce | Compréhension du journal de débogage | 259 Number of SOSL queries: 0 out of 20 Number of DML statements: 0 out of 150 Number of DML rows: 0 out of 10000 Number of code statements: 1 out of 200000 Maximum heap size: 0 out of 6000000 Number of callouts: 0 out of 10 Number of Email Invocations: 0 out of 10 Number of fields describes: 0 out of 100 Number of record type describes: 0 out of 100 Number of child relationships describes: 0 out of 100 Number of picklist describes: 0 out of 100 Number of future calls: 0 out of 10 11:47:46.546|CUMULATIVE_LIMIT_USAGE_END 11:47:46.038 (38715000)|CODE_UNIT_FINISHED|myAccountTrigger on Account trigger event BeforeUpdate for [001D000000IzMaE] 11:47:47.154 (1154831000)|CODE_UNIT_FINISHED|TRIGGERS 11:47:47.154 (1154881000)|EXECUTION_FINISHED Définition de filtres de journal de débogage pour des classes et déclencheurs Apex Le filtrage d'un journal de débogage offre un mécanisme d'ajustement de la verbosité du journal au niveau du déclencheur et de la classe. Ce mécanisme est particulièrement utile pour le débogage de la logique Apex. Par exemple, pour évaluer le résultat d'un processus complexe, vous pouvez augmenter la verbosité du journal d'une classe donnée lors de la désactivation de la consignation d'autres classes ou déclencheurs dans une requête unique. Lorsque vous remplacez les niveaux du journal de débogage d'une classe ou d'un déclencheur, ces niveaux de débogage s'appliquent également aux méthodes de la classe appelées par votre classe ou déclencheur et aux déclencheurs exécutés en tant que résultat. L'ensemble des méthodes et déclencheurs de classe du chemin d'exécution héritent des paramètres du journal de débogage de leur appelant, sauf si ces paramètres sont remplacés. Le diagramme suivant illustre le remplacement des niveaux du journal de débogage au niveau de la classe et du déclencheur. Dans ce scénario, supposons que Class1 crée des problèmes que vous souhaitez examiner de plus près. Pour cela, les niveaux du journal de débogage de Class1 sont augmentés vers une granularité maximale. Class3 ne remplace pas ces niveaux de consignation et hérite par conséquent des filtres de consignation granulaires de Class1. Cependant, UtilityClass a déjà été testée et elle fonctionne correctement, donc ses filtres de consignation sont désactivés. De même, Class2 n'est pas le chemin du code à l'origine du problème. Par conséquent, sa consignation est réduite pour consigner uniquement les erreurs de la catégorie Code Apex. Trigger2 hérite de ces paramètres de consignation de Class2. Débogage du langage Apex salesforce | Compréhension du journal de débogage | 260 Figure 6: Ajustement de la consignation de débogage pour des classes et des déclencheurs L'exemple suivant présente un code sur lequel repose le diagramme. 1. Trigger1 appelle une méthode de Class1 et une autre méthode de Class2. Par exemple : trigger Trigger1 on Account (before insert) { Class1.someMethod(); Class2.anotherMethod(); } 2. Class1 appelle une méthode de Class3 qui à son tour appelle une méthode d'une classe utilitaire. Par exemple : public class Class1 { public static void someMethod() { Class3.thirdMethod(); } } public class Class3 { public static void thirdMethod() { UtilityClass.doSomething(); } } 3. Class2 entraîne l'exécution d'un déclencheur, Trigger2. Par exemple : public class Class2 { public static void anotherMethod() { // Some code that causes Trigger2 to be fired. Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 261 } } Pour définir des filtres de journaux : 1. Dans la page de détails d'une classe ou d'un déclencheur, cliquez sur Filtres de journaux. 2. Cliquez sur Ignorer les filtres de journaux. Les filtres de journaux sont définis sur les niveaux de consignation par défaut. 3. Sélectionnez le niveau de consignation voulu pour chaque catégorie de consignation. Pour en savoir plus sur les catégories, les niveaux et les événements du journal de débogage, reportez-vous à Configuration des filtres du journal de débogage. Voir aussi : Utilisation de journaux dans la Console du développeur Débogage des appels d'API Apex Utilisation de journaux dans la Console du développeur Utilisez l'onglet des journaux dans la Console du développeur pour ouvrir les journaux de débogage. 1. Cliquez sur Ouvrir pour ouvrir le journal sélectionné dans une nouvelle vue Journal système. 2. Cliquez sur Ouvrir le journal brut pour ouvrir le contenu du journal sélectionné dans une vue de texte brut, sans mise en forme. 3. Cliquez sur Télécharger le journal pour télécharger le journal sélectionné. 4. Cliquez sur Masquer automatiquement les journaux pour masquer automatiquement tous les journaux existants lors de la prochaine actualisation de la page. Ce bouton permet de basculer : un nouveau clic active l'affichage tous les journaux. 5. Cliquez sur Effacer pour effacer tous les journaux de la liste. Conseil: Si vous surveillez des journaux de débogage pour un utilisateur, ils restent accessibles depuis la page Journal de débogage. Cliquez sur Votre nom > Configuration > Surveillance > Journaux de débogage. 6. Sélectionnez Afficher mes journaux actuels uniquement pour afficher uniquement les journaux que vous avez générés depuis l'ouverture de la Console du développeur. Désactivez cette option pour afficher tous les journaux de débogage enregistrés pour votre organisation, y compris les journaux système récemment générés, créés par d'autres utilisateurs. 7. Cliquez sur Filtrer, puis saisissez un texte de filtrage les journaux visibles. Par exemple, pour afficher les journaux de débogage d'un utilisateur spécifique, saisissez le nom de cet utilisateur. Le filtre est sensible à la casse. Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 262 Les journaux s'ouvrent dans une vue Journal système. Une vue Journal système est une visionneuse d'exécution sensible au contexte qui affiche la source d'une opération, l'événement ayant déclenché l'opération et l'action ensuite exécutée. Cette vue permet d'examiner les journaux de débogage qui comprennent les événements de la base de données, le traitement Apex, le workflow et la logique de validation. Pour plus d'informations sur l'utilisation de journaux dans une vue Journal système de la Console du développeur, reportez-vous à Vue Journal système. Lors de l'utilisation de la Console du développeur ou de la surveillance d'un journal de débogage, vous pouvez spécifier le niveau d'information inclus dans le journal. Catégorie de consignation Le type d'information consignée, telles que des informations Apex ou des règles de workflow. Niveau de consignation La quantité d'informations consignées. Type d’événement La combinaison d'une catégorie de consignation et d'un niveau de consignation qui spécifie le type des événements enregistrés. Chaque événement peut consigner des informations supplémentaires, tels que le numéro de ligne et de caractère du début de l'événement, les champs associés à l'événement, la durée en millisecondes de l'événement, etc. Catégories de journal de débogage Vous pouvez indiquer les catégories de consignation ci-dessous. La quantité d'informations consignées pour chaque catégorie dépend du niveau de consignation : Catégorie de consignation Description Database Comprend des informations sur l'activité de la base de données, y compris chaque instruction du langage de manipulation de données (DML) ou chaque requête en ligne SOQL ou SOSL. Workflow Comprend des informations sur les règles de workflow, telles que leur nom, les actions engagées, etc. Validation Comprend des informations sur les règles de validation, telles que leur nom, si la règle a évalué true ou false, etc. Callout Inclut le fichier XML de requête-réponse que le serveur envoie et reçoit d'un service Web externe. Il est utile lors du débogage des problèmes liés à l'utilisation des appels API des services Web Force.com. Apex Code Comprend des informations relatives au code Apex, et peut inclure des informations telles que des messages du journal générés par des instructions DML, des requêtes en ligne SOQL ou SOSL, le début et la fin de chaque déclencheur, le début et la fin de chaque méthode de test, etc. Apex Profiling Comprend des informations sur les profils cumulés, telles que les limitations de votre espace de noms, le nombre d'e-mails envoyés, etc. Visualforce Comprend des informations relatives aux événements Visualforce qui incluent la sérialisation et la désérialisation de l'état de la vue ou l'évaluation d'un champ de formule dans une page Visualforce. Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 263 Catégorie de consignation Description System Comprend des informations relatives aux appels à toutes les méthodes système, telles que la méthode System.debug. Niveaux du journal de débogage Vous pouvez indiquer les niveaux de consignation suivants. Les niveaux sont répertoriés du plus bas au plus élevé. Les événements spécifiques sont consignés selon une combinaison de catégorie et de niveaux. La consignation de la plupart des événements commence au niveau INFO. Le niveau est cumulé, c.-à-d. que si vous sélectionnez FINE, le journal comprend également tous les événements consignés aux niveaux DEBUG, INFO, WARN et ERROR. Remarque: Les niveaux ne sont pas tous disponibles pour toutes les catégories. Seuls les niveaux qui correspondent à un ou plusieurs événements sont disponibles. • • • • • • • ERROR WARN INFO DEBUG FINE FINER FINEST Important: Avant d'exécuter un déploiement, assurez-vous que le niveau du journal Code Apex n'est pas défini sur FINEST. Si le niveau du journal Code Apex est défini sur FINEST, le déploiement peut être plus long que prévu. Si la Console du développeur est ouverte, les niveaux de journal dans la Console du développeur affectent tous les journaux, y compris ceux créés pendant un déploiement. Types d'événement de débogage L'exemple suivant présente les informations consignées dans le journal de débogage. L'événement est USER_DEBUG. Le format est timestamp | event identifier : • • timestamp : comprend l'heure à laquelle se produit l'événement et une valeur entre parenthèses. L'heure est définie dans le fuseau horaire de l'utilisateur, sous le format HH:mm:ss.SSS La valeur représente le temps écoulé en nanosecondes depuis le début de la requête. La valeur de temps écoulé est exclue des journaux révisés dans la Console du développeur. event identifier : comprend l'événement spécifique qui a déclenché l'écriture dans le journal de débogage, tel que SAVEPOINT_RESET ou VALIDATION_RULE, ainsi que toute information consignée avec cet événement, telle que le nom de la méthode ou le numéro de ligne, et de caractère où le code a été exécuté. Voici un exemple de ligne de journal de débogage : Figure 7: Exemple de ligne d'un journal de débogage Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 264 Dans cet exemple, l'identifiant de l'événement comprend : • Nom de l'événement : USER_DEBUG • Numéro de ligne et de caractère de l'événement dans le code : [2] • Niveau de consignation défini pour la méthode System.Debug : DEBUG • Chaîne fournie par l'utilisateur pour la méthode System.Debug : Hello world! L'exemple de ligne de journal suivant est déclenché dans cet extrait de code. Figure 8: Extrait de code de ligne de journal de débogage La ligne de journal suivante est enregistrée lorsque le test atteint la ligne 5 du code : 15:51:01.071 (55856000)|DML_BEGIN|[5]|Op:Insert|Type:Invoice_Statement__c|Rows:1 Dans cet exemple, l'identifiant de l'événement comprend : • Nom de l'événement : DML_BEGIN • Numéro de ligne et de caractère de l'événement dans le code : [5] • Type d'opération DML : Insert : Op:Insert • Nom de l'objet : Type:Invoice_Statement__c Débogage du langage Apex • salesforce | Utilisation de journaux dans la Console du développeur | 265 Nombre de lignes passées dans l'opération DML : Rows:1 Le tableau ci-dessous présente les types d'événement qui sont consignés, les champs ou d'autres informations consignées avec chaque événement, ainsi que la combinaison d'un niveau et d'une catégorie de journal qui déclenche la consignation d'un événement. Nom de l'événement Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné BULK_HEAP_ALLOCATE Nombre d'octets alloués Code Apex FINEST CALLOUT_REQUEST Numéro de la ligne, en-têtes de la requête Appel INFO et supérieur CALLOUT_RESPONSE Numéro de la ligne, corps de la réponse Appel INFO et supérieur CODE_UNIT_FINISHED Aucun Code Apex ERROR et supérieur CODE_UNIT_STARTED Numéro de la ligne, nom de l'unité de code Code Apex tel que MyTrigger on Account trigger ERROR et supérieur event BeforeInsert for [new] CONSTRUCTOR_ENTRY Numéro de la ligne, ID de la classe Apex, la Code Apex chaîne <init>() avec les types de paramètre, le cas échéant, entre parenthèses DEBUG et supérieur CONSTRUCTOR_EXIT Numéro de la ligne, la chaîne <init>() avec Code Apex les types de paramètre, le cas échéant, entre parenthèses DEBUG et supérieur CUMULATIVE_LIMIT_USAGE Aucun Profil Apex INFO et supérieur CUMULATIVE_LIMIT_USAGE_END Aucun Profil Apex INFO et supérieur Aucun Profil Apex FINE et supérieur CUMULATIVE_PROFILING_BEGIN Aucun Profil Apex FINE et supérieur CUMULATIVE_PROFILING_END Aucun Profil Apex FINE et supérieur CUMULATIVE_PROFILING DML_BEGIN Numéro de la ligne, opération (telle que Insert, Update, etc.), nom ou type d'enregistrement, nombre de lignes traitées dans l'opération DML Code Apex INFO et supérieur DML_END Numéro de la ligne Code Apex INFO et supérieur EMAIL_QUEUE Numéro de la ligne Code Apex INFO et supérieur ENTERING_MANAGED_PKG Espace de noms du package Code Apex INFO et supérieur EXCEPTION_THROWN Numéro de la ligne, type d'exception, message Code Apex INFO et supérieur EXECUTION_FINISHED Aucun Code Apex ERROR et supérieur EXECUTION_STARTED Aucun Code Apex ERROR et supérieur Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 266 Nom de l'événement Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné FATAL_ERROR Type d'exception, message, trace de pile Code Apex ERROR et supérieur HEAP_ALLOCATE Numéro de la ligne, nombre d'octets Code Apex FINER et supérieur HEAP_DEALLOCATE Numéro de la ligne, nombre d'octets désaffectés Code Apex FINER et supérieur IDEAS_QUERY_EXECUTE Numéro de la ligne Base de données FINEST LIMIT_USAGE_FOR_NS Espace de noms, limites suivantes : Profil Apex FINEST Number of SOQL queries Number of query rows Number of SOSL queries Number of DML statements Number of DML rows Number of code statements Maximum heap size Number of callouts Number of Email Invocations Number of fields describes Number of record type describes Number of child relationships describes Débogage du langage Apex Nom de l'événement salesforce | Utilisation de journaux dans la Console du développeur | 267 Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné Number of picklist describes Number of future calls Number of find similar calls Number of System.runAs() invocations METHOD_ENTRY Numéro de la ligne, ID Force.com de la classe, signature de la méthode Code Apex DEBUG et supérieur METHOD_EXIT Numéro de la ligne, ID Force.com de la classe, signature de la méthode. Code Apex DEBUG et supérieur Pour les constructeurs, les informations suivantes sont consignées : Numéro de la ligne, nom de la classe. POP_TRACE_FLAGS Numéro de la ligne, ID Force.com de la classe Système ou du déclencheur dont les filtres de consignation sont définis et compris dans la portée, le nom de cette classe ou de ce déclencheur, les paramètres du filtre de consignation actuellement actifs après avoir quitté cette portée INFO et supérieur PUSH_TRACE_FLAGS Numéro de la ligne, ID Force.com de la classe Système ou du déclencheur dont les filtres de consignation sont définis et non compris dans la portée, le nom de cette classe ou de ce déclencheur, les paramètres du filtre de consignation actuellement actifs après être entrés dans cette portée INFO et supérieur QUERY_MORE_ITERATIONS Numéro de la ligne, nombre d'itérations de Base de données INFO et supérieur queryMore SAVEPOINT_ROLLBACK Numéro de la ligne, nom Savepoint Base de données INFO et supérieur SAVEPOINT_SET Numéro de la ligne, nom Savepoint Base de données INFO et supérieur SLA_END Nombre de requêtes, durée de chargement, durée de traitement, nombre de jalons de Workflow INFO et supérieur Débogage du langage Apex Nom de l'événement salesforce | Utilisation de journaux dans la Console du développeur | 268 Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné requêtes à insérer/mettre à jour/supprimer, nouveau déclencheur SLA_EVAL_MILESTONE ID de jalon Workflow INFO et supérieur SLA_NULL_START_DATE Aucun Workflow INFO et supérieur SLA_PROCESS_CASE ID de requête Workflow INFO et supérieur SOQL_EXECUTE_BEGIN Numéro de la ligne, nombre d'agrégations, source de la requête Base de données INFO et supérieur SOQL_EXECUTE_END Numéro de la ligne, nombre de lignes, durée Base de données en millisecondes INFO et supérieur SOSL_EXECUTE_BEGIN Numéro de la ligne, source de la requête Base de données INFO et supérieur SOSL_EXECUTE_END Numéro de la ligne, nombre de lignes, durée Base de données en millisecondes INFO et supérieur STACK_FRAME_VARIABLE_LIST Numéro de cadre, liste de variables sous la forme : Numéro de variable | Valeur. Profil Apex FINE et supérieur Code Apex FINER et supérieur Par exemple : var1:50 var2:'Hello World' STATEMENT_EXECUTE Numéro de la ligne STATIC_VARIABLE_LIST Liste de variables sous la forme : Numéro de Profil Apex variable | Valeur. Par exemple : FINE et supérieur var1:50 var2:'Hello World' SYSTEM_CONSTRUCTOR_ENTRY Numéro de la ligne, la chaîne <init>() avec Système DEBUG les types de paramètre, le cas échéant, entre parenthèses SYSTEM_CONSTRUCTOR_EXIT Numéro de la ligne, la chaîne <init>() avec Système les types de paramètre, le cas échéant, entre parenthèses DEBUG SYSTEM_METHOD_ENTRY Numéro de la ligne, signature de la méthode Système DEBUG SYSTEM_METHOD_EXIT Numéro de la ligne, signature de la méthode Système DEBUG SYSTEM_MODE_ENTER Nom de mode Système INFO et supérieur SYSTEM_MODE_EXIT Nom de mode Système INFO et supérieur Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 269 Nom de l'événement Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné TESTING_LIMITS Aucun Profil Apex INFO et supérieur Profil Apex FINE et supérieur TOTAL_EMAIL_RECIPIENTS_QUEUED Nombre d'e-mails envoyés USER_DEBUG Numéro de la ligne, niveau de consignation, Code Apex chaîne fournie par l'utilisateur DEBUG et supérieur par défaut. Si l'utilisateur définit le niveau de consignation pour la méthode System.Debug, l'événement est consigné à la place à ce niveau. VALIDATION_ERROR Message d'erreur Validation INFO et supérieur VALIDATION_FAIL Aucun Validation INFO et supérieur VALIDATION_FORMULA Source de formule, valeurs Validation INFO et supérieur VALIDATION_PASS Aucun Validation INFO et supérieur VALIDATION_RULE Nom de règle Validation INFO et supérieur VARIABLE_ASSIGNMENT Numéro de la ligne, nom de la variable, représentation par chaîne de la valeur de la variable, adresse de la variable Code Apex FINEST VARIABLE_SCOPE_BEGIN Numéro de la ligne, nom de la variable, type, Code Apex valeur indiquant si la variable peut être référencée, valeur indiquant si la variable est statique FINEST VARIABLE_SCOPE_END Aucun Code Apex FINEST VF_APEX_CALL Nom d'élément, nom de méthode, type de renvoi Code Apex INFO et supérieur VF_DESERIALIZE_VIEWSTATE_BEGIN ID d'état de la vue Visualforce INFO et supérieur VF_DESERIALIZE_VIEWSTATE_END Aucun Visualforce INFO et supérieur VF_EVALUATE_FORMULA_BEGIN ID d'état de la vue, formule Visualforce FINER et supérieur VF_EVALUATE_FORMULA_END Aucun Visualforce FINER et supérieur VF_PAGE_MESSAGE Texte du message Code Apex INFO et supérieur VF_SERIALIZE_VIEWSTATE_BEGIN ID d'état de la vue Visualforce INFO et supérieur VF_SERIALIZE_VIEWSTATE_END Aucun Visualforce INFO et supérieur WF_ACTION Description de l'action Workflow INFO et supérieur WF_ACTION_TASK Objet de la tâche, ID d'action, règle, propriétaire, date d'échéance Workflow INFO et supérieur WF_ACTIONS_END Récapitulatif des actions exécutées Workflow INFO et supérieur Débogage du langage Apex salesforce | Utilisation de journaux dans la Console du développeur | 270 Nom de l'événement Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné WF_APPROVAL Type de transition, EntityName: Workflow NameField Id, nom de noeud de processus INFO et supérieur WF_APPROVAL_REMOVE EntityName: NameField Id Workflow INFO et supérieur WF_APPROVAL_SUBMIT EntityName: NameField Id Workflow INFO et supérieur WF_ASSIGN Propriétaire, ID de modèle de bénéficiaire Workflow INFO et supérieur WF_CRITERIA_BEGIN EntityName: NameField Id, nom de Workflow INFO et supérieur WF_CRITERIA_END Valeur booléenne indiquant le succès (true ou Workflow false) INFO et supérieur WF_EMAIL_ALERT ID d'action, règle Workflow INFO et supérieur WF_EMAIL_SENT ID de modèle d'e-mail, destinataires, e-mails Workflow CC INFO et supérieur WF_ENQUEUE_ACTIONS Récapitulatif des actions mises en file d'attente Workflow INFO et supérieur WF_ESCALATION_ACTION ID de requête, heures d'ouverture Workflow INFO et supérieur WF_ESCALATION_RULE Aucun Workflow INFO et supérieur WF_EVAL_ENTRY_CRITERIA Nom de processus, ID de modèle d'e-mail, valeur booléenne indiquant le résultat (true ou false) Workflow INFO et supérieur WF_FIELD_UPDATE EntityName: NameField Id, nom Workflow INFO et supérieur règle, ID de règle, type de déclencheur (si la règle respecte les types de déclencheur) d'objet ou de champ WF_FORMULA Source de formule, valeurs Workflow INFO et supérieur WF_HARD_REJECT Aucun Workflow INFO et supérieur WF_NEXT_APPROVER Propriétaire, type de propriétaire suivant, champ Workflow INFO et supérieur WF_NO_PROCESS_FOUND Aucun Workflow INFO et supérieur WF_OUTBOUND_MSG EntityName: NameField Id, ID Workflow INFO et supérieur d'action, règle WF_PROCESS_NODE Nom de processus Workflow INFO et supérieur WF_REASSIGN_RECORD EntityName: NameField Id, propriétaire Workflow INFO et supérieur WF_RESPONSE_NOTIFY Nom du notificateur, e-mail du notificateur, Workflow ID de modèle du notificateur INFO et supérieur WF_RULE_ENTRY_ORDER Entier, ordre d'indication Workflow INFO et supérieur WF_RULE_EVAL_BEGIN Type de règle Workflow INFO et supérieur WF_RULE_EVAL_END Aucun Workflow INFO et supérieur Débogage du langage Apex salesforce | Débogage des appels d'API Apex | 271 Nom de l'événement Champs ou informations consignées avec l'événement Catégorie consignée Événement consigné WF_RULE_EVAL_VALUE Valeur Workflow INFO et supérieur WF_RULE_FILTER Critères de filtrage Workflow INFO et supérieur WF_RULE_INVOCATION EntityName: NameField Id Workflow INFO et supérieur WF_RULE_NOT_EVALUATED Aucun Workflow INFO et supérieur WF_SOFT_REJECT Nom de processus Workflow INFO et supérieur WF_SPOOL_ACTION_BEGIN Type de noeud Workflow INFO et supérieur WF_TIME_TRIGGER EntityName: NameField Id, action Workflow INFO et supérieur Workflow INFO et supérieur temporelle, conteneur d'action temporelle, date/heure d'évaluation WF_TIME_TRIGGERS_BEGIN Aucun Voir aussi : Compréhension du journal de débogage Débogage des appels d'API Apex Débogage des appels d'API Apex Tous les appels d'API qui invoquent Apex prennent en charge une fonctionnalité de débogage qui permet d'accéder à des informations détaillées sur l'exécution du code, notamment tous les appels à System.debug(). En plus de la Console du développeur, un en-tête d'entrée SOAP appelé DebuggingHeader permet de définir la granularité de consignation selon les niveaux présentés dans le tableau suivant. Nom de l'élément Type Description LogCategory string Spécifie le type d'information renvoyé dans le journal de débogage. Les valeurs valides sont : • Db • Workflow • Validation • Callout • Apex_code • Apex_profiling • All Débogage du langage Apex salesforce | Débogage des appels d'API Apex | 272 Nom de l'élément Type Description LogCategoryLevel string Spécifie la quantité d'information renvoyée dans le journal de débogage. Seul l'élément Apex_code LogCategory utilise les niveaux de catégorie du journal. Les niveaux de consignation valides (répertoriés du plus bas au plus élevé) sont les suivants : • • • • • • • ERROR WARN INFO DEBUG FINE FINER FINEST De plus, les niveaux de consignation suivant sont pris en charge dans l'élément DebuggingHeader pour la rétrocompatibilité. Niveau de consignation Description NONE Ne comprend aucun message du journal. DEBUGONLY Comprend les messages de niveau inférieur, ainsi que les messages générés par des appels à la méthode System.debug. DB Comprend les messages du journal générés par des appels à la méthode System.debug, ainsi que chaque instruction DML (langage de manipulation de données), ou requête SOQL ou SOSL. PROFILE Comprend les messages du journal générés par des appels à la méthode System.debug, chaque instruction DML, ou requête SOQL ou SOSL en ligne, ainsi que l'entrée et la sortie de chaque méthode définie par l'utilisateur. De plus, la fin du journal de débogage contient des informations de profilage globales pour les portions de la requête qui ont utilisé la plus grande quantité de ressources en termes d'instructions SOQL et SOSL, d'opérations DML et d'invocations de méthode Apex. Ces trois sections répertorient les emplacements du code qui consomment le plus de temps, par ordre décroissant de durée cumulée totale, ainsi que le nombre d'exécutions. CALLOUT Comprend le fichier XML de requête-réponse que le serveur envoie et reçoit d'un service Web externe. Il est utile pour déboguer des problèmes liés à l'utilisation des appels d'API des services Web Force.com. DETAIL Comprend tous les messages générés par le niveau PROFILE, ainsi que les éléments suivants : • Instructions de déclaration de variable • Début des exécutions de boucle • Tous les contrôles de boucle, tels que break et continue • Les exceptions levées * • Le code d'initialisation statique et de classe * • Toute modification dans le contexte with sharing Débogage du langage Apex salesforce | Gestion des exceptions non détectées | 273 L'en-tête de sortie correspondant, DebuggingInfo, contient le journal de débogage qui en résulte. Pour plus informations, reportez-vous à DebuggingHeader à la page 803. Voir aussi : Compréhension du journal de débogage Utilisation de journaux dans la Console du développeur Gestion des exceptions non détectées Si du code Apex contient un bogue ou ne détecte pas une exception au niveau du code : • • Une explication simple du problème s'affiche pour l'utilisateur dans l'interface de l'application. Ce message d'erreur inclut la trace de la pile Apex. Le développeur spécifié dans le champ LastModifiedBy reçoit l'erreur par e-mail avec la trace de la pile Apex ainsi que l'ID d'utilisateur et de l'organisation du client. Aucune autre donnée de consommateur n'est renvoyée avec le rapport. Notez que pour un code Apex exécuté de façon synchrone, certains e-mails d'erreur sont supprimés en raison d'erreurs d'exception dupliquées. Pour un code Apex exécuté de façon asynchrone (Apex par lot, Apex planifié ou méthodes futures (méthodes annotées avec @future), les e-mails d'erreur pour des exceptions dupliquées ne sont pas supprimés. Compréhension des limitations et des gouverneurs d'exécution Comme le langage Apex est exécuté dans un environnement mutualisé, le moteur d'exécution Apex applique une limitation stricte pour empêcher qu'un emballement de code Apex ne monopolise des ressources partagées. Ces limitations, ou gouverneurs, surveillent et appliquent les statistiques présentées dans le tableau suivant. Si un code Apex dépasse une limite, le gouverneur associé génère une exception à l'exécution qui ne peut pas être gérée. Les limitations du gouverneur s'appliquent à l'ensemble de l'organisation, ainsi qu'à des espaces de noms spécifiques. Par exemple, si vous installez un package géré créé par un FAI partenaire de salesforce.com à partir de Force.com AppExchange, les composants du package appartiennent à un espace de noms unique par rapport à d'autres composants dans votre organisation. Par conséquent, tout code Apex inclus dans un package peut générer jusqu'à 150 instructions DML pendant l'exécution. De plus, tout code Apex natif de votre organisation peut générer jusqu'à 150 instructions DML, ce qui signifie que plus de 150 instructions DML peuvent être exécutées durant une demande unique si un code du package géré et un code de votre organisation native sont exécutés. Inversement, si vous installez un package à partir d'AppExchange qui n'est pas créé par un FAI partenaire de salesforce.com, le code de ce package n'inclut pas son propre décompte séparé de limitations du gouverneur. Toutes les ressources qu'il utilise sont prises en compte dans le total de votre organisation. Les messages de ressources cumulées et les e-mails d'avertissement sont également générés sur la base des espaces de noms du package géré. Pour plus d'informations sur les packages de FAI partenaires de salesforce.com, reportez-vous à Programmes partenaires salesforce.com. Description Nombre total de requêtes SOQL émises Limitation 1 100 1 Nombre total de requêtes SOQL émises pour des méthodes Apex par lot et futures 200 Nombre de total d'enregistrements récupérés par des requêtes SOQL 50 000 Débogage du langage Apex salesforce | Compréhension des limitations et des gouverneurs d'exécution | 274 Description Limitation Nombre total d'enregistrements récupérés par Database.getQueryLocator 10 000 Nombre total de requêtes SOSL émises 20 Nombre de total d'enregistrements récupérés par une seule requête SOQL 200 Nombre total d'instructions DML émises2 150 Nombre total d'enregistrements traités suite à des instructions DML, Approval.process 10 000 ou database.emptyRecycleBin Nombre total d'instructions de code exécutées 200 000 Nombre total d'instructions de code exécutées pour des méthodes Apex par lot et futures 1 000 000 Taille totale du segment mémoire3 6 Mo Taille totale du segment mémoire pour les méthodes Apex par lot et futures 12 Mo Profondeur totale de la pile pour toute invocation Apex qui active récursivement des déclencheurs en raison d'instructions insert, update ou delete4 16 Taille par lot de la liste de boucles For 200 Nombre total d'appels (demandes HTTP ou appels de services Web) dans une demande 10 Délai d'expiration maximal de tous les appels (demandes HTTP ou appels de services Web) 120 secondes dans une demande Délai d'expiration par défaut des appels (demandes HTTP ou appels de services Web) dans 10 secondes une demande Nombre total de méthodes avec l'annotation future autorisées par invocation Apex5 10 Taille maximale de demande ou de réponse d'appel (demande HTTP ou appel de service Web)6 3 Mo Nombre total de méthodes sendEmail autorisées 10 Nombre total d'informations describe autorisées7 100 Nombre total de classe qui peuvent être planifiées simultanément 25 Nombre total de classes de test qui peuvent être mises en file d'attente par période de 24 heures8 La valeur la plus grande entre 500 et 10 multiplié par le monde de classes de test dans l'organisation 1 Dans une requête SOQL avec des sous-requêtes de relation parent-enfant, chaque relation parent-enfant est considérée comme une requête supplémentaire. Ces types de requête sont limitées à trois fois le nombre de requêtes de niveau supérieur. Le nombre de lignes de ces requêtes relationnelles est pris en compte dans le nombre de lignes de l'exécution globale du code. En plus des instructions SOQL statiques, les appels aux méthodes suivantes sont prises en compte dans le nombre d'instructions SOQL émises dans une demande. • • Database.countQuery Database.getQueryLocator Débogage du langage Apex • 2 • • • • • • • • • • • • 3 salesforce | Compréhension des limitations et des gouverneurs d'exécution | 275 Database.query Les appels aux méthodes suivantes sont pris en compte dans le nombre de requêtes DML émises dans une demande. Approval.process Database.convertLead Database.emptyRecycleBin Database.rollback Database.setSavePoint delete et Database.delete insert et Database.insert merge undelete et Database.undelete update et Database.update upsert et Database.upsert System.runAs La taille du segment mémoire des services de messagerie st de 36 Mo. 4 Un code Apex récursif qui n'active aucun déclencheur avec des instructions insert, update ou delete existe dans une invocation unique, avec une pile unique. Inversement, un code Apex unique qui active un déclencheur engendre le déclencheur dans une nouvelle invocation Apex, séparée de l'invocation du code qui a activé le déclencheur. Comme la génération d'une nouvelle invocation de code Apex est une opération plus coûteuse qu'un appel récursif dans une invocation unique, les restrictions sur la profondeur de la pile de ces types d'appel récursif sont plus élevées. 5 Salesforce impose également une limite sur le nombre d'invocations de méthode future : 200 appels de méthode par licence utilisateur Salesforce complète, licence utilisateur Salesforce Platform ou licence utilisateur Force.com App Subscription, par 24 heures. Cette limitation s'applique à l'échelle de l'organisation. Les licences utilisateur Chatter Only, Utilisateurs clients de Chatter, Customer Portal User et Partner portal ne sont pas incluses dans le calcul de cette limitation. Par exemple, supposons que votre organisation possède trois licences Salesforce complètes, deux licences Salesforce Platform et 100 licences Customer Portal User. Votre organisation entière est limitée à 1000 appels de méthode par 24 heures, calculés par la formule 200 * (3+2), pas 200 * (3+2+100). 6 Les tailles des demandes et des réponses HTTP sont prises en compte dans la taille totale du segment mémoire. 7 Les informations describe comprennent les méthodes et les objets suivants : • • • • • Objets ChildRelationship Objets RecordTypeInfo Objets PicklistEntry Appels fields Appels fieldsets 8 Cette limite s'applique lorsque vous lancez des tests asynchrones en sélectionnant des classes de test pour l'exécution via la page Exécution du test Apex. Des limitations s'appliquent individuellement à chaque testMethod. Utilisez les méthodes Limits pour déterminer les limites d'exécution de votre code pendant son exécution. Par exemple, vous pouvez utiliser la méthode getDMLStatements pour déterminer le nombre d'instructions DML qui ont déjà été appelées par votre programme, ou la méthode getLimitDMLStatements pour déterminer le nombre total d'instructions DML disponibles pour votre code. Débogage du langage Apex salesforce | Compréhension des limitations et des gouverneurs d'exécution | 276 Pour de meilleures performances, les requêtes SOQL doivent être sélectives, notamment pour les requêtes dans des déclencheurs. Pour éviter les délais d'exécution importants, les requêtes SOQL non sélectives peuvent être terminées par le système. Les développeurs reçoivent un message d'erreur lorsqu'une requête non sélective dans un déclencheur est exécutée sur un objet qui contient plus de 100 000 enregistrements. Pour éviter cette erreur, assurez-vous d'utiliser une requête sélective. Reportez-vous à Requêtes SOQL plus efficaces. Pour un code Apex enregistré en utilisant l'API Salesforce.com version 20.0 ou antérieure, si un appel d'API active un déclencheur, le lot de 200 enregistrements à traiter est divisé en lots de 100 enregistrements. Pour un code Apex enregistré en utilisant l'API Salesforce.com versions 21.0 et supérieures, les lots d'API ne sont pas divisés. Notez que les valeurs de variable statique sont réinitialisées entre les lots, contrairement aux limitations du gouverneur. N'utilisez pas des variables statiques pour suivre les informations d'état entre les lots. En plus des limitations du gouverneur d'exécution, le langage Apex comprend les limitations suivantes : • • • Le nombre maximal de caractères pour une classe est de 1 million. Le nombre maximal de caractères pour un déclencheur est de 1 million. Le volume maximal de code Apex utilisé dans une organisation est de 3 Mo. Remarque: Cette limite ne s'applique pas aux packages gérés certifiés installés à partir d'AppExchange, (c.-à-d. une application marquée Certifiée AppExchange). Le code de ces types de package appartient à un espace de noms unique du code de votre organisation. Pour plus d'informations sur les packages certifiés AppExchange, reportez-vous à l'aide en ligne de Force.com AppExchange. Cette limite ne s'applique à aucun code inclus dans une classe définie avec l'annotation @isTest. • • • • • • • La taille de la méthode est limitée. Les méthodes volumineuses qui dépassent la limite autorisée entraînent une exception à l'exécution de votre code. Comme dans Java, dans le langage Apex la limite de la taille de méthode est de 65 535 octets de bytecode d'instructions compilées. Si une requête SOQL est exécutée pendant plus de 120 secondes, elle peut être annulée par Salesforce. Chaque demande Apex est limitée à 10 minutes d'exécution. Une demande d'appel est limitée à 20 demandes simultanées à des URL avec le même hôte. L'hôte est défini par le sous-domaine unique de l'URL, par exemple www.monsite.com et extra.monsite.com sont deux hôtes différents. La limite est calculée sur l'ensemble des organisations qui accèdent au même hôte. Si la limite est dépassée, une exception CalloutException est levée. Le nombre maximal d'enregistrements qu'un rapport d'événement renvoie est de 20 000 pour un utilisateur non administrateur système et de 100 000 pour un administrateur système. Chaque organisation a droit à 10 demandes synchrones simultanées pour des demandes longues dont l'exécution dépasse 5 secondes. Si des demandes supplémentaires sont effectuées alors que 10 longues demandes sont en cours d'exécution, elles sont refusées. Un utilisateur peut avoir jusqu'à 50 curseurs de requête ouverts en même temps. Par exemple, si 50 curseurs sont ouverts et qu'une application cliente toujours connectée sous le même utilisateur tente d'en ouvrir un nouveau, le plus ancien des 50 curseurs est libéré. Notez que cette limitation est différente pour la méthode start Apex, qui peut avoir jusqu'à cinq curseurs de requête ouverts à la fois par utilisateur. Les autres méthodes Apex par lot ont une limite plus élevée de 50 curseurs. Les limitations en curseur des différentes fonctionnalités Force.com sont suivies séparément. Par exemple, vous pouvez avoir 50 Apex curseurs de requête, 50 curseur par lot et 50 curseurs Visualforce ouverts en même temps. • Dans une transaction unique, vous pouvez référencer uniquement 10 espaces de noms uniques. Par exemple, supposons que vous avez un objet qui exécute une classe dans un package géré lors de la mise à jour de l'objet. Ensuite, cette classe met à jour un deuxième objet, qui à son tour exécute une classe différente dans un package différent. Bien que le deuxième Débogage du langage Apex • • salesforce | Compréhension des limitations et des gouverneurs d'exécution | 277 package n'a pas été accédé directement par le premier, car il se produit dans la même transaction, il est inclus dans le nombre d'espaces de noms accédés dans une transaction unique. Tout déploiement d'un code Apex est limité à 5000 unités de code de classes et de déclencheurs. Si vous utilisez le produit Mise à jour Data.com et ses tâches automatisées, et que vous avez défini des déclencheurs Apex avec des requêtes SOQL qui s'exécutent sur des enregistrements de compte, de contact ou de piste, les requêtes peuvent interférer avec les tâches de Mise à jour de ces objets. Vos déclencheurs Apex (combinés) ne doivent pas dépasser 200 requêtes SOQL par lot. Sinon, votre tâche Mise à jour pour cet objet échoue. De plus, si vos déclencheurs appellent des méthodes future, ils sont soumis à une limite de 10 appels future par lot. Limitations en e-mails Limitations en e-mails entrants Services de messagerie : Nombre maximal d'e-mails traités (inclut la limite pour E-mail vers requête à la demande) Nombre de licences utilisateur multiplié par 1 000, jusqu'à un maximum de 1 000 000 par jour Services de messagerie : Taille maximale d'un e-mail (corps et pièces jointes) 10 Mo1 E-mail vers requête à la demande : Taille maximale d'une pièce jointe 10 Mo E-mail vers requête à la demande : Nombre maximal d'e-mails traités Nombre de licences utilisateur multiplié par 1 000, jusqu'à un maximum de 1 000 000 par jour (pris en compte dans la limitation des services de messagerie) 1 La taille maximale des e-mails des services de messagerie varie en fonction de la langue et du jeu de caractères. Lors de la définition de services de messagerie, notez les éléments suivants : • • • • • Un service de messagerie traite uniquement les messages qu'il reçoit à l'une de ses adresses. Salesforce limite le nombre total de messages que tous les services de messagerie combinés, E-mail vers requête à la demande inclus, peuvent traiter quotidiennement. Les messages qui dépassent cette limite sont renvoyés, ignorés ou mis en file d'attente pour un traitement le jour suivant, en fonction de la configuration des paramètres de réponse d'échec de chaque service de messagerie. Salesforce calcule la limite en multipliant le nombre de licences utilisateur par 1 000, jusqu'à une valeur quotidienne maximale de 1 000 000. Par exemple, si vous avez 10 licences, votre organisation peut traiter jusqu'à 10 000 e-mails par jour. Les adresses de services de messagerie que vous créez dans votre sandbox ne peuvent pas être copiées dans votre organisation de production. Pour chaque service de messagerie, vous pouvez instruire Salesforce d'envoyer les messages d'erreur à une adresse spécifique au lieu de celle de l'expéditeur. Les services de messagerie rejettent les e-mails et notifient l'expéditeur si l'e-mail (corps de texte, corps HTML et pièces jointes combinés) dépassent 10 Mo environ (selon la langue et le jeu de caractères). E-mail sortant : Limitations pour les e-mails individuels et en masse envoyés en utilisant Apex Vous pouvez envoyer des e-mails individuels au maximum à 1000 adresses e-mail externes par jour, en fonction de l'heure GMT. Les e-mails individuels envoyés en utilisant l'application ne sont pas pris en compte dans cette limite. Vous pouvez envoyer des e-mails en masse à 1 000 adresses e-mail externes par organisation et par jour, en fonction de l'heure GMT. Le nombre maximum d'adresses externes que vous pouvez inclure dans chaque e-mail en masse dépend de l'édition Salesforce que vous utilisez : Débogage du langage Apex salesforce | Compréhension des limitations et des gouverneurs d'exécution | 278 Edition Limitation en adresses par e-mails en masse Professional 250 Enterprise Edition 500 Unlimited Edition 1000 Remarque: Notez les points suivants sur les limitation en e-mails : • • • Les limitations en e-mails individuels et en masse ne prennent pas en compte les adresses uniques. Par exemple, si vous avez 10 fois l'adresse [email protected] dans votre e-mail, 10 adresses sont prises en compte dans la limite. Vous pouvez envoyer un nombre illimité d'e-mails à vos utilisateurs internes. Ces limites s'appliquent également aux e-mails envoyés à l'aide de l'API et d'Apex. Dans les organisations Developer Edition et dans celles qui utilisent Salesforce pendant une période d'évaluation, votre organisation peut envoyer un e-mail en masse au maximum à 10 adresses e-mail externes par jour. Cette limitation inférieure ne s'applique pas si votre organisation a été créée avant la version Winter '12 et si elle a déjà activé les e-mails en masse avec une limitation supérieure. Limitations du gouverneur pour une tâche Apex par lot Notez les limitations du gouverneur suivantes pour une tâche Apex par lot : • • Jusqu'à cinq tâches par lot en file d'attente ou actives sont autorisées pour Apex. Un utilisateur peut avoir jusqu'à 50 curseurs de requête ouverts en même temps. Par exemple, si 50 curseurs sont ouverts et qu'une application cliente toujours connectée sous le même utilisateur tente d'en ouvrir un nouveau, le plus ancien des 50 curseurs est libéré. Notez que cette limitation est différente pour la méthode start Apex, qui peut avoir jusqu'à cinq curseurs de requête ouverts à la fois par utilisateur. Les autres méthodes Apex par lot ont une limite plus élevée de 50 curseurs. Les limitations en curseur des différentes fonctionnalités Force.com sont suivies séparément. Par exemple, vous pouvez avoir 50 Apex curseurs de requête, 50 curseur par lot et 50 curseurs Visualforce ouverts en même temps. • • • • • • • Un maximum de 50 millions d'enregistrements peuvent être renvoyés dans l'objet Database.QueryLocator. Si plus de 50 millions d'enregistrements sont renvoyés, la tâche par lot est immédiatement terminée et marquée comme échouée. Si la méthode start renvoie un QueryLocator, le paramètre de portée facultatif de Database.executeBatch peut avoir une valeur maximale de 2000. Si vous définissez une valeur plus importante, Salesforce segmente les enregistrements renvoyés par QueryLocator en lots plus petits limités à 2000 enregistrements. Si la méthode start renvoie un itérable, la valeur du paramètre de portée n'a pas de limite maximale. Si vous utilisez une valeur très élevée, vous risquez toutefois d'atteindre d'autres limites. Si aucune taille n'est spécifiée avec le paramètre facultatif scope de Database.executeBatch, Salesforce segmente les enregistrements renvoyés par la méthode start en lots de 200, puis passe chaque lot à la méthode execute. Les limitations du gouverneur Apex sont réinitialisés à chaque exécution de la méthode execute. Chaque méthode start, execute et finish peut mettre en oeuvre jusqu'à 10 appels. Les exécutions par lot sont limitées à 10 appels par exécution de méthode. Le nombre maximal d'exécutions par lot est de 250 000 par 24 heures. Une seule méthode start d'une tâche Apex par lot peut être exécutée à la fois dans une organisation. Les tâches par lot qui n'ont pas encore commencé restent dans la file d'attente jusqu'à leur démarrage. Notez que cette limite n'entraîne aucun Débogage du langage Apex salesforce | Utilisation d'avertissements par e-mail pour les limitations du gouverneur | 279 échec de tâche par lot, et les méthodes execute des tâches Apex par lot continuent de s'exécuter en parallèle si plusieurs taches simultanées sont en cours. Voir aussi : Quelles sont les limitations du langage Apex ? Annotation Future https://help.salesforce.com/apex/HTViewHelpDoc?id=data_dot_com_clean_scheduling_jobs.htm Utilisation d'avertissements par e-mail pour les limitations du gouverneur Lorsqu'un utilisateur invoque un code Apex qui dépasse de plus de 50 % une limitation du gouverneur, vous pouvez désigner un utilisateur de votre organisation comme destinataire d'un e-mail de notification de l'événement qui contient des informations supplémentaires. Pour activer les avertissements par e-mail : 1. 2. 3. 4. 5. Connectez-vous à Salesforce en tant qu'utilisateur administrateur. Cliquez sur Votre nom > Configuration > Gérer les utilisateurs > Utilisateurs. Cliquez sur Modifier en regard du nom de l'utilisateur qui doit recevoir les notifications par e-mail. Sélectionnez l'option Envoyer des e-mails d'avertissement Apex. Cliquez sur Enregistrer. Chapitre 9 Développement de code Apex dans des packages gérés Sujets : • • • Versions de package Dépréciation du code Apex Comportement dans les versions de package Un package est un conteneur qui regroupe des éléments aussi petits qu'un composant individuel ou aussi grands qu'une série d'applications associées. Une fois le package créé, vous pouvez le distribuer à d'autres utilisateurs et organisations Salesforce, même hors de votre société. Une organisation peut créer un seul package géré que de nombreuses organisations peuvent télécharger et installer. Les packages gérés diffèrent des packages non gérés car ils contiennent certains composants verrouillés pour permettre leur mise à niveau ultérieure. Les packages non gérés ne comprennent pas les composants verrouillés et ne peuvent pas être mis à niveau. Ce chapitre comprend les rubriques suivantes relatives au développement de code Apex dans des packages gérés : • • • Versions de package Dépréciation du code Apex Comportement dans des versions de package Développement de code Apex dans des packages gérés salesforce | Versions de package | 281 Versions de package Une version de package est un numéro qui identifie l'ensemble de composants chargés dans un package. Le numéro de version se présente sous la forme numéroMajeur.numéroMineur.numéroCorrectif (par exemple, 2.1.3). Les numéros majeur et mineur sont incrémentés à chaque publication d'une version importante. Le numéroCorrectif est généré et téléchargé uniquement lors de la publication d'un correctif. Les packages non gérés ne peuvent pas être mis à niveau. Chaque version du package est un simple ensemble de composants à distribuer. Une version de package a plus d'importance pour des packages gérés. Les packages peuvent exposer des comportements différents selon les versions. Les versions de package permettent aux éditeurs d'adapter les composants de leurs packages gérés en publiant gracieusement des versions ultérieures sans gêner les intégrations clientes existantes utilisant le package. Lorsqu'un abonné existant installe une nouvelle version d'un package, le package ne contient qu'une seule instance de chaque composant, mais les composants peuvent émuler des versions antérieures. Par exemple, un abonné peut utiliser un package géré qui contient une classe Apex. Si l'éditeur décide de rendre obsolète une méthode dans la classe Apex et de publier une nouvelle version du package, l'abonné continue de voir une seule instance de la classe Apex après l'installation de la nouvelle version. Cependant, cette classe Apex peut continuer à émuler la version précédente de tout code qui référence la méthode obsolète de la version antérieure. Notez les points suivants lors du développement de code Apex dans des packages gérés : • • • • • • • • Le code inclus dans une classe ou un déclencheur Apex faisant partie d'un package géré est automatiquement masqué et ne peut pas être affiché dans une organisation d'installation. Les seules exceptions sont les méthodes déclarées comme globales, dont les signatures peuvent être affichées dans une organisation d'installation. Les packages gérés reçoivent un espace de noms unique. Cet espace de noms est automatiquement ajouté au début du nom de vos classes, méthodes, variables, etc., afin d'empêcher la duplication des noms dans l'organisation d'installation. Dans une transaction unique, vous pouvez référencer uniquement 10 espaces de noms uniques. Par exemple, supposons que vous avez un objet qui exécute une classe dans un package géré lors de la mise à jour de l'objet. Ensuite, cette classe met à jour un deuxième objet, qui à son tour exécute une classe différente dans un package différent. Bien que le deuxième package n'a pas été accédé directement par le premier, car il se produit dans la même transaction, il est inclus dans le nombre d'espaces de noms accédés dans une transaction unique. Le code inclus dans un code Apex faisant partie d'un package géré est automatiquement masqué et ne peut pas être affiché dans une organisation d'installation. Les seules exceptions sont les méthodes déclarées comme globales, dont les signatures peuvent être affichées dans une organisation d'installation. Les développeurs de packages peuvent utiliser l'annotation deprecated pour identifier des méthodes, des classes, des exceptions, des énumérations, des interfaces ou des variables qui ne peuvent plus être référencées dans les versions successives du package géré dans lequel elles résident. Elles sont utiles pour refactoriser un code dans des packages gérés dont les exigences évoluent. Vous pouvez écrire des méthodes de test qui changent le contexte de version d'un package en une version de package différente en utilisant la méthode système runAs. Vous ne pouvez pas ajouter une méthode à une interface globale ou une méthode abstraite à une classe globale une fois l'interface ou la classe chargée dans une version de package Géré-Publié. Si la classe du package Géré-Publié est virtuelle, la méthode que vous pouvez lui ajouter doit également être virtuelle et avoir une mise en oeuvre. Le code Apex contenu dans un package non géré qui référence explicitement un espace de noms ne peut pas être chargé. Développement de code Apex dans des packages gérés salesforce | Dépréciation du code Apex | 282 Dépréciation du code Apex Les développeurs de packages peuvent utiliser l'annotation deprecated pour identifier des méthodes, des classes, des exceptions, des énumérations, des interfaces ou des variables qui ne peuvent plus être référencées dans les versions successives du package géré dans lequel elles résident. Elles sont utiles pour refactoriser un code dans des packages gérés dont les exigences évoluent. Après avoir chargé une autre version de package Géré-Publié, les nouveaux abonnés qui installent la toute dernière version du package ne peuvent pas afficher les éléments dépréciés, alors que les éléments continuent de fonctionner pour les abonnés existants et les intégrations d'API. Un élément déprécié, tel qu'une méthode ou une classe, peut toujours être référencé en interne par le développeur du package. Remarque: Vous ne pouvez pas utiliser l'annotation deprecated dans des classes ou des déclencheurs Apex pour des packages non gérés. Les développeurs de package peuvent utiliser des versions Géré-Publié à des fins d'évaluation et de commentaires avec un ensemble d'utilisateurs pilote dans des organisations Salesforce différentes. Si un développeur déprécie un identificateur Apex, puis charge une version de package Géré-Publié, les abonnés qui installent la version du package continuent à afficher l'identificateur déprécié dans cette version. Si le développeur du package charge ensuite une version du package Géré-Publié, les abonnés n'affichent plus l'identificateur déprécié dans la version du package après l'avoir installée. Comportement dans les versions de package Un composant de package peut avoir un comportement différent selon la version d'un package. La gestion des comportements dans les différentes versions permet d'ajouter de nouveaux composants à votre package et d'affiner vos composants existants, tout en préservant un fonctionnement sans heurt pour les abonnés existants. Si un développeur ajoute un nouveau composant à un package, puis charge une nouvelle version, le nouveau composant est disponible pour tous les abonnés qui installent la nouvelle version du package. Gestion des versions de comportement dans un code Apex Les développeurs de package peuvent utiliser une logique conditionnelle dans des classes et des déclencheurs Apex afin d'exposer des comportements différents d'une version à l'autre. Le développeur d'un package peut ainsi préserver la prise en charge d'un comportement existant dans les classes et les déclencheurs de versions précédentes du package, tout en faisant évoluer le code. Lorsque les abonnés installent plusieurs versions de votre package et écrivent un code qui référence des classes ou des déclencheurs Apex dans votre package, ils doivent sélectionner la version qu'ils référencent. Dans le code Apex référencé dans votre package, vous pouvez exécuter différents chemins de code conditionnels basés sur le paramètre de version du code Apex appelant qui définit la référence. Le paramètre de version de package du code appelant peut être déterminé dans le code du package en appelant la méthode System.requestVersion. Les développeurs d'un package peuvent ainsi déterminer le contexte de la demande et spécifier un comportement différent pour les diverses versions du package. L'exemple suivant utilise la méthode System.requestVersion et instancie la classe System.Version afin de définir différents comportements dans un déclencheur Apex pour les diverses versions du package. trigger oppValidation on Opportunity (before insert, before update) { for (Opportunity o : Trigger.new){ Développement de code Apex dans des packages gérés salesforce | Éléments de code Apex invariables dans les différentes versions | 283 // Add a new validation to the package // Applies to versions of the managed package greater than 1.0 if (System.requestVersion().compareTo(new Version(1,0)) > 0) { if (o.Probability >= 50 && o.Description == null) { o.addError('All deals over 50% require a description'); } } // Validation applies to all versions of the managed package. if (o.IsWon == true && o.LeadSource == null) { o.addError('A lead source must be provided for all Closed Won deals'); } } } Pour consulter une liste complète de méthodes qui fonctionnent dans des versions de package, reportez-vous à Méthodes Version et à la méthode System.requestVersion dans Méthodes System. Le contexte de la demande est préservé si une classe dans le package installé invoque une méthode dans une autre classe de package. Par exemple, un abonné a installé le package RapportsGeo qui contient les classes Apex UtilPays et UtilContinent. L'abonné crée une classe RapportsGeoEx et utilise les paramètres de version pour la lier à la version 2.3 du package RapportsGeo. Si RapportsGeoEx invoque une méthode dans UtilContinent qui invoque en interne une méthode dans UtilPays, le contexte de la demande est propagé de UtilContinent à UtilPays, et la méthode System.requestVersion dans UtilPays renvoie la version 2.3 du package RapportsGeo. Éléments de code Apex invariables dans les différentes versions Vous pouvez modifier le comportement de certains éléments Apex entre les différentes versions d'un package. Par exemple, vous pouvez déprécier une méthode afin que les nouveaux abonnés ne puissent plus référencer le package dans une version ultérieure. Cependant, les modificateurs, les mots clés et les annotations de la liste ci-dessous ne peuvent pas différer d'une version à l'autre. Si le développeur d'un package change l'un de ces modificateurs, mots clés ou annotations, les modifications sont reflétées dans toutes les versions du package. Les modifications que vous pouvez apporter à certains de ces éléments sont limitées lorsqu'ils sont utilisés dans un code Apex dans des packages gérés. Les développeurs de package peuvent ajouter ou supprimer les éléments suivants : • • • @future @isTest with sharing Développement de code Apex dans des packages gérés • • salesforce | Test du comportement dans les versions de package | 284 without sharing transient Les développeurs de package peuvent apporter des modifications limitées aux éléments suivants : • • • • • private : ne peut pas être changé en global public : ne peut pas être changé en global protected : ne peut pas être changé en global abstract : ne peut pas être changé en virtual, mais ne peut pas être supprimé final : ne peut pas être supprimé, mais ne peut pas être ajouté Les développeurs de package peuvent supprimer ou modifier les éléments suivants : • • global virtual Les développeurs de package peuvent ajouter le mot clé webService, mais une fois ajouté, il ne peut pas être supprimé. Remarque: Vous ne pouvez pas déprécier les méthodes ou les variables webService dans le code d'un package géré. Test du comportement dans les versions de package Lorsque vous modifiez le comportement dans une classe ou un déclencheur Apex pour différentes versions d'un package, il est important de tester l'exécution de votre code dans toutes les versions. Vous pouvez écrire des méthodes de test qui changent le contexte de version d'un package en une version de package différente en utilisant la méthode système runAs. Vous pouvez également utiliser runAs dans une méthode de test. L'exemple suivant présente un déclencheur dont le comportement diffère selon la version du package. trigger oppValidation on Opportunity (before insert, before update) { for (Opportunity o : Trigger.new){ // Add a new validation to the package // Applies to versions of the managed package greater than 1.0 if (System.requestVersion().compareTo(new Version(1,0)) > 0) { if (o.Probability >= 50 && o.Description == null) { o.addError('All deals over 50% require a description'); } } // Validation applies to all versions of the managed package. if (o.IsWon == true && o.LeadSource == null) { Développement de code Apex dans des packages gérés salesforce | Test du comportement dans les versions de package | 285 o.addError('A lead source must be provided for all Closed Won deals'); } } } La classe de test suivante utilise la méthode runAs pour vérifier le comportement du déclencheur avec et sans version spécifique : @isTest private class OppTriggerTests{ static testMethod void testOppValidation(){ // Set up 50% opportunity with no description Opportunity o = new Opportunity(); o.Name = 'Test Job'; o.Probability = 50; o.StageName = 'Prospect'; o.CloseDate = System.today(); // Test running as latest package version try{ insert o; } catch(System.DMLException e){ System.assert( e.getMessage().contains( 'All deals over 50% require a description'), e.getMessage()); } // Run test as managed package version 1.0 System.runAs(new Version(1,0)){ try{ insert o; Développement de code Apex dans des packages gérés salesforce | Test du comportement dans les versions de package | 286 } catch(System.DMLException e){ System.assert(false, e.getMessage()); } } // Set up a closed won opportunity with no lead source o = new Opportunity(); o.Name = 'Test Job'; o.Probability = 50; o.StageName = 'Prospect'; o.CloseDate = System.today(); o.StageName = 'Closed Won'; // Test running as latest package version try{ insert o; } catch(System.DMLException e){ System.assert( e.getMessage().contains( 'A lead source must be provided for all Closed Won deals'), e.getMessage()); } // Run test as managed package version 1.0 System.runAs(new Version(1,0)){ try{ insert o; } catch(System.DMLException e){ System.assert( e.getMessage().contains( Développement de code Apex dans des packages gérés salesforce | Test du comportement dans les versions de package | 287 'A lead source must be provided for all Closed Won deals'), e.getMessage()); } } } } Chapitre 10 Exposition de méthodes Apex en tant que services Web SOAP Sujets : • Méthodes WebService Vous pouvez exposer vos méthodes Apex en tant que services Web SOAP afin de permettre à des applications externes d'accéder à votre code et à votre application. Pour exposer vos méthodes Apex, utilisez les Méthodes WebService. Conseil: • • Les services Web SOAP Apex permettent à une application externe d'invoquer des méthodes Apex via des services Web SOAP. Les appels Apex permettent à un code Apex d'invoquer des services Web ou HTTP externes. L'API REST Apex expose vos classes et méthodes Apex en tant que services Web REST. Reportez-vous à Exposition de classes Apex en tant que services Web REST. Exposition de méthodes Apex en tant que services Web SOAP salesforce | Méthodes WebService | 289 Méthodes WebService Les méthodes de classe Apex peuvent être exposées en tant qu'appels de services Web SOAP. Cela permet à une application externe d'invoquer un service Web Apex pour exécuter une action dans Salesforce. Utilisez le mot clé webService pour définir ces méthodes. Par exemple : global class MyWebService { webService static Id makeContact(String lastName, Account a) { Contact c = new Contact(lastName = 'Weissman', AccountId = a.Id); insert c; return c.id; } } Un développeur d'une application externe peut l'intégrer à une classe Apex contenant des méthodes webService en générant un WSDL pour la classe. Pour générer un WSDL à partir d'une page de détail de la classe Apex : 1. Dans l'application, accédez à Votre nom > Configuration > Développer > Classes Apex. 2. Cliquez sur le nom de la classe qui contient les méthodes webService. 3. Cliquez sur Générer WSDL. Exposition des données avec des méthodes WebService L'invocation d'une méthode webService personnalisée utilise toujours le contexte système. Par conséquent, les identifiants de l'utilisateur actif ne sont pas utilisés, et tout utilisateur ayant accès à ces méthodes peut exploiter toute leur puissance, quelles que soient les autorisations, la sécurité au niveau du champ ou les règles de partage. Les développeurs qui exposent des méthodes avec le mot clé webService doivent par conséquent s'assurer de ne pas révéler accidentellement des données confidentielles. ATTENTION: Les méthodes de classe Apex exposées via l'API avec le mot clé webService n'imposent pas les autorisations d'objet et la sécurité au niveau du champ par défaut. Nous recommandons d'utiliser l'objet approprié ou les méthodes field describe result afin de vérifier le niveau d'accès de l'utilisateur actif sur les objets et les champs auxquels la méthode webService a accès. Reportez-vous à Schema.DescribeSObjectResult et à Schema.DescribeFieldResult. De plus, les règles de partage (accès au niveau de l'enregistrement) sont appliquées uniquement lors de la déclaration d'une classe avec le mot clé with sharing. Cette exigence s'applique à toutes les classes Apex, y compris les classes qui contiennent des méthodes webService. Pour appliquer des règles de partage pour des méthodes webService, déclarez la classe qui contient ces méthodes avec le mot clé with sharing. Reportez-vous à Utilisation des mots clés with sharing ou without sharing. Considérations sur l'utilisation du mot clé WebService Lors de l'utilisation du mot clé webService, tenez compte des considérations suivantes : Exposition de méthodes Apex en tant que services Web SOAP • • • • • • • • • Vous ne pouvez pas utiliser le mot clé webService lors de la définition d'une classe. Vous pouvez toutefois l'utiliser pour définir des méthodes de classe externe de niveau supérieur et des méthodes de classe interne. Vous ne pouvez pas utiliser le mot clé webService pour définir une interface, ou des méthodes et des variables d'une interface. Les énumérations définies par le système ne peuvent pas être utilisées dans des méthodes de service Web. Vous ne pouvez pas utiliser le mot clé webService dans un déclencheur, car il n'est pas possible de définir une méthode dans un déclencheur. Toutes les classes qui contiennent des méthodes définies avec le mot clé webService doivent être déclarées global. Si une méthode ou une classe interne est déclarée global, la classe externe de niveau supérieur doit également être définie comme global. Les méthodes définies avec le mot clé webService sont par nature globales. Ces méthodes peuvent être utilisées avec n'importe quel code Apex qui a accès à la classe. Vous pouvez considérer le mot clé webService comme un type de modificateur d'accès qui offre un accès supérieur à global. Vous devez définir toutes les méthodes qui utilisent le mot clé webService comme static. Vous ne pouvez pas déprécier les méthodes ou les variables webService dans le code d'un package géré. Comme il n'existe aucun analogue SOAP à certains éléments Apex, les méthodes définies avec le mot-clé webService ne peuvent pas accepter les éléments suivants en tant que paramètres. Ces éléments peuvent être utilisés dans une méthode, mais ils ne peuvent pas être marqués en tant que valeur renvoyée. ◊ ◊ ◊ ◊ ◊ • • • salesforce | Considérations sur l'utilisation du mot clé WebService | 290 Maps Sets Objets Pattern Objets Matcher Objets Exception Vous devez utiliser le mot clé webService avec toutes les variables de membre que vous souhaitez exposer dans un service Web. Vous ne devez pas marquer ces variables de membre comme static. Salesforce refuse l'accès à des requêtes de service Web et executeanonymous provenant d'un package AppExchange dont l'accès est Restricted. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. L'exemple suivant montre une classe avec des variables de membre de service Web ainsi qu'une méthode de service Web : global class SpecialAccounts { global class AccountInfo { webService String AcctName; webService Integer AcctNumber; } webService static Account createAccount(AccountInfo info) { Account acct = new Account(); acct.Name = info.AcctName; Exposition de méthodes Apex en tant que services Web SOAP salesforce | Considérations sur l'utilisation du mot clé WebService | 291 acct.AccountNumber = String.valueOf(info.AcctNumber); insert acct; return acct; } webService static Id [] createAccounts(Account parent, Account child, Account grandChild) { insert parent; child.parentId = parent.Id; insert child; grandChild.parentId = child.Id; insert grandChild; Id [] results = new Id[3]; results[0] = parent.Id; results[1] = child.Id; results[2] = grandChild.Id; return results; } testMethod static void testAccountCreate() { AccountInfo info = new AccountInfo(); info.AcctName = 'Manoj Cheenath'; info.AcctNumber = 12345; Account acct = SpecialAccounts.createAccount(info); System.assert(acct != null); } } Vous pouvez invoquer ce service Web en utilisant AJAX. Pour plus informations, reportez-vous à Apex dans AJAX à la page 121. Exposition de méthodes Apex en tant que services Web SOAP salesforce | Surcharge des méthodes WebService | 292 Surcharge des méthodes WebService SOAP et WSDL n'offrent pas une bonne prise en charge de la surcharge des méthodes. Par conséquent, le langage Apex ne permet pas à deux méthodes marquées avec le mot clé webService de porter le même nom. Les méthodes de service Web qui portent le même nom dans la même classe génèrent une erreur à la compilation. Chapitre 11 Exposition de classes Apex en tant que services Web REST Sujets : • • • • • Introduction à REST Apex Annotations REST Apex Méthodes REST Apex Exposition de données avec des méthodes de services Web REST Apex Exemples de code REST Apex Vous pouvez exposer vos classes et méthodes Apex pour permettre à des applications externes d'accéder à votre code et à votre application via l'architecture REST. Ce chapitre montre comment exposer vos classes Apex en tant que services Web REST. Il présente également des annotations de classe et de méthode, ainsi que des exemples de code qui montrent la mise en oeuvre cette fonctionnalité. Exposition de classes Apex en tant que services Web REST salesforce | Introduction à REST Apex | 294 Introduction à REST Apex Vous pouvez exposer votre classe et vos méthodes Apex pour permettre à des applications externes d'accéder à votre code et à votre application via l'architecture REST. Pour cela, définissez votre classe Apex avec l'annotation @RestResource pour l'exposer en tant que ressource REST. De la même façon, ajoutez des annotations à vos méthodes pour les exposer via REST. Pour plus informations, reportez-vous à Annotations REST Apex à la page 168 Limitations du gouverneur Les appels aux classes REST Apex sont prises en compte dans les limitations du gouverneur API de l'organisation. Toutes les limitations du gouverneur Apex standard s'appliquent aux classes REST Apex. Par exemple, la taille maximale de demande de réponse est de 3 Mo. Pour plus d'informations, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution. Authentification REST Apex prend en charge les mécanismes d'authentification suivants : • • OAuth 2.0 ID de session Reportez-vous à Step Two: Set Up Authorization dans le guide REST API Developer's Guide. Annotations REST Apex Six nouvelles annotations ont été ajoutées, qui permettent d'exposer une classe Apex en tant que service Web RESTful. • • • • • @RestResource(urlMapping='/yourUrl') @HttpDelete @HttpGet @HttpPost @HttpPut Méthodes REST Apex REST Apex prend en charge deux formats de représentation de ressources : JSON et XML. Les représentations JSON sont passées par défaut dans le corps d'une demande ou d'une réponse, et le format est indiqué par la propriété Content-Type dans l'en-tête HTTP. Vous pouvez récupérer le corps en tant que Blob dans l'objet HttpRequest si aucun paramètre n'est défini dans la méthode Apex. Si des paramètres sont définis dans la méthode Apex, une tentative de désérialisation du corps de la demande dans ces paramètres est effectuée. Si la méthode Apex a un type de renvoi non-void, la représentation de la ressource est sérialisée dans le corps de la réponse. Seuls les types de renvoi et de paramètre suivants sont autorisés : • • • • Primitifs Apex (sObject et Blob exclus). sObjects Listes ou mappages de primitifs ou sObjects Apex (seuls les mappages avec des clés String sont pris en charge) Types définis par l'utilisateur qui contiennent des variables de membre des types répertoriés ci-dessus. Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 295 Les méthodes annotées avec @HttpGet ou @HttpDelete ne doivent avoir aucun paramètre. Les requêtes GET et DELETE n'ayant pas de corps de demande, il n'existe aucun élément à désérialiser. Une classe Apex unique annotée avec @RestResource peut inclure plusieurs méthodes annotées avec la même méthode de requête HTTP. Par exemple, la même classe ne peut pas avoir deux méthodes annotées avec @HttpGet. Remarque: Actuellement, REST Apex ne prend pas en charge les demandes de Content-Type multipart/form-data. Considérations sur les méthodes REST Apex Voici quelques points à considérer lorsque vous définissez des méthodes REST Apex. • Les objets RestRequest et RestResponse sont disponibles par défaut dans vos méthodes Apex via l'objet RestContext statique. Cet exemple montre comment accéder à ces objets via RestContext : RestRequest req = RestContext.request; RestResponse res = RestContext.response; • • • Si la méthode Apex ne contient aucun paramètre, REST Apex copie le corps de la requête HTTP dans la propriété RestRequest.requestBody. Si la méthode contient des paramètres, REST Apex tente de désérialiser les données dans ces paramètres et les données ne sont pas désérialisées dans la propriété RestRequest.requestBody. REST Apex utilise une logique de sérialisation similaire pour la réponse. Une méthode Apex avec un type de renvoi non-void contient la valeur de renvoi sérialisée dans RestResponse.responseBody. Les méthodes REST Apex peuvent être utilisées dans des packages gérés et non gérés. Lors de l'appel de méthodes REST Apex contenues dans un package géré, vous devez inclure l'espace de noms du package géré dans l'URL de l'appel REST. Par exemple, si la classe est incluse dans l'espace de noms d'un package géré appelé EspaceNomsPackage et que les méthodes REST Apex utilisent un mappage d'URL de /MaMethode/*, l'URL utilisée via REST pour appeler ces méthodes se présente comme suit : https://instance.salesforce.com/services/apexrest/EspaceNomsPackage/MaMethode/. Pour plus d'informations sur les packages gérés, reportez-vous à Développement de code Apex dans des packages gérés. Types définis par l'utilisateur Vous pouvez utiliser des types définis par l'utilisateur pour les paramètres dans vos méthodes REST Apex. REST Apex désérialise les données des demandes en variables de membre de classe public, private ou global d'un type défini par l'utilisateur, sauf si la variable est déclarée comme static ou transient. Par exemple une méthode REST Apex qui contient un paramètre d'un type défini par l'utilisateur peut se présenter comme suit : @RestResource(urlMapping='/user_defined_type_example/*') global with sharing class MyOwnTypeRestResource { @HttpPost global static MyUserDefinedClass echoMyType(MyUserDefinedClass ic) { return ic; } Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 296 global class MyUserDefinedClass { global String string1; global String string2 { get; set; } private String privateString; global transient String transientString; global static String staticString; } } Les données de demandes JSON et XML valides pour cette méthode se présentent comme suit : { "ic" : { "string1" : "value for string1", "string2" : "value for string2", "privateString" : "value for privateString" } } <request> <ic> <string1>value for string1</string1> <string2>value for string2</string2> <privateString>value for privateString</privateString> </ic> </request> Si une valeur pour staticString ou transientString est fournie dans les données de l'exemple de demande ci-dessus, une réponse de code d'état HTTP 400 est générée. Notez que les variables de membre de classe public, private ou global doivent être des types autorisés par REST Apex : • • • Primitifs Apex (sObject et Blob exclus). sObjects Listes ou mappages de primitifs ou sObjects Apex (seuls les mappages avec des clés String sont pris en charge) Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 297 Lors de la création de types définis par l'utilisateur utilisés en tant que paramètres de méthode REST Apex, évitez d'introduire des définitions de variables de membre de classe qui entraînent des cycles (définitions interdépendantes) à l'exécution dans vos types définis par l'utilisateur. Voici un exemple simple : @RestResource(urlMapping='/CycleExample/*') global with sharing class ApexRESTCycleExample { @HttpGet global static MyUserDef1 doCycleTest() { MyUserDef1 def1 = new MyUserDef1(); MyUserDef2 def2 = new MyUserDef2(); def1.userDef2 = def2; def2.userDef1 = def1; return def1; } global class MyUserDef1 { MyUserDef2 userDef2; } global class MyUserDef2 { MyUserDef1 userDef1; } } Le code de l'exemple précédent compile, mais lorsqu'une demande est effectuée à l'exécution, REST Apex détecte un cycle entre les instances de def1 et def2, et génère une réponse d'erreur de code d'état HTTP 400. Considérations sur les données de demande Notez les points suivants sur les données de demandes pour vos méthodes REST Apex : • Le nom des paramètres Apex est important, mais pas leur ordre. Par exemple, les requêtes valides en XML et JSON se présentent comme suit : @HttpPost global static void myPostMethod(String s1, Integer i1, Boolean b1, String s2) { Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 298 "s1" : "my first string", "i1" : 123, "s2" : "my second string", "b1" : false } <request> <s1>my first string</s1> <i1>123</i1> <s2>my second string</s2> <b1>false</b1> </request> • • • Certains types de paramètre et de renvoi ne peuvent pas être utilisés avec XML en tant que Content-Type pour la requête ou en tant que format accepté pour la réponse. Par conséquent, les méthodes qui ont ces types de paramètre ou de renvoi ne peuvent pas être utilisées avec XML. Les mappages ou collections de collections, par exemple List<List<String>> ne sont pas pris en charge. Vous pouvez toutefois utiliser ces types avec JSON. Si la liste de paramètres inclut un type non valide pour XML et que XML est envoyé, un code d'état HTTP 415 est renvoyé. Si le type de renvoi est un type non valide pour XML et que XML est le format de réponse requis, un code d'état HTTP 406 est renvoyé. Pour des données de requête en JSON ou XML, les valeurs correctes des paramètres booléens sont les suivantes : true, false (les deux sont traités comme insensibles à la casse), 1 et 0 (les valeurs numériques, pas les chaînes de « 1 » ou « 0 »). Toute autre valeur pour des paramètres booléens génère une erreur. Si les données de requête JSON ou XML contiennent plusieurs paramètres portant le même nom, une réponse d'erreur de code d'état HTTP 400 est générée. Par exemple, si votre méthode spécifie un paramètre d'entrée nommé x, les données de requête JSON suivantes génèrent une erreur : { "x" : "value1", "x" : "value2" } De la même façon, pour des types définis par l'utilisateur, si les données de requête contiennent plusieurs données pour la même variable de membre d'un type défini par l'utilisateur, une erreur est générée. Par exemple, avec cette méthode REST Apex et ce type défini par l'utilisateur : @RestResource(urlMapping='/DuplicateParamsExample/*') global with sharing class ApexRESTDuplicateParamsExample { @HttpPost global static MyUserDef1 doDuplicateParamsTest(MyUserDef1 def) { return def; } Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 299 global class MyUserDef1 { Integer i; } } Les données de requête JSON suivantes génèrent également une erreur : { "def" : { "i" : 1, "i" : 2 } } • • Si vous devez spécifier une valeur nulle pour l'un de vos paramètres dans vos données de requête, vous pouvez omettre le paramètre ou spécifier une valeur nulle. Dans JSON, vous pouvez spécifier null en tant que valeur. Dans XML, vous devez utiliser l'espace de noms http://www.w3.org/2001/XMLSchema-instance avec une valeur nulle. Pour des données de requête XML, vous devez spécifier un espace de noms XML qui référence tous les espaces de noms Apex utilisés par votre méthode. Ainsi par exemple, si vous définissez une méthode REST Apex telle que : @RestResource(urlMapping='/namespaceExample/*') global class MyNamespaceTest { @HttpPost global static MyUDT echoTest(MyUDT def, String extraString) { return def; } global class MyUDT { Integer count; } } Vous pouvez utiliser les données de requête XML suivantes : <request> <def xmlns:MyUDT="http://soap.sforce.com/schemas/class/MyNamespaceTest"> Exposition de classes Apex en tant que services Web REST salesforce | Méthodes REST Apex | 300 <MyUDT:count>23</MyUDT:count> </def> <extraString>test</extraString> </request> Pour plus d'informations sur les espaces de noms XML et Apex, reportez-vous à Espaces de noms XML. Codes de statut de réponse Le code de statut d'une réponse est défini automatiquement. Le tableau ci-dessous présente quelques codes de statut HTTP ainsi que leur signification dans le contexte de la méthode de requête HTTP. Pour une liste complète de codes de statut de réponse, reportez-vous à Méthodes RestResponse. Méthode de requête Code d'état de réponse Description GET 200 La requête a réussi. PATCH 200 La requête a réussi et le type renvoyé est non-void. PATCH 204 La requête a réussi et le type renvoyé est void. DELETE, GET, PATCH, POST, PUT 400 Une exception utilisateur non gérée s'est produite. DELETE, GET, PATCH, POST, PUT 403 Vous n'avez pas accès à la classe Apex spécifiée. DELETE, GET, PATCH, POST, PUT 404 L'URL est sans mappage dans une annotation @RestResource existante. DELETE, GET, PATCH, POST, PUT 404 L'extension URL n'est pas prise en charge. DELETE, GET, PATCH, POST, PUT 404 La classe Apex avec l'espace de noms spécifié est introuvable. DELETE, GET, PATCH, POST, PUT 405 La méthode de requête n'a pas de méthode Apex correspondante. DELETE, GET, PATCH, POST, PUT 406 La propriété Content-Type dans l'en-tête a été définie sur une valeur autre que JSON ou XML. DELETE, GET, PATCH, POST, PUT 406 L'en-tête spécifié dans la requête HTTP n'est pas pris en charge. GET, PATCH, POST, PUT 406 Le type de renvoi XML spécifié pour le format n'est pas pris en charge. DELETE, GET, PATCH, POST, PUT 415 Le type de paramètre XML n'est pas pris en charge. DELETE, GET, PATCH, POST, PUT 415 Le type Content-Header spécifié dans l'en-tête de requête HTTP n'est pas pris en charge. DELETE, GET, PATCH, POST, PUT 500 Une exception Apex non gérée s'est produite. Exposition de classes Apex en tant que services Web REST salesforce | Exposition de données avec des méthodes de services Web REST Apex | 301 Exposition de données avec des méthodes de services Web REST Apex L'invocation d'une méthode de service Web REST Apex personnalisée utilise toujours le contexte système. Par conséquent, les identifiants de l'utilisateur actif ne sont pas utilisés, et tout utilisateur ayant accès à ces méthodes peut exploiter toute leur puissance, quelles que soient les autorisations, la sécurité au niveau du champ ou les règles de partage. Les développeurs qui exposent des méthodes en utilisant les annotations REST Apex doivent par conséquent s'assurer de ne pas révéler accidentellement des données confidentielles. ATTENTION: Les méthodes de classe Apex exposées via l'API REST Apex n'imposent pas les autorisations d'objet et la sécurité au niveau du champ par défaut. Nous recommandons d'utiliser l'objet approprié ou les méthodes field describe result afin de vérifier le niveau d'accès de l'utilisateur actif sur les objets et les champs auxquels la méthode d'API REST Apex a accès. Reportez-vous à Schema.DescribeSObjectResult et à Schema.DescribeFieldResult. De plus, les règles de partage (accès au niveau de l'enregistrement) sont appliquées uniquement lors de la déclaration d'une classe avec le mot clé with sharing. Cette exigence s'applique à toutes les classes Apex, y compris aux classes exposées via l'API REST Apex. Pour appliquer des règles de partage pour des méthodes d'API REST Apex, déclarez la classe qui contient ces méthodes avec le mot clé with sharing. Reportez-vous à Utilisation des mots clés with sharing ou without sharing. Exemples de code REST Apex Ces exemples de code montrent comment exposer des classes et des méthodes Apex via l'architecture REST, et comment appeler ces ressources à partir d'un client. • • Exemple de code de base REST Apex : Présente un exemple de classe REST Apex avec trois méthodes que vous pouvez appeler pour supprimer, obtenir et mettre à jour un enregistrement. Exemple de code REST Apex utilisant RestRequest : Présente un exemple de classe REST Apex qui ajoute une pièce jointe à un enregistrement en utilisant l'objet RestRequest. Exemple de code de base REST Apex Cet exemple montre comment mettre en oeuvre une simple API REST dans Apex qui traite trois méthodes de requête HTTP différentes. Pour plus d'informations sur l'authentification avec cURL, reportez-vous à la section Quick Start du guide REST API Developer's Guide. 1. Créez une classe Apex dans votre instance, en cliquant sur Votre nom > Configuration > Développer > Classes Apex > Nouveau, puis ajoutez ce code à votre nouvelle classe : @RestResource(urlMapping='/Account/*') global with sharing class MyRestResource { @HttpDelete global static void doDelete() { RestRequest req = RestContext.request; Exposition de classes Apex en tant que services Web REST salesforce | Exemple de code de base REST Apex | 302 RestResponse res = RestContext.response; String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1); Account account = [SELECT Id FROM Account WHERE Id = :accountId]; delete account; } @HttpGet global static Account doGet() { RestRequest req = RestContext.request; RestResponse res = RestContext.response; String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1); Account result = [SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId]; return result; } @HttpPost global static String doPost(String name, String phone, String website) { Account account = new Account(); account.Name = name; account.phone = phone; account.website = website; insert account; return account.Id; } } 2. Pour appeler la méthode doGet à partir d'un client, ouvrez une fenêtre de ligne de commande, puis exécutez la commande cURL suivante pour récupérer un compte par ID : curl -H "Authorization: Bearer sessionId" "https://instance.salesforce.com/services/apexrest/Account/accountId" • • • Remplacez sessionId par l'élément <sessionId> que vous avez noté dans la réponse de connexion. Remplacez instance par votre élément <serverUrl>. Remplacez accountId par l'ID d'un compte existant dans votre organisation. Exposition de classes Apex en tant que services Web REST salesforce | Exemple de code de base REST Apex | 303 Après avoir appelé la méthodedoGet, Salesforce renvoie une réponse JSON avec des données, telles que : { "attributes" : { "type" : "Account", "url" : "/services/data/v22.0/sobjects/Account/accountId" }, "Id" : "accountId", "Name" : "Acme" } Remarque: Les exemples cURL de cette section n'utilisent pas de classe Apex dans un espace de noms, par conséquent l'URL ne contient pas d'espace de noms. 3. Créez un fichier appelé compte.txt pour inclure les données du compte que vous allez créer à l'étape suivante. { "name" : "Wingo Ducks", "phone" : "707-555-1234", "website" : "www.wingo.ca.us" } 4. En utilisant une fenêtre de ligne de commande, exécutez la commande cURL ci-dessous pour créer un compte : curl -H "Authorization: Bearer sessionId" -H "Content-Type: application/json" -d @compte.txt "https://instance.salesforce.com/services/apexrest/Account/" Une fois la méthode doPost appelée, Salesforce renvoie une réponse avec des données, telles que : "accountId" L'élément accountId est l'ID du compte que vous venez de créer avec la requête POST. 5. En utilisant une fenêtre de ligne de commande, exécutez la commande cURL ci-dessous pour supprimer un compte en spécifiant l'ID : curl —X DELETE —H "Authorization: Bearer sessionId" "https://instance.salesforce.com/services/apexrest/Account/accountId" Voir aussi : Annotations REST Apex Exposition de classes Apex en tant que services Web REST salesforce | Exemple de code REST Apex utilisant RestRequest | 304 Exemple de code REST Apex utilisant RestRequest L'exemple suivant montre comment ajouter une pièce jointe à une requête en utilisant l'objet RestRequest. Pour plus d'informations sur l'authentification avec cURL, reportez-vous à la section Quick Start du guide REST API Developer's Guide. Dans ce code, les données du fichier binaire sont stockées dans l'objet RestRequest et la classe du service Apex accède aux données binaires dans l'objet RestRequest. 1. Créez une classe Apex dans votre instance, en cliquant sur Votre nom > Configuration > Développer > Classes Apex. Cliquez sur Nouveau, puis ajoutez le code suivant à votre nouvelle classe : @RestResource(urlMapping='/CaseManagement/v1/*') global with sharing class CaseMgmtService { @HttpPost global static String attachPic(){ RestRequest req = RestContext.request; RestResponse res = Restcontext.response; Id caseId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1); Blob picture = req.requestBody; Attachment a = new Attachment (ParentId = caseId, Body = picture, ContentType = 'image/jpg', Name = 'VehiclePicture'); insert a; return a.Id; } } 2. Ouvrez une fenêtre de ligne de commande, puis exécutez la commande cURL suivante pour charger la pièce jointe dans une requête : curl -H "Authorization: Bearer sessionId" -H "X-PrettyPrint: 1" -H "Content-Type: image/jpeg" --data-binary @file "https://instance.salesforce.com/services/apexrest/CaseManagement/v1/caseId" • • • Remplacez sessionId par l'élément <sessionId> que vous avez noté dans la réponse de connexion. Remplacez instance par votre élément <serverUrl>. Remplacez caseId par l'ID de la requête à laquelle vous souhaitez ajouter la pièce jointe. Exposition de classes Apex en tant que services Web REST • salesforce | Exemple de code REST Apex utilisant RestRequest | 305 Remplacez file par le chemin et le nom du fichier que vous souhaitez joindre. Votre commande doit se présenter comme suit (avec votre ID de session à la place de sessionId) : curl -H "Authorization: Bearer sessionId" -H "X-PrettyPrint: 1" -H "Content-Type: image/jpeg" --data-binary @c:\test\vehiclephoto1.jpg "https://na1.salesforce.com/services/apexrest/CaseManagement/v1/500D0000003aCts" Remarque: Les exemples cURL de cette section n'utilisent pas de classe Apex dans un espace de noms, par conséquent l'URL ne contient pas d'espace de noms. La classe Apex renvoie une réponse JSON qui contient l'ID de la pièce jointe, telle que : "00PD0000001y7BfMAI" 3. Pour vérifier que la pièce jointe et l'image ont été ajoutées à la requête, accédez à Requêtes, puis sélectionnez la vue Toutes les requêtes ouvertes. Cliquez sur la requête, puis naviguez jusqu'à la liste associée Pièces jointes. La pièce jointe que vous venez de créer doit être indiquée. Chapitre 12 Invocation d'appels en utilisant le langage Apex Sujets : • • • • • Ajout des paramètres de site distant Services SOAP : Définition d'une classe à partir d'un document WSDL Invocation d'appels HTTP Utilisation de certificats Limitations des appels Un appel Apex permet d'intégrer étroitement votre Apex à un service externe en passant un appel à un service Web externe ou en envoyant une demande HTTP via un code Apex, puis en recevant la réponse. Le langage Apex fournit l'intégration avec les services Web qui utilisent SOAP et WSDL, ou des services HTTP (services RESTful). Remarque: Pour qu'un appel Apex puisse appeler un site externe, ce site doit être enregistré dans la page Paramètres de site distant, sinon l'appel échoue. Salesforce empêche les appels à partir d'adresses réseau non autorisées. Pour plus d'informations sur les types d'appel, reportez-vous à : • • Services SOAP : Définition d'une classe à partir d'un document WSDL à la page 307 Invocation d'appels HTTP à la page 322 Conseil: Les appels permettent au code Apex d'invoquer des services Web externes ou HTTP. Les services Web Apex permettent à une application externe d'invoquer des méthodes Apex via des services Web. Invocation d'appels en utilisant le langage Apex salesforce | Ajout des paramètres de site distant | 307 Ajout des paramètres de site distant Pour qu'un appel Apex puisse appeler un site externe, ce site doit être enregistré dans la page Paramètres de site distant, sinon l'appel échoue. Salesforce empêche les appels à partir d'adresses réseau non autorisées. Pour ajouter un paramètre de site distant : 1. 2. 3. 4. 5. 6. Cliquez sur Votre nom > Configuration > Contrôles de sécurité > Paramètres de site distant. Cliquez sur Nouveau site distant. Saisissez un terme descriptif dans Nom du site distant. Saisissez l'URL du site distant. Vous pouvez également saisir une description du site. Cliquez sur Enregistrer. Services SOAP : Définition d'une classe à partir d'un document WSDL Des classes peuvent être générées automatiquement à partir d'un document WSDL stocké sur un disque dur local ou sur le réseau. La création d'une classe à l'aide d'un document WSDL permet aux développeurs de passer des appels vers le service Web externe dans leur code Apex. Remarque: Utilisez la Messagerie sortante pour gérer les solutions d'intégration, si possible. Utilisez les appels à des services Web tiers seulement si nécessaire. Pour générer une classe Apex à partir d'un document WSDL : 1. Dans l'application, cliquez sur Votre nom > Configuration > Développer > Classes Apex. 2. Cliquez sur Générer à partir du WSDL. 3. Cliquez sur Parcourir pour accéder à un document WSDL situé sur votre disque dur local ou sur le réseau, ou saisissez le chemin d'accès complet. Ce document WSDL est la base de la classe Apex que vous créez. Remarque: Le document WSDL que vous spécifiez peut contenir un emplacement de point de destination SOAP référençant un port de sortie. Pour des raisons de sécurité, Salesforce limite les ports de sortie que vous pouvez spécifier à l'un des ports suivants : • • • 80 : ce port accepte uniquement les connexions HTTP. 443 : ce port accepte uniquement les connexions HTTPS. 1024 à 66535 (inclus) : Ces ports acceptent les connexions HTTP ou HTTPS. 4. Cliquez sur Analyser le document WSDL pour vérifier le contenu du document WSDL. L'application génère un nom de classe par défaut pour chaque espace de noms dans le document WSDL et signale les erreurs éventuelles. L'analyse échoue si le WSDL contient des types ou des constructions de schéma non prises en charge par les classes Apex, ou si les classes résultantes dépassent la limite de 1 million de caractères pour des classes Apex. Par exemple, le WSDL de l'API SOAP de Salesforce ne peut pas être analysé. Invocation d'appels en utilisant le langage Apex salesforce | Invocation d'un service externe | 308 5. Modifiez les noms de classe, si nécessaire. Vous pouvez enregistrer plusieurs espaces de noms WSDL dans une seule classe en utilisant le même nom de classe pour chaque espace de noms, néanmoins les classes Apex ne peuvent pas dépasser 1 million de caractères au total. 6. Cliquez sur GénérerApex. La dernière page de l'assistant indique les classes dont la génération a réussi, ainsi que les erreurs provenant d'autres classes. La page fournit également un lien qui permet d'afficher le code généré avec succès. La classe Apex générée avec succès comprend des classes stub et type permettant d'appeler le service Web tiers représenté par le document WSDL. Ces classes permettent d'appeler le service Web externe à partir du code Apex. Notez les points suivants sur le code Apex généré : • • • Si un document WSDL contient un mot réservé Apex, le suffixe _x est ajouté à ce mot lors de la génération de la classe Apex. Par exemple, limit dans un document WSDL est converti en limit_x dans la classe Apex générée. Reportez-vous à Mots clés réservés. Pour plus d'informations sur la gestion des caractères dans des noms d'élément d'un document WSDL non pris en charge dans des noms de variable Apex, reportez-vous à Considérations sur l'utilisation de documents WSDL. Si une opération dans le WSDL a un message sortant contenant plusieurs éléments, l'Apex généré incorpore les éléments à une classe interne. La méthode Apex qui représente l'opération WSDL renvoie la classe interne au lieu des éléments individuels. Comme les points (.) ne sont pas autorisés dans des noms de classe Apex, les points dans des noms WSDL utilisés pour générer des classes Apex sont remplacés par des traits de soulignement (_) dans le code Apex généré. Après avoir généré une classe à partir du document WSDL, vous pouvez invoquer le service externe référencé par le WSDL. Remarque: Avant de pouvoir utiliser les exemples dans le reste de cette section, vous devez copier la classe Apex docSampleClass dans Compréhension du code généré, puis l'ajouter à votre organisation. Invocation d'un service externe Pour invoquer un service externe après avoir utilisé son document WSDL afin de générer une classe Apex, créez une instance du stub dans votre code Apex, puis appelez les méthodes. Par exemple, pour invoquer le service de référence d'adresse IP StrikeIron à partir du code Apex, vous pouvez écrire un code similaire à : // Create the stub strikeironIplookup.DNSSoap dns = new strikeironIplookup.DNSSoap(); // Set up the license header dns.LicenseInfo = new strikeiron.LicenseInfo(); dns.LicenseInfo.RegisteredUser = new strikeiron.RegisteredUser(); dns.LicenseInfo.RegisteredUser.UserID = '[email protected]'; dns.LicenseInfo.RegisteredUser.Password = 'your-password'; // Make the Web service call strikeironIplookup.DNSInfo info = dns.DNSLookup('www.myname.com'); Invocation d'appels en utilisant le langage Apex salesforce | Prise en charge d'en-tête HTTP | 309 Prise en charge d'en-tête HTTP Vous pouvez définir les en-têtes HTTP dans un appel de services Web. Par exemple, vous pouvez utiliser cette fonctionnalité pour définir la valeur d'un cookie dans un en-tête d'autorisation. Pour définir des en-têtes HTTP, ajoutez inputHttpHeaders_x et outputHttpHeaders_x au stub. Remarque: Dans l'API versions 16.0 et supérieures, les réponses HTTP à des appels sont toujours des codées en UTF-8, quel que soit l'en-tête Content-Type. Dans l'API versions 17.0 et supérieure, les réponses HTTP sont décodées en utilisant le codage spécifié dans l'en-tête Content-Type. Les exemples suivants fonctionnent avec l'exemple de fichier WSDL dans Compréhension du code généré à la page 313: Envoi d'en-têtes HTTP dans un appel de service Web docSample.DocSamplePort stub = new docSample.DocSamplePort(); stub.inputHttpHeaders_x = new Map<String, String>(); //Setting a basic authentication header stub.inputHttpHeaders_x.put('Authorization', 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='); //Setting a cookie header stub.inputHttpHeaders_x.put('Cookie', 'name=value'); //Setting a custom HTTP header stub.inputHttpHeaders_x.put('myHeader', 'myValue'); String input = 'This is the input string'; String output = stub.EchoString(input); Si une valeur est spécifiée pour inputHttpHeaders_x, elle remplace l'ensemble d'en-têtes standard. Accès à des en-têtes de réponse HTTP à partir d'une réponse d'appel de service Web docSample.DocSamplePort stub = new docSample.DocSamplePort(); stub.outputHttpHeaders_x = new Map<String, String>(); String input = 'This is the input string'; String output = stub.EchoString(input); Invocation d'appels en utilisant le langage Apex salesforce | Fonctionnalités WSDL prises en charge | 310 //Getting cookie header String cookie = stub.outputHttpHeaders_x.get('Set-Cookie'); //Getting custom header String myHeader = stub.outputHttpHeaders_x.get('My-Header'); La valeur de outputHttpHeaders_x est nulle par défaut. Vous devez définir outputHttpHeaders_x avant d'accéder au contenu des en-têtes dans la réponse. Fonctionnalités WSDL prises en charge Le langage Apex prend en charge le style de document WSDL littéral encapsulé, ainsi que les types de données primitifs et intégrés suivants : Type de schéma Type Apex xsd:anyURI String xsd:boolean Boolean xsd:date Date xsd:dateTime Datetime xsd:double Double xsd:float Double xsd:int Integer xsd:integer Integer xsd:language String xsd:long Long xsd:Name String xsd:NCName String xsd:nonNegativeInteger Integer xsd:NMTOKEN String xsd:NMTOKENS String xsd:normalizedString String xsd:NOTATION String xsd:positiveInteger Integer xsd:QName String xsd:short Integer Invocation d'appels en utilisant le langage Apex Type de schéma Type Apex xsd:string String xsd:time Datetime xsd:token String xsd:unsignedInt Integer xsd:unsignedLong Long xsd:unsignedShort Integer salesforce | Fonctionnalités WSDL prises en charge | 311 Remarque: Le type de données Salesforce anyType n'est pas pris en charge dans les WSDL utilisés pour générer un code Apex enregistré en utilisant l'API versions 15.0 et supérieures. Pour un code enregistrer en utilisant l'API versions 14.0 et supérieures, anyType est mappé avec String. Le langage Apex prend également en charge les constructions de schéma suivantes : • • • • • xsd:all, dans le code Apex enregistré en utilisant l'API versions 15.0 et supérieures xsd:annotation, dans le code Apex enregistré en utilisant l'API versions 15.0 et supérieures xsd:attribute, dans le code Apex enregistré en utilisant l'API versions 15.0 et supérieures xsd:choice, dans le code Apex enregistré en utilisant l'API versions 15.0 et supérieures xsd:element. Dans le code Apex enregistré en utilisant l'API versions 15.0 et supérieures, l'attribut ref est également pris en charge avec des restrictions suivantes : ◊ Vous ne pouvez pas appeler un ref dans un espace de noms différent. ◊ Un élément global ne peut pas utiliser ref. ◊ Si un élément contient ref, il ne peut pas non plus contenir name ou type. • xsd:sequence Les types de données suivants sont pris en charge uniquement lorsqu'ils sont utilisé en tant qu'appels entrants, c.-à-d. lorsqu'un service Web externe appel une méthode de service Web Apex. Ces types de données ne sont pas pris en charge en tant qu'appels sortants, c.-à-d. lorsqu'une méthode de service Web Apex appelle un service Web externe. • • • blob decimal enum Le langage Apex ne prend en charge aucun autre type, construction ou service WSDL, comprenant : • • • Les RPC/services encodés Les fichiers WSDL avec plusieurs portTypes, plusieurs services ou plusieurs liaisons Les fichiers WSDL qui importent des schémas externes. Par exemple, le fragment WSDL suivant importe un schéma externe, qui n'est pas prise en charge : <wsdl:types> <xsd:schema elementFormDefault="qualified" Invocation d'appels en utilisant le langage Apex salesforce | Fonctionnalités WSDL prises en charge | 312 targetNamespace="http://s3.amazonaws.com/doc/2006-03-01/"> <xsd:include schemaLocation="AmazonS3.xsd"/> </xsd:schema> </wsdl:types> Cependant, une importation dans le même schéma est prise en charge. Dans l'exemple suivant, le WSDL est collé dans le WSDL que vous convertissez : <wsdl:types> <xsd:schema xmlns:tns="http://s3.amazonaws.com/doc/2006-03-01/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://s3.amazonaws.com/doc/2006-03-01/"> <xsd:element name="CreateBucket"> <xsd:complexType> <xsd:sequence> [...] </xsd:schema> </wsdl:types> • • • Tout type de schéma non documenté dans le tableau précédent Les WSDL qui dépassent la taille limite, y compris les WSDL Salesforce Les WSDL qui n'utilisent pas un style de document littéral encapsulé. L'extrait de WSDL suivant n'utilise pas de style de document littéral encapsulé et génère une erreur « complexType introuvable » à l'importation. <wsdl:types> <xsd:schema targetNamespace="http://test.org/AccountPollInterface/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="SFDCPollAccountsResponse" type="tns:SFDCPollResponse"/> <xsd:simpleType name="SFDCPollResponse"> <xsd:restriction base="xsd:string" /> </xsd:simpleType> </xsd:schema> </wsdl:types> Invocation d'appels en utilisant le langage Apex salesforce | Compréhension du code généré | 313 Cette version modifiée encapsule l'élément simpleType en tant que complexType qui contient une séquence d'éléments. Elle suit le style de document littéral et est prise en charge. <wsdl:types> <xsd:schema targetNamespace="http://test.org/AccountPollInterface/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="SFDCPollAccountsResponse" type="tns:SFDCPollResponse" /> <xsd:complexType name="SFDCPollResponse"> <xsd:sequence> <xsd:element name="SFDCOutput" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:schema> </wsdl:types> Compréhension du code généré L'exemple suivant montre la création d'une classe Apex à partir de document WSDL. La classe Apex est automatiquement générée lorsque vous importez le WSDL. Le code suivant montre un exemple de document WSDL : <wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://doc.sample.com/docSample" targetNamespace="http://doc.sample.com/docSample" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <!-- Above, the schema targetNamespace maps to the Apex class name. --> <!-- Below, the type definitions for the parameters are listed. Each complexType and simpleType parameteris mapped to an Apex class inside the parent class for the WSDL. Then, each element in the complexType is mapped to a public field inside the class. --> <wsdl:types> Invocation d'appels en utilisant le langage Apex salesforce | Compréhension du code généré | 314 <s:schema elementFormDefault="qualified" targetNamespace="http://doc.sample.com/docSample"> <s:element name="EchoString"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="input" type="s:string" /> </s:sequence> </s:complexType> </s:element> <s:element name="EchoStringResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="EchoStringResult" type="s:string" /> </s:sequence> </s:complexType> </s:element> </s:schema> </wsdl:types> <!--The stub below defines operations. --> <wsdl:message name="EchoStringSoapIn"> <wsdl:part name="parameters" element="tns:EchoString" /> </wsdl:message> <wsdl:message name="EchoStringSoapOut"> <wsdl:part name="parameters" element="tns:EchoStringResponse" /> </wsdl:message> <wsdl:portType name="DocSamplePortType"> <wsdl:operation name="EchoString"> <wsdl:input message="tns:EchoStringSoapIn" /> <wsdl:output message="tns:EchoStringSoapOut" /> </wsdl:operation> </wsdl:portType> Invocation d'appels en utilisant le langage Apex salesforce | Compréhension du code généré | 315 <!--The code below defines how the types map to SOAP. --> <wsdl:binding name="DocSampleBinding" type="tns:DocSamplePortType"> <wsdl:operation name="EchoString"> <soap:operation soapAction="urn:dotnet.callouttest.soap.sforce.com/EchoString" style="document" /> <wsdl:input> <soap:body use="literal" /> </wsdl:input> <wsdl:output> <soap:body use="literal" /> </wsdl:output> </wsdl:operation> </wsdl:binding> <!-- Finally, the code below defines the endpoint, which maps to the endpoint in the class --> <wsdl:service name="DocSample"> <wsdl:port name="DocSamplePort" binding="tns:DocSampleBinding"> <soap:address location="http://YourServer/YourService" /> </wsdl:port> </wsdl:service> </wsdl:definitions> À partir de ce document WSDL, la classe Apex suivante est automatiquement générée. Le nom de classe docSample est spécifié lors de l'importation du WSDL. //Generated by wsdl2apex public class docSample { public class EchoStringResponse_element { Invocation d'appels en utilisant le langage Apex salesforce | Compréhension du code généré | 316 public String EchoStringResult; private String[] EchoStringResult_type_info = new String[]{ 'EchoStringResult', 'http://www.w3.org/2001/XMLSchema', 'string','0','1','false'}; private String[] apex_schema_type_info = new String[]{ 'http://doc.sample.com/docSample', 'true'}; private String[] field_order_type_info = new String[]{ 'EchoStringResult'}; } public class DocSamplePort { public String endpoint_x = 'http://YourServer/YourService'; private String[] ns_map_type_info = new String[]{ 'http://doc.sample.com/docSample', 'docSample'}; public String EchoString(String input) { docSample.EchoString_element request_x = new docSample.EchoString_element(); docSample.EchoStringResponse_element response_x; request_x.input = input; Map<String, docSample.EchoStringResponse_element> response_map_x = new Map<String, docSample.EchoStringResponse_element>(); response_map_x.put('response_x', response_x); WebServiceCallout.invoke( Invocation d'appels en utilisant le langage Apex salesforce | Compréhension du code généré | 317 this, request_x, response_map_x, new String[]{endpoint_x, 'urn:dotnet.callouttest.soap.sforce.com/EchoString', 'http://doc.sample.com/docSample', 'EchoString', 'http://doc.sample.com/docSample', 'EchoStringResponse', 'docSample.EchoStringResponse_element'} ); response_x = response_map_x.get('response_x'); return response_x.EchoStringResult; } } public class EchoString_element { public String input; private String[] input_type_info = new String[]{ 'input', 'http://www.w3.org/2001/XMLSchema', 'string','0','1','false'}; private String[] apex_schema_type_info = new String[]{ 'http://doc.sample.com/docSample', 'true'}; private String[] field_order_type_info = new String[]{'input'}; } } Notez les mappages suivants dans le document WSDL d'origine : • • • • L'espace de noms cible WSDL est mappé avec le nom de la classe Apex. Chaque type complexe devient une classe. Chaque élément dans le type est un champ public dans la classe. Le nom du port WSDL est mappé avec la classe stub. Chaque opération dans le WSDL est mappée avec une méthode publique. Invocation d'appels en utilisant le langage Apex salesforce | Test des appels de service Web | 318 La classe générée ci-dessus peut être utilisée pour invoquer des services Web externes. Le code suivant montre comment appeler la méthode echoString dans le serveur externe : docSample.DocSamplePort stub = new docSample.DocSamplePort(); String input = 'This is the input string'; String output = stub.EchoString(input); Test des appels de service Web Le code généré est enregistré en tant que classe Apex contenant les méthodes que vous pouvez invoquer pour appeler le service Web. Pour déployer ou encapsuler cette classe Apex et un autre code qui l'accompagne, 75 % du code doit avoir une couverture de test, y compris les méthodes dans la classe générée. Par défaut, les méthodes de test ne prennent pas en charge les appels de services Web, et les tests qui effectuent des appels de service Web sont ignorés. Pour empêcher les tests d'être ignorés et augmenter la couverture de code, le langage Apex fournit l'interface intégrée WebServiceMock et la méthode Test.setMock que vous pouvez utiliser pour recevoir des réponses fictives dans une méthode de test. Spécification d'une réponse fictive pour tester les appels de service Web Lorsque vous créez une classe Apex à partir d'un WSDL, les méthodes de la classe automatiquement générée appellent WebServiceCallout.invoke, qui effectue l'appel au service externe. Lors du test de ces méthodes, vous pouvez instruire l'exécution du code Apex pour générer une réponse fictive dès que WebServiceCallout.invoke est appelé. Pour cela, mettez en oeuvre l'interface WebServiceMock et spécifier une réponse fictive que l'exécution du code Apex doit envoyer. Les étapes sont présentées ci-dessous. Commencer par mettre en oeuvre l'interface WebServiceMock et spécifier la réponse fictive dans la méthode doInvoke. global class YourWebServiceMockImpl implements WebServiceMock { global void doInvoke( Object stub, Object request, Map<String, Object> response, String endpoint, String soapAction, String requestName, String responseNS, String responseName, String responseType) { // Create response element from the autogenerated class. // Populate response element. // Add response element to the response parameter, as follows: response.put('response_x', responseElement); Invocation d'appels en utilisant le langage Apex salesforce | Test des appels de service Web | 319 } } Remarque: La classe qui met en oeuvre l'interface WebServiceMock peut être globale ou publique. Vous pouvez annoter cette classe avec @isTest, car elle sera utilisée uniquement dans un contexte de test. Vous pouvez ainsi l'exclure de la taille limite de code de 3 Mo d'une organisation. • • Une fois les valeurs de la réponse fictive spécifiées, instruisez le code Apex d'envoyer cette réponse à l'exécution en appelant Test.setMock dans votre méthode de test. Passez WebServiceMock.class pour le premier argument, puis passez une nouvelle instance de votre mise en oeuvre d'interface de WebServiceMock pour le deuxième argument, comme suit : Test.setMock(WebServiceMock.class, new YourWebServiceMockImpl()); À ce stade, si un appel de service Web est invoqué dans un contexte de test, l'appel n'est pas effectué et vous recevez la réponse fictive spécifiée dans la mise en oeuvre de votre méthode doInvoke. Remarque: Si le code effectué par l'appel est inclus dans un package géré, vous devez appeler Test.setMock à partir d'une méthode de test dans le même package avec le même espace de noms pour feindre l'appel. Cet exemple complet montre comment tester un appel de service Web. La mise en oeuvre de l'interface WebServiceMock est répertoriée en premier. Cet exemple met en oeuvre la méthode doInvoke, qui renvoie la réponse que vous spécifiez. Dans ce cas, l'élément de réponse de la classe automatiquement généré est créé et une valeur lui est attribuée. Ensuite, le paramètre de réponse Map est renseigné avec la réponse fictive. Cet exemple est basé sur le WSDL répertorié dans Compréhension du code généré. Importez ce WSDL, puis générez une classe appelée docSample avant d'enregistrer cette classe. @isTest global class WebServiceMockImpl implements WebServiceMock { global void doInvoke( Object stub, Object request, Map<String, Object> response, String endpoint, String soapAction, String requestName, String responseNS, String responseName, String responseType) { docSample.EchoStringResponse_element respElement = new docSample.EchoStringResponse_element(); respElement.EchoStringResult = 'Mock response'; Invocation d'appels en utilisant le langage Apex salesforce | Test des appels de service Web | 320 response.put('response_x', respElement); } } Cette méthode passe un appel de service Web. public class WebSvcCallout { public static String callEchoString(String input) { docSample.DocSamplePort sample = new docSample.DocSamplePort(); sample.endpoint_x = 'http://api.salesforce.com/foo/bar'; // This invokes the EchoString method in the generated class String echo = sample.EchoString(input); return echo; } } Cette classe de test contient la méthode de test qui définit le mode d'appel fictif. Elle appelle la méthode callEchoString dans la classe précédente et vérifie qu'une réponse fictive est reçue. @isTest private class WebSvcCalloutTest { @isTest static void testEchoString() { // This causes a fake response to be generated Test.setMock(WebServiceMock.class, new WebServiceMockImpl()); // Call the method that invokes a callout String output = WebSvcCallout.callEchoString('Hello World!'); // Verify that a fake result is returned System.assertEquals('Mock response', output); } } Voir aussi : Interface WebServiceMock Invocation d'appels en utilisant le langage Apex salesforce | Considérations sur l'utilisation de WSDL | 321 Considérations sur l'utilisation de WSDL Notez les points suivants lors de la génération de classes Apex à partir d'un WSDL. Mappage d'en-têtes Les en-têtes définis dans un document WSDL deviennent des champs publics dans le stub de la classe générée. Ce fonctionnement est similaire à AJAX Toolkit et à .NET. Compréhension des événements à l'exécution Les vérifications suivantes sont effectuées lorsque le code Apex passe un appel à un service externe. • • • • • Pour plus d'informations sur les limites d'expiration lors d'une requête HTTP ou d'un appel à des services Web, reportez-vous à Limitations des appels à la page 325. Les références circulaires dans les classes Apex ne sont pas autorisées. Plusieurs connexions de rebouclage à des domaines Salesforce sont autorisées. Pour autoriser l'accès à un point de terminaison, il doit être enregistré dans Votre nom > Configuration > Sécurité > Paramètre du site distant. Pour empêcher les connexions de base de données d'être attaquées, aucune transaction ne peut être ouverte. Compréhension des caractères non pris en charge dans les noms de variables Un fichier WSDL peut inclure un nom d'élément qui n'est pas autorisé dans un nom de variable Apex. Les règles suivantes s'appliquent lors de la génération de noms de variables Apex à partir d'un fichier WSDL : • • • • • Si le premier caractère d'un nom de variable n'est pas alphabétique, le préfixe x est ajouté au nom de la variable Apex générée. Si le dernier caractère d'un nom de variable n'est pas autorisé dans un nom de variable Apex, le suffixe x est ajouté au nom de la variable Apex générée. Si un nom d'élément contient un caractère non autorisé dans un nom de variable Apex, le caractère est remplacé par un trait de soulignement (_). Si un nom d'élément contient deux caractères successifs non autorisés dans un nom de variable Apex, le premier caractère est remplacé par un trait de soulignement (_) et le deuxième par un caractère x. Cela évite de générer un nom de variable avec deux traits de soulignements successifs, ce qui est interdit dans Apex. Supposons que vous avez une opération contenant deux paramètres, a_ et a_x. Le code Apex généré contient deux variables, les deux nommées a_x. La classe n'est pas compilée. Vous devez modifier le code Apex manuellement et changer l'un des noms de variable. Débogage de classes générées à partir de fichiers WSDL Salesforce teste le code avec l'API SOAP, .NET et Axis. Si vous utilisez d'autres outils, vous risquez de rencontrer des problèmes. Vous pouvez utiliser l'en-tête de débogage pour renvoyer le fichier XML dans les messages de demande et de réponse SOAP afin de faciliter le diagnostic des problèmes. Pour plus d'informations, reportez-vous à API SOAP et en-têtes SOAP pour Apex à la page 788. Invocation d'appels en utilisant le langage Apex salesforce | Invocation d'appels HTTP | 322 Invocation d'appels HTTP Le langage Apex fournit plusieurs classes intégrées pour travailler avec des services HTTP et créer des requêtes HTTP telles que GET, POST, PUT et DELETE. Vous pouvez utiliser ces classes HTTP pour l'intégration à des services basés sur REST. Elles permettent également d'intégrer des services Web basés sur SOAP en tant qu'option alternative pour générer un code Apex à partir d'un WSDL. En utilisant les classes HTTP au lieu de démarrer un WSDL, vous prenez plus de responsabilité dans la gestion de la construction du message SOAP pour la demande et la réponse. Pour plus d'informations et d'exemples, reportez-vous à Classes de services HTTP (RESTful). En outre, les outils Force.com Toolkit for Google Data APIs utilise abondamment les appels HTTP. Utilisation de certificats Vous pouvez utiliser une authentification SSL bidirectionnelle en envoyant un certificat généré dans Salesforce ou signé par une autorité de certification (CA) avec votre appel. Cela renforce la sécurité, car la cible de l'appel reçoit le certificat et peut l'utiliser pour authentifier la demande avec son magasin de clés. Pour activer l'authentification SSL bidirectionnelle pour un appel : 1. Générez un certificat. 2. Intégrez le certificat à votre code. Reportez-vous à Utilisation de certificats avec des services SOAP et à Utilisation de certificats avec des requêtes HTTP. 3. Si vous vous connectez à un tiers et que vous utilisez un certificat auto-signé, partagez le certificat Salesforce avec ce tiers pour lui permettre de l'ajouter à son magasin de clés. Si vous vous connectez à une autre application utilisée au sein de votre organisation, configurez votre serveur Web ou d'applications pour demander un certificat client. Ce processus dépend du type de serveur Web ou d'applications que vous utilisez. Pour consulter un exemple de configuration d'une authentification SSL bidirectionnelle avec Apache Tomcat, reportez-vous à wiki.developerforce.com/index.php/Making_Authenticated_Web_Service_Callouts_Using_Two-Way_SSL. 4. Configurez les paramètres de site distant pour l'appel. Pour qu'un appel Apex puisse appeler un site externe, ce site doit être enregistré dans la page Paramètres de site distant, sinon l'appel échoue. Génération de certificats Vous pouvez utiliser un certificat auto-signé généré dans Salesforce ou un certificat signé par une autorité de certification (CA). Pour générer un certificat pour un appel : 1. Accédez à Votre nom > Configuration > Contrôles de sécurité > Gestion des certificats et des clés. 2. Sélectionnez Créer un certificat auto-signé ou Créer un certificat signé par AC, en fonction du type de certificat accepté par votre site Web externe. Une fois le certificat créé, vous ne pouvez pas modifier son type. 3. Saisissez une étiquette explicite pour le certificat Salesforce. Ce nom est essentiellement utilisé par les administrateurs pour visualiser les certificats. 4. Saisissez le Nom unique. Ce nom est automatiquement renseigné en fonction de l'étiquette de certificat saisie. Ce nom peut contenir uniquement des traits de soulignement et des caractères alphanumériques, et doit être unique dans votre organisation. Il doit commencer par une lettre, ne pas inclure d’espace, ne pas se terminer pas un trait de soulignement et Invocation d'appels en utilisant le langage Apex salesforce | Utilisation de certificats avec des services SOAP | 323 ne pas contenir deux traits de soulignement consécutifs. Utilisez le Nom unique pour référencer le certificat en utilisant l'API des services Web Force.com ou Apex. 5. Sélectionnez une Taille de clé pour votre certificat et vos clés générés. Nous recommandons d'utiliser la clé par défaut 2048 pour des raisons de sécurité. La sélection de 2048 génère un certificat utilisant des clés 2048 bits, qui est valide pendant deux ans. La sélection de 1024 génère un certificat utilisant des clés 1024 bits, qui est valide pendant un an. Remarque: Une fois un certificat Salesforce enregistré, vous ne pouvez pas modifier la taille des clés. 6. Si vous créez un certificat signé par AC, vous devez également saisir les informations ci-dessous. Ces champs sont associés pour générer un certificat unique. Champ Description Nom usuel Nom de domaine complet de la société qui demande le certificat signé. Il se présente généralement sous la forme : http://www.masociete.com. Adresse e-mail Adresse e-mail associée à ce certificat. Société Nom légal de votre société ou votre nom. Département Service de votre société qui utilise le certificat, tel que le marketing ou la comptabilité. Ville Ville dans laquelle la société est implantée. Région/Province Région dans laquelle la société est implantée. Code de pays Code à deux lettres qui indique le pays dans lequel la société est implantée. Pour la France, la valeur est FR. 7. Cliquez sur Enregistrer. Une fois enregistré avec succès, un certificat Salesforce ainsi que les clés correspondantes sont automatiquement générés. Après avoir créé un certificat signé par AC, vous devez le charger avant de pouvoir l'utiliser. Reportez-vous à « Chargement de certificats signés par une autorité de certification (CA) » dans l'aide en ligne de Salesforce. Utilisation de certificats avec des services SOAP Après avoir généré un certificat dans Salesforce, vous pouvez l'utiliser afin de prendre en charge une authentification bidirectionnelle pour un appel à un service Web SOAP. Pour intégrer le certificat à votre code Apex : 1. Recevez le WSDL pour le service Web d'un tiers ou générez-le à partir de l'application à laquelle vous souhaitez vous connecter. 2. Générez des classes Apex à partir du WSDL pour le service Web. Reportez-vous à Services SOAP : Définition d'une classe à partir d'un document WSDL. 3. La classe Apex générée inclut une classe stub pour appeler le service Web tiers représenté par le document WSDL. Modifiez les classes Apex, puis attribuez une valeur à une variable clientCertName_x dans une instance de la classe stub. La valeur Invocation d'appels en utilisant le langage Apex salesforce | Utilisation de certificats avec des requêtes HTTP | 324 doit correspondre au Nom unique du certificat que vous générez dans Votre nom > Configuration > Contrôles de sécurité > Gestion des certificats et des clés. L'exemple ci-dessous illustre la dernière étape de la procédure précédente et fonctionne avec l'exemple de fichier WSDL de la section Compréhension du code généré. Il suppose que vous avez déjà généré un certificat portant un Nom unique de DocSampleCert. docSample.DocSamplePort stub = new docSample.DocSamplePort(); stub.clientCertName_x = 'DocSampleCert'; String input = 'This is the input string'; String output = stub.EchoString(input); Il existe un processus hérité permettant d'utiliser un certificat provenant d'un tiers pour votre organisation. Encodez votre clé de certificat client en base64, puis attribuez-la à la variable clientCert_x de la classe stub. Par nature, ce processus est moins sûr que l'utilisation d'un certificat Salesforce, car il ne suit pas les meilleures pratiques de sécurité relatives à la protection des clés privées. Lorsque vous utilisez un certificat Salesforce, la clé privée n'est pas partagée hors de Salesforce. Remarque: N'utilisez pas un certificat généré dans Votre nom > Configuration > Développer > API > Générer Certificat client. Vous devez utiliser un certificat émis par un tiers pour votre organisation si vous suivez le processus hérité. L'exemple ci-dessous illustre le processus hérité et fonctionne avec l'exemple de fichier WSDL de la section Compréhension du code généré à la page 313. docSample.DocSamplePort stub = new docSample.DocSamplePort(); stub.clientCert_x = 'MIIGlgIBAzCCBlAGCSqGSIb3DQEHAaCCBkEEggY9MIIGOTCCAe4GCSqGSIb3DQEHAaCCAd8EggHb'+ 'MIIB1zCCAdMGCyqGSIb3DQEMCgECoIIBgjCCAX4wKAYKKoZIhvcNAQwBAzAaBBSaUMlXnxjzpfdu'+ '6YFwZgJFMklDWFyvCnQeuZpN2E+Rb4rf9MkJ6FsmPDA9MCEwCQYFKw4DAhoFAAQU4ZKBfaXcN45w'+ '9hYm215CcA4n4d0EFJL8jr68wwKwFsVckbjyBz/zYHO6AgIEAA=='; // Password for the keystore stub.clientCertPasswd_x = 'passwd'; String input = 'This is the input string'; String output = stub.EchoString(input); Utilisation de certificats avec des requêtes HTTP Après avoir généré un certificat dans Salesforce, vous pouvez l'utiliser afin de prendre en charge une authentification bidirectionnelle pour un appel à une requête HTTP. Invocation d'appels en utilisant le langage Apex salesforce | Limitations des appels | 325 Pour intégrer le certificat à votre code Apex : 1. Générez un certificat. Notez le Nom unique du certificat. 2. Dans votre code Apex, utilisez la méthode setClientCertificateName de la classe HttpRequest. La valeur utilisée pour l'argument de cette méthode doit correspondre au Nom unique du certificat que vous avez généré à l'étape précédente. L'exemple ci-dessous illustre la dernière étape de la procédure précédente. Il suppose que vous avez déjà généré un certificat portant un Nom unique de DocSampleCert. HttpRequest req = new HttpRequest(); req.setClientCertificateName('DocSampleCert'); Limitations des appels Les limitations suivantes s'appliquent lorsque le code Apex effectue un appel à une requête HTTP ou à des services Web. L'appel de services Web peut être un appel d'API SOAP ou n'importe quel appel de service Web externe. • • • • Une transaction Apex unique peut effectuer jusqu'à 10 appels à une requête HTTP ou un appel d'API. L'expiration par défaut est de 10 secondes. Une expiration personnalisée peut être définie pour chaque appel. La valeur minimale est 1 milliseconde et la valeur maximale est 120 secondes. Pour définir une expiration personnalisée pour des appels de services Web ou HTTP, reportez-vous à l'exemple ci-dessous. L'expiration cumulée maximale pour des appels effectués par une transaction Apex unique est de 120 secondes. Cette valeur s'ajoute à tous les appels invoqués par la transaction Apex. Vous ne pouvez pas effectuer un appel lorsque des opérations sont en attente dans la même transaction. Les éléments qui génèrent des opérations en attente sont des instructions DML, un code Apex asynchrone (tel que des méthodes futures et des tâches Apex par lot), un code Apex planifié ou l'envoi d'un e-mail. Vous pouvez effectuer des appels avant d'exécuter ces types d'opération. Définition d'expirations d'appels L'exemple suivant définit une expiration personnalisée pour des appels de service Web. Il fonctionne avec l'exemple de fichier WSDL et la classe DocSamplePort générée présentés dans Compréhension du code généré à la page 313. Définissez la valeur d'expiration en millisecondes en attribuant une valeur à la variable timeout_x de la classe stub. docSample.DocSamplePort stub = new docSample.DocSamplePort(); stub.timeout_x = 2000; // timeout in milliseconds L'exemple suivant définit une expiration personnalisée pour des appels HTTP : HttpRequest req = new HttpRequest(); req.setTimeout(2000); // timeout in milliseconds Chapitre 13 Référence Sujets : La référence Apex présente des informations sur le langage Apex. • • • • • Opérations DML (langage de manipulation de données) Apex Méthodes et classes standard Apex Classes Apex Interfaces Apex • • • Opérations DML (langage de manipulation de données) : utilisées pour manipuler des données dans la base de données SiClasses et méthodes standard : disponibles pour des types de données primitifs, des collections, des sObjects et d'autres parties du code Apex Classes Apex : classes prédéfinies qui peuvent être utilisées Interfaces Apex : interfaces que vous pouvez mettre en oeuvre De plus, les méthodes et les objets API SOAP sont disponibles pour le langage Apex. Reportez-vous à API SOAP et en-têtes SOAP pour Apex à la page 788 en annexe. Référence salesforce | Opérations DML (langage de manipulation de données) Apex | 327 Opérations DML (langage de manipulation de données) Apex Les opérations DML (langage de manipulation de données) permettent d'insérer, de mettre à jour, de supprimer et de restaurer des données dans une base de données. Vous pouvez exécuter des opérations à l'aide de deux formats différents : Des instructions DML Apex, telles que : • insert SObject[] Des méthodes de base de données DML Apex, telles que : • Database.SaveResult[] result = Database.Insert(SObject[]) La plupart des opérations DML sont disponibles dans les deux formats, mais certaines existent sous un seul format. Les deux formats d'opération DML permettent de traiter différents types d'exception : Utilisez des instructions DML pour renvoyer les erreurs qui se produisent durant un traitement DML en masse en tant qu'exceptions Apex qui suspendent immédiatement le flux de contrôle (en utilisant des blocs try. . .catch). Ce comportement est semblable au traitement des exceptions dans la plupart des langages de procédure de base de données. Utilisez des méthodes de base de données DML pour autoriser le succès partiel d'une opération DML en masse. Si un enregistrement échoue, le reste de l'opération DML peut réussir. Votre application peut ensuite inspecter les éléments refusés et recommencer l'opération. Ce format permet d'écrire un code qui ne renvoie jamais d'erreur d'exception DML. À la place, votre code peut utiliser le tableau de résultats approprié pour déterminer un succès ou un échec. Notez que les méthodes de base de données DML incluent également une syntaxe qui prend en charge les exceptions renvoyées, de la même façon que les instructions DML. • • Les opérations DML Apex suivantes sont disponibles : convertLead1 • • • • • • • delete insert merge2 undelete update upsert Contexte système et règles de partage La plupart des opérations DML sont exécutées dans un contexte système, en ignorant les autorisations de l'utilisateur actif, la sécurité au niveau du champ, les paramètres par défaut de l'organisation, la position dans la hiérarchie des rôles et les règles de partage. Cependant lorsqu'une opération DML est appelée dans une classe définie avec les mots clés with sharing, les règles de partage de l'utilisateur actif sont prises en compte. Pour plus d'informations, reportez-vous à Utilisation des mots clés with sharing ou without sharing à la page 156. 1 2 convertLead est disponible uniquement en tant que méthode de base de données. merge est disponible en tant qu'instruction DML Apex. Référence salesforce | Opération ConvertLead | 328 Troncature du champ String et version API Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Opération ConvertLead L'opération DML convertLead convertit une piste en compte et en contact, et (facultatif) en opportunité. Remarque: convertLead est disponible uniquement en tant que méthode de base de données. Syntaxe de la méthode de base de données • • LeadConvertResult Database.convertLead(LeadConvert leadToConvert, Boolean opt_allOrNone) LeadConvertResult[] Database.convertLead(LeadConvert[] leadsToConvert, Boolean opt_allOrNone) Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Règles et consignes Lors de la conversion de pistes, tenez compte des règles et des consignes suivantes : • Mappages de champs : le système mappe automatiquement les champs de piste standard avec des champs de compte, de contact et d'opportunité standard. Votre administrateur Salesforce peut spécifier le mappage des champs de pistes personnalisés avec des champs de compte, de contact et d'opportunité personnalisés. Pour plus d'informations sur les mappages, reportez-vous à l'aide en ligne de Salesforce. Si vous n'avez pas activé la validation et les déclencheurs à partir de la piste convertie, la conversion de piste échoue si les données d'un champ de piste personnalisé ne sont pas prises en charge dans le champ de contact personnalisé correspondant. Par exemple, si un champ de piste personnalisé est mappé avec un champ de liste de sélection de contact, la valeur de la piste doit être explicitement définie en tant que valeur de liste de sélection de contact. Sinon, la piste n'est pas convertie. Pour résoudre cette erreur, assurez-vous que les valeurs du champ sont déjà définies. Pour éviter cette erreur, nous recommandons d'activer la validation et les déclencheurs à partir de la piste convertie dans les paramètres de piste de votre organisation. • • • • Champs de fusion : si des données sont fusionnées dans des objets de compte et de contact existants, seuls les champs vides de l'objet cible sont remplacés, les données existantes (y compris les ID) ne sont pas remplacées. Il existe une exception si vous définissez setOverwriteLeadSource dans l'objet LeadConvert sur true. Dans ce cas, le champ LeadSource de l'objet de contact cible est remplacé par le contenu du champ LeadSource dans l'objet LeadConvert source. Types d'enregistrement : si l'organisation utilise des types d'enregistrement, le type d'enregistrement par défaut du nouveau propriétaire est attribué aux enregistrements créés pendant la conversion de pistes. Le type d'enregistrement par défaut de l'utilisateur qui convertit la piste détermine les valeurs sources de la piste disponibles durant la conversion. Si les valeurs sources de la piste souhaitées ne sont pas disponibles, ajoutez les valeurs au type d'enregistrement par défaut de l'utilisateur qui convertit la piste. Pour plus d'informations sur les types d'enregistrement, reportez-vous à l'aide en ligne de Salesforce. Valeurs de liste de sélection : le système associe des valeurs de liste de sélection par défaut au compte, au contact et à l'opportunité lors du mappage de n'importe quel champ vide dans une liste de sélection de piste standard. Si votre organisation utilise des types d'enregistrement, les valeurs vides sont remplacées par les valeurs de liste de sélection par défaut du nouveau propriétaire de l'enregistrement. Abonnements automatiques à un fil : lors de la conversion d'une liste en nouveau compte, contact et opportunité, le propriétaire de la piste est désabonné du compte de la piste. Le propriétaire de la piste, le propriétaire des enregistrements générés et les utilisateurs désabonnés de la piste ne sont pas automatiquement abonnés aux enregistrements générés, sauf Référence salesforce | Opération ConvertLead | 329 si les abonnements automatiques sont activés dans leurs paramètres de fil Chatter. Ils doivent avoir les abonnements automatiques activés pour pouvoir afficher les modifications des enregistrements de compte, de contact et d'opportunité dans leur fil d'actualité. Les utilisateurs doivent activer les abonnements automatiques en sélectionnant la case Suivre automatiquement les enregistrements que je crée dans Votre nom > Configuration > Mes paramètres Chatter > Mes fils pour s'abonner aux enregistrements qu'ils créent. Un utilisateur peut s'abonner à un enregistrement pour pouvoir consulter les modifications apportées à un enregistrement dans le fil d'actualité de sa page d'accueil. Il peut ainsi rester informé des modifications apportées aux enregistrements dans Salesforce. Étapes de base de conversion des pistes La conversion des pistes inclut les étapes de base suivantes : 1. Votre application détermine si les ID de la ou des pistes doivent être convertis. 2. En option, votre application peut déterminer l'ID du ou des comptes dans lesquels la piste doit être fusionnée. Votre application peut utiliser une requête SOQL pour rechercher les comptes qui correspondent au nom de la piste, comme dans l'exemple suivant : SELECT Id, Name FROM Account WHERE Name='CompanyNameOfLeadBeingMerged' 3. En option, votre application peut déterminer l'ID du ou des contacts dans lesquels la piste doit être fusionnée. Votre application peut utiliser une requête SOQL pour rechercher les contacts qui correspondent au nom de contact de la piste, comme dans l'exemple suivant : SELECT Id, Name FROM Contact WHERE FirstName='FirstName' AND LastName='LastName' AND AccountId = '001...' 4. En option, l'application détermine si des opportunités doivent être créées à partir de pistes. 5. L'application interroge le tableau LeadSource pour obtenir toutes les options possibles de statut converti (SELECT ... FROM LeadStatus WHERE IsConverted='1'), puis sélectionne la valeur pour le statut converti. 6. L'application appelle convertLead. 7. L'application itère le ou les résultats renvoyés et examine chaque objet LeadConvertResult afin de déterminer si la conversion a réussi pour chaque piste. 8. En option, lors de la conversion de pistes appartenant à une file d'attente, le propriétaire doit être spécifié, car les comptes et les contacts ne peuvent pas appartenir à une file d'attente. Même si vous spécifiez un compte ou un contact existant, vous devez préciser le propriétaire. Méthodes d'objet LeadConvert La méthode de base de données convertLead accepte jusqu'à 100 objets LeadConvert. Un objet LeadConvert prend en charge les méthodes suivantes : Nom Arguments Type de renvoi Description getAccountId ID Obtient l'ID du compte dans lequel la piste va être fusionnée. getContactId ID Obtient l'ID du contact dans lequel la piste va être fusionnée. getConvertedStatus String Obtient la valeur de statut d'une piste convertie. getLeadID ID Obtient l'ID de la piste à convertir. getOpportunityName String Obtient le nom de l'opportunité à créer. Référence Nom salesforce | Opération ConvertLead | 330 Arguments Type de renvoi Description getOwnerID ID Obtient l'ID de la personne qui doit être propriétaire de tout nouveau compte, contact ou opportunité créé. isDoNotCreateOpportunity Boolean Indique si une opportunité est créée durant la conversion de piste (false, par défaut) ou (true). isOverWriteLeadSource Boolean Indique si le champ LeadSource dans l'objet Contact cible est remplacé par le contenu du champ LeadSource dans l'objet Piste source (true) ou (false, par défaut). isSendNotificationEmail Boolean Indique si un e-mail de notification est envoyé au propriétaire spécifié par setOwnerId (true) ou (false, par défaut). setAccountId ID ID Void Définit l'ID du compte dans lequel la piste va être fusionnée. Cette valeur est requise uniquement lors de la mise à jour d'un compte existant, y compris les comptes personnels. Sinon, si setAccountID est spécifié, un compte est créé. setContactId ID ID Void Définit l'ID du contact dans lequel la piste est fusionnée (ce contact doit être associé au compte spécifié avec setAccountId, et setAccountId doit être spécifié). Cette valeur est requise uniquement lors de la mise à jour d'un contact existant. Important: Si vous convertissez une piste en compte personnel, ne spécifiez pas setContactId, qui entraînerait une erreur. Spécifiez uniquement la méthode setAccountId du compte personnel. Si setContactID est spécifié, l'application crée un contact qui est implicitement associé au compte. Le nom du contact et les autres données existantes ne sont pas remplacées (sauf si setOverwriteLeadSource est défini sur true, auquel cas seul le champ LeadSource est remplacé). setConvertedStatus String Status Void setDoNotCreateOpportunity Boolean Void CreateOpportunity setLeadId ID ID Void setOpportunityName String OppName Void Définit la valeur de statut d'une piste convertie. Ce champ est obligatoire. Spécifie la création ou non d'une opportunité durant la conversion de piste. La valeur par défaut est false : des opportunités sont créées par défaut. Définissez cet indicateur sur true uniquement si vous ne souhaitez pas créer une opportunité à partir de la piste. Définit l'ID de la piste à convertir. Ce champ est obligatoire. Définit le nom de l'opportunité à créer. Si aucun nom n'est spécifié, cette valeur applique par défaut le nom de société de la piste. La longueur maximale de ce champ est de 80 caractères. Si setDoNotCreateOpportunity est true, aucune Opportunité n'est créée et ce champ doit rester vide, sinon une erreur est renvoyée. Référence Nom salesforce | Opération ConvertLead | 331 Arguments Type de renvoi setOverwriteLeadSource Boolean Void OverwriteLeadSource setOwnerId ID ID setSendNotificationEmail Boolean SendEmail Description Spécifie le remplacement ou non du champ LeadSource de l'objet de contact cible par le contenu du champ LeadSource de l'objet de piste source. La valeur par défaut est false, qui indique de ne pas remplacer le champ. Si vous le définissez sur true, vous devez également spécifier setContactId pour le contact cible. Void Spécifie l'ID de la personne qui doit être propriétaire de tout nouveau compte, contact ou opportunité créé. Si l'application ne spécifie pas cette valeur, le propriétaire du nouvel objet est le propriétaire de la piste. Cette méthode ne s'applique pas lors d'une fusion avec des objets existants : si setOwnerId est spécifié, le champ ownerId n'est pas remplacé dans un compte ou contact existant. Void Spécifie l'envoi ou non d'un e-mail de notification au propriétaire désigné par setOwnerId. La valeur par défaut est false, qui indique de ne pas envoyer d'e-mail. Objet LeadConvertResult Un tableau d'objets LeadConvertResult est renvoyé avec la méthode de base de données convertLead. Chaque élément du tableau LeadConvertResult correspond au tableau SObject transmis en tant que paramètre SObject[] dans la méthode de base de données convertLead, c.-à-d. que le premier élément du tableau LeadConvertResult correspond au premier élément transmis dans le tableau SObject, le deuxième élément correspond deuxième élément, etc. Si un seul SObject est transmis, le tableau LeadConvertResults contient un seul élément. Un objet LeadConvertResult contient les méthodes suivantes : Nom Type Description getAccountId ID L'ID du nouveau compte (si un nouveau compte a été spécifié) ou l'ID du compte spécifié lors de l'invocation de convertLead. getContactId ID L'ID du nouveau contact (si un nouveau contact a été spécifié) ou l'ID du contact spécifié lors de l'invocation de convertLead. getErrors Database.Error Si une erreur s'est produite, un tableau d'un ou de plusieurs []Database.Error [] objets d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. getLeadId ID L'ID de la piste convertie. getOpportunityId ID L'ID de la nouvelle opportunité, si elle a été créée lors de l'invocation de convertLead. isSuccess Boolean Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Référence salesforce | Opération Delete | 332 Exemple de méthode de base de données Lead myLead = new Lead(LastName = 'Fry', Company='Fry And Sons'); insert myLead; Database.LeadConvert lc = new database.LeadConvert(); lc.setLeadId(myLead.id); LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1]; lc.setConvertedStatus(convertStatus.MasterLabel); Database.LeadConvertResult lcr = Database.convertLead(lc); System.assert(lcr.isSuccess()); Opération Delete L'opération DML delete supprime un ou plusieurs enregistrements sObject existants, tels que des comptes ou des contacts individuels, des données de votre organisation. delete est semblable à l'instruction delete() dans l'API SOAP. Syntaxe de l'instruction DML delete sObject | Record.ID Syntaxe de la méthode de base de données • • DeleteResult Database.Delete((sObject recordToDelete | RecordID ID), Boolean opt_allOrNone) DeleteResult[] Database.Delete((sObject[] recordsToDelete | RecordIDs LIST<>IDs{}), Boolean opt_allOrNone) Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Règles et consignes Lors de la suppression d'enregistrements sObject, tenez compte des règles et des consignes suivantes : • Pour assurer l'intégrité de référence, delete prend en charge les suppressions en cascade. Si vous supprimez un objet parent, vous supprimez automatiquement ses enfants, si chaque enregistrement enfant peut être supprimé. Par exemple, si vous supprimez un enregistrement de requête, Apex supprime automatiquement tout enregistrement CaseComment, CaseHistory et CaseSolution associé à cette requête. Cependant, si un enregistrement enfant particulier ne peut pas être supprimé ou est actuellement utilisé, l'opération delete sur l'enregistrement de requête parent échoue. • • Certains sObjects ne peuvent pas être supprimés. Pour supprimer un enregistrement sObject, sa propriété deletable doit être définie sur true. Reportez-vous également à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Vous pouvez passer jusqu'à 10 000 enregistrements sObject à une méthode delete unique. Référence salesforce | Opération Delete | 333 Objet DeleteResult Un tableau d'objets Database.DeleteResult est renvoyé avec la méthode de base de données delete. Chaque élément du tableau DeleteResult correspond au tableau sObject transmis en tant que paramètre sObject[] dans la méthode de base de données delete, c.-à-d. que le premier élément du tableau DeleteResult correspond au premier élément transmis dans le tableau sObject, le deuxième élément correspond deuxième élément, etc. Si un seul sObject est transmis, le tableau DeleteResults contient un seul élément. Un objet Database.DeleteResult contient les méthodes suivantes : Nom Type Description getErrors Database.Error Si une erreur s'est produite, un tableau d'un ou de plusieurs objets [] d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. getId ID L'ID du sObject que vous avez essayé de supprimer. Si ce champ contient une valeur, l'objet a été supprimé avec succès. Si ce champ est vide, l'opération n'a pas réussi pour cet objet. isSuccess Boolean Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Exemple d'instruction DML L'exemple suivant supprime tous les comptes nommés 'DotCom' : Account[] doomedAccts = [SELECT Id, Name FROM Account WHERE Name = 'DotCom']; try { delete doomedAccts; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Exemple de méthode de base de données L'exemple suivant supprime un compte nommé 'DotCom' : Account[] doomedAccts = [SELECT Id, Name FROM Account WHERE Name = 'DotCom']; Database.DeleteResult[] DR_Dels = Database.delete(doomedAccts); Référence salesforce | Opération Insert | 334 Opération Insert L'opération DML insert ajoute un ou plusieurs sObjects, tels que des comptes ou des contacts, aux données de votre organisation. insert est semblable à l'instruction INSERT dans SQL. Syntaxe de l'instruction DML insert sObject insert sObject[] Syntaxe de la méthode de base de données • • SaveResult Database.insert(sObject recordToInsert, Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions) SaveResult[] Database.insert(sObject[] recordsToInsert, Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions) Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Par exemple : Database.SaveResult[] MySaveResult = Database.Insert(MyAccounts, false); Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Par exemple : //AssignmentRuleHeader //UseDefaultRule Database.DMLOptions dmo = new database.DMLOptions(); dmo.AssignmentRuleHeader.UseDefaultRule= true; Lead l = new Lead(Company='ABC', LastName='Smith'); l.setOptions(dmo); insert l; Pour plus d'informations, reportez-vous à Propriétés d'options DML de base de données à la page 485. Règles et consignes Lors de l'insertion d'enregistrements sObject, tenez compte des règles et des consignes suivantes : • Certains sObjects ne peuvent pas être créés. Pour créer un enregistrement sObject, sa propriété createable doit être définie sur true. Référence • • • • • • • salesforce | Opération Insert | 335 Vous devez saisir une valeur non null dans tous les champs obligatoires. Vous pouvez passer jusqu'à 10 000 enregistrements sObject à une méthode insert unique. L'instruction insert définit automatiquement la valeur ID de tous les nouveaux enregistrements sObject. L'insertion d'un enregistrement qui inclut déjà un ID (existe déjà dans les données de votre organisation) génère une erreur. Pour plus d'informations, reportez-vous à Listes. Pour certains sObjects qui incluent des champs avec des contraintes uniques, l'insertion d'enregistrements sObject dupliqués génère une erreur. Par exemple, l'insertion de sObjects CollaborationGroup portant le même nom entraîne une erreur, car les enregistrements CollaborationGroup doivent avoir des noms uniques. L'instruction insert peut définir uniquement l'ID de clé étrangère d'enregistrements sObject. Les champs des enregistrements associés ne peuvent pas être mis à jour avec insert. Par exemple, lors de l'insertion d'un nouveau contact, vous pouvez spécifier l'enregistrement du compte associé du contact en définissant la valeur du champ AccountId. Cependant, vous ne pouvez pas modifier le nom du compte sans mettre à jour le compte lui-même dans un appel DML séparé. L'instruction insert n'est pas prise en charge avec certains sObjects. Reportez-vous à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Cette opération examine chaque lot d'enregistrements pour vérifier s'ils contiennent des valeurs ID dupliquées. Si des doublons sont détectés, les cinq premiers sont traités. À partir du sixième ID dupliqué, l'objet SaveResult de ces entrées est marqué avec une erreur similaire à la suivante : Maximum number of duplicate updates in one batch (5 allowed). Attempt to update Id more than once in this API call: number_of_attempts. Objet SaveResult Un tableau d'objets SaveResult est renvoyé avec les méthodes de base de données insert et update. Chaque élément du tableau SaveResult correspond au tableau sObject transmis en tant que paramètre sObject[] dans la méthode de base de données, c.-à-d. que le premier élément du tableau SaveResult correspond au premier élément transmis dans le tableau sObject, le deuxième élément correspond deuxième élément, etc. Si un seul sObject est transmis, le tableau SaveResults contient un seul élément. Un objet SaveResult contient les méthodes suivantes : Nom Type Description getErrors Database.Error [] Si une erreur s'est produite, un tableau d'un ou de plusieurs objets d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. getId ID L'ID du sObject que vous avez essayé d'insérer ou de mettre à jour. Si ce champ contient une valeur, l'objet a été inséré ou mis à jour avec succès. Si ce champ est vide, l'opération n'a pas réussi pour cet objet. isSuccess Boolean Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Référence salesforce | Opération Insert | 336 Exemple d'instruction DML L'exemple suivant insère un compte nommé 'Acme' : Account newAcct = new Account(name = 'Acme'); try { insert newAcct; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Exemple de méthode de base de données L'exemple suivant insère un compte nommé 'Acme' : Account a = new Account(name = 'Acme'); Database.SaveResult[] lsr = Database.insert(new Account[]{a, new Account(Name = 'Acme')}, false); // Iterate through the Save Results for(Database.SaveResult sr:lsr){ if(!sr.isSuccess()) Database.Error err = sr.getErrors()[0]; } Création d'enregistrements parents et enfants dans une instruction unique en utilisant des clés étrangères Vous pouvez utiliser des champs ID externes en tant que clés étrangères afin de créer en seule une étape des enregistrements parents et enfants pour différents types de sObject, au lieu de créer le parent, de demander ensuite son ID, puis de créer l'enregistrement enfant. Pour cela : • • • • • Créez le sObject enfant, puis renseignez ses champs obligatoires et les champs facultatifs de votre choix. Créez le sObject parent de référence utilisé uniquement pour définir la référence de clé étrangère du parent dans le sObject enfant. Seul le champ ID externe est défini pour cet sObject (aucun autre champ). Définissez le champ de clé étrangère du sObject enfant sur le sObject parent de référence que vous venez de créer. Créez un autre sObject parent à transmettre à l'instruction insert. Les champs obligatoires de cet sObject doivent être définis (et éventuellement d'autres champs facultatifs) en plus du champ ID externe. Appelez insert en lui passant un tableau de sObjects à créer. Le sObject parent doit précéder le sObject enfant dans le tableau, c.-à-d. que l'index de tableau du parent doit être inférieur à l'index de l'enfant. Référence salesforce | Opération Insert | 337 Les enregistrements parent et ses enfants sont associés via une relation prédéfinie telle qu'une relation principal-détails ou une relation de référence. Vous pouvez créer jusqu'à 10 niveaux d'enregistrements associés. De plus, les enregistrements associés créés dans un même appel doivent avoir des types Object différents. Pour plus d'informations, reportez-vous à Creating Records for Different Object Types dans le guide SOAP API Developer's Guide. L'exemple suivant montre comment créer une opportunité avec un compte parent utilisant la même instruction insert. Cet exemple crée un sObject Opportunity et renseigne certains de ses champs, puis crée deux objets Account. Le premier compte sert uniquement à la relation de clé étrangère. Le deuxième sert à la création du compte et les champs du compte sont définis. Les deux comptes incluent le champ ID externe, MyExtID__c, défini. Ensuite, l'exemple appelle Database.insert en lui passant un tableau de sObjects. Le premier élément du tableau est le sObject parent et le deuxième est le sObject opportunité. L'instruction Database.insert crée l'opportunité avec son compte parent en une seule étape. Pour terminer, l'exemple vérifie les résultats, puis écrit les ID des enregistrements créés dans le journal de débogage, ou la première erreur si la création de l'enregistrement échoue. Cet exemple nécessite un champ de texte d'ID externe dans le compte appelé MyExtID. public class ParentChildSample { public static void InsertParentChild() { Date dt = Date.today(); dt = dt.addDays(7); Opportunity newOpportunity = new Opportunity( Name='OpportunityWithAccountInsert', StageName='Prospecting', CloseDate=dt); // Create the parent reference. // Used only for foreign key reference // and doesn't contain any other fields. Account accountReference = new Account( MyExtID__c='SAP111111'); newOpportunity.Account = accountReference; // Create the Account object to insert. // Same as above but has Name field. // Used for the insert. Account parentAccount = new Account( Name='Hallie', MyExtID__c='SAP111111'); // Create the account and the opportunity. Database.SaveResult[] results = Database.insert(new SObject[] { Référence salesforce | Instruction Merge | 338 parentAccount, newOpportunity }); // Check results. for (Integer i = 0; i < results.size(); i++) { if (results[i].isSuccess()) { System.debug('Successfully created ID: ' + results[i].getId()); } else { System.debug('Error: could not create sobject ' + 'for array element ' + i + '.'); System.debug(' The error reported was: ' + results[i].getErrors()[0].getMessage() + '\n'); } } } } Instruction Merge L'instruction merge fusionne jusqu'à trois enregistrements du même type de sObject dans l'un des enregistrements, en supprimant les autres et en réapparentant tous les enregistrements associés. Remarque: Cette instruction DML n'a pas de méthode de base de données système de mappage. Syntaxe merge sObject sObject merge sObject sObject[] merge sObject ID merge sObject ID[] Le premier paramètre représente l'enregistrement principal dans lequel les autres enregistrements sont fusionnés. Le deuxième paramètre représente le ou les enregistrements qui doivent être fusionnés, puis supprimés. Vous pouvez passer ces autres enregistrements dans l'instruction merge en tant qu'enregistrement sObject ou ID unique, ou dans une liste de deux enregistrements sObject ou ID. Règles et consignes Lors de la fusion d'enregistrements sObject, tenez compte des règles et des consignes suivantes : Référence salesforce | Opération Undelete | 339 Seuls des contacts, des pistes et des comptes peuvent être fusionnés. Reportez-vous à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Vous pouvez passer un enregistrement principal et jusqu'à deux enregistrements sObject supplémentaires à une seule méthode merge. • • Pour plus d'informations sur la fusion de pistes, de contacts et de comptes, reportez-vous à l'aide en ligne de Salesforce. Exemple L'exemple suivant fusionne deux comptes nommés 'Acme Inc.' et 'Acme' dans un seul enregistrement : List<Account> ls = new List<Account>{new Account(name='Acme Inc.'),new Account(name='Acme')}; insert ls; Account masterAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme Inc.' LIMIT 1]; Account mergeAcct = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1]; try { merge masterAcct mergeAcct; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Opération Undelete L'opération DML undelete restaure un ou plusieurs sObjects existants, tels que des comptes ou des contacts, depuis la Corbeille de votre organisation. undelete est semblable à l'instruction UNDELETE dans SQL. Syntaxe de l'instruction DML undelete sObject | Record.ID undelete sObject[] | LIST<>ID[] Syntaxe de la méthode de base de données • • UndeleteResult Database.Undelete((sObject recordToUndelete | RecordID ID), Boolean opt_allOrNone) UndeleteResult[] Database.Undelete((sObject[] recordsToUndelete | RecordIDs LIST<>IDs{}), Boolean opt_allOrNone) Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Règles et consignes Lors de la restauration d'enregistrements sObject, tenez compte des règles et des consignes suivantes : Référence • salesforce | Opération Undelete | 340 Pour garantir l'intégrité référentielle, undelete restaure les associations d'enregistrements pour les types de relations suivants : ◊ Les comptes parents (comme indiqué dans le champ Compte principal d'un compte) ◊ Les requêtes parentes (comme indiqué dans le champ Requête principale d'une requête) ◊ Les solutions parentes pour les solutions traduites (comme indiqué dans le champ Solution principale d'une solution) ◊ Les responsables de contacts (comme indiqué dans le champ Rattachement hiérarchique) ◊ Les produits associés à des actifs (comme indiqué dans le champ Produit d'un actif) ◊ Opportunités associées à des devis (comme indiqué dans le champ Opportunité d'un devis) ◊ Toutes les relations de référence personnalisées ◊ Les membres de groupe de relations dans les comptes et les groupes de relations, avec certaines exceptions ◊ Les balises ◊ Les catégories, le statut de publication et les attributions d'un article Remarque: Salesforce restaure uniquement les relations de référence qui n'ont pas été remplacées. Par exemple, si un actif est associé à un produit différent avant l'enregistrement d'un produit d'origine restauré, la relation actif-produit n'est pas restaurée. • • • • • Certains sObjects ne peuvent pas être restaurés. Pour déterminer si un enregistrement sObject peut être restauré, vérifiez si la propriété undeletable du sObject est définie sur true. Vous pouvez passer jusqu'à 10 000 enregistrements sObject à une méthode undelete unique. Vous pouvez restaurer des enregistrements qui ont été supprimés suite à une fusion, mais les objets enfants doivent être réapparentés, car ils ne peuvent pas être restaurés. Utilisez les paramètres ALL ROWS dans une requête SOQL pour identifier les enregistrements supprimés, notamment lors d'une fusion. Reportez-vous à Demande de tous les enregistrements avec une instruction SOQL à la page 90. La restauration n'est pas prise en charge avec certains sObjects. Reportez-vous à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Objet UndeleteResult Un tableau d'objets Database.UndeleteResult est renvoyé avec la méthode de base de données undelete. Chaque élément du tableau UndeleteResult correspond au tableau sObject transmis en tant que paramètre sObject[] dans la méthode de base de données undelete, c.-à-d. que le premier élément du tableau UndeleteResult correspond au premier élément transmis dans le tableau sObject, le deuxième élément correspond deuxième élément, etc. Si un seul sObject est transmis, le tableau UndeleteResults contient un seul élément. Un objet undeleteResult contient les méthodes suivantes : Nom Type Description getErrors Database.Error [] Si une erreur s'est produite, un tableau d'un ou de plusieurs objets d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. getId ID L'ID du sObject que vous avez essayé de restaurer. Si ce champ contient une valeur, l'objet a été restauré avec succès. Référence salesforce | Opération Undelete | 341 Nom Type Description Si ce champ est vide, l'opération n'a pas réussi pour cet objet. Boolean isSuccess Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Exemple d'instruction DML L'exemple suivant restaure un compte nommé 'Trump' : Les mots clés ALL ROWS interrogent toutes les lignes sur les relations de niveau supérieur et agrégés, y compris les enregistrements supprimés et les activités archivées. Account a = new Account(Name='AC1'); insert(a); insert(new Contact(LastName='Carter',AccountId=a.Id)); Account[] savedAccts = [SELECT Id, Name FROM Account WHERE Name = 'Trump' ALL ROWS]; try { undelete savedAccts; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Exemple de méthode de base de données L'exemple suivant restaure un compte nommé 'Trump' : Les mots clés ALL ROWS interrogent toutes les lignes sur les relations de niveau supérieur et agrégés, y compris les enregistrements supprimés et les activités archivées. public class DmlTest2 { public void undeleteExample() { Account[] SavedAccts = [SELECT Id, Name FROM Account WHERE Name = 'Trump' ALL ROWS]; Database.UndeleteResult[] UDR_Dels = Database.undelete(SavedAccts); for(integer i =0; i< 10; i++) if(UDR_Dels[i].getErrors().size()>0){ // Process any errors here } Référence salesforce | Opération Update | 342 } } Opération Update L'opération DML update modifie un ou plusieurs sObjects existants, tels que des relevés de comptes ou de contacts individuels, dans les données de votre organisation. update est semblable à l'instruction UPDATE dans SQL. Syntaxe de l'instruction DML update sObject update sObject[] Syntaxe de la méthode de base de données • • UpdateResult Update(sObject recordToUpdate, Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions) UpdateResult[] Update(sObject[] recordsToUpdate[], Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions) Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Pour plus d'informations, reportez-vous à Propriétés d'options DML de base de données à la page 485. Règles et consignes Lors de la mise à jour d'enregistrements sObject, tenez compte des règles et des consignes suivantes : • • • • • • • Certains sObjects ne peuvent pas être mis à jour. Pour mettre à jour un enregistrement sObject, sa propriété updateable doit être définie sur true. Lors de la mise à jour de champs obligatoires, vous devez saisir une valeur non null. Contrairement à l'API SOAP, le langage Apex permet de changer les valeurs de champ en null sans mettre à jour le tableau fieldsToNull dans l'enregistrement sObject. L'API nécessite une mise à jour pour ce tableau en raison du traitement incohérent des valeurs null par les nombreux fournisseurs SOAP. Puisque le langage Apex est exécuté exclusivement sur la plate-forme Force.com, cette précaution est inutile. L'ID d'un enregistrement sObject mis à jour ne peut pas être modifié, mais l'ID des enregistrements associés peut être modifié. Cette opération examine chaque lot d'enregistrements pour vérifier s'ils contiennent des valeurs ID dupliquées. Si des doublons sont détectés, les cinq premiers sont traités. À partir du sixième ID dupliqué, l'objet SaveResult de ces entrées est marqué avec une erreur similaire à la suivante : Maximum number of duplicate updates in one batch (5 allowed). Attempt to update Id more than once in this API call: number_of_attempts. L'instruction update modifie automatiquement les valeurs de certains champs tels que LastModifiedDate, LastModifiedById et SystemModstamp. Vous ne pouvez pas spécifier explicitement ces valeurs dans votre code Apex. Vous pouvez passer jusqu'à 10 000 enregistrements sObject à une méthode update unique. Référence • salesforce | Opération Update | 343 Une instruction update unique peut modifier un seul type de sObject à la fois. Par exemple, lors de la mise à jour d'un champ de compte via un contact existant qui n'a pas été modifié, deux instructions update sont requises : // Use a SOQL query to access data for a contact Contact c = [SELECT Account.Name FROM Contact WHERE LastName = 'Carter' LIMIT 1]; // Now we can change fields for both the contact and its // associated account c.Account.Name = 'salesforce.com'; c.LastName = 'Roth'; // To update the database, the two types of records must be // updated separately update c; // This only changes the contact's last name update c.Account; // This updates the account name • La mise à jour n'est pas prise en charge avec certains sObjects. Reportez-vous à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Objet SaveResult Un tableau d'objets SaveResult est renvoyé avec les méthodes de base de données insert et update. Chaque élément du tableau SaveResult correspond au tableau sObject transmis en tant que paramètre sObject[] dans la méthode de base de données, c.-à-d. que le premier élément du tableau SaveResult correspond au premier élément transmis dans le tableau sObject, le deuxième élément correspond deuxième élément, etc. Si un seul sObject est transmis, le tableau SaveResults contient un seul élément. Un objet SaveResult contient les méthodes suivantes : Nom Type Description getErrors Database.Error [] Si une erreur s'est produite, un tableau d'un ou de plusieurs objets d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. getId ID L'ID du sObject que vous avez essayé d'insérer ou de mettre à jour. Si ce champ contient une valeur, l'objet a été inséré ou mis à jour avec succès. Si ce champ est vide, l'opération n'a pas réussi pour cet objet. Référence salesforce | Opération Update | 344 Nom Type Description isSuccess Boolean Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Exemple d'instruction DML L'exemple suivant met à jour le champ BillingCity dans un compte unique nommé 'Acme' : Account a = new Account(Name='Acme2'); insert(a); Account myAcct = [SELECT Id, Name, BillingCity FROM Account WHERE Id = :a.Id]; myAcct.BillingCity = 'San Francisco'; try { update myAcct; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Exemple de méthode de base de données L'exemple suivant met à jour le champ BillingCity dans un compte unique nommé 'Acme' : Account a = new Account(Name='Acme2'); insert(a); Account myAcct = [SELECT Id, Name, BillingCity FROM Account WHERE Id = :a.Id]; myAcct.BillingCity = 'San Francisco'; Database.SaveResult SR = database.update(myAcct); for(Database.Error err: SR.getErrors()) { // process any errors here Référence salesforce | Opération Upsert | 345 } Opération Upsert L'opération DML upsert crée des enregistrements sObject et met à jour les enregistrements sObject existants dans une seule instruction, en utilisant un champ personnalisé facultatif pour déterminer la présence d'objets existants. Syntaxe de l'instruction DML upsert sObject opt_external_id upsert sObject[] opt_external_id opt_external_id est une variable facultative qui spécifie le champ personnalisé à utiliser pour mapper les enregistrements qui existent déjà dans les données de votre organisation. Ce champ personnalisé doit être créé avec l'attribut External Id sélectionné. De plus, si l'attribut Unique n'est pas sélectionné pour le champ, l'utilisateur contextuel doit avoir l'autorisation au niveau de l'objet « Afficher tout » pour l'objet cible ou l'autorisation « Afficher toutes les données » afin d'empêcher upsert d'insérer accidentellement un enregistrement double. Si opt_external_id n'est pas spécifié, le champ ID de l'enregistrement sObject est utilisé par défaut. Remarque: Le mappage de champs personnalisés est insensible à la casse uniquement si les attributs Unique et Considérer « ABC » et « abc » comme des valeurs en double (insensible à la casse) sont sélectionnés dans la définition du champ personnalisé. S'ils sont sélectionnés, « ABC123 » est mappé avec « abc123 ». Pour plus d'informations, reportez-vous à Création de champs personnalisés. Syntaxe de la méthode de base de données • • UpsertResult Database.Upsert(sObject recordToUpsert, Schema.SObjectField External_ID_Field, Boolean opt_allOrNone) UpsertResult[] Database.Upsert(sObject[] recordsToUpsert, Schema.SObjectField External_ID_Field, Boolean opt_allOrNone) Le paramètre facultatif External_ID_Field est une variable qui spécifie le champ personnalisé à utiliser pour mapper les enregistrements qui existent déjà dans les données de votre organisation. Ce champ personnalisé doit être créé avec l'attribut External Id sélectionné. De plus, si l'attribut Unique n'est pas sélectionné pour le champ, l'utilisateur contextuel doit avoir l'autorisation au niveau de l'objet « Afficher tout » pour l'objet cible ou l'autorisation « Afficher toutes les données » afin d'empêcher upsert d'insérer accidentellement un enregistrement double. Le paramètre External_ID_Field est de type Schema.SObjectField, c.-à-d. un jeton de champ. Recherchez le jeton du champ en utilisant la méthode spéciale fields. Par exemple, Schema.SObjectField f = Account.Fields.MyExternalId. Si External_ID_Field n'est pas spécifié, le champ ID de l'enregistrement sObject est utilisé par défaut. Remarque: Le mappage de champs personnalisés est insensible à la casse uniquement si les attributs Unique et Considérer « ABC » et « abc » comme des valeurs en double (insensible à la casse) sont sélectionnés dans la définition du champ personnalisé. S'ils sont sélectionnés, « ABC123 » est mappé avec « abc123 ». Pour plus d'informations, reportez-vous à Création de champs personnalisés. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Référence salesforce | Opération Upsert | 346 Comment l'opération Upsert choisit Insert ou Update L'opération Upsert utilise la clé primaire de l'enregistrement sObject (ou l'ID externe, si spécifié) pour déterminer si elle doit créer un objet ou mettre à jour un objet existant : • • • Si la clé n'est pas mappée, un nouvel enregistrement d'objet est créé. Si la clé est mappée une fois, l'enregistrement d'objet existant est mis à jour. Si la clé est mappée plusieurs fois, une erreur est générée et l'enregistrement d'objet n'est ni inséré ni mis à jour. Règles et consignes Lors de la mise à jour ou l'insertion d'enregistrements sObject, tenez compte des règles et des consignes suivantes : • • • • • • • • Certains sObjects ne peuvent pas être insérés ou mis à jour. Pour insérer un enregistrement sObject, sa propriété createable doit être définie sur true. Pour mettre à jour un enregistrement sObject, sa propriété updateable doit être définie sur true. Vous devez saisir une valeur non null dans les champs obligatoires de tous les enregistrements insérés. L'ID d'un enregistrement sObject ne peut pas être modifié, mais l'ID des enregistrements associés peut être modifié. Cette action est interprétée en tant que mise à jour. L'instruction upsert modifie automatiquement les valeurs de certains champs tels que LastModifiedDate, LastModifiedById et SystemModstamp. Vous ne pouvez pas spécifier explicitement ces valeurs dans votre code Apex. Chaque instruction upsert est formée de deux opérations, l'une pour l'insertion d'enregistrements et l'autre pour la mise à jour des enregistrements. Chacune de ces opérations est soumise à des limitations d'exécution, respectivement pour insert et update. Par exemple, si vous insérez/mettez à jour plus de 10 000 enregistrements, et qu'ils sont tous mis à jour, vous recevez une erreur (reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273). L'instruction upsert peut définir uniquement l'ID d'enregistrements sObject associés. Les champs des enregistrements associés ne peuvent pas être modifiés avec upsert. Par exemple, lors de la mise à jour d'un contact existant, vous pouvez spécifier l'enregistrement du compte associé du contact en définissant la valeur du champ AccountId. Cependant, vous ne pouvez pas modifier le nom du compte sans mettre à jour le compte lui-même dans une instruction DML séparée. L'opération Upsert n'est pas prise en charge avec certains sObjects. Reportez-vous à sObjects qui ne prennent pas en charge les opérations DML à la page 351. Vous pouvez utiliser des clés étrangères pour insérer/mettre à jour des enregistrements sObject s'ils ont été définis en tant que champs de référence. Pour plus d'informations, reportez-vous à Field Types dans le guide Object Reference for Salesforce and Force.com. Objet UpsertResult Un tableau d'objets Database.UpsertResult est renvoyé avec la méthode de base de données upsert. Chaque élément du tableau UpsertResult correspond au tableau sObject transmis en tant que paramètre sObject[] dans la méthode de base de données upsert, c.-à-d. que le premier élément du tableau UpsertResult correspond au premier élément transmis dans le tableau sObject, le deuxième élément correspond deuxième élément, etc. Si un seul sObject est transmis, le tableau UpsertResults contient un seul élément. Un objet UpsertResult contient les méthodes suivantes : Nom Type Description getErrors Database.Error [] Si une erreur s'est produite, un tableau d'un ou de plusieurs objets d'erreur de base de données indiquant le code et la description de l'erreur. Pour plus d'informations, reportez-vous à Méthodes d'objet d'erreur de base de données à la page 489. Référence salesforce | Opération Upsert | 347 Nom Type Description getId ID L'ID du sObject que vous avez essayé de mettre à jour ou d'insérer. Si ce champ contient une valeur, l'objet a été mis à jour ou inséré avec succès. Si ce champ est vide, l'opération n'a pas réussi pour cet objet. isCreated Boolean Une valeur booléenne est définie sur true si l'enregistrement a été créé, sur false si l'enregistrement a été mis à jour. isSuccess Boolean Une valeur booléenne définie sur true si l'opération DML a réussi pour cet objet, sinon définie sur false. Exemples d'instructions DML L'exemple suivant met à jour le nom de la ville de tous les comptes existants situés à Bombay, puis insère un nouveau compte situé à San Francisco : Account[] acctsList = [SELECT Id, Name, BillingCity FROM Account WHERE BillingCity = 'Bombay']; for (Account a : acctsList) { a.BillingCity = 'Mumbai'; } Account newAcct = new Account(Name = 'Acme', BillingCity = 'San Francisco'); acctsList.add(newAcct); try { upsert acctsList; } catch (DmlException e) { // Process exception here } Remarque: Pour plus d'informations sur le traitement des exceptions DmlException, reportez-vous à Gestion des exceptions DML en masse à la page 353. Utilisez upsert avec un ID externe pour réduire le nombre d'instructions DML dans votre code et ne pas atteindre les limitations du gouverneur (reportez-vous à Compréhension des limitations et des gouverneurs d'exécution). L'exemple suivant utilise upsert et le champ ID externe Line_Item_Id__c dans l'objet Asset pour maintenir une relation un-à-un entre un actif et un élément de ligne d'opportunité. Référence salesforce | Opération Upsert | 348 Remarque: Avant d'exécuter cet exemple, créez un champ de texte personnalisé dans l'objet Asset nommé Line_Item_Id__c, puis marquez-le en tant qu'ID externe. Pour plus d'informations sur les champs personnalisés, reportez-vous à l'aide en ligne de Salesforce. public void upsertExample() { Opportunity opp = [SELECT Id, Name, AccountId, (SELECT Id, PricebookEntry.Product2Id, PricebookEntry.Name FROM OpportunityLineItems) FROM Opportunity WHERE HasOpportunityLineItem = true LIMIT 1]; Asset[] assets = new Asset[]{}; // Create an asset for each line item on the opportunity for (OpportunityLineItem lineItem:opp.OpportunityLineItems) { //This code populates the line item Id, AccountId, and Product2Id for each asset Asset asset = new Asset(Name = lineItem.PricebookEntry.Name, Line_Item_ID__c = lineItem.Id, AccountId = opp.AccountId, Product2Id = lineItem.PricebookEntry.Product2Id); assets.add(asset); } try { upsert assets Line_Item_ID__c; // This line upserts the assets list with // the Line_Item_Id__c field specified as the // Asset field that should be used for matching // the record that should be upserted. } catch (DmlException e) { System.debug(e.getMessage()); } } Référence salesforce | Opération Upsert | 349 Exemple de méthode de base de données L'exemple suivant utilise la méthode de base de données upsert pour insérer/mettre à jour une collection de pistes passées. Cet exemple autorise le traitement partiel des enregistrements, c.-à-d. que si le traitement de certains enregistrements échoue, les autres enregistrements sont insérés ou mis à jour. Il itère dans les résultats et ajoute une nouvelle tâche à chaque enregistrement dont le traitement a réussi. Les sObjects de la tâche sont enregistrés dans une liste, qui est ensuite insérée en masse. Cet exemple contient également une méthode de test qui permet d'évaluer l'exemple. /* This class demonstrates and tests the use of the * partial processing DML operations */ public class dmlSamples { /* This method accepts a collection of lead records and creates a task for the owner(s) of any leads that were created as new, that is, not updated as a result of the upsert operation */ public static List<Database.upsertResult> upsertLeads(List<Lead> leads) { /* Perform the upsert. In this case the unique identifier for the insert or update decision is the Salesforce record ID. If the record ID is null the row will be inserted, otherwise an update will be attempted. */ List<Database.upsertResult> uResults = Database.upsert(leads,false); /* This is the list for new tasks that will be inserted when new leads are created. */ List<Task> tasks = new List<Task>(); for(Database.upsertResult result:uResults) { if (result.isSuccess() && result.isCreated()) tasks.add(new Task(Subject = 'Follow-up', WhoId = result.getId())); } /* If there are tasks to be inserted, insert them */ Database.insert(tasks); return uResults; Référence salesforce | Opération Upsert | 350 } public static testMethod void testUpsertLeads() { /* We only need to test the insert side of upsert */ List<Lead> leads = new List<Lead>(); /* Create a set of leads for testing */ for(Integer i = 0;i < 100; i++) { leads.add(new Lead(LastName = 'testLead', Company = 'testCompany')); } /* Switch to the runtime limit context */ Test.startTest(); /* Exercise the method */ List<Database.upsertResult> results = DmlSamples.upsertLeads(leads); /* Switch back to the test context for limits */ Test.stopTest(); /* ID set for asserting the tasks were created as expected */ Set<Id> ids = new Set<Id>(); /* Iterate over the results, asserting success and adding the new ID to the set for use in the comprehensive assertion phase below. */ for(Database.upsertResult result:results) { System.assert(result.isSuccess()); ids.add(result.getId()); } /* Assert that exactly one task exists for each lead that was inserted. */ for(Lead l:[SELECT Id, (SELECT Subject FROM Tasks) FROM Lead WHERE Id IN :ids]) { System.assertEquals(1,l.tasks.size()); Référence salesforce | sObjects qui ne prennent pas en charge les opérations DML | 351 } } } sObjects qui ne prennent pas en charge les opérations DML Les opérations DML ne sont pas prises en charge avec les objets suivants sObjects dans le langage Apex : • • • • • • • • • • • • • • • • • AccountTerritoryAssignmentRule AccountTerritoryAssignmentRuleItem ApexComponent ApexPage BusinessHours BusinessProcess CategoryNode CurrencyType DatedConversionRate ProcessInstance* Profile RecordType SelfServiceUser StaticResource UserAccountTeamMember UserTerritory WebLink * Vous ne pouvez pas créer, mettre à jour ou supprimer ProcessInstance dans l'API SOAP. sObjects non utilisables dans des opérations DML Certains sObjects nécessitent l'exécution d'opérations DML d'un seul type par transaction. Par exemple, vous ne pouvez pas insérer un compte, puis insérer un utilisateur ou un membre de groupe dans la même transaction. Les sObjects suivants ne peuvent pas être utilisés ensemble dans une transaction : • • FieldPermissions Group Vous pouvez uniquement insérer ou mettre un jour un groupe dans une transaction avec d'autres sObjects. Les autres opérations DML ne sont pas autorisées. • GroupMember Vous pouvez uniquement insérer et mettre à jour un membre de groupe dans une transaction avec d'autres sObjects dans un code Apex enregistré en utilisant l'API Salesforce.com versions 14.0 et supérieures. Référence • • • • • • salesforce | sObjects non utilisables dans des opérations DML | 352 ObjectPermissions PermissionSet PermissionSetAssignment QueueSObject SetupEntityAccess User Vous pouvez insérer un utilisateur dans une transaction avec d'autres sObjects dans un code Apex enregistré en utilisant l'API Salesforce.com versions 14.0 et supérieures. Vous pouvez insérer un utilisateur dans une transaction avec d'autres sObjects dans un code Apex enregistré en utilisant l'API Salesforce.com versions 15.0 et supérieures si UserRoleId est spécifié comme null. Vous pouvez mettre à jour un utilisateur dans une transaction avec d'autres sObjects dans un code Apex enregistré en utilisant l'API Salesforce.com versions 14.0 et supérieures. Vous pouvez mettre à jour un utilisateur dans une transaction avec d'autres sObjects dans un code Apex enregistré en utilisant l'API Salesforce.com versions 15.0 et supérieures si les champs suivants sont également mis à jour : ◊ ◊ ◊ ◊ ◊ ◊ • • • • UserRoleId IsActive ForecastEnabled IsPortalEnabled Username ProfileId UserRole UserTerritory Territory Les paramètres personnalisés dans un code Apex sont enregistrés en utilisant l'API Salesforce.com versions 17.0 et supérieures. Ces sObjects ne font l'objet d'aucune restriction pour les opérations DML delete. Si vous utilisez une page Visualforce avec un contrôleur personnalisé, vous pouvez effectuer des opérations DML uniquement sur un seul type de sObject dans une requête ou une action unique. Vous pouvez toutefois effectuer des opérations DML sur différents types de sObjects dans les requêtes successives, par exemple pour créer un compte avec un bouton Enregistrer, puis créer un utilisateur avec un bouton Soumettre. Vous pouvez effectuer des opérations DML sur plusieurs types de sObject dans une classe unique en utilisant le processus suivant : 1. Créez une méthode qui effectue une opération DML sur un type de sObject. 2. Créez une deuxième méthode qui utilise l'annotation future pour manipuler un deuxième type de sObject. Opérations DML autorisées dans des méthodes de test dans des blocs System.RunAs() Les méthodes de test permettent d'effectuer des opérations DML mixtes entre les sObjects répertoriés ci-dessus et d'autres sObjects si le code qui effectue les opérations DML est inclus entre des blocs de méthode System.runAs. Vous pouvez par exemple créer un utilisateur avec un rôle et d'autres sObjects dans le même test. L'exemple suivant montre comment inclure des opérations DML mixtes dans des blocs System.runAs pour éviter l'erreur de DML mixtes. Le premier bloc est exécuté dans le contexte de l'utilisateur actif. Il crée un utilisateur test et un compte test. Référence salesforce | Gestion des exceptions DML en masse | 353 Le deuxième bloc est exécuté dans le contexte de l'utilisateur test et met à jour le compte. Avant d'exécuter cet exemple, remplacez la valeur du rôle utilisateur dans la requête par un rôle utilisateur existant dans votre organisation. @isTest private class MixedDML { static testMethod void MixedDMLExample() { User u; Account a; User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()]; // Insert account as current user System.runAs (thisUser) { Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; UserRole r = [SELECT Id FROM UserRole WHERE Name='SalesRep']; u = new User(alias = 'jsmtih', email='[email protected]', emailencodingkey='UTF-8', lastname='Smith', languagelocalekey='en_US', localesidkey='en_US', profileid = p.Id, userroleid = r.Id, timezonesidkey='America/Los_Angeles', username='[email protected]'); insert u; a = new Account(name='Acme'); insert a; } // Update account as the new user System.runAs(u) { a.website = 'www.salesforce.com'; update a; } } } Gestion des exceptions DML en masse La gestion des exceptions générées par un appel DML en masse (y compris les opérations DML récursives dans des déclencheurs activés suite à l'appel) diffère selon l'origine de l'appel : Référence • • salesforce | Méthodes et classes standard Apex | 354 Lorsque des erreurs se produisent suite à un appel DML en masse provenant directement d'instructions DML Apex ou si le paramètre all_or_none d'une méthode de base de données DML a été spécifié comme true, le moteur d'exécution suit la règle « tout ou rien » : durant une opération unique, tous les enregistrements doivent être mis à jour avec succès, sinon l'opération entière revient au point précédant immédiatement l'instruction DML. Lorsque des erreurs se produisent suite à un appel DML en masse provenant de l'API SOAP, le moteur d'exécution tente au moins un enregistrement partiel : 1. Lors de la première tentative, le moteur d'exécution traite tous les enregistrements. Tous les enregistrements qui génèrent une erreur, due à des problèmes de règles de validation ou de violations d'index unique, sont mis de côté. 2. Si des erreurs se sont produites lors de la première tentative, le moteur d'exécution effectue une deuxième tentative qui inclut uniquement les enregistrements qui n'ont pas généré d'erreur. Tous les enregistrements qui n'ont pas généré d'erreur lors de la première tentative sont traités. Si parmi ces enregistrements certains entraînent une erreur (peu probable), ils sont également mis de côté. 3. Si des erreurs supplémentaires se sont produites lors de la deuxième tentative, le moteur d'exécution effectue une troisième tentative qui inclut uniquement les enregistrements qui n'ont pas généré d'erreur lors des première et deuxième tentatives. Si un enregistrement génère une erreur, l'opération entière échoue avec le message « Too many batch retries in the presence of Apex triggers and partial failures ». Remarque: Lors des deuxième et troisième tentatives, les limitations du gouverneur sont réinitialisées à leur état d'origine avant la première tentative. Reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Méthodes et classes standard Apex Apex fournit des classes standard qui contiennent des méthodes statiques et d'instance pour des expressions de type de données primitif, ainsi que des objets plus complexes. Les méthodes statiques standard sont similaires à Java et se présentent toujours sous la forme : Class.method(args) Les méthodes statiques standard de type de données primitif n'ont pas de paramètre implicite et sont invoquées sans aucun contexte d'objet. Par exemple, l'expression suivante arrondi la valeur de 1,75 au nombre entier le plus proche sans utiliser une autre valeur. Math.roundToLong(1.75); Toutes les méthodes d'instance se présentent dans des expressions d'un type de données particulier, tel qu'une liste, un ensemble ou une chaîne. Par exemple : String s = 'Hello, world'; Integer i = s.length(); Référence salesforce | Méthodes Primitives Apex | 355 Remarque: Si une méthode est appelée avec une expression d'objet qui évalue à null, le moteur d'exécution Apex lève une exception d'indicateur null. Certaines classes utilisent un espace de noms en tant que mécanisme de regroupement de leurs méthodes. Par exemple, la classe message utilise l'espace de noms ApexPages. ApexPages.Message myMsg = new ApexPages.Message(ApexPages.FATAL, 'My Error Message'); Les classes standard Apex sont regroupées dans les catégories suivantes : • • • • • • Primitives Collections Enums sObjects System Exceptions Méthodes Primitives Apex Dans le langage Apex, de nombreux types de données primitifs contiennent des méthodes qui peuvent être utilisées des manipulations supplémentaires de données. Les primitifs disposant de méthodes sont les suivants : • • • • • • • • • • Blob Boolean Date Datetime Decimal Double ID Long String Time Méthodes Blob La méthode ci-dessous est une méthode statique système pour Blob. Nom Arguments Type de renvoi Description toPdf String S Blob Crée un objet binaire hors de la chaîne donnée en le codant en tant que fichier PDF. valueOf String S Blob Convertit la chaîne String S spécifiée en Blob. Par exemple : String myString = 'StringToBlob'; Blob myBlob = Blob.valueof(myString); Référence salesforce | Méthodes Primitives Apex | 356 Les méthodes ci-dessous sont les méthodes d'instance pour Blob. Nom Arguments size Type de renvoi Description Integer Renvoie le nombre de caractères du blob. Par exemple : String myString = 'StringToBlob'; Blob myBlob = Blob.valueof(myString); Integer size = myBlob.size(); String toString Convertit le blob en chaîne. Pour plus d'informations sur les blobs, reportez-vous à Types de données Primitif à la page 29. Méthodes Boolean Les méthodes ci-dessous sont les méthodes statiques pour Boolean. Nom Arguments Type de renvoi Description valueOf String s Boolean Convertit la chaîne spécifiée en une valeur booléenne et renvoie true si la valeur de la chaîne spécifiée est 'true'. Sinon, renvoie false. Si l'argument spécifié est null, cette méthode lève une exception. Exemple : Boolean b = Boolean.valueOf('true'); System.assertEquals(true, b); valueOf Object fieldValue Boolean Convertit la valeur de champ de suivi d'historique spécifiée en une valeur booléenne. Utilisez cette méthode avec les champs OldValue ou NewValue de sObjects historiques, tels que AccountHistory, lorsque le type de champ correspond à Boolean, par exemple un champ de case à cocher. Exemple : List<AccountHistory> ahlist = [SELECT Field,OldValue,NewValue FROM AccountHistory]; for(AccountHistory ah : ahlist) { System.debug('Field: ' + ah.Field); Référence Nom salesforce | Méthodes Primitives Apex | 357 Arguments Type de renvoi Description if (ah.field == 'IsPlatinum__c') { Boolean oldValue = Boolean.valueOf(ah.OldValue); Boolean newValue = Boolean.valueOf(ah.NewValue); } Pour plus d'informations sur le type Boolean, reportez-vous à Types de données Primitive à la page 29. Méthodes Date Les méthodes ci-dessous sont les méthodes statiques système pour Date. Nom Arguments Type de renvoi Description daysInMonth Integer year Integer Renvoie le nombre de jours du mois de year et month spécifiés (1=Jan). L'exemple suivant récupère le nombre de jours du mois de février de l'année 1960 : Integer month Integer numberDays = date.daysInMonth(1960, 2); isLeapYear Integer year Boolean Renvoie true si year spécifié est une année bissextile. newInstance Integer year Date Construit une date à partir des représentations d'entier de year, month (1=Jan) et day. L'exemple suivant crée la date du 17 février 1960 : Integer month Integer date Date myDate = date.newinstance(1960, 2, 17); parse String Date Date Construit une date à partir d'une chaîne. Le format de la chaîne dépend du format de la date locale. L'exemple suivant fonctionne dans certains paramètres régionaux : date mydate = date.parse('12/27/2009'); today Date Renvoie la date actuelle dans le fuseau horaire de l'utilisateur actuel. Référence salesforce | Méthodes Primitives Apex | 358 Nom Arguments Type de renvoi Description valueOf String s Date Renvoie une date qui contient la valeur de la chaîne spécifiée. La chaîne doit utiliser le format de date standard « aaaa-MM-jj HH:mm:ss » dans le fuseau horaire local. Par exemple : string year = '2008'; string month = '10'; string day = '5'; string hour = '12'; string minute = '20'; string second = '20'; string stringDate = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; Date myDate = date.valueOf(stringDate); valueOf Object fieldValue Date Convertit la valeur du champ de suivi d'historique spécifiée en date. Utilisez cette méthode avec les champs OldValue ou NewValue de sObjects historiques, tels que AccountHistory, lorsque le champ est un champ de date. Exemple : List<AccountHistory> ahlist = [SELECT Field,OldValue,NewValue FROM AccountHistory]; for(AccountHistory ah : ahlist) { System.debug('Field: ' + ah.Field); if (ah.field == 'MyDate__c') { Date oldValue = Date.valueOf(ah.OldValue); Date newValue = Date.valueOf(ah.NewValue); Référence Nom salesforce | Méthodes Primitives Apex | 359 Arguments Type de renvoi Description } Les méthodes ci-dessous sont les méthodes d'instance pour Date. Nom Arguments Type de renvoi Description addDays Integer addlDays Date Ajoute le nombre spécifié de addlDays à une date. Par exemple : date myDate = date.newInstance(1960, 2, 17); date newDate = mydate.addDays(2); addMonths Integer addlMonths Date Ajoute le nombre spécifié de addlMonths à une date. addYears Integer addlYears Date Ajoute le nombre spécifié de addlYears à une date. day Integer Renvoie le composant jour-du-mois d'une date. Par exemple, 5 février 1999 est jour 5. dayOfYear Integer Renvoie le composant jour-de-l'année d'une date. Par exemple, 5 février 1999 est jour 36. Integer Renvoie le nombre de jours entre la date qui a appelé la méthode et le compDate. Si la date qui appelle la méthode se situe après le compDate, la valeur renvoyée est négative. Par exemple : daysBetween Date compDate date startDate = date.newInstance(2008, 1, 1); date dueDate = date.newInstance(2008, 1, 30); integer numberDaysDue = startDate.daysBetween(dueDate); format isSameDay Date compDate String Renvoie la date sous forme de chaîne en utilisant les paramètres régionaux de l'utilisateur actif. Boolean Renvoie true si la date qui a appelé la méthode est identique à compDate. Par exemple : date myDate = date.today(); date dueDate = Référence Nom salesforce | Méthodes Primitives Apex | 360 Arguments Type de renvoi Description date.newInstance(2008, 1, 30); boolean dueNow = myDate.isSameDay(dueDate); Integer Renvoie le composant mois d'une date (1=Jan). Integer Renvoie le nombre de mois entre la date qui a appelé la méthode et le compDate. Par exemple, 1er mars et 30 mars de la même année ont 0 mois qui les sépare. toStartOfMonth Date Renvoie le premier du mois de la date qui a appelé la méthode. Par exemple, 14 juillet 1999 renvoie 1er juillet 1999. toStartOfWeek Date Renvoie le début de la semaine de la date qui a appelé la méthode, en fonction des paramètres régionaux de l'utilisateur actif. Par exemple, le début d'une semaine est dimanche dans les paramètres régionaux des États-Unis et lundi dans les paramètres régionaux européens. Par exemple : month monthsBetween Date compDate date myDate = date.today(); date weekStart = myDate.toStartofWeek(); Integer year Renvoie le composant année d'une date Pour plus d'informations sur le type Date, reportez-vous à Types de données Primitive à la page 29. Méthodes Datetime Les méthodes ci-dessous sont les méthodes statiques système pour Datetime. Nom Arguments Type de renvoi Description newInstance Long l Datetime Construit une date/heure et l'initialise pour représenter le nombre de millisecondes spécifié depuis le 1er janvier 1970, 00:00:00 GMT. La date est renvoyée dans le fuseau horaire GMT. newInstance Date Date Datetime Construit une date/heure à partir des date et time spécifiées dans le fuseau horaire local. La date est renvoyée dans le fuseau horaire GMT. Datetime Construit une date/heure à partir des représentations d'entier year, month (1=Jan) et day à minuit dans le fuseau horaire local. La date est renvoyée dans le fuseau horaire GMT. Time Time newInstance Integer year Integer month Référence Nom salesforce | Méthodes Primitives Apex | 361 Arguments Type de renvoi Integer day Description Par exemple : datetime myDate = datetime.newInstance(2008, 12, 1); newInstance Integer year Datetime Integer day Construit une date/heure à partir des représentations d'entier de year, month (1=Jan), day, hour, minute et second dans le fuseau horaire local. La date est renvoyée dans le fuseau horaire GMT. Integer hour Par exemple : Integer month Integer minute Datetime myDate = Integer second newInstanceGmt Date date datetime.newInstance(2008, 12, 1, 12, 30, 2); Datetime Construit une date/heure à partir des date et time spécifiées dans le fuseau horaire GMT. Datetime Construit une date/heure à partir des représentations d'entier de year, month (1=Jan) et day à minuit dans le fuseau horaire GMT. Datetime Construit une date/heure à partir des représentations d'entier de year, month (1=Jan), day, hour, minute et second dans le fuseau horaire GMT. Datetime Renvoie la date/heure actuelle basée sur un calendrier GMT. Time time newInstanceGmt Integer year Integer month Integer date newInstanceGmt Integer year Integer month Integer date Integer hour Integer minute Integer second now Par exemple : datetime myDateTime = datetime.now(); Le format de la date/heure renvoyée est le suivant : 'MM/DD/YYYY HH:MM PERIOD' parse String datetime Datetime Construit une date/heure à partir de la chaîne datetime dans le fuseau horaire local et en fonction des paramètres régionaux de l'utilisateur. La date est renvoyée dans le fuseau horaire GMT. Référence Nom salesforce | Méthodes Primitives Apex | 362 Arguments Type de renvoi Description Cet exemple utilise parse pour créer une date/heure à partir d'une date passée sous forme de chaîne et mise en forme pour les paramètres régionaux Anglais (États-Unis). Vous devez modifier le format de la chaîne de date si vous avez des paramètres régionaux différents. Datetime dt = DateTime.parse( '10/14/2011 11:46 AM'); String myDtString = dt.format(); system.assertEquals( myDtString, '10/14/2011 11:46 AM'); valueOf String s Datetime Renvoie une date/heure qui contient la valeur de la chaîne spécifiée. La chaîne doit utiliser le format de date standard « aaaa-MM-jj HH:mm:ss » dans le fuseau horaire local. La date est renvoyée dans le fuseau horaire GMT. Par exemple : string year = '2008'; string month = '10'; string day = '5'; string hour = '12'; string minute = '20'; string second = '20'; string stringDate = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; Datetime myDate = datetime.valueOf(stringDate); valueOf Object fieldValue Datetime Convertit la valeur du champ de suivi d'historique spécifiée en date/heure. Utilisez cette méthode avec les champs OldValue ou NewValue de sObjects historiques, tels que Référence Nom salesforce | Méthodes Primitives Apex | 363 Arguments Type de renvoi Description AccountHistory, lorsque le champ est un champ de date/heure. Exemple : List<AccountHistory> ahlist = [SELECT Field,OldValue,NewValue FROM AccountHistory]; for(AccountHistory ah : ahlist) { System.debug('Field: ' + ah.Field); if (ah.field == 'MyDatetime__c') { Datetime oldValue = Datetime.valueOf(ah.OldValue); Datetime newValue = Datetime.valueOf(ah.NewValue); } valueOfGmt String s Datetime Renvoie une date/heure qui contient la valeur de la chaîne spécifiée. La chaîne doit utiliser le format de date standard « aaaa-MM-jj HH:mm:ss » dans le fuseau horaire GMT. Les méthodes ci-dessous sont les méthodes d'instance pour Datetime. Nom Arguments Type de renvoi Description addDays Integer addlDays Datetime Ajoute le nombre spécifié de addlDays à une date/heure. Par exemple : datetime myDate = datetime.newInstance (1960, 2, 17); datetime newDate = mydate.addDays(2); addHours Integer addlHours Datetime addMinutes Integer addlMinutes Datetime Ajoute le nombre spécifié de addlMinutes à une date/heure. addMonths Integer addlMonths Ajoute le nombre spécifié de addlMonths à une date/heure. Datetime Ajoute le nombre spécifié de addlHours à une date/heure. Référence salesforce | Méthodes Primitives Apex | 364 Nom Arguments Type de renvoi Description addSeconds Integer addlSeconds Datetime Ajoute le nombre spécifié de addlSeconds à une date/heure. addYears Integer addlYears Datetime Ajoute le nombre spécifié de addlYears à une date/heure. date Date Renvoie le composant date d'une date/heure dans le fuseau horaire local de l'utilisateur actif. dateGMT Date Renvoie le composant date d'une date/heure dans le fuseau horaire GMT. day Integer Renvoie le composant date-du-mois d'une date/heure dans le fuseau horaire local de l'utilisateur actif. Par exemple, 5 février 1999 08:30:12 est jour 5. dayGmt Integer Renvoie le composant date-du-mois d'une date/heure dans le fuseau horaire GMT. Par exemple, 5 février 1999 08:30:12 est jour 5. dayOfYear Integer Renvoie le composant date-de-l'année d'une date/heure dans le fuseau horaire local de l'utilisateur actif. Par exemple, le 5 février 2008 08:30:12 est jour 36. Datetime myDate = datetime.newInstance (2008, 2, 5, 8, 30, 12); system.assertEquals (myDate.dayOfYear(), 36); dayOfYearGmt Integer Renvoie le composant date-de-l'année d'une date/heure dans le fuseau horaire GMT. Par exemple, 5 février 1999 08:30:12 est jour 36. format String Convertit la date dans le fuseau horaire local et renvoie la date convertie sous forme de chaîne mise en forme en utilisant les paramètres régionaux de l'utilisateur actif. Si le fuseau horaire ne peut pas être déterminé, GMT est utilisé. String Convertit la date dans le fuseau horaire local et renvoie la date convertie sous forme de chaîne mise en forme en utilisant le format de date simple Java fourni. Si le fuseau horaire ne peut pas être déterminé, GMT est utilisé. Par exemple : format String dateFormat Datetime myDT = Datetime.now(); String myDate = myDT.format('h:mm a'); Pour plus d'informations sur le format de date simple Java, reportez-vous à Java SimpleDateFormat. Référence salesforce | Méthodes Primitives Apex | 365 Nom Arguments Type de renvoi Description format String dateFormat String Convertit la date dans le fuseau horaire spécifié et renvoie la date convertie sous forme de chaîne mise en forme en utilisant le format de date simple Java fourni. Si le format du fuseau horaire fourni est incorrect, GMT est utilisé. String timezone Pour plus d'informations sur le format de date simple Java, reportez-vous à Java SimpleDateFormat. Les valeurs de fuseau horaire correctes pour l'argument timezone sont les fuseaux horaires de la classe Java TimeZone qui correspondent aux fuseaux horaires renvoyés par la méthode TimeZone.getAvailableIDs dans Java. Nous recommandons d'utiliser les noms de fuseau horaire complets, pas les abréviations en trois lettres. Cet exemple utilise format pour convertir une date GMT en fuseau horaire Amérique/New_York, et met en forme la date en utilisant le format de date spécifié. Datetime GMTDate = Datetime.newInstanceGmt(2011,6,1,12,1,5); String strConvertedDate = GMTDate.format('MM/dd/yyyy HH:mm:ss', 'America/New_York'); // Date is converted to // the new time zone and is adjusted // for daylight saving time. System.assertEquals( '06/01/2011 08:01:05', strConvertedDate); formatGmt StringdateFormat String Renvoie une date/heure sous forme de chaîne en utilisant le format de date simple Java fourni et le fuseau horaire GMT. Pour plus d'informations sur le format de date simple Java, reportez-vous à Java SimpleDateFormat. formatLong String Convertit la date dans le fuseau horaire local et renvoie la date convertie sous le format de date longue. Par exemple : // Passing local date based on the PST // time zone Référence Nom salesforce | Méthodes Primitives Apex | 366 Arguments Type de renvoi Description Datetime dt = DateTime.newInstance( 2012,12,28,10,0,0); // Writes 12/28/2012 10:00:00 AM PST System.debug('dt.formatLong()=' + dt.formatLong()); getTime Long Renvoie le nombre de millisecondes depuis le 1er janvier 1970, 00:00:00 GMT représenté par cet objet Date/heure. hour Integer Renvoie le composant heure d'une date/heure dans le fuseau horaire local de l'utilisateur actif. hourGmt Integer Renvoie le composant heure d'une date/heure dans le fuseau horaire GMT. Boolean Renvoie true si la date/heure qui a appelé la méthode est identique à compDt dans le fuseau horaire local de l'utilisateur actif. Par exemple : isSameDay Datetime compDt datetime myDate = datetime.now(); datetime dueDate = datetime.newInstance(2008, 1, 30); boolean dueNow = myDate.isSameDay(dueDate); millisecond Integer Renvoie le composant milliseconde d'une date/heure dans le fuseau horaire local de l'utilisateur actif. millisecondGmt Integer Renvoie le composant milliseconde d'une date/heure dans le fuseau horaire GMT. minute Integer Renvoie le composant minute d'une date/heure dans le fuseau horaire local de l'utilisateur actif. minuteGmt Integer Renvoie le composant minute d'une date/heure dans le fuseau horaire GMT. month Integer Renvoie le composant mois d'une date/heure dans le fuseau horaire local de l'utilisateur actif (1=Jan). monthGmt Integer Renvoie le composant mois d'une date/heure dans le fuseau horaire GMT (1=Jan). second Integer Renvoie le composant seconde d'une date/heure dans le fuseau horaire local de l'utilisateur actif. Référence Nom salesforce | Méthodes Primitives Apex | 367 Arguments Type de renvoi Description secondGmt Integer Renvoie le composant seconde d'une date/heure dans le fuseau horaire GMT. time Time Renvoie le composant heure d'une date/heure dans le fuseau horaire local de l'utilisateur actif. timeGmt Time Renvoie le composant heure d'une date/heure dans le fuseau horaire GMT. year Integer Renvoie le composant année d'une date/heure dans le fuseau horaire local de l'utilisateur actif. yearGmt Integer Renvoie le composant année d'une date/heure dans le fuseau horaire GMT. Pour plus d'informations sur Datetime, reportez-vous à Types de données Primitive à la page 29. Méthodes Decimal Les méthodes ci-dessous sont les méthodes statiques système pour Decimal. Nom Arguments Type de renvoi Description valueOf Double d Decimal Renvoie une décimale qui contient la valeur de la double spécifiée. valueOf Long l Decimal Renvoie une décimale qui contient la valeur de la longueur spécifiée. valueOf String s Decimal Renvoie une décimale qui contient la valeur de la chaîne spécifiée. Comme dans Java, la chaîne est interprétée en tant que décimale signée. Par exemple : String temp = '12.4567'; Decimal myDecimal = decimal.valueOf(temp); Les méthodes ci-dessous sont les méthodes d'instance pour Decimal. Nom Arguments abs divide Decimal divisor, Integer scale Type de renvoi Description Decimal Renvoie la valeur absolue de la décimale. Decimal Divise cette décimale par divisor et définit l'échelle, c.-à-d. le nombre de décimales du résultat, en utilisant Référence Nom salesforce | Méthodes Primitives Apex | 368 Arguments Type de renvoi Description scale. Dans l'exemple suivant, D a la valeur de 0.190 : Decimal D = 19; D.Divide(100, 3); divide Decimal divisor, Integer scale, Object Decimal roundingMode Divise cette décimale par divisor, définit l'échelle, c.-à-d. le nombre de décimales du résultat, en utilisant scale et, si nécessaire, arrondi la valeur en utilisant roundingMode. Pour plus d'informations sur les valeurs valides pour roundingMode, reportez-vous à Mode d'arrondi. Par exemple : Decimal myDecimal = 12.4567; Decimal divDec = myDecimal.divide (7, 2, System.RoundingMode.UP); system.assertEquals(divDec, 1.78); doubleValue Double Renvoie la valeur double de cette décimale. format String Renvoie la valeur de chaîne de cette décimale en utilisant les paramètres régionaux de l'utilisateur contextuel. La notation scientifique est utilisée si un exposant est requis. intValue Integer Renvoie la valeur de l'entier de cette décimale. longValue Long Renvoie la valeur de longueur de cette décimale. Decimal Renvoie la valeur de cette décimale élevée à la puissance de exponent. La valeur de exponent doit être comprise entre 0 and 32 767. Par exemple : pow Integer exponent Decimal myDecimal = 4.12; Decimal powDec = myDecimal.pow(2); system.assertEquals(powDec, 16.9744); Référence Nom salesforce | Méthodes Primitives Apex | 369 Arguments Type de renvoi Description Si vous utilisez MyDecimal.pow(0), 1 est renvoyé. La Méthode Math pow n'accepte pas les valeurs négatives. precision Integer Renvoie le nombre total de chiffres de la décimale. Par exemple, si la valeur de la décimale est 123,45, precision renvoie 5. Si la valeur de la décimale est 123,123, precision renvoie 6. Par exemple : Decimal D1 = 123.45; Integer precision1 = D1.precision(); system.assertEquals(precision1, 5); Decimal D2 = 123.123; Integer precision2 = D2.precision(); system.assertEquals(precision2, 6); round Long Renvoie l'approximation arrondie de cette décimale. Le chiffre est arrondi à zéro décimale en utilisant le mode d'arrondi au dixième supérieur, c.-à-d. qu'il arrondit au « plus proche voisin », sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit au voisin pair. Notez que statistiquement ce mode d'arrondi réduit les erreurs cumulées lorsqu'il est régulièrement appliqué dans une séquence de calculs. Pour plus d'informations sur le mode d'arrondi au dixième supérieur, reportez-vous à Mode d'arrondi. Par exemple : Decimal D1 = 5.5; Long L1 = D1.round(); system.assertEquals(L1, 6); Référence Nom salesforce | Méthodes Primitives Apex | 370 Arguments Type de renvoi Description Decimal D2= 5.2; Long L2= D2.round(); system.assertEquals(L2, 5); Decimal D3= -5.7; Long L3= D3.round(); system.assertEquals(L3, -6); round System.RoundingMode Long roundingMode scale setScale Integer scale Renvoie l'approximation arrondie de cette décimale. Le nombre est arrondi à zéro décimale en utilisant le mode d'arrondi spécifié par roundingMode. Pour plus d'informations sur les valeurs valides pour roundingMode, reportez-vous à Mode d'arrondi. Integer Renvoie l'échelle de la décimale, c.-à-d. le nombre de décimales. Decimal Définit l'échelle de la décimale du nombre donné de décimales à l'aide de l'arrondi au dixième supérieur, si nécessaire. Le mode d'arrondi au dixième supérieur arrondit au « plus proche voisin », sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit au voisin pair. Pour plus d'informations sur le mode d'arrondi au dixième supérieur, reportez-vous à Mode d'arrondi. La valeur de scale doit être comprise entre -33 and 33. Si vous ne définissez pas explicitement l'échelle d'une décimale, elle est déterminée par l'élément à partir duquel la décimale est créée : Référence Nom salesforce | Méthodes Primitives Apex | 371 Arguments Type de renvoi Description • • • setScale Integer scale, Decimal System.RoundingMode roundingMode Si la décimale est créée dans le cadre d'une requête, l'échelle est basée sur l'échelle du champ renvoyé depuis la requête. Si la décimale est créée depuis une chaîne, l'échelle est le nombre de caractères après le point décimal de la chaîne. Si la décimale est créée à partir d'un nombre non-décimal, l'échelle est déterminée en convertissant le nombre en chaîne, puis en utilisant le nombre de caractères après le point décimal. Définit l'échelle de la décimale du nombre donné de décimales, en utilisant le mode d'arrondi spécifié par roundingMode, si nécessaire. Pour plus d'informations sur les valeurs valides pour roundingMode, reportez-vous à Mode d'arrondi. La valeur de scale doit être comprise entre -32 768 and 32 767. Si vous ne définissez pas explicitement l'échelle d'une décimale, elle est déterminée par l'élément à partir duquel la décimale est créée : • • • Si la décimale est créée dans le cadre d'une requête, l'échelle est basée sur l'échelle du champ renvoyé depuis la requête. Si la décimale est créée depuis une chaîne, l'échelle est le nombre de caractères après le point décimal de la chaîne. Si la décimale est créée à partir d'un nombre non-décimal, l'échelle est déterminée en convertissant le nombre en chaîne, puis en utilisant le nombre de caractères après le point décimal. stripTrailingZeros Decimal Renvoie la décimale avec tous les zéros de fin supprimés. toPlainString String Renvoie la valeur de chaîne de cette décimale, sans utiliser la notation scientifique. Pour plus d'informations sur la décimale, reportez-vous à Types de données Primitive à la page 29. Mode d'arrondi Le mode d'arrondi spécifie le comportement de l'arrondi des opérations numériques capables d'ignorer la précision. Chaque mode d'arrondi indique comment le chiffre renvoyé le moins significatif d'un résultat arrondi doit être calculé. Les valeurs valides sont les suivantes pour roundingMode. Référence salesforce | Méthodes Primitives Apex | 372 Nom Description CEILING Arrondit à l'infini positif. Si le résultat est positif, ce mode se comporte comme le mode d'arrondi UP. Si le résultat est négatif, il se comporte comme le mode d'arrondi DOWN. Notez que ce mode d'arrondi ne diminue jamais la valeur calculée. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi CEILING : 6 • Nombre saisi 1,1 : résultat du mode d'arrondi CEILING : 2 • Nombre saisi -1,1 : résultat du mode d'arrondi CEILING : -1 • Nombre saisi -2,7 : résultat du mode d'arrondi CEILING : -2 DOWN Arrondit à zéro. Ce mode d'arrondi diminue toujours les fractions (point décimal) avant l'exécution. Notez que ce mode d'arrondi n'augmente jamais la magnitude de la valeur calculée. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi DOWN : 5 • Nombre saisi 1,1 : résultat du mode d'arrondi DOWN : 1 • Nombre saisi -1,1 : résultat du mode d'arrondi DOWN : -1 • Nombre saisi -2,7 : résultat du mode d'arrondi DOWN : -2 FLOOR Arrondit à l'infini négatif. Si le résultat est positif, ce mode se comporte comme le mode d'arrondi DOWN. S'il est négatif, il se comporte comme le mode d'arrondi UP. Notez que ce mode d'arrondi n'augmente jamais la valeur calculée. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi FLOOR : 5 • Nombre saisi 1,1 : résultat du mode d'arrondi FLOOR : 1 • Nombre saisi -1,1 : résultat du mode d'arrondi FLOOR : -2 • Nombre saisi -2,7 : résultat du mode d'arrondi FLOOR : -3 HALF_DOWN Arrondit au « plus proche voisin », sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit à un nombre inférieur. Ce mode d'arrondi se comporte comme le mode d'arrondi UP si la fraction ignorée (point décimal) est > 0,5, sinon il se comporte comme le mode d'arrondi DOWN. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi HALF_DOWN : 5 • Nombre saisi 1,1 : résultat du mode d'arrondi HALF_DOWN : 1 • Nombre saisi -1,1 : résultat du mode d'arrondi HALF_DOWN : -1 • Nombre saisi -2,7 : résultat du mode d'arrondi HALF_DOWN : -2 HALF_EVEN Arrondit au « plus proche voisin », sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit au voisin pair. Ce mode d'arrondi se comporte comme le mode d'arrondi HALF_UP si le chiffre de gauche de la fraction ignorée (virgule) est impair. Il se comporte comme le mode d'arrondi HALF_DOWN s'il est pair. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi HALF_EVEN : 6 • Nombre saisi 1,1 : résultat du mode d'arrondi HALF_EVEN : 1 • Nombre saisi -1,1 : résultat du mode d'arrondi HALF_EVEN : -1 • Nombre saisi -2,7 : résultat du mode d'arrondi HALF_EVEN : -3 Notez que statistiquement ce mode d'arrondi réduit les erreurs cumulées lorsqu'il est régulièrement appliqué dans une séquence de calculs. HALF_UP Arrondit au « plus proche voisin » sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit à un nombre supérieur. Cette méthode d'arrondi se comporte comme la Référence salesforce | Méthodes Primitives Apex | 373 Nom Description méthode d'arrondi UP si la fraction ignorée (virgule) est >= 0,5, sinon elle se comporte comme la méthode d'arrondi DOWN. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi HALF_UP : 6 • Nombre saisi 1,1 : résultat du mode d'arrondi HALF_UP : 1 • Nombre saisi -1,1 : résultat du mode d'arrondi HALF_UP : -1 • Nombre saisi -2,7 : résultat du mode d'arrondi HALF_UP : -3 UNNECESSARY Affirme que l'opération demandée a un résultat exact, ce qui signifie qu'aucun arrondi n'est nécessaire. Si ce mode d'arrondi est spécifié dans une opération qui génère un résultat inexact, une exception est levée. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi UNNECESSARY : Exception • Nombre saisi 1,0 : résultat du mode d'arrondi UNNECESSARY : 1 UP Arrondit en s'écartant de zéro. Ce mode d'arrondi tronque toujours les fractions (point décimal) avant l'exécution. Notez que ce mode d'arrondi ne diminue jamais la magnitude de la valeur calculée. Par exemple : • Nombre saisi 5,5 : résultat du mode d'arrondi UP : 6 • Nombre saisi 1,1 : résultat du mode d'arrondi UP : 2 • Nombre saisi -1,1 : résultat du mode d'arrondi UP : -2 • Nombre saisi -2,7 : résultat du mode d'arrondi UP : -3 Méthodes Double Les méthodes ci-dessous sont les méthodes statiques système pour Double. Nom Arguments Type de renvoi Description valueOf String s Double Renvoie une double qui contient la valeur de la chaîne spécifiée. Comme dans Java, la chaîne est interprétée en tant que décimale signée. Par exemple : Double DD1 = double.valueOf('3.14159'); valueOf Object fieldValue Double Convertit la valeur du champ de suivi d'historique spécifiée en une valeur double. Utilisez cette méthode avec les champs OldValue ou NewValue de sObjects historiques, tels que AccountHistory, lorsque le type de champ correspond à un type Double, par exemple un champ numérique. Exemple : List<AccountHistory> ahlist = [SELECT Field,OldValue,NewValue Référence Nom salesforce | Méthodes Primitives Apex | 374 Arguments Type de renvoi Description FROM AccountHistory]; for(AccountHistory ah : ahlist) { System.debug('Field: ' + ah.Field); if (ah.field == 'NumberOfEmployees') { Double oldValue = Double.valueOf(ah.OldValue); Double newValue = Double.valueOf(ah.NewValue); } Les méthodes ci-dessous sont les méthodes d'instance pour Double. Nom Arguments Type de renvoi Description format String Renvoie la valeur de chaîne pour cette double en utilisant les paramètres régionaux de l'utilisateur actif. intValue Integer Renvoie la valeur de nombre entier de cette double la convertissant en nombre entier. Par exemple : Double DD1 = double.valueOf('3.14159'); Integer value = DD1.intValue(); system.assertEquals(value, 3); longValue Long Renvoie la valeur de longueur de cette double. round Long Renvoie la valeur arrondie de cette double. Le chiffre est arrondi à zéro décimale en utilisant le mode d'arrondi au dixième supérieur, c.-à-d. qu'il arrondit au « plus proche voisin », sauf si les deux voisins sont équidistants, auquel cas ce mode arrondit au voisin pair. Notez que statistiquement ce mode d'arrondi réduit les erreurs cumulées lorsqu'il est régulièrement appliqué dans une séquence de calculs. Pour plus d'informations sur le mode d'arrondi au dixième supérieur, reportez-vous à Mode d'arrondi. Par exemple : Double D1 = 5.5; Long L1 = D1.round(); system.assertEquals(L1, 6); Référence Nom salesforce | Méthodes Primitives Apex | 375 Arguments Type de renvoi Description Double D2= 5.2; Long L2= D2.round(); system.assertEquals(L2, 5); Double D3= -5.7; Long L3= D3.round(); system.assertEquals(L3, -6); Pour plus d'informations sur le type Double, reportez-vous à Types de données Primitive à la page 29. Méthodes ID Les méthodes suivantes sont les méthodes statiques pour ID. Méthode Arguments Type de renvoi Description valueOf String s ID Convertit la chaîne spécifiée en ID et renvoie l'ID. Les méthodes suivantes sont les méthodes d'instance pour ID. Méthode Arguments addError String errorMsg Type de renvoi Description Marque un enregistrement avec un message d'erreur personnalisé et empêche toute opération DML de se produire. L'argument errorMsg est le message d'erreur avec lequel marquer l'enregistrement. Cette méthode est similaire à la méthode de sObject addError. addError Exception exception Marque un enregistrement avec un message d'erreur personnalisé et empêche toute opération DML de se produire. L'argument exception est un objet Exception ou un objet d'exception personnalisé contenant le message d'erreur avec lequel marquer l'enregistrement. Cette méthode est similaire à la méthode de sObject addError. Référence salesforce | Méthodes Primitives Apex | 376 Méthode Arguments getSObjectType Type de renvoi Description Schema.SObjectType Renvoie le jeton du sObject correspondant à cet ID. Cette méthode est utilisée principalement avec des informations de description. Pour plus d'informations sur l'information describe, reportez-vous à Compréhension de l'information Describe Apex. Pour un exemple, reportez-vous à Exemple : Obtention d'un jeton de sObject à partir d'un ID. Exemple : Obtention d'un jeton de sObject à partir d'un ID Cet exemple montre comment utiliser la méthode getSObjectType pour obtenir un jeton de sObject à partir d'un ID. La méthode updateOwner dans cet exemple accepte une liste d'ID de sObjects pour mettre à jour le champ ID de propriétaire (ownerId). Cette liste contient les ID de sObjects de même type. Le deuxième paramètre est le nouvel ID du propriétaire. Notez qu'il s'agit d'une méthode future, par conséquent, elle n'accepte pas les types de sObject en tant que paramètres. Elle accepte les ID de sObjects. Cette méthode obtient le jeton de sObject à partir du premier ID de la liste, exécute describe pour obtenir le nom d'objet, puis construit une requête de façon dynamique. Elle demande ensuite tous les sObjects et met à jour leur champ ID de propriétaire avec le nouvel ID de propriétaire. public class MyDynamicSolution { @future public static void updateOwner(List<ID> objIds, ID newOwnerId) { // Validate input System.assert(objIds != null); System.assert(objIds.size() > 0); System.assert(newOwnerId != null); // Get the sObject token from the first ID // (the List contains IDs of sObjects of the same type). Schema.SObjectType token = objIds[0].getSObjectType(); // Using the token, do a describe // and construct a query dynamically. Schema.DescribeSObjectResult dr = token.getDescribe(); String queryString = 'SELECT ownerId FROM ' + dr.getName() + ' WHERE '; for(ID objId : objIds) { Référence salesforce | Méthodes Primitives Apex | 377 queryString += 'Id=\'' + objId + '\' OR '; } // Remove the last ' OR' queryString = queryString.subString(0, queryString.length() - 4); sObject[] objDBList = Database.query(queryString); System.assert(objDBList.size() > 0); // Update the owner ID on the sObjects for(Integer i=0;i<objDBList.size();i++) { objDBList[i].put('ownerId', newOwnerId); } Database.SaveResult[] srList = Database.update(objDBList, false); for(Database.SaveResult sr : srList) { if (sr.isSuccess()) { System.debug('Updated owner ID successfully for ' + dr.getName() + ' ID ' + sr.getId()); } else { System.debug('Updating ' + dr.getName() + ' returned the following errors.'); for(Database.Error e : sr.getErrors()) { System.debug(e.getMessage()); } } } } } Méthodes Integer Les méthodes ci-dessous sont les méthodes statiques système pour Integer. Référence salesforce | Méthodes Primitives Apex | 378 Nom Arguments Type de renvoi Description valueOf String s Integer Renvoie un nombre entier qui contient la valeur de la chaîne spécifiée. Comme dans Java, la chaîne est interprétée en tant que nombre entier décimal signé. Par exemple : Integer myInt = integer.valueOf('123'); valueOf Object fieldValue Integer Convertit la valeur du champ de suivi d'historique spécifiée en nombre entier. Utilisez cette méthode avec les champs OldValue ou NewValue de sObjects historiques, tels que AccountHistory, lorsque le type correspond à Integer, tel qu'un champ numérique. Exemple : List<AccountHistory> ahlist = [SELECT Field,OldValue,NewValue FROM AccountHistory]; for(AccountHistory ah : ahlist) { System.debug('Field: ' + ah.Field); if (ah.field == 'NumberOfEmployees') { Integer oldValue = Integer.valueOf(ah.OldValue); Integer newValue = Integer.valueOf(ah.NewValue); } Les méthodes ci-dessous sont les méthodes d'instance pour Integer. Nom format Arguments Type de renvoi Description String Renvoie le nombre entier sous forme de chaîne en utilisant les paramètres régionaux de l'utilisateur actif Pour plus d'informations sur le type Integer, reportez-vous à Types de données Primitive à la page 29. Méthodes Long La méthode ci-dessous est la méthode statique système pour Long. Référence salesforce | Méthodes Primitives Apex | 379 Nom Arguments Type de renvoi Description valueOf String s Long Renvoie une longueur qui contient la valeur de la chaîne spécifiée. Comme dans Java, la chaîne est interprétée en tant que longueur de décimale signée. Par exemple : Long L1 = long.valueOf('123456789'); Les méthodes ci-dessous sont les méthodes d'instance pour Long. Nom Arguments Type de renvoi Description format String Renvoie le format de chaîne pour cette longueur en utilisant les paramètres régionaux de l'utilisateur actif. intValue Integer Renvoie la valeur de nombre entier pour cette longueur. Pour plus d'informations sur le type Long, reportez-vous à Types de données Primitive à la page 29. Méthodes String Les méthodes ci-dessous sont les méthodes statiques système pour une chaîne. Nom Type de renvoi Description escapeSingleQuotes String s String Renvoie une chaîne avec le caractère d'échappement (\) ajouté avant tous les guillemets simples dans la chaîne s. Cette méthode est utile lors de la création d'une instruction SOQL dynamique, afin d'éviter une injection SOQL. Pour plus d'informations sur SOQL dynamique, reportez-vous à SOQL dynamique. Reportez-vous également à Exemple de fractionnement de chaîne. String s String Traite la chaîne actuelle en tant que modèle pouvant être utilisé pour une substitution de la même façon que apex:outputText. String Renvoie une chaîne à partir des valeurs de la liste de nombres entiers. String Renvoie une séquence initiale de caractères sous forme de chaîne qui est commune à toutes les chaînes spécifiées. format Arguments List<String> arguments fromCharArray List<Integer> charArray getCommonPrefix List<String> strings Exemple : List<String> ls = new List<String> {'SFDCApex', Référence Nom salesforce | Méthodes Primitives Apex | 380 Arguments Type de renvoi Description 'SFDCVisualforce'}; String prefix = String.getCommonPrefix( ls); System.assertEquals( 'SFDC', prefix); isBlank String s Boolean Renvoie true si la chaîne spécifiée est un espace blanc, vide ('') ou null, sinon renvoie false. isEmpty String s Boolean Renvoie true si la chaîne spécifiée est vide ('') ou null, sinon renvoie false. isNotBlank String s Boolean Renvoie true si la chaîne spécifiée n'est pas un espace, n'est pas vide ('') et n'est pas null, sinon renvoie false. isNotEmpty String s Boolean Renvoie true si la chaîne spécifiée n'est pas vide ('') et n'est pas null, sinon renvoie false. join Object String Joint les éléments de l'objet itérable spécifié, tel qu'une liste, dans une chaîne unique séparée par le délimiteur spécifié. iterableObj String separator Exemple : List<Integer> li = new List<Integer> {10, 20, 30}; String s = String.join( li, '/'); System.assertEquals( '10/20/30', s); valueOf Date d String Renvoie une chaîne qui représente la date spécifiée sous format standard « aaaa-MM-jj ». Par exemple : Date myDate = Date.Today(); String sDate = String.valueOf(myDate); valueOf Datetime dt String Renvoie une chaîne qui représente la date/heure spécifiée sous format standard « aaaa-MM-jj HH:mm:ss » dans le fuseau horaire local. Référence salesforce | Méthodes Primitives Apex | 381 Nom Arguments Type de renvoi Description valueOf Decimal d String Renvoie une chaîne qui représente la décimale spécifiée. valueOf Double d String Renvoie une chaîne qui représente le double spécifié. Exemple : Double myDouble = 12.34; String myString = String.valueOf(myDouble); System.assertEquals( '12.34', myString); valueOf Integer I String Renvoie une chaîne qui représente le nombre entier spécifié. valueOf Long l String Renvoie une chaîne qui représente la longueur spécifiée. valueOf Object x String Renvoie une représentation de chaîne de l'argument d'objet spécifié. Exemple : List<Integer> ls = new List<Integer>(); ls.add(10); ls.add(20); String strList = String.valueOf(ls); System.assertEquals( '(10, 20)', strList); Si l'argument n'est pas une chaîne, la méthode valueOf le convertit en chaîne en appelant la méthode toString dans l'argument, si disponible, ou toute méthode toString remplacée si l'argument est un type défini par l'utilisateur. Sinon, si aucune méthode toString n'est disponible, elle renvoie une représentation de chaîne de l'argument. valueOfGmt Datetime dt String Renvoie une chaîne qui représente la date/heure spécifiée sous format standard « aaaa-MM-jj HH:mm:ss » dans le fuseau horaire GMT. Référence salesforce | Méthodes Primitives Apex | 382 Les méthodes ci-dessous sont les méthodes d'instance pour une chaîne. Nom Arguments Type de renvoi Description abbreviate Integer maxWidth String Renvoie une version abrégée de la chaîne, de la longueur spécifiée et avec des ellipses ajoutées si la chaîne actuelle est plus longue que la longueur spécifiée, sinon renvoie la chaîne d'origine sans ellipse. Si maxWidth est inférieur à quatre, cette méthode lève une exception d'exécution. Exemple : String s = 'Hello Maximillian'; String s2 = s.abbreviate(8); System.assertEquals( 'Hello...', s2); System.assertEquals( 8, s2.length()); abbreviate Integer maxWidth Integer offset String Renvoie une version abrégée de la chaîne en commençant par le décalage de caractère spécifié et de la longueur spécifiée. Des ellipses sont ajoutées à la chaîne renvoyée, au début et à la fin si des caractères ont été supprimés à ces emplacements. Notez que le décalage n'est pas obligatoirement le caractère le plus à gauche de la chaîne renvoyée ou le premier caractère qui suit les ellipses, mais il figure quelque part dans le résultat. Dans tous les cas, abbreviate ne renvoie pas une chaîne de longueur supérieure à maxWidth. Si maxWidth est trop petit, cette méthode lève une exception d'exécution. Cette méthode est basée sur son équivalent abbreviate dans la bibliothèque Apache Commons Lang StringUtils. Exemple : String s = 'Hello Maximillian'; // Start at M String s2 = Référence Nom salesforce | Méthodes Primitives Apex | 383 Arguments Type de renvoi Description s.abbreviate(9,6); System.assertEquals( '...Max...', s2); System.assertEquals( 9, s2.length()); String capitalize Renvoie la chaîne actuelle avec la première lettre modifiée en initiale majuscule, comme la méthode Java Character.toTitleCase(char). Exemple : String s = 'hello maximillian'; String s2 = s.capitalize(); System.assertEquals( 'Hello maximillian', s2); center Integer size String Renvoie une version de la chaîne actuelle de la taille spécifiée, remplie avec des espaces à gauche et à droite pour figurer au centre. Si la taille spécifiée est plus petite que la taille de la chaîne actuelle, la chaîne entière est renvoyée sans espaces ajoutés. Exemple : String s = 'hello'; String s2 = s.center(9); System.assertEquals( ' hello ', s2); center Integer size String padStr String Renvoie une version de la chaîne actuelle à la taille spécifiée, remplie avec la chaîne spécifiée à gauche et à droite pour figurer au centre. Si la taille spécifiée est plus Référence Nom salesforce | Méthodes Primitives Apex | 384 Arguments Type de renvoi Description petite que la taille de la chaîne actuelle, la chaîne entière est renvoyée sans remplissage. Exemple : String s = 'hello'; String s2 = s.center(9); System.assertEquals( '--hello--', s2); compareTo String compString Integer Compare deux chaînes d'un point de vue lexicographique, en fonction de la valeur Unicode de chaque caractère des chaînes. Le résultat est le suivant : • Un nombre entier négatif si la chaîne qui a appelé la méthode de façon lexicographique précède compString • • Un nombre entier positif si la chaîne qui a appelé la méthode de façon lexicographique suit compString Zéro si les chaînes sont égales S'il n'existe aucune position d'index à laquelle les chaînes diffèrent, la chaîne la plus courte précède de façon lexicographique la plus longue. Par exemple : String myString1 = 'abcde'; String myString2 = 'abcd'; Integer result = myString1.compareTo(myString2); System.assertEquals(result, 1); Notez que cette méthode renvoie 0 chaque fois que la méthode equals renvoie true. contains String compString Boolean Renvoie true si et seulement si la chaîne qui a appelé la méthode contient la séquence spécifiée de caractères dans compString. Par exemple : String myString1 = 'abcde'; String myString2 = 'abcd'; Boolean result = Référence Nom salesforce | Méthodes Primitives Apex | 385 Arguments Type de renvoi Description myString1.contains(myString2); System.assertEquals(result, true); containsAny String compString Boolean Renvoie true si la chaîne actuelle contient l'un des caractères de la chaîne spécifiée, sinon renvoie false. Exemple : String s = 'hello'; Boolean b1 = s.containsAny('hx'); Boolean b2 = s.containsAny('x'); System.assertEquals( true, b1); System.assertEquals( false, b2); containsIgnoreCase String compString Boolean Renvoie true si la chaîne actuelle contient la séquence de caractères spécifiée, quelle que soit la casse, sinon renvoie false. Exemple : String s = 'hello'; Boolean b = s.containsIgnoreCase('HE'); System.assertEquals( true, b); containsNone String compString Boolean Renvoie true si la chaîne actuelle ne contient pas la séquence de caractères spécifiée, sinon renvoie false. Si compString est une chaîne vide ou si la chaîne actuelle est vide, cette méthode renvoie true. Si compString est null, cette méthode renvoie une exception d'exécution. Référence salesforce | Méthodes Primitives Apex | 386 Nom Arguments Type de renvoi containsOnly String compString Boolean Description Renvoie true si la chaîne actuelle contient uniquement des caractères de la séquence de caractères spécifiée, et aucun autre, sinon renvoie false. Exemple : String s1 = 'abba'; String s2 = 'abba xyz'; Boolean b1 = s1.containsOnly('abcd'); System.assertEquals( true, b1); Boolean b2 = s2.containsOnly('abcd'); System.assertEquals( false, b2); containsWhitespace countMatches deleteWhitespace difference Boolean String compString Integer String String compString String Renvoie true si la chaîne spécifiée contient des caractères d'espacement, sinon renvoie false. Renvoie le nombre d'occurrences de la sous-chaîne dans la chaîne actuelle. Renvoie une version de la chaîne actuelle avec tous les caractères d'espacement supprimés. Renvoie la différence entre la chaîne actuelle et la chaîne spécifiée. Si compString est une chaîne vide, cette méthode renvoie une chaîne vide. Si compString est null, cette méthode lève une exception d'exécution. Exemple : String s = 'Hello Jane'; String d1 = s.difference('Hello Max'); System.assertEquals( Référence Nom salesforce | Méthodes Primitives Apex | 387 Arguments Type de renvoi Description 'Max', d1); String d2 = s.difference('Goodbye'); System.assertEquals( 'Goodbye', d2); String suffix Boolean Renvoie true si la chaîne qui a appelé la méthode se termine par le suffix spécifié. endsWithIgnoreCase String suffix Boolean Renvoie true si la chaîne spécifiée se termine par le suffixe spécifié, sinon renvoie false. endsWith equals String compString Boolean Renvoie true si compString n'est pas nul et représente la même séquence binaire de caractères que la chaîne qui a appelé la méthode. Cette méthode est true chaque fois que la méthode compareTo renvoie 0. Par exemple : String myString1 = 'abcde'; String myString2 = 'abcd'; Boolean result = myString1.equals(myString2); System.assertEquals(result, false); Notez que l'opérateur == exécute également une comparaison de chaîne, mais il n'est pas sensible à la casse pour se conformer aux sémantiques Apex. (== est sensible à la casse pour une comparaison d'ID pour la même raison). equalsIgnoreCase String compString Boolean Renvoie true si le compString n'est pas null et représente la même séquence de caractères que la chaîne qui a appelé la méthode, en ignorant la casse. Par exemple : String myString1 = 'abcd'; String myString2 = 'ABCD'; Boolean result = myString1.equalsIgnoreCase(myString2); System.assertEquals(result, true); Référence Nom escapeCsv salesforce | Méthodes Primitives Apex | 388 Arguments Type de renvoi Description String Renvoie une chaîne d'une colonne CSV placée entre guillemets doubles, si nécessaire. Si la chaîne contient une virgule, un saut de ligne ou un guillemet double, la chaîne est renvoyée entre guillemets doubles. De même, tout caractère guillemet double de la chaîne est échappé avec un autre guillemet double. Si la chaîne ne contient pas de virgule, de saut de ligne ou de guillemet double, elle est renvoyée sans modification. Cette méthode est basée sur son équivalent escapeCsv dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = 'Max1, "Max2"'; String s2 = s1.escapeCsv(); System.assertEquals( '"Max1, ""Max2"""', s2); escapeEcmaScript String Échappe les caractères de la chaîne à l'aide des règles de chaîne EcmaScript. À la différence des chaînes Apex, dans les chaînes EcmaScript un guillemet unique et une barre oblique (/) sont échappés. Cette méthode est basée sur son équivalent escapeEcmaScript dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"grade": 3.9/4.0'; String s2 = s1.escapeEcmaScript(); System.debug(s2); // Output is: // \"grade\": 3.9\/4.0 System.assertEquals( Référence Nom salesforce | Méthodes Primitives Apex | 389 Arguments Type de renvoi Description '\\"grade\\": 3.9\\/4.0', s2); escapeHtml3 String Échappe les caractères d'une chaîne à l'aide des entités HTML 3.0. Cette méthode est basée sur son équivalent escapeHtml3 dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.escapeHtml3(); System.debug(s2); // Output: // "<Black& // White>" escapeHtml4 String Échappe les caractères d'une chaîne à l'aide des entités HTML 4.0. Cette méthode est basée sur son équivalent escapeHtml4 dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.escapeHtml4(); System.debug(s2); // Output: // "<Black& // White>" Référence Nom salesforce | Méthodes Primitives Apex | 390 Arguments escapeXml Type de renvoi Description String Échappe les caractères d'une chaîne à l'aide des entités XML. Prend en charge uniquement les cinq entités XML de base (gt, lt, quot, amp, apos). Ne prend pas en charge les DTD ou les entités externes. Les caractères Unicode supérieurs à 0x7f ne sont pas échappés. Cette méthode est basée sur son équivalent escapeXml dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.escapeXml(); System.debug(s2); // Output: // "<Black& // White>" getLevenshtein Distance String s Integer Renvoie la distance Levenshtein entre la chaîne actuelle et la chaîne spécifiée. La distance Levenshtein est le nombre de modifications nécessaires pour changer une chaîne en une autre chaîne. Chaque changement est une modification de caractère unique (suppression, insertion ou substitution). Exemple : String s = 'Hello Joe'; Integer i = s.getLevenshteinDistance( 'Hello Max'); System.assertEquals( 3, i); Référence salesforce | Méthodes Primitives Apex | 391 Nom Arguments Type de renvoi Description getLevenshtein Distance String s Integer Renvoie la distance Levenshtein entre la chaîne actuelle et la chaîne spécifiée si elle est inférieure ou égale au seuil donné, sinon renvoie -1. Integer threshold La distance Levenshtein est le nombre de modifications nécessaires pour changer une chaîne en une autre chaîne. Chaque changement est une modification de caractère unique (suppression, insertion ou substitution). Exemple : Dans cet exemple, la distance Levenshtein est 3, mais l'argument de seuil est 2, qui est inférieur à la distance, donc cette méthode renvoie -1. String s = 'Hello Jane'; Integer i = s.getLevenshteinDistance( 'Hello Max', 2); System.assertEquals( -1, i); indexOf String subString Integer Renvoie l'index de la première occurrence de la sous-chaîne spécifiée. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. indexOf String substring Integer Renvoie l'index basé sur zéro de la première occurrence de la sous-chaîne spécifiée à partir du point d'index i. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. Par exemple : Integer i String myString1 = 'abcd'; String myString2 = 'bc'; Integer result = myString1.indexOf(myString2, 0); System.assertEquals(1, result); indexOfAny String substring Integer Renvoie l'index basé sur zéro de la première occurrence de tout caractère spécifié dans la sous-chaîne. Si aucun des caractères ne se présente, cette méthode renvoie -1. Exemple : String s1 = 'abcd'; Référence Nom salesforce | Méthodes Primitives Apex | 392 Arguments Type de renvoi Description String s2 = 'xc'; Integer result = s1.indexOfAny(s2); System.assertEquals( 2, result); indexOfAnyBut String substring Integer Renvoie l'index basé sur zéro de la première occurrence d'un caractère qui n'est pas dans la sous-chaîne spécifiée. Sinon, cette méthode renvoie -1. Exemple : String s1 = 'abcd'; String s2 = 'xc'; Integer result = s1.indexOfAnyBut(s2); System.assertEquals( 0, result); indexOfDifference String s Integer Renvoie l'index basé sur zéro du caractère auquel la chaîne actuelle commence à être différente de la chaîne spécifiée. Exemple : String s1 = 'abcd'; String s2 = 'abxc'; Integer result = s1.indexOfDifference(s2); System.assertEquals( 2, result); indexOfIgnoreCase String substring Integer Renvoie l'index basé sur zéro de la première occurrence de la sous-chaîne spécifiée, quelle que soit la casse. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. Par exemple : String s1 = 'abcd'; String s2 = 'BC'; Integer result = Référence Nom salesforce | Méthodes Primitives Apex | 393 Arguments Type de renvoi Description s1.indexOfIgnoreCase(s2, 0); System.assertEquals(1, result); indexOfIgnoreCase String substring Integer Renvoie l'index basé sur zéro de la première occurrence de la sous-chaîne spécifiée à partir du point d'index i, quelle que soit la casse. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. isAllLowerCase Boolean Renvoie true si tous les caractères de la chaîne actuelle sont en minuscules, sinon renvoie false. isAllUpperCase Boolean Renvoie true si tous les caractères de la chaîne actuelle sont en majuscules, sinon renvoie false. isAlpha Boolean Renvoie true si tous les caractères de la chaîne actuelle sont uniquement des lettres Unicode, sinon renvoie false. Integer startPosition Exemple : // Letters only String s1 = 'abc'; // Returns true Boolean b1 = s1.isAlpha(); System.assertEquals( true, b1); // Letters and numbers String s2 = 'abc 21'; // Returns false Boolean b2 = s2.isAlpha(); System.assertEquals( false, b2); isAlphaSpace Boolean Renvoie true si tous les caractères de la chaîne actuelle sont uniquement des lettres Unicode ou des espaces, sinon renvoie false. Référence Nom isAlphanumeric salesforce | Méthodes Primitives Apex | 394 Arguments Type de renvoi Description Boolean Renvoie true si tous les caractères de la chaîne actuelle sont uniquement des lettres Unicode ou des nombres, sinon renvoie false. Exemple : // Letters only String s1 = 'abc'; // Returns true Boolean b1 = s1.isAlphanumeric(); System.assertEquals( true, b1); // Letters and numbers String s2 = 'abc021'; // Returns true Boolean b2 = s2.isAlphanumeric(); System.assertEquals( true, b2); isAlphanumericSpace Boolean Renvoie true si tous les caractères de la chaîne actuelle sont uniquement des lettres Unicode, des chiffres ou des espaces, sinon renvoie false. isAsciiPrintable Boolean Renvoie true si la chaîne spécifiée contient uniquement des caractères imprimables ASCII, sinon renvoie false. isNumeric Boolean Renvoie true si la chaîne spécifiée contient uniquement des chiffres Unicode, sinon renvoie false. Un point décimal (1.2) n'est pas un chiffre Unicode. isNumericSpace Boolean Renvoie true si la chaîne spécifiée contient uniquement des chiffres Unicode ou des espaces, sinon renvoie false. Un point décimal (1.2) n'est pas un chiffre Unicode. isWhitespace Boolean Renvoie true si la chaîne spécifiée uniquement des caractères d'espacement, sinon renvoie false. Référence salesforce | Méthodes Primitives Apex | 395 Nom Arguments Type de renvoi Description lastIndexOf String substring Integer Renvoie l'index de la dernière occurrence de la sous-chaîne spécifiée. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. lastIndexOf String substring Integer Renvoie l'index de la dernière occurrence de la sous-chaîne spécifiée, en commençant à partir du caractère à l'index 0 et en terminant à l'index spécifié. Integer endPosition Si la sous-chaîne ne se présente pas ou si endPosition est négatif, cette méthode renvoie -1. Si endPosition est supérieur au dernier index de la chaîne actuelle, la chaîne entière est recherchée. Exemple : String s1 = 'abcdaacd'; Integer i1 = s1.lastIndexOf('c', 7); System.assertEquals( 6, i1); Integer i2 = s1.lastIndexOf('c', 3); System.assertEquals( 2, i2); lastIndexOfIgnore String substring Case Integer Renvoie l'index de la dernière occurrence de la sous-chaîne spécifiée, quelle que soit la casse. Si la sous-chaîne ne se présente pas, cette méthode renvoie -1. Exemple : String s1 = 'abcdaacd'; Integer i1 = s1.lastIndexOfIgnoreCase('DAAC'); System.assertEquals( 3, i1); lastIndexOfIgnore String substring Case Integer endPosition Integer Renvoie l'index de la dernière occurrence de la sous-chaîne spécifiée, quelle que soit la casse, en Référence Nom salesforce | Méthodes Primitives Apex | 396 Arguments Type de renvoi Description commençant à partir du caractère à l'index 0 et en terminant à l'index spécifié. Si la sous-chaîne ne se présente pas ou si endPosition est négatif, cette méthode renvoie -1. Si endPosition est supérieur au dernier index de la chaîne actuelle, la chaîne entière est recherchée. Exemple : String s1 = 'abcdaacd'; Integer i1 = s1.lastIndexOfIgnoreCase('C', 7); System.assertEquals( 6, i1); left Integer length String Renvoie les caractères les plus à gauche de la chaîne actuelle de la longueur spécifiée. Si length est supérieur à la taille de la chaîne, la chaîne entière est renvoyée. Exemple : String s1 = 'abcdaacd'; String s2 = s1.left(3); System.assertEquals( 'abc', s2); leftPad Integer length String Renvoie la chaîne actuelle remplie d'espaces à gauche et de la longueur spécifiée. Si length est inférieur ou égal à la taille de la chaîne actuelle, la chaîne entière est renvoyée sans remplissage. Exemple : String s1 = 'abc'; String s2 = s1.leftPad(5); System.assertEquals( ' abc', s2); Référence Nom salesforce | Méthodes Primitives Apex | 397 Arguments length Type de renvoi Description Integer Renvoie le nombre de caractères Unicode 16 bits contenus dans la chaîne. Par exemple : String myString = 'abcd'; Integer result = myString.length(); System.assertEquals(result, 4); mid Integer startIndex String Integer length Renvoie une nouvelle chaîne commençant par le caractère au startIndex basé sur zéro spécifié, avec le nombre de caractères spécifié par length. Si startIndex est négatif, il est considéré comme étant égal à zéro. Si length est négatif ou zéro, une chaîne vide est renvoyée. Si length est supérieur aux caractères restants, le reste de la chaîne est renvoyé. Cette méthode est similaire aux méthodes substring(startIndex) et substring(startIndex, endIndex), à l'exception du deuxième argument qui correspond au nombre de caractères à renvoyer. Exemple : String s = 'abcde'; String s2 = s.mid(2, 3); System.assertEquals( 'cde', s2); normalizeSpace String Renvoie la chaîne actuelle avec les caractères d'espacement de tête, de fin et répétés supprimés. Cette méthode est basée sur son équivalent normalizeSpace dans la bibliothèque Apache Commons Lang StringUtils. Exemple : String s1 = 'Salesforce \t force.com'; String s2 = s1.normalizeSpace(); System.assertEquals( Référence Nom salesforce | Méthodes Primitives Apex | 398 Arguments Type de renvoi Description 'Salesforce force.com', s2); remove String substring String Supprime toutes les occurrences de la sous-chaîne spécifiée et renvoie le résultat de la chaîne. Exemple : String s1 = 'Salesforce and force.com'; String s2 = s1.remove('force'); System.assertEquals( 'Sales and .com', s2); removeEnd String substring String Supprime la sous-chaîne spécifiée uniquement si elle se présente à la fin de la chaîne. Exemple : String s1 = 'Salesforce and force.com'; String s2 = s1.removeEnd('.com'); System.assertEquals( 'Salesforce and force', s2); removeEndIgnoreCase String substring String Supprime la sous-chaîne spécifiée uniquement si elle se présente à la fin de la chaîne en utilisant une correspondance insensible à la casse. Exemple : String s1 = 'Salesforce and force.com'; String s2 = s1.removeEndIgnoreCase('.COM'); System.assertEquals( 'Salesforce and force', s2); Référence salesforce | Méthodes Primitives Apex | 399 Nom Arguments Type de renvoi Description removeStart String substring String Supprime la sous-chaîne spécifiée uniquement si elle se présente au début de la chaîne. Exemple : String s1 = 'Salesforce and force.com'; String s2 = s1.removeStart('Sales'); System.assertEquals( 'force and force.com', s2); removeStartIgnore String substring Case String Supprime la sous-chaîne spécifiée uniquement si elle se présente au début de la chaîne en utilisant une correspondance insensible à la casse. Exemple : String s1 = 'Salesforce and force.com'; String s2 = s1.removeStartIgnoreCase('SALES'); System.assertEquals( 'force and force.com', s2); repeat Integer numTimes String Renvoie la chaîne actuelle répétée le nombre de fois spécifié. Exemple : String s1 = 'SFDC'; String s2 = s1.repeat(2); System.assertEquals( 'SFDCSFDC', s2); repeat String separator Integer numTimes String Renvoie la chaîne actuelle répétée le nombre de fois spécifié, en utilisant le séparateur spécifié pour séparer les chaînes répétées. Exemple : String s1 = 'SFDC'; String s2 = Référence Nom salesforce | Méthodes Primitives Apex | 400 Arguments Type de renvoi Description s1.repeat('-', 2); System.assertEquals( 'SFDC-SFDC', s2); replace String target String Remplace chaque sous-chaîne d'une chaîne correspondant à la séquence cible littérale target par la séquence de remplacement littérale replacement spécifiée. String Remplace chaque sous-chaîne d'une chaîne correspondant à l'expression régulière regExp par la séquence de remplacement replacement. Pour plus d'informations sur les expressions régulières, reportez-vous à la classe Java Pattern. String Remplace la première sous-chaîne d'une chaîne correspondant à l'expression régulière regExp par la séquence de remplacement replacement. Pour plus d'informations sur les expressions régulières, reportez-vous à la classe Java Pattern. String Renvoie une chaîne avec tous les caractères inversés. String Renvoie les caractères les plus à droite de la chaîne actuelle de la longueur spécifiée. String replacement replaceAll String regExp String replacement replaceFirst String regExp String replacement reverse right Integer length Si length est supérieur à la taille de la chaîne, la chaîne entière est renvoyée. Exemple : String s1 = 'Hello Max'; String s2 = s1.right(3); System.assertEquals( 'Max', s2); rightPad Integer length String Renvoie la chaîne actuelle remplie d'espaces à droite et de la longueur spécifiée. Si length est inférieur ou égal à la taille de la chaîne actuelle, la chaîne entière est renvoyée sans remplissage. Exemple : String s1 = 'abc'; String s2 = Référence Nom salesforce | Méthodes Primitives Apex | 401 Arguments Type de renvoi Description s1.rightPad(5); System.assertEquals( 'abc split String regExp Integer limit String[] ', s2); Renvoie un liste contenant chaque sous-chaîne de la chaîne qui se termine par l'expression régulière regExp ou la fin de la chaîne. Pour plus d'informations sur les expressions régulières, reportez-vous à la classe Java Pattern. Les sous-chaînes sont placées dans la liste dans le même ordre que dans la chaîne. Si regExp ne correspond à aucune partie de la chaîne, la liste résultante contient un seul élément avec la chaîne d'origine. Le paramètre facultatif limit contrôle le nombre de fois où le modèle s'applique. Par conséquent, il affecte la longueur de la liste : • • • Si limit est supérieur à zéro, le modèle s'applique au maximum à limit - 1 fois, la longueur de la liste n'est pas supérieure à limit et la dernière entrée de la liste contient toutes les entrées au-delà du dernier délimiteur correspondant. Si limit est non positif, le modèle s'applique autant de fois que possible et la liste peut avoir n'importe quelle longueur. Si limit est égal à zéro, le modèle s'applique autant de fois que possible, la liste peut avoir n'importe quelle longueur et les chaînes vides de fin peuvent être ignorées. Par exemple, pour String s = 'boo:and:foo' : • s.split(':', 2) résulte en {'boo', 'and:foo'} • s.split(':', 5) résulte en {'boo', 'and', 'foo'} • s.split(':', -2) résulte en {'boo', 'and', 'foo'} • s.split('o', 5) résulte en {'b', '', ':and:f', '', ''} • s.split('o', -2) résulte en {'b', '', ':and:f', '', ''} • s.split('o', 0) résulte en {'b', '', ':and:f'} Référence Nom salesforce | Méthodes Primitives Apex | 402 Arguments Type de renvoi Description Reportez-vous également à Exemple de fractionnement de chaîne. List<String> splitByCharacterType Fractionne la chaîne actuelle par type de caractère et renvoie une liste de groupes de caractères continus du même type que les jetons complets. Pour plus d'informations sur les types de caractère utilisés, reportez-vous à java.lang.Character.getType(char). Exemple : String s1 = 'Force.com platform'; List<String> ls = s1.splitByCharacterType(); System.debug(ls); // Writes this output: // (F, orce, ., com, List<String> splitByCharacterType CamelCase , platform) Fractionne la chaîne actuelle par type de caractère et renvoie une liste de groupes de caractères continus du même type que les jetons complets, avec l'exception suivante : le caractère majuscule, le cas échéant, précédant immédiatement un jeton de caractère minuscule appartient au jeton de caractère suivant plutôt qu'au précédent. Pour plus d'informations sur les types de caractère utilisés, reportez-vous à java.lang.Character.getType(char). Exemple : String s1 = 'Force.com platform'; List<String> ls = s1.splitByCharacterTypeCamelCase(); System.debug(ls); // Writes this output: // (Force, ., com, startsWith String prefix Boolean , platform) Renvoie true si la chaîne qui a appelé la méthode commence par le prefix spécifié. Référence salesforce | Méthodes Primitives Apex | 403 Nom Arguments Type de renvoi Description startsWith IgnoreCase String prefix Boolean Renvoie true si la chaîne actuelle commence par le préfixe spécifié, quelle que soit la casse du préfixe. substring Integer startIndex String Renvoie une nouvelle chaîne qui commence par le caractère au startIndex basé sur zéro spécifié, et se termine à la fin de la chaîne. substring Integer startIndex String Renvoie une nouvelle chaîne qui commence par le caractère au startIndex basé sur zéro spécifié, et se termine au caractère à endIndex - 1. Par exemple : Integer endIndex 'hamburger'.substring(4, 8); // Returns "urge" 'smiles'.substring(1, 5); // Returns "mile" substringAfter String separator String Renvoie la sous-chaîne qui se présente après la première occurrence du séparateur spécifié. Exemple : String s1 = 'Force.com.platform'; String s2 = s1.substringAfter('.'); System.assertEquals( 'com.platform', s2); substringAfterLast String separator String Renvoie la sous-chaîne qui se présente après la dernière occurrence du séparateur spécifié. Exemple : String s1 = 'Force.com.platform'; String s2 = s1.substringAfterLast('.'); System.assertEquals( 'platform', s2); Référence Nom salesforce | Méthodes Primitives Apex | 404 Arguments substringBefore String separator Type de renvoi Description String Renvoie la sous-chaîne qui se présente avant la première occurrence du séparateur spécifié. Exemple : String s1 = 'Force.com.platform'; String s2 = s1.substringBefore('.'); System.assertEquals( 'Force', s2); substringBeforeLast String separator String Renvoie la sous-chaîne qui se présente avant la dernière occurrence du séparateur spécifié. Exemple : String s1 = 'Force.com.platform'; String s2 = s1.substringBeforeLast('.'); System.assertEquals( 'Force.com', s2); substringBetween String tag String Renvoie la sous-chaîne qui se présente entre deux instances de la chaîne spécifiée. Exemple : String s1 = 'tagYellowtag'; String s2 = s1.substringBetween('tag'); System.assertEquals( 'Yellow', s2); substringBetween String open String close String Renvoie la sous-chaîne qui se présente entre les deux chaînes spécifiées. Exemple : String s1 = 'xYellowy'; String s2 = s1.substringBetween('x','y'); Référence Nom salesforce | Méthodes Primitives Apex | 405 Arguments Type de renvoi Description System.assertEquals( 'Yellow', s2); swapCase String open String String close Permute la casse de tous les caractères et renvoie la chaîne résultante. Une majuscule et une initiale majuscule sont converties en minuscules, et une minuscule est convertie en majuscule. Exemple : String s1 = 'Force.com'; String s2 = s1.swapCase(); System.assertEquals( 'fORCE.COM', s2); toLowerCase toLowerCase String locale toUpperCase String Convertit tous les caractères de la chaîne en minuscules en utilisant les règles des paramètres régionaux par défaut. String Convertit tous les caractères de la chaîne en minuscules en utilisant les règles des paramètres régionaux spécifiés. String Convertit tous les caractères de la chaîne en majuscules en utilisant les règles des paramètres régionaux par défaut. Par exemple : String myString1 = 'abcd'; String myString2 = 'ABCD'; myString1 = myString1.toUpperCase(); Boolean result = myString1.equals(myString2); System.assertEquals(result, true); toUpperCase String locale String Convertit tous les caractères de la chaîne en majuscules en utilisant les règles des paramètres régionaux spécifiés. Référence Nom trim salesforce | Méthodes Primitives Apex | 406 Arguments Type de renvoi Description String Renvoie une copie de la chaîne qui ne contient plus aucun caractère d'espacement de début ou de fin. Les caractères de contrôle ASCII de début et de fin, tels que les tabulations et les nouvelles lignes, sont également supprimés. Les caractères d'espace et de contrôle qui ne sont pas placés au début ou à la fin de la phrase ne sont pas supprimés. uncapitalize String Renvoie la chaîne actuelle avec la première lettre en minuscule. Exemple : String s1 = 'Hello max'; String s2 = s1.uncapitalize(); System.assertEquals( 'hello max', s2); unescapeCsv String Renvoie une chaîne représentant une colonne CSV non échappée. Si la chaîne est placée entre guillemets doubles et contient une virgule, un saut de ligne ou un guillemet double, les guillemets sont supprimés. De même, tout caractère guillemet double échappé (paire de guillemets doubles) est réduit à un seul guillemet double. Si la chaîne n'est pas placée entre guillemets doubles, ou si elle l'est et ne contient pas de virgule, de saut de ligne ou de double guillemet, elle est renvoyée inchangée. Cette méthode est basée sur son équivalent unescapeCsv dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"Max1, ""Max2"""'; String s2 = s1.unescapeCsv(); System.assertEquals( Référence Nom salesforce | Méthodes Primitives Apex | 407 Arguments Type de renvoi Description 'Max1, "Max2"', s2); unescapeEcmaScript String L'échappement de tout littéral EcmaScript trouvé dans la chaîne est supprimé. Cette méthode est basée sur son équivalent unescapeEcmaScript dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '\"3.8\",\"3.9\"'; String s2 = s1.unescapeEcmaScript(); System.assertEquals( '"3.8","3.9"', s2); unescapeHtml3 String L'échappement des caractères d'une chaîne utilisant des entités HTML 3.0 est supprimé. Cette méthode est basée sur son équivalent unescapeHtml3 dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.unescapeHtml3(); System.assertEquals( '"<Black&White>"', s2); Référence Nom unescapeHtml4 salesforce | Méthodes Primitives Apex | 408 Arguments Type de renvoi Description String L'échappement des caractères d'une chaîne utilisant des entités HTML 4.0 est supprimé. Cette méthode est basée sur son équivalent unescapeHtml4 dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.unescapeHtml4(); System.assertEquals( '"<Black&White>"', s2); unescapeXml String L'échappement des caractères d'une chaîne utilisant des entités XML est supprimé. Prend en charge uniquement les cinq entités XML de base (gt, lt, quot, amp, apos). Ne prend pas en charge les DTD ou les entités externes. Cette méthode est basée sur son équivalent unescapeXml dans la bibliothèque Apache Commons Lang StringEscapeUtils. Exemple : String s1 = '"<Black&White>"'; String s2 = s1.unescapeXml(); System.assertEquals( '"<Black&White>"', s2); Pour plus d'informations sur le type String, reportez-vous à Types de données Primitif à la page 29. Référence salesforce | Méthodes Primitives Apex | 409 Exemple de fractionnement de chaîne Dans l'exemple suivant, une chaîne est fractionnée en utilisant une barre oblique inversée comme délimiteur : public String removePath(String filename) { if (filename == null) return null; List<String> parts = filename.split('\\\\'); filename = parts[parts.size()-1]; return filename; } static testMethod void testRemovePath() { System.assertEquals('PPDSF100111.csv', EmailUtilities.getInstance(). removePath('e:\\processed\\PPDSF100111.csv')); } Méthodes Time Les méthodes ci-dessous sont les méthodes statiques système Time. Nom Arguments Type de renvoi Description newInstance Integer hour Time Construit une heure à partir de représentations de nombre entier de hour, minutes, seconds et milliseconds. L'exemple suivant crée une heure 18:30:2:20 : Integer minutes Integer seconds Integer Time myTime = milliseconds Time.newInstance(18, 30, 2, 20); Les méthodes ci-dessous sont les méthodes d'instance pour Time. Nom Arguments Type de renvoi Description addHours Integer addlHours Time Ajoute le nombre spécifié de addlHours à une heure. addMilliseconds Integer Time addlMilliseconds Ajoute le nombre spécifié de addlMilliseconds à une heure. Référence salesforce | Méthodes Collection Apex | 410 Nom Arguments Type de renvoi Description addMinutes Integer Time Ajoute le nombre spécifié de addlMinutes à une heure. Par exemple : addlMinutes Time myTime = Time.newInstance(18, 30, 2, 20); Integer myMinutes = myTime.minute(); myMinutes = myMinutes + 5; System.assertEquals(myMinutes, 35); addSeconds Integer Time Ajoute le nombre spécifié de addlSeconds à une heure. Integer Renvoie le composant heure d'une heure. Par exemple : addlSeconds hour Time myTime = Time.newInstance(18, 30, 2, 20); myTime = myTime.addHours(2); Integer myHour = myTime.hour(); System.assertEquals(myHour, 20); millisecond Integer Renvoie le composant milliseconde d'une heure. minute Integer Renvoie le composant minute d'une heure. second Integer Renvoie le composant seconde d'une heure. Pour plus d'informations sur le type Time, reportez-vous à Types de données Primitive à la page 29. Méthodes Collection Apex Toutes les collections dans Apex ont des méthodes qui leur sont associées pour l'attribution, la récupération et la manipulation des données. Les méthodes collection sont les suivantes : • • • List Map Set Référence salesforce | Méthodes Collection Apex | 411 Remarque: Le nombre d'éléments qu'une collection peut contenir n'est pas limité. Il existe toutefois une limite globale en taille de segment mémoire. Méthodes List Les méthodes de liste sont toutes des méthodes d'instance, c.-à-d. qu'elles fonctionnent dans une instance particulière d'une liste. L'exemple suivant supprime tous les éléments de myList : myList.clear(); La méthode clear ne contient aucun paramètre, mais la liste qui l'appelle est son paramètre implicite. Les paramètres suivants sont les paramètres d'instance pour List. Remarque: Dans le tableau ci-dessous, List_elem représente un élément unique de même type que la liste. Nom Arguments Type de renvoi Description add N'importe quel type e Void Ajoute un élément e à la fin de la liste. Par exemple : List<Integer> myList = new List<Integer>(); myList.add(47); Integer myNumber = myList.get(0); system.assertEquals(myNumber, 47); add Integer i Void N'importe quel type e Insère un élément e dans la liste à la position d'index i. Dans l'exemple suivant, une liste de six éléments est créée et des nombres entiers sont ajoutés aux première et deuxième positions d'index. List<Integer> myList = new Integer[6]; myList.add(0, 47); myList.add(1, 52); system.assertEquals(myList.get(1), 52); addAll List l Void Ajoute tous les éléments de la liste l à la liste qui appelle la méthode. Notez que les deux listes doivent être de même type. Référence salesforce | Méthodes Collection Apex | 412 Nom Arguments Type de renvoi Description addAll Set s Void Ajoute tous les éléments de l'ensemble s à la liste qui appelle la méthode. Notez que l'ensemble et la liste doivent être de même type. clear Void Supprime tous les éléments de la liste, en définissant ainsi la longueur de la liste sur zéro. clone List (de même type) Effectue une copie d'une liste. Notez que s'il s'agit d'une liste d'enregistrements de sObject, la liste dupliquée est uniquement une copie superficielle de la liste. Par conséquent, la copie référence chaque objet, mais les enregistrements de sObject eux-mêmes ne sont pas dupliqués. Par exemple : Account a = new Account(Name='Acme', BillingCity='New York'); Account b = new Account(); Account[] q1 = new Account[]{a,b}; Account[] q2 = q1.clone(); q1[0].BillingCity = 'San Francisco'; System.assertEquals( q1[0].BillingCity, 'San Francisco'); System.assertEquals( q2[0].BillingCity, 'San Francisco'); Pour copier également les enregistrements de sObject, utilisez la méthode deepClone. Référence salesforce | Méthodes Collection Apex | 413 Nom Arguments Type de renvoi deepClone Boolean opt_preserve_id List (de même type Effectue une copie d'une liste d'enregistrements de d'objet) sObject, qui inclut les enregistrements de sObject eux-mêmes. Par exemple : Boolean opt_preserve_readonly_timestamps Boolean opt_preserve_autonumber Description Account a = new Account(Name='Acme', BillingCity='New York'); Account b = new Account( Name='Salesforce'); Account[] q1 = new Account[]{a,b}; Account[] q2 = q1.deepClone(); q1[0].BillingCity = 'San Francisco'; System.assertEquals( q1[0].BillingCity, 'San Francisco'); System.assertEquals( q2[0].BillingCity, 'New York'); Remarque: deepClone fonctionne uniquement avec des listes de sObjects, pas avec des listes de primitifs. L'argument facultatif opt_preserve_id détermine si les ID des objets d'origine sont conservés ou effacés dans les copies. Si défini sur true, les ID sont copiés dans les objets clonés. La valeur par défaut est false, c.-à-d. que les ID sont effacés. Référence Nom salesforce | Méthodes Collection Apex | 414 Arguments Type de renvoi Description Remarque: Pour un code Apex enregistré en utilisant l'API Salesforce.com version 22.0 ou antérieure, la valeur par défaut de l'argument opt_preserve_id est true, c.-à-d. que les ID sont conservés. L'argument facultatif opt_preserve_readonly_timestamps détermine si l'horodatage et les champs d'ID utilisateur en lecture seule sont conservés ou effacés dans les copies. Si défini sur true, les champs en lecture seule CreatedById, CreatedDate, LastModifiedById et LastModifiedDate sont copiés dans les objets clonés. La valeur par défaut est false, c.-à-d. que les valeurs sont effacées. L'argument facultatif opt_preserve_autonumber détermine si les champs à numérotation automatique des objets d'origine sont conservés ou effacés dans les copies. Si défini sur true, les champs à numérotation automatique sont copiés dans les objets clonés. La valeur par défaut est false, c.-à-d. que les champs à numérotation automatique sont effacés. Cet exemple est basé sur l'exemple précédent et montre comment cloner une liste avec un horodatage et des champs d'ID utilisateur en lecture seule préservés. insert q1; List<Account> accts = [SELECT CreatedById, CreatedDate, LastModifiedById, LastModifiedDate, BillingCity FROM Account WHERE Name='Acme' OR Name='Salesforce']; // Clone list while preserving // timestamp and user ID fields. Référence Nom salesforce | Méthodes Collection Apex | 415 Arguments Type de renvoi Description Account[] q3 = accts.deepClone(false,true,false); // Verify timestamp fields are // preserved for the first // list element. System.assertEquals( q3[0].CreatedById, accts[0].CreatedById); System.assertEquals( q3[0].CreatedDate, accts[0].CreatedDate); System.assertEquals( q3[0].LastModifiedById, accts[0].LastModifiedById); System.assertEquals( q3[0].LastModifiedDate, accts[0].LastModifiedDate); Pour effectuer une copie superficielle d'une liste sans dupliquer les enregistrements de sObject qu'elle contient, utilisez la méthode clone. get Integer i Array element Renvoie les éléments de liste stockés à l'index i. Par exemple : List<Integer> myList = new List<Integer>(); myList.add(47); Integer myNumber = myList.get(0); system.assertEquals(myNumber, 47); Pour référencer un élément d'une liste unidimensionnelle de primitifs ou de sObjects, vous pouvez également ajouter la position d'index de Référence Nom salesforce | Méthodes Collection Apex | 416 Arguments Type de renvoi Description l'élément, entre crochets, après le nom de la liste. Par exemple : List<String> colors = new String[3]; colors[0] = 'Red'; colors[1] = 'Blue'; colors[2] = 'Green'; getSObjectType Schema.SObjectType Renvoie le jeton du type de sObject qui forme une liste de sObjects. Utilisez-la avec l'information describe afin de déterminer si une liste contient des sObjects d'un type particulier. Par exemple : Account a = new Account(name='test'); insert a; // Create a generic sObject // variable s SObject s = Database.query ('SELECT Id FROM Account ' + 'LIMIT 1'); // Verify if that sObject // variable is // an Account token System.assertEquals( s.getSObjectType(), Account.sObjectType); // Create a list of // generic sObjects List<sObject> q = new Account[]{}; // Verify if the list of // sObjects Référence Nom salesforce | Méthodes Collection Apex | 417 Arguments Type de renvoi Description // contains Account tokens System.assertEquals( q.getSObjectType(), Account.sObjectType); Notez que cette méthode peut être utilisée uniquement avec des listes composées de sObjects. Pour plus d'informations, reportez-vous à Compréhension de l'information Describe Apex à la page 204. isEmpty Boolean Renvoie true si la liste contient zéro élément. iterator Iterator Renvoie une instance d'un itérateur. À partir de l'itérateur, vous pouvez utiliser les méthodes itérables hasNext et next pour parcourir la liste. Par exemple : global class CustomIterable implements Iterator<Account>{ List<Account> accs {get; set;} Integer i {get; set;} public CustomIterable(){ accs = [SELECT Id, Name, NumberOfEmployees FROM Account WHERE Name = 'false']; i = 0; } global boolean hasNext(){ if(i >= accs.size()) { return false; } else { Référence Nom salesforce | Méthodes Collection Apex | 418 Arguments Type de renvoi Description return true; } } global Account next(){ // 8 is an arbitrary // constant in this example // that represents the // maximum size of the list. if(i == 8){return null;} i++; return accs[i-1]; } } Remarque: Il n'est pas nécessaire de mettre en oeuvre l'interface iterable pour utiliser les méthodes iterable avec une liste. remove Integer i Array element Supprime l'élément qui stocké au ième index d'une liste, en renvoyant l'élément qui a été supprimé. Par exemple : List<String> colors = new String[3]; colors[0] = 'Red'; colors[1] = 'Blue'; colors[2] = 'Green'; String S1 = colors.remove(2); system.assertEquals(S1, 'Green'); set Integer i N'importe quel type e Void Attribue e à la position d'index de liste i. Par exemple : List<Integer> myList = new Integer[6]; Référence Nom salesforce | Méthodes Collection Apex | 419 Arguments Type de renvoi Description myList.set(0, 47); myList.set(1, 52); system.assertEquals(myList.get(1), 52); Pour définir un élément d'une liste unidimensionnelle de primitifs ou de sObjects, vous pouvez également ajouter la position d'index de l'élément, entre crochets, après le nom de la liste. Par exemple : List<String> colors = new String[3]; colors[0] = 'Red'; colors[1] = 'Blue'; colors[2] = 'Green'; size Integer Renvoie le nombre d'éléments de la liste. Par exemple : List<Integer> myList = new List<Integer>(); Integer size = myList.size(); system.assertEquals(size, 0); List<Integer> myList2 = new Integer[6]; Integer size2 = myList2.size(); system.assertEquals(size2, 6); sort Void Trie les éléments de la liste par ordre croissant. Dans l'exemple suivant, la liste compte trois éléments. Lorsque la liste est triée, le premier élément est nul, car aucune valeur ne lui est attribuée, alors que le deuxième élément a la valeur 5: List<Integer> q1 = new Integer[3]; // Assign values to the first // two elements Référence salesforce | Méthodes Collection Apex | 420 Nom Arguments Type de renvoi Description q1[0] = 10; q1[1] = 5; q1.sort(); // First element is null, second is 5 system.assertEquals(q1.get(1), 5); Remarque: Avec cette méthode, vous pouvez trier des types primitifs, des éléments SelectOption et des sObjects (objets standard et objets personnalisés). Pour plus d'informations sur l'ordre de tri utilisé pour les sObjects, reportez-vous à Tri des listes. Vous pouvez également trier des types personnalisés (vos classes Apex) s'ils mettent en oeuvre l'interface Comparable. Pour plus d'informations sur le type List, reportez-vous à Lists à la page 38. Méthodes Map Les méthodes Map sont toutes des méthodes d'instance, c.-à-d. qu'elle fonctionnent dans une instance particulière d'un mappage. Les méthodes ci-dessous sont les méthodes d'instance pour les mappages. Remarque: • • • Nom clear Dans le tableau ci-dessous, Key_type et Value_type représentent respectivement le type de données de clé de mappage et de valeur, qui peuvent correspondent à n'importe quel type de données : primitive, collections, sObjects, types définis par l'utilisateur et types Apex intégrés. Pour déterminer si les clés de mappage de types définis par l'utilisateur sont uniques, les méthodes equals et hashCode sont utilisées, que vous fournissez dans vos classes. Pour déterminer si tous les autres types non primitifs sont uniques, les champs des objets sont comparés. Les clés de mappage de type String sont sensibles à la casse. Deux clés dont seule la casse diffère sont considérées comme uniques et ont des entrées de mappage correspondantes distinctes. Par conséquent, les méthodes Map, qui comprennent put, get, containsKey et remove, traitent ces clés comme étant distinctes. Arguments Type de renvoi Description Void Supprime toutes les paires clé-valeur du mappage. Référence Nom salesforce | Méthodes Collection Apex | 421 Arguments clone Type de renvoi Description Map (de même type) Effectue une copie du mappage. Notez que s'il s'agit d'un mappage avec des valeurs d'enregistrement de sObject, le mappage dupliqué est seulement une copie superficielle du mappage. Cela signifie que la copie a des références à chaque enregistrement de sObject, mais que les enregistrements eux-mêmes ne sont pas dupliqués. Par exemple : Account a = new Account( Name='Acme', BillingCity='New York'); Map<Integer, Account> map1 = new Map<Integer, Account> {}; map1.put(1, a); Map<Integer, Account> map2 = map1.clone(); map1.get(1).BillingCity = 'San Francisco'; System.assertEquals( map1.get(1).BillingCity, 'San Francisco'); System.assertEquals( map2.get(1).BillingCity, 'San Francisco'); Pour copier également les enregistrements de sObject, utilisez la méthode deepClone. containsKey Key type key Boolean Renvoie true si le mappage contient un mappage de la key spécifiée. Si la clé est une chaîne, la valeur de clé est sensible à la casse. Référence Nom salesforce | Méthodes Collection Apex | 422 Arguments Type de renvoi Description Par exemple : Map<string, string> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); Boolean contains = colorCodes.containsKey('Blue'); System.assertEquals(contains, True); deepClone Map (de même type) Effectue une copie d'un mappage, comprenant les enregistrements de sObject s'il s'agit d'un mappage avec des valeurs d'enregistrement de sObject. Par exemple : Account a = new Account( Name='Acme', BillingCity='New York'); Map<Integer, Account> map1 = new Map<Integer, Account> {}; map1.put(1, a); Map<Integer, Account> map2 = map1.deepClone(); map1.get(1).BillingCity = 'San Francisco'; System.assertEquals(map1.get(1). BillingCity, 'San Francisco'); System.assertEquals(map2.get(1). Référence Nom salesforce | Méthodes Collection Apex | 423 Arguments Type de renvoi Description BillingCity, 'New York'); Pour effectuer une copie superficielle d'un mappage sans dupliquer les enregistrements de sObject qu'elle contient, utilisez la méthode clone(). get Key type key Value_type Renvoie la valeur avec laquelle la key spécifiée est mappée, ou null si le mappage ne contient aucune valeur pour cette clé. Si la clé est une chaîne, la valeur de clé est sensible à la casse. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); String code = colorCodes.get('Blue'); System.assertEquals(code, '0000A0'); // The following is not a color // in the map String code2 = colorCodes.get('Magenta'); System.assertEquals(code2, null); getSObjectType Schema.SObjectType Renvoie le jeton du type de sObject qui forme les valeurs du mappage. Utilisez-la avec l'information describe afin de déterminer si un mappage contient des sObjects d'un type particulier. Par exemple : Account a = new Account( Name='Acme'); Référence Nom salesforce | Méthodes Collection Apex | 424 Arguments Type de renvoi Description insert a; // Create a generic sObject // variable s SObject s = Database.query ('SELECT Id FROM Account ' + 'LIMIT 1'); // Verify if that sObject // variable // is an Account token System.assertEquals( s.getSObjectType(), Account.sObjectType); // Create a map of generic // sObjects Map<Integer, Account> M = new Map<Integer, Account>(); // Verify if the list of sObjects // contains Account tokens System.assertEquals( M.getSObjectType(), Account.sObjectType); Notez que cette méthode peut être utilisée uniquement avec des mappages ayant des valeurs de sObject. Pour plus d'informations, reportez-vous à Compréhension de l'information Describe Apex à la page 204. Référence Nom salesforce | Méthodes Collection Apex | 425 Arguments isEmpty Type de renvoi Description Boolean Renvoie true si le mappage n'a aucune paire clé-valeur. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); Boolean empty = colorCodes.isEmpty(); system.assertEquals(empty, true); Set of Key_type keySet Renvoie un ensemble contenant toutes les clés du mappage. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); Set <String> colorSet = new Set<String>(); colorSet = colorCodes.keySet(); put Key key, Value_type Value value Associe la valeur spécifiée à la key spécifiée dans le mappage. Si le mappage contenait précédemment un mappage pour cette clé, l'ancienne valeur est renvoyée par la méthode, puis remplacée. Si la clé est une chaîne, la valeur de clé est sensible à la casse. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'ff0000'); colorCodes.put('Red', '#FF0000'); // Red is now #FF0000 putAll Map m Void Copie l'ensemble du mappage m spécifié dans le mappage d'origine. Les nouveaux mappages de m remplacent les mappages d'origine. Référence salesforce | Méthodes Collection Apex | 426 Nom Arguments putAll sObject[] l remove Key key Type de renvoi Description Si le mappage inclut des ID ou des chaînes mappés avec des sObjects, elle ajoute la liste des enregistrements de sObjects l au mappage de la même façon que le constructeur Map avec cette entrée. Value_type Supprime le mappage de cette key du mappage s'il est présent. La valeur est renvoyée par la méthode, puis supprimée. Si la clé est une chaîne, la valeur de clé est sensible à la casse. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); String myColor = colorCodes.remove('Blue'); String code2 = colorCodes.get('Blue'); System.assertEquals(code2, null); size Integer Renvoie le nombre de paires clé-valeur du mappage. Par exemple : Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); Integer mSize = colorCodes.size(); system.assertEquals(mSize, 2); Référence salesforce | Méthodes Collection Apex | 427 Nom Arguments Type de renvoi Description list of Value_type Renvoie une liste contenant toutes les valeurs du mappage dans un ordre arbitraire. Par exemple : valeurs Map<String, String> colorCodes = new Map<String, String>(); colorCodes.put('Red', 'FF0000'); colorCodes.put('Blue', '0000A0'); List<String> colors = new List<String>(); colors = colorCodes.values(); Pour plus d'informations sur le type Map, reportez-vous à Maps à la page 47. Méthodes Set Les méthodes Set fonctionnent dans un ensemble, c.-à-d. sur une collection non triée d'éléments qui ont été initialisés en utilisant le mot clé set. Un ensemble contient uniquement des éléments uniques et n'a pas de valeurs copiées. Les éléments d'ensemble peuvent avoir n'importe quel type de données : primitif, collection, sObject, type défini par l'utilisateur et type Apex intégré. Les méthodes Set sont toutes des méthodes d'instance, c.-à-d. qu'elles fonctionnent sur une instance particulière d'un ensemble. Les méthodes ci-dessous sont les méthodes d'instance pour Set. Remarque: • • • Dans le tableau ci-dessous, Set_elem représente un élément unique dans l'ensemble. L'unicité des éléments d'ensemble des types définis par l'utilisateur est déterminée par les méthodes equals et hashCode que vous fournissez dans vos classes. L'unicité de tous les autres types non primitifs est déterminée en comparant les champs de l'objet. Si l'ensemble contient des éléments de type String, ils sont sensibles à la casse. Deux éléments d'ensemble dont seule la casse est différente sont considérés comme distincts. Nom Arguments Type de renvoi Description add Set element e Boolean Ajoute un élément à l'ensemble s'il n'est pas déjà présent. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. Par exemple : set<string> myString = new Set<String>{'a', 'b', 'c'}; Boolean result; Référence Nom salesforce | Méthodes Collection Apex | 428 Arguments Type de renvoi Description result = myString.add('d'); system.assertEquals(result, true); addAll List l Boolean Ajoute tous les éléments de la liste spécifiée à l'ensemble s'ils ne sont pas déjà présents. Cette méthode entraîne l'union de la liste et de l'ensemble. La liste doit être de même type que l'ensemble qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. addAll Set s Boolean Ajoute tous les éléments de l'ensemble spécifié à l'ensemble qui appelle la méthode s'il ne sont pas déjà présents. Cette méthode entraîne l'union des deux ensembles. L'ensemble spécifié doit être de même type que l'ensemble d'origine qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. Par exemple : set<string> myString = new Set<String>{'a', 'b'}; set<string> sString = new Set<String>{'c'}; Boolean result1; result1 = myString.addAll(sString); system.assertEquals(result1, true); clear Void Renvoie tous les éléments à partir de l'ensemble. clone Set (de même type) Effectue une copie de l'ensemble. Boolean Renvoie true si l'ensemble contient l'élément spécifié. Par exemple : contains Set element e set<string> myString = new Set<String>{'a', 'b'}; Boolean result; result = myString.contains('z'); system.assertEquals(result, false); Référence salesforce | Méthodes Collection Apex | 429 Nom Arguments Type de renvoi Description containsAll List l Boolean Renvoie true si l'ensemble contient tous les éléments de la liste spécifiée. La liste doit être de même type que l'ensemble qui appelle la méthode. containsAll Set s Boolean Renvoie true si l'ensemble contient tous les éléments de l'ensemble spécifié. L'ensemble spécifié doit être de même type que l'ensemble d'origine qui appelle la méthode. Par exemple : set<string> myString = new Set<String>{'a', 'b'}; set<string> sString = new Set<String>{'c'}; set<string> rString = new Set<String>{'a', 'b', 'c'}; Boolean result1, result2; result1 = myString.addAll(sString); system.assertEquals(result1, true); result2 = myString.containsAll(rString); system.assertEquals(result2, true); Boolean isEmpty Renvoie true si l'ensemble compte zéro élément. Par exemple : Set<integer> mySet = new Set<integer>(); Boolean result; result = mySet.isEmpty(); system.assertEquals(result, true); remove Set Element e Boolean Supprime l'élément spécifié de l'ensemble s'il est présent. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. removeAll List l Boolean Supprime les éléments de la liste spécifiée de l'ensemble s'ils sont présents. Cette méthode entraîne le complément Référence Nom salesforce | Méthodes Collection Apex | 430 Arguments Type de renvoi Description relatif des deux ensembles. La liste doit être de même type que l'ensemble qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. Par exemple : Set<integer> mySet = new Set<integer>{1, 2, 3}; List<integer> myList = new List<integer>{1, 3}; Boolean result = mySet.removeAll(myList); System.assertEquals(result, true); Integer result2 = mySet.size(); System.assertEquals(result2, 1); removeAll Set s Boolean Supprime les éléments de l'ensemble spécifié de l'ensemble d'origine s'ils sont présents. Cette méthode entraîne le complément relatif des deux ensembles. L'ensemble spécifié doit être de même type que l'ensemble d'origine qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. retainAll List l Boolean Conserve uniquement les éléments de cet ensemble qui sont inclus dans la liste spécifiée. Cette méthode entraîne l'intersection de la liste et de l'ensemble. La liste doit être de même type que l'ensemble qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. Par exemple : Set<integer> mySet = new Set<integer>{1, 2, 3}; List<integer> myList = new List<integer>{1, 3}; Boolean result = mySet.retainAll(myList); Référence Nom salesforce | Méthodes Enum | 431 Arguments Type de renvoi Description System.assertEquals(result, true); retainAll Set s Boolean Conserve uniquement les éléments de l'ensemble d'origine qui sont inclus dans la liste spécifiée. Cette méthode entraîne l'intersection des deux ensembles. L'ensemble spécifié doit être de même type que l'ensemble d'origine qui appelle la méthode. Cette méthode renvoie true si l'ensemble d'origine a été modifié suite à l'appel. size Integer Renvoie le nombre d'éléments de l'ensemble (sa cardinalité). Par exemple : Set<integer> mySet = new Set<integer>{1, 2, 3}; List<integer> myList = new List<integer>{1, 3}; Boolean result = mySet.retainAll(myList); System.assertEquals(result, true); Integer result2 = mySet.size(); System.assertEquals(result2, 2); Pour plus d'informations sur le type Set, reportez-vous à Sets à la page 46. Méthodes Enum Bien que des méthodes définies par l'utilisateur ne peuvent pas être ajoutées à des valeurs Enum, toutes les valeurs d'énumération, y compris les valeurs d'énumération système, ont les méthodes suivantes définies dans Apex : Nom Type de renvoi Description name String Renvoie le nom de l'élément Enum sous forme de chaîne. ordinal Integer Renvoie la position de l'élément dans la liste des valeurs d'énumération, en commençant par zéro. Référence salesforce | Méthodes sObject Apex | 432 Enum inclut également la méthode ci-dessous. Nom Type de renvoi Description valeurs List<Enum type> Renvoie les valeurs de l'énumération sous forme de liste du même type d'énumération. Par exemple : Integer i = StatusCode.DELETE_FAILED.ordinal(); String s = StatusCode.DELETE_FAILED.name(); List<StatusCode> values = StatusCode.values(); Pour plus d'informations sur le type Enum, reportez-vous à Enums à la page 53. Méthodes sObject Apex Le terme sObject fait référence à tout objet qui peut être stocké dans la base de données de la plate-forme Salesforce. Les méthodes sObject Apex suivantes contiennent des méthodes qui peuvent être utilisées avec tous les sObjects ainsi qu'avec les classes plus générales décrivant des structures de sObject : • • • • • • Schema sObject sObject Describe Results Field Describe Results Méthodes Schema.FieldSet Custom Settings Méthodes Schéma Le tableau suivant répertorie les méthodes système pour Schema. Nom getGlobalDescribe Arguments Type de renvoi Description Map<String, Schema.SObjectType> Renvoie un mappage de tous les noms (clés) de sObject avec les jetons (valeurs) de sObject pour les objets standard et personnalisés définis dans votre organisation. Par exemple : Map<String, Schema.SObjectType> gd = Référence Nom salesforce | Méthodes sObject Apex | 433 Arguments Type de renvoi Description Schema.getGlobalDescribe(); Pour plus d'informations, reportez-vous à Accès à tous les sObjects à la page 207. describeDataCategory Groups String List<Schema.Describe List<sObjectNames> DataCategoryGroupResult> Renvoie une liste des groupes de catégories associés aux objets spécifiés. Vous pouvez spécifier l'un des sObjectNames suivants : • KnowledgeArticleVersion : pour récupérer des groupes de catégories associés à des types d'article. • Question : pour récupérer des groupes de catégories associés à des questions. Pour plus d'informations et des exemples de code utilisant des groupes describeDataCategory, reportez-vous à Accès à toutes les catégories de données associées à un sObject. Pour des informations supplémentaires sur les articles et les questions, reportez-vous à « Gestion des articles et des traductions » et à « Présentation des réponses » dans l'aide en ligne de Salesforce. describeDataCategory GroupStructures pairs, List<Schema.Describe Renvoie les groupes de catégories topCategoriesOnly DataCategoryGroupStructureResult> disponibles avec leur structure de catégorie de données pour les objets spécifiés dans la requête. Pour plus d'informations et des exemples de code utilisant des GroupStructures describeDataCategory, reportez-vous à Accès à toutes les catégories de données associées à un sObject. Référence salesforce | Méthodes sObject Apex | 434 Arguments de structure de groupe de catégories de données de description La méthode GroupStructures describeDataCategory renvoie les groupes de catégories disponibles avec leur structure de catégorie de données. Les arguments ci-dessous sont les arguments pour cette méthode. Nom Type de renvoi Description pairs List<Schema.DataCategoryGroupSobjectTypePair> Spécifie un ou plusieurs groupes de catégories et objets pour demander Schema.DataCategoryGroupSobjectTypePair. Les catégories de données visibles sont récupérées pour l'objet spécifié. Pour plus d'informations sur la visibilité des groupes de catégories, reportez-vous à « À propos de la visibilité des groupes de catégories » dans l'aide en ligne de Salesforce. topCategoriesOnly Boolean Spécifie true pour renvoyer uniquement la catégorie la plus visible qui classe l'objet. Spécifie false pour renvoyer toutes les catégories parents et enfants visibles. Les deux valeurs dépendent des paramètres de visibilité des groupes de catégories de données de l'utilisateur. Pour plus d'informations sur la visibilité des groupes de catégories, reportez-vous à « À propos de la visibilité des groupes de catégories » dans l'aide en ligne de Salesforce. Objet Schema.DataCategoryGroupSobjectTypePair Schema.DataCategoryGroupSobjectTypePair spécifie un groupe de catégories et un objet associé. Il est utilisé par la méthode describeDataCategory GroupStructures pour renvoyer les catégories disponibles pour cet objet. Le tableau suivant répertorie toutes les méthodes pour Schema.DataCategoryGroupSobjectTypePair. Nom Type de renvoi Description getDataCategoryGroupName String Renvoie le nom unique utilisé par l'API pour accéder au groupe de catégories de données. getSobject String Renvoie le nom d'objet associé au groupe de catégories de données. setDataCategoryGroupName String Spécifie le nom unique utilisé par l'API pour accéder au groupe de catégories de données. setSobject Arguments String sObjectName Void Le sObjectName est le nom d'objet associé au groupe de catégories de données. Les valeurs valides sont : • KnowledgeArticleVersion : pour les types d'article. • Question : pour les questions depuis Réponses. Référence salesforce | Méthodes sObject Apex | 435 Objet Schema.DescribeDataCategoryGroupResult La méthode describeDataCategory Groups renvoie un objet Schema.DescribeDataCategoryGroupResult contenant la liste des groupes de catégories associés à l'objet spécifié. L'exemple suivant montre comment instancier un objet de résultat de description de groupe de catégories de données : List <String> objType = new List<String>(); objType.add('KnowledgeArticleVersion'); objType.add('Question'); List<Schema.DescribeDataCategoryGroupResult> describeCategoryResult = Schema.describeDataCategoryGroups(objType); Pour plus d'informations et des exemples de code utilisant describeDataCategory Groups, reportez-vous à Accès à toutes les catégories de données associées à un sObject. Le tableau suivant répertorie toutes les méthodes disponibles dans le résultat de description de groupe de catégories de données. Aucune méthode ne prend d'argument. Nom Type de renvoi Description getCategoryCount Integer Renvoie le nombre de catégories de données visibles dans le groupe de catégories de données. getDescription String Renvoie la description du groupe de catégories de données. getLabel String Renvoie l'étiquette du groupe de catégories de données utilisée dans l'interface utilisateur de Salesforce. getName String Renvoie le nom unique utilisé par l'API pour accéder au groupe de catégories de données. getSobject String Renvoie le nom d'objet associé au groupe de catégories de données. Objet Schema.DescribeDataCategoryGroupStructureResult La méthode describeDataCategory GroupStructures renvoie une liste d'objets Schema.Describe DataCategoryGroupStructureResult contenant les groupes de catégories et les catégories associés à l'objet spécifié. L'exemple suivant montre comment instancier un objet de résultat de description de structure de groupe de catégories de données : List <DataCategoryGroupSobjectTypePair> pairs = new List<DataCategoryGroupSobjectTypePair>(); DataCategoryGroupSobjectTypePair pair1 = new DataCategoryGroupSobjectTypePair(); Référence salesforce | Méthodes sObject Apex | 436 pair1.setSobject('KnowledgeArticleVersion'); pair1.setDataCategoryGroupName('Regions'); DataCategoryGroupSobjectTypePair pair2 = new DataCategoryGroupSobjectTypePair(); pair2.setSobject('Questions'); pair2.setDataCategoryGroupName('Regions'); pairs.add(pair1); pairs.add(pair2); List<Schema.DescribeDataCategoryGroupStructureResult>results = Schema.describeDataCategoryGroupStructures(pairs, true); Pour plus d'informations et des exemples de code utilisant des GroupStructures describeDataCategory, reportez-vous à Accès à toutes les catégories de données associées à un sObject. Le tableau suivant répertorie toutes les méthodes disponibles dans le résultat de description de structure de groupe de catégories de données. Aucune méthode ne prend d'argument. Nom Type de renvoi Description getDescription String Renvoie la description du groupe de catégories de données. getLabel String Renvoie l'étiquette du groupe de catégories de données utilisée dans l'interface utilisateur de Salesforce. getName String Renvoie le nom unique utilisé par l'API pour accéder au groupe de catégories de données. getSobject String Renvoie le nom de l'objet associé au groupe de catégories de données. getTopCategories List<Schema.DataCategory> Renvoie un objet Schema.DataCategory contenant les catégories les plus visibles, selon les paramètres de visibilité des groupes de catégories de données de l'utilisateur. Pour plus d'informations sur la visibilité des groupes de catégories, reportez-vous à « À propos de la visibilité des groupes de catégories » dans l'aide en ligne de Salesforce. Objet Schema.DataCategory Un objet Schema.DataCategory représente les catégories au sein d'un groupe de catégories. L'objet Schema.DataCategory est renvoyé par la méthode getTopCategories. Le tableau suivant répertorie toutes les méthodes pour l'objet Schema.DataCategory. Aucune méthode ne prend d'argument. Référence salesforce | Méthodes sObject Apex | 437 Nom Type de renvoi Description getChildCategories List<Schema.DataCategory> Renvoie un objet récursif contenant les sous-catégories visibles dans la catégorie de données. getLabel String Renvoie l'étiquette de la catégorie de données utilisée dans l'interface utilisateur de Salesforce. getName String Renvoie le nom unique utilisé par l'API pour accéder à la catégorie de données. Méthodes sObject Les méthodes sObject sont toutes des méthodes d'instance, c.-à-d. qu'elles sont appelées par un opérateur dans une instance particulière d'un sObject, tel qu'un compte ou un contact. Les méthodes ci-dessous sont les méthodes d'instance pour sObjects. Nom Arguments Type de renvoi Description addError String errorMsg Void Marque un enregistrement avec un message d'erreur personnalisé et empêche toute opération DML de se produire. L'argument errorMsg est le message d'erreur avec lequel marquer l'enregistrement. Lorsqu'elle est utilisée dans Trigger.new, dans les déclencheurs before insert et before update, et dans Trigger.old, dans les déclencheurs before delete, le message d'erreur est affiché dans l'interface de l'application. Reportez-vous à Déclencheurs et Exceptions de déclencheur. Lorsqu'elle est utilisée dans des contrôleurs Visualforce, le message créé est ajouté à la collection d'erreurs de la page. Pour plus d'informations, reportez-vous à Validation Rules and Standard Controllers dans le guide Visualforce Developer's Guide. addError Exception exception Marque un enregistrement avec un message d'erreur personnalisé et empêche toute opération DML de se produire. L'argument exception est un objet Exception ou un objet d'exception personnalisé contenant le message d'erreur avec lequel marquer l'enregistrement. Lorsqu'elle est utilisée dans Trigger.new, dans les déclencheurs before insert et Référence Nom salesforce | Méthodes sObject Apex | 438 Arguments Type de renvoi Description before update, et dans Trigger.old, dans les déclencheurs before delete, le message d'erreur est affiché dans l'interface de l'application. Reportez-vous à Déclencheurs et Exceptions de déclencheur. Lorsqu'elle est utilisée dans des contrôleurs Visualforce, le message créé est ajouté à la collection d'erreurs de la page. Pour plus d'informations, reportez-vous à Validation Rules and Standard Controllers dans le guide Visualforce Developer's Guide. field.addError String errorMsg Void Place le message d'erreur spécifié dans le champ qui appelle cette méthode dans l'interface de l'application, et empêche toute opération DML de se produire. Par exemple : Trigger.new[0].myField__C.addError('bad'); Remarque : • • • • Lorsqu'elle est utilisée dans Trigger.new, dans les déclencheurs before insert et before update, et dans Trigger.old, dans les déclencheurs before delete, le message d'erreur est affiché dans l'interface de l'application. Lorsqu'elle est utilisée dans des contrôleurs Visualforce, si un composant inputField est lié au champ, le message est joint au composant. Pour plus d'informations, reportez-vous à Validation Rules and Standard Controllers dans le guide Visualforce Developer's Guide. Cette méthode est hautement spécialisée, car en réalité l'identifiant de champ n'est pas l'objet qui invoque, c'est l'enregistrement de sObject qui invoque. Le champ est utilisé seulement pour identifier le champ à utiliser pour afficher l'erreur. Cette méthode sera probablement modifiée dans les prochaines versions d'Apex. Reportez-vous à Déclencheurs et Exceptions de déclencheur. Référence Nom salesforce | Méthodes sObject Apex | 439 Arguments clear clone Type de renvoi Description Void Efface toutes les valeurs de champ. Boolean opt_preserve_id sObject (de même Crée une copie de l'enregistrement de sObject. type) Boolean opt_IsDeepClone L'argument facultatif opt_preserve_id détermine si l'ID de l'objet d'origine est Boolean conservé ou effacé dans la copie. Si true, l'ID opt_preserve_readonly_timestamps est copié dans la copie. La valeur par défaut est Boolean false, c.-à-d. que l'ID est effacé. opt_preserve_autonumber Remarque: Pour un code Apex enregistré en utilisant l'API Salesforce.com version 22.0 ou antérieure, la valeur par défaut de l'argument opt_preserve_id est true, c.-à-d. que l'ID est conservé. L'argument facultatif opt_IsDeepClone détermine si la méthode crée une copie complète du champ de sObject ou seulement une référence : • • Si true, la méthode crée une copie complète du sObject. Tous les champs du sObject sont copiés en mémoire, y compris les champs de relation. Par conséquent, si vous modifiez un champ sur un sObject cloné, le sObject d'origine n'est pas affecté. Si false, la méthode crée une copie superficielle des champs du sObject. Toutes les champs de relation copiés référencent les sObjects d'origine. Par conséquent, si vous modifiez un champ de relation sur le sObject cloné, le champ correspondant sur le sObject d'origine est également affecté, et inversement. Le paramètre par défaut est false. L'argument facultatif opt_preserve_readonly_timestamps détermine si les champs d'horodatage en lecture seule sont conservés ou effacés dans la copie. Si true, les champs en lecture seule CreatedById, CreatedDate, LastModifiedById et LastModifiedDate sont copiés dans la copie. La valeur par défaut est false, c.-à-d. que les valeurs sont effacées. L'argument facultatif opt_preserve_autonumber détermine si les Référence Nom salesforce | Méthodes sObject Apex | 440 Arguments Type de renvoi Description champs à numérotation automatique de l'objet d'origine sont conservés ou effacés dans la copie. Si true, les champs à numérotation automatique sont copiés dans l'objet cloné. La valeur par défaut est false, c.-à-d. que les champs à numérotation automatique sont effacés. get String fieldName Object Renvoie la valeur du champ spécifié par fieldName, tel que AccountNumber. Pour plus d'informations, reportez-vous à SOQL dynamique. get Schema.sObjectField Field Object Renvoie la valeur du champ spécifié par le jeton de champ Schema.sObjectField (par exemple, Schema.Account.AccountNumber). Pour plus d'informations, reportez-vous à SOQL dynamique. Database. DMLOptions getOptions Renvoie l'objet database.DMLOptions du sObject. Pour plus d'informations, reportez-vous à Propriétés Database DMLOptions. getSObject String fieldName sObject Renvoie la valeur du champ spécifié par fieldName. Cette méthode est utilisée principalement avec une instruction DML dynamique pour accéder aux valeurs d'ID externes. Pour plus d'informations, reportez-vous à DML dynamique. getSObject Schema.SObjectField fieldName sObject Renvoie la valeur du champ spécifié par le jeton de champ Schema.fieldName (par exemple, Schema.MyObj.MyExternalId). Cette méthode est utilisée principalement avec une instruction DML dynamique pour accéder aux valeurs d'ID externes. Pour plus d'informations, reportez-vous à DML dynamique. getSObjects String fieldName sObject[] Renvoie les valeurs du champ spécifié par fieldName. Cette méthode est utilisée principalement avec une instruction DML Référence Nom salesforce | Méthodes sObject Apex | 441 Arguments Type de renvoi Description dynamique pour accéder aux valeurs des objets associés, tels que des relations enfants. Pour plus d'informations, reportez-vous à DML dynamique. getSObjects Schema.SObjectType fieldName sObject[] Renvoie la valeur du champ spécifié par le jeton de champ Schema.fieldName (par exemple, Schema.Account.Contact). Cette méthode est utilisée principalement avec une instruction DML dynamique pour accéder aux valeurs des objets associés, tels que des relations enfants. Pour plus d'informations, reportez-vous à DML dynamique. Schema.SObjectType Renvoie le jeton de ce sObject. Cette méthode est utilisée principalement avec l'information describe. getSObjectType Pour plus d'informations, reportez-vous à Compréhension de l'information Describe Apex. put String fieldName Object Object value Définit la valeur du champ spécifié par fieldName et renvoie la valeur précédente de ce champ. Pour plus d'informations, reportez-vous à SOQL dynamique. put Schema.SObjectField fieldName Object Object value Définit la valeur du champ spécifié par le jeton de champ Schema.sObjectField (par exemple, Schema.Account.AccountNumber). Pour plus d'informations, reportez-vous à SOQL dynamique. putSObject String fieldName sObject sObject value Définit la valeur du champ spécifié par fieldName. Cette méthode est utilisée principalement avec une instruction DML dynamique pour la définition des ID externes. La méthode renvoie la valeur précédente du champ. Pour plus d'informations, reportez-vous à SOQL dynamique. putSObject Schema.sObjectType fieldName sObject sObject value Définit la valeur du champ spécifié par le jeton Schema.sObjectType. Cette méthode est utilisée principalement avec une instruction Référence Nom salesforce | Méthodes sObject Apex | 442 Arguments Type de renvoi Description DML dynamique pour la définition des ID externes. La méthode renvoie la valeur précédente du champ. Pour plus d'informations, reportez-vous à SOQL dynamique. setOptions database.DMLOptions DMLOptions Void Définit l'objet DMLOptions du sObject. Pour plus d'informations, reportez-vous à Propriétés Database DMLOptions. Pour plus d'informations sur les sObjects, reportez-vous à Types sObject à la page 32. Méthodes de résultat de description de sObject Le tableau suivant décrit les méthodes disponibles pour le résultat de description de sObject, l'objet DescribeSObjectResult. Aucune méthode ne prend d'argument. Nom Type de données Description fields Spécial Renvoie un type de données spécial qui ne doit pas être utilisé seul. La méthode fields doit toujours être suivie par un nom de variable de membre de champ ou par la méthode getMap. Par exemple : Schema.DescribeFieldResult F = Schema.SObjectType.Account.fields.Name; Pour plus d'informations, reportez-vous à Compréhension de l'information Describe Apex. fieldSets Spécial Renvoie un type de données spécial qui ne doit pas être utilisé seul. La méthode fieldsSets doit toujours être suivie par un nom d'ensemble de champ ou par la méthode getMap. Par exemple : Schema.DescribeSObjectResult d = Account.sObjectType.getDescribe(); Map<String, Schema.FieldSet> FsMap = d.fieldSets.getMap(); Pour plus d'informations, reportez-vous à Méthodes Schema.FieldSet. Référence salesforce | Méthodes sObject Apex | 443 Nom Type de données Description getChildRelationships List<Schema.ChildRelationship> Renvoie une liste de relations enfants, qui correspondent aux noms des sObjects ayant une clé étrangère pour le sObject décrit. Par exemple, l'objet Compte a Contacs et Opportunités comme relations enfants. getKeyPrefix String Renvoie le code du préfixe à trois caractères de l'objet. Les ID d'enregistrement ont un préfixe avec des codes de trois caractères qui spécifie le type de l'objet (par exemple, les comptes ont un préfixe 001 et les opportunités ont un préfixe 006). L'objet DescribeSobjectResult renvoie une valeur des objets ayant un préfixe stable. Pour les types d'objet n'ayant pas de préfixe stable ou prévisible, ce champ est vide. Les applications clientes qui dépendent de ces codes peuvent utiliser cette méthode de détermination du type d'objet pour la compatibilité ascendante. getLabel String Renvoie l'étiquette de l'objet, qui peut correspondre ou non au nom de l'objet. Par exemple, une organisation dans le secteur médical peut changer l'étiquette Compte en Patient. Cette étiquette est ensuite utilisée dans l'interface utilisateur de Salesforce. Pour plus d'informations, reportez-vous à l'aide en ligne de Salesforce. getLabelPlural String Renvoie l'étiquette plurielle de l'objet, qui peut correspondre ou non au nom de l'objet. Par exemple, une organisation dans le secteur médical peut changer l'étiquette Comptes en Patients. Cette étiquette est ensuite utilisée dans l'interface utilisateur de Salesforce. Pour plus d'informations, reportez-vous à l'aide en ligne de Salesforce. getLocalName String Renvoie le nom de l'objet, similaire à la méthode getName. Cependant, si l'objet fait partie de l'espace de noms actuel, la partie espace de noms du nom est ignorée. getName String Renvoie le nom de l'objet. getRecordTypeInfos List<Schema.RecordTypeInfo> Renvoie une liste des types d'enregistrement pris en charge par cet objet. Il n'est pas nécessaire que l'utilisateur dispose d'un accès au type d'enregistrement pour l'afficher dans cette liste. getRecordTypeInfosByID Map<ID, Schema.RecordTypeInfo> Renvoie un mappage des ID d'enregistrement avec leur type d'enregistrement associé. Il n'est pas Référence Nom salesforce | Méthodes sObject Apex | 444 Type de données Description nécessaire que l'utilisateur actuel ait accès au type d'enregistrement pour l'afficher dans ce mappage. getRecordTypeInfosByName Map<String, Schema.RecordTypeInfo> Renvoie un mappage des étiquettes d'enregistrement avec leur type d'enregistrement associé. Il n'est pas nécessaire que l'utilisateur actuel ait accès au type d'enregistrement pour l'afficher dans ce mappage. getSobjectType Schema.SObjectType Renvoie l'objet Schema.SObjectType du sObject. Vous pouvez l'utiliser pour créer un sObject similaire. Pour plus d'informations, reportez-vous à Schema.SObjectType. isAccessible Boolean Renvoie true si l'utilisateur actuel peut afficher ce champ, sinon renvoie false. isCreateable Boolean Renvoie true si l'objet peut être créé par l'utilisateur actuel, sinon renvoie false. isCustom Boolean Renvoie true si l'objet est un champ personnalisé, renvoie false si l'objet est standard. isCustomSetting Boolean Renvoie true si l'objet est un paramètre personnalisé, sinon renvoie false. isDeletable Boolean Renvoie true si l'objet peut être supprimé par l'utilisateur actuel, sinon renvoie false. isDeprecatedAndHidden Boolean Réservé pour une utilisation future. isFeedEnabled Boolean Renvoie true si des fils Chatter sont activés pour l'objet, sinon renvoie false. Cette méthode est disponible uniquement pour les classes et déclencheurs Apex enregistrés en utilisant l'API Salesforce.com versions 19.0 et ultérieures. isMergeable Boolean Renvoie true si l'objet peut être fusionné avec d'autres objets de ce type par l'utilisateur actuel, sinon renvoie false. true est renvoyé pour des pistes, des contacts ou des comptes. isQueryable Boolean Renvoie true si l'objet peut être demandé par l'utilisateur actuel, sinon renvoie false. isSearchable Boolean Renvoie true si l'objet peut être recherché par l'utilisateur actuel, sinon renvoie false. isUndeletable Boolean Renvoie true si l'objet ne peut pas être restauré par l'utilisateur actuel, sinon renvoie false. isUpdateable Boolean Renvoie true si l'objet peut être mis à jour par l'utilisateur actuel, sinon renvoie false. Référence salesforce | Méthodes sObject Apex | 445 Méthodes ChildRelationship Si un sObject est un objet parent, vous pouvez accéder à la relation enfant ainsi qu'au sObject enfant à l'aide des méthodes d'objet ChildRelationship. Un objet ChildRelationship est renvoyé à partir du résultat de description de sObject en utilisant la méthode getChildRelationship. Par exemple : Schema.DescribeSObjectResult R = Account.SObjectType.getDescribe(); List<Schema.ChildRelationship> C = R.getChildRelationships(); Vous pouvez utiliser 100 appels de méthode getChildRelationship par demande Apex seulement. Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Le tableau suivant décrit les méthodes disponibles pour l'objet ChildRelationship. Aucune méthode ne prend d'argument. Nom Type de données Description getChildSObject Schema.SObjectType Renvoie le jeton du sObject enfant pour lequel une clé étrangère existe sur le sObject parent. getField Schema.SObjectField Renvoie le jeton du champ qui a une clé étrangère dans le sObject parent. getRelationshipName String Renvoie le nom de la relation. isCascadeDelete Boolean Renvoie true si l'objet enfant est supprimé lorsque l'objet parent est supprimé, sinon renvoie false. isDeprecatedAndHidden Boolean Réservé pour une utilisation future. isRestrictedDelete Boolean Renvoie true si l'objet parent ne peut pas être supprimé parce qu'il est référencé par un objet enfant, sinon renvoie false. Méthodes RecordTypeInfo Si un type d'enregistrement est associé à un sObject, vous pouvez accéder aux informations sur le type d'enregistrement en utilisant les méthodes d'objet RecordTypeInfo. Un objet RecordTypeInfo est renvoyé à partir du résultat de description de sObject en utilisant la méthode getRecordTypeInfos. Par exemple : Schema.DescribeSObjectResult R = Account.SObjectType.getDescribe(); List<Schema.RecordTypeInfo> RT = R.getRecordTypeInfos(); En plus de la méthode getRecordTypeInfos, vous pouvez utiliser les méthodes getRecordTypeInfosById et getRecordTypeInfosByName. Ces méthodes renvoient les mappages qui associent RecordTypeInfo respectivement à des ID d'enregistrement et à des étiquettes d'enregistrement. Vous pouvez renvoyer 100 objets RecordTypeInfo par requête Apex seulement. Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Référence salesforce | Méthodes sObject Apex | 446 L'exemple suivant suppose qu'un type d'enregistrement au moins a été créé pour l'objet Account : RecordType rt = [SELECT Id,Name FROM RecordType WHERE SobjectType='Account' LIMIT 1]; Schema.DescribeSObjectResult d = Schema.SObjectType.Account; Map<Id,Schema.RecordTypeInfo> rtMapById = d.getRecordTypeInfosById(); Schema.RecordTypeInfo rtById = rtMapById.get(rt.id); Map<String,Schema.RecordTypeInfo> rtMapByName = d.getRecordTypeInfosByName(); Schema.RecordTypeInfo rtByName = rtMapByName.get(rt.name); System.assertEquals(rtById,rtByName); Le tableau suivant décrit les méthodes disponibles pour l'objet RecordTypeInfo. Aucune méthode ne prend d'argument. Nom Type de données Description getName String Renvoie le nom de ce type d'enregistrement. getRecordTypeId ID Renvoie l'ID de ce type d'enregistrement. isAvailable Boolean Renvoie true si ce type d'enregistrement est disponible pour l'utilisateur actuel, sinon renvoie false. Utilisez cette méthode pour afficher une liste des types d'enregistrement disponibles pour l'utilisateur lorsqu'il crée un enregistrement. isDefaultRecordTypeMapping Boolean Renvoie true s'il s'agit du mappage de type d'enregistrement par défaut, sinon renvoie false. Méthodes Describe Field Result Le tableau suivant présente les méthodes disponibles dans les résultats de description de champ. L'exemple suivant montre comment instancier un objet de résultat de description de champ : Schema.DescribeFieldResult F = Account.AccountNumber.getDescribe(); Aucune méthode ne prend d'argument. Nom Type de données Description getByteLength Integer Pour les champs à longueur variable (y compris les champs binaires), renvoie la taille maximale du champ, en octets getCalculatedFormula String Renvoie la formule spécifiée pour ce champ getController Schema.sObjectField Renvoie le jeton du champ de contrôle getDefaultValue Object Renvoie la valeur par défaut pour ce champ Référence salesforce | Méthodes sObject Apex | 447 Nom Type de données Description getDefaultValueFormula String Renvoie la valeur par défaut spécifiée pour ce champ si une formule n'est pas utilisée getDigits Integer Renvoie le nombre maximal de chiffres spécifié pour ce champ. Cette méthode est valide uniquement avec des champs Integer getInlineHelpText String Renvoie le contenu de l'aide au niveau du champ. Pour plus d'informations, reportez-vous à Définition de l'aide au niveau du champ. getLabel String Renvoie l'étiquette du texte affiché en regard du champ dans l'interface utilisateur de Salesforce. Cette étiquette peut être traduite. getLength Integer Pour les champs de chaîne, renvoie la taille maximale du champ en caractères Unicode (pas en octets) getLocalName String Renvoie le nom du champ, similaire à la méthode getName. Cependant, si le champ fait partie de l'espace de noms actuel, la partie espace de noms est ignorée. getName String Renvoie le nom de champ utilisé dans le code Apex getPicklistValues List <Schema.PicklistEntry> Renvoie une liste d'objets PicklistEntry. Une erreur d'exécution est renvoyée si le champ n'est pas une liste de sélection. getPrecision Integer Pour les champs de type Double, renvoie le nombre maximal de chiffres pouvant être stockés, qui comprennent les chiffres à gauche et à droite de la virgule (mais pas le caractère décimal) getReferenceTo List <Schema.sObjectType> Renvoie une liste d'objets Schema.sObjectType pour les objets parents de ce champ. Si la méthode isNamePointing renvoie true, plusieurs entrées existent dans la liste, sinon une seule existe. getRelationshipName String Renvoie le nom de la relation. Pour plus d'informations sur les relations et les noms de relation, reportez-vous à Understanding Relationship Names dans le document Force.com SOQL and SOSL Reference. getRelationshipOrder Integer Renvoie 1 si le champ est un enfant, sinon renvoie 0. Pour plus d'informations sur les relations et les noms de relation, reportez-vous à Understanding Relationship Names dans le document Force.com SOQL and SOSL Reference. getScale Integer Pour les champs de type Double, renvoie le nombre de chiffres à droite du caractère décimal. Tout chiffre Référence Nom salesforce | Méthodes sObject Apex | 448 Type de données Description supplémentaire à droite du caractère décimal est tronqué. Cette méthode renvoie une réponse de défaut si le nombre de chiffres à gauche du caractère décimal est excessif. getSOAPType Schema.SOAPType Renvoie l'une des valeurs d'énumération SoapType, en fonction du type du champ. Pour plus d'informations, reportez-vous à Valeurs enum Schema.SOAPType à la page 455. getSObjectField Schema.sObjectField Renvoie le jeton de ce champ getType Schema.DisplayType Renvoie l'une des valeurs d'énumération DisplayType, en fonction du type du champ. Pour plus informations, reportez-vous à Valeurs enum Schema.DisplayType à la page 450. isAccessible Boolean Renvoie true si l'utilisateur actuel peut afficher ce champ, sinon renvoie false isAutoNumber Boolean Renvoie true si le champ est un champ de numérotation automatique, sinon renvoie false. Similaires à un type IDENTITY SQL, les champs à numérotation automatique sont en lecture seule, ne peuvent pas être créés et ont une longueur maximale de 30 caractères. Les champs à numérotation automatique sont utilisés pour fournir un ID unique indépendant de l'ID d'objet interne (tel qu'un numéro de commande ou un numéro de facture). Les champs à numérotation automatique sont entièrement configurés dans l'interface utilisateur de Salesforce. isCalculated Boolean Renvoie true si le champ est un champ de formule personnalisé, sinon renvoie false. Notez que les champs de formule personnalisés sont toujours en lecture seule. isCascadeDelete Boolean Renvoie true si l'objet enfant est supprimé lorsque l'objet parent est supprimé, sinon renvoie false. isCaseSensitive Boolean Renvoie true si le champ est sensible à la casse, sinon renvoie false. isCreateable Boolean Renvoie true si le champ peut être créé par l'utilisateur actif, sinon renvoie false. isCustom Boolean Renvoie true si le champ est un champ personnalisé, false s'il est un objet standard. isDefaultedOnCreate Boolean Renvoie true si le champ reçoit une valeur par défaut à sa création, sinon renvoie false. Si true, Salesforce attribue implicitement une valeur à ce Référence Nom salesforce | Méthodes sObject Apex | 449 Type de données Description champ à la création de l'objet, même si aucune valeur pour ce champ n'est passée lors de l'appel de création. Par exemple, dans l'objet Opportunité, le champ Probabilité a cet attribut, car sa valeur est dérivée du champ Phase. De même, le Propriétaire a cet attribut dans la plupart des objets, car sa valeur est dérivée de l'utilisateur actif (si le champ Propriétaire n'est pas spécifié). isDependentPicklist Boolean Renvoie true si la liste de sélection est une liste de sélection dépendante, sinon renvoie false. isDeprecatedAndHidden Boolean Réservé pour une utilisation future. isExternalID Boolean Renvoie true si le champ est utilisé en tant qu'ID externe, sinon renvoie false. isFilterable Boolean Renvoie true si le champ peut être utilisé dans le cadre du critère de filtre d'une instruction WHERE, sinon renvoie false. isGroupable Boolean Renvoie true si le champ peut être inclus dans la clause GROUP BY d'une requête SOQL, sinon renvoie false. Cette méthode est disponible uniquement pour les classes et les déclencheurs Apex enregistrés en utilisant l'API versions 18.0 et supérieures. isHtmlFormatted Boolean Renvoie true si le champ a été mis en forme pour HTML et doit être codé pour l'affichage en HTML, sinon renvoie false. Un champ de formule personnalisé d'un lien hypertexte est un exemple de champ qui renvoie true pour cette méthode. Un champ de formule personnalisé qui a une fonction de texte IMAGE est un autre exemple. isIdLookup Boolean Renvoie true si le champ peut être utilisé pour spécifier un enregistrement dans une méthode upsert, sinon renvoie false. isNameField Boolean Renvoie true si le champ est un champ de nom, sinon renvoie false. Cette méthode est utilisée pour identifier le champ de nom pour des objets standard (tels queAccountName pour un objet Compte) et des objets personnalisés. Les objets ne peuvent avoir qu'un seul champ de nom, sauf lorsque des champs Prénom et Nom sont utilisés à la place (comme dans l'objet Contact). Si un nom composé existe, par exemple le champ Nom dans un compte personnel, isNameField est défini sur true pour cet enregistrement. Référence salesforce | Méthodes sObject Apex | 450 Nom Type de données Description isNamePointing Boolean Renvoie true si le champ peut avoir plusieurs types d'objet en tant que parents. Par exemple, une tâche qui peut avoir à la fois le champ ID de piste/ID de contact (WhoId) et le champ ID d'opportunité/ID de compte (WhatId) renvoie true pour cette méthode, car ces deux objets peuvent être le parent d'un enregistrement de tâche particulier. Sinon, cette méthode renvoie false. isNillable Boolean Renvoie true si le champ peut être nul, sinon renvoie false. Un champ qui peut être nul peut avoir un contenu vide. Un champ ne peut pas être nul doit inclure une valeur pour l'objet à créer ou à enregistrer. isPermissionable Boolean Renvoie true si des autorisations de champ peuvent être spécifiées pour ce champ, sinon renvoie false. isRestrictedDelete Boolean Renvoie true si l'objet parent ne peut pas être supprimé parce qu'il est référencé par un objet enfant, sinon renvoie false. isRestrictedPicklist Boolean Renvoie true si le champ est une liste de sélection restreinte, sinon renvoie false. isSortable Boolean Renvoie true si une requête peut être triée sur le champ, sinon renvoie false. isUnique Boolean Renvoie true si une valeur pour le champ doit être unique, sinon renvoie false. isUpdateable Boolean Renvoie true si : • Le champ peut être modifié par l'utilisateur actif, ou • Des enregistrements enfants dans un champ de relation principal-détails d'un objet personnalisé peuvent être attribués à d'autres enregistrements parents Sinon renvoie false. isWriteRequiresMasterRead Boolean Renvoie true si l'écriture dans l'objet de détail nécessite un partage en lecture au lieu du partage en lecture/écriture du parent. Valeurs enum Schema.DisplayType Une valeur d'énumération Schema.DisplayType est renvoyée par la méthode getType du résultat de description de champ. Pour plus d'informations, reportez-vous à Field Types dans le document Object Reference for Salesforce and Force.com. Pour plus d'informations sur les méthodes partagées par toutes les énumérations, reportez-vous à Méthodes Enum à la page 431. Référence salesforce | Méthodes sObject Apex | 451 Valeur de champ de type Contenu de l'objet champ anytype Toute valeur de type : String, Picklist, Boolean, Integer, Double, Percent, ID, Date, DateTime, URL ou Email. base64 Données binaires arbitraires codées en base64 (de type base64Binary) Boolean Valeurs booléennes (true ou false) Combobox Listes déroulantes qui fournissent une série de valeurs énumérées et permettent à l'utilisateur de spécifier une valeur ne figurant pas dans la liste Currency Valeurs de devise DataCategoryGroupReference Référence à un groupe de catégorie de données ou à un nom de catégorie unique Date Valeurs de date DateTime Valeurs de date/heure Double Valeurs de doublons Email Adresses e-mail EncryptedString Chaîne cryptée ID Champ de clé primaire d'un objet Integer Valeurs de nombre entier MultiPicklist Listes de sélection multiple qui fournissent une série de valeurs énumérées dans laquelle plusieurs valeurs peuvent être sélectionnées Percent Valeurs de poucentage Phone Numéros de téléphone. Les valeurs peuvent inclure des caractères alphanumériques. Les applications clientes sont responsables de la mise en forme du numéro de téléphone Picklist Listes de sélection unique qui fournissent une série de valeurs énumérées dans laquelle une seule valeur peut être sélectionnée Reference Référence croisée à un objet différent, semblable à un champ de clé étrangère String Valeurs de chaîne TextArea Valeurs de chaîne affichées en tant que champs de texte multilignes Time Valeurs temporelles URL Valeurs d'URL affichées en tant que liens hypertexte Méthodes Schema.PicklistEntry Les champs de liste de sélection contiennent un ou plusieurs éléments dans lesquels un utilisateur choisit un élément unique. Ils s'affichent sous la forme de listes déroulantes dans l'interface utilisateur de Salesforce. Un élément peut être défini comme élément par défaut. Un objet Schema.PicklistEntry est renvoyé depuis le résultat de description de champ en utilisant la méthode getPicklistValues. Par exemple : Schema.DescribeFieldResult F = Account.Industry.getDescribe(); List<Schema.PicklistEntry> P = F.getPicklistValues(); Référence salesforce | Méthodes sObject Apex | 452 Vous pouvez utiliser uniquement 100 appels de méthode getPicklistValue par demande Apex. Pour plus d'informations sur les limitations du gouverneur, reportez-vous à Compréhension des limitations et des gouverneurs d'exécution à la page 273. Le tableau suivant décrit les méthodes disponibles avec l'objet PicklistEntry. Aucune méthode ne prend d'argument. Nom Type de données Description getLabel String Renvoie le nom d'affichage de cet élément dans la liste de sélection getValue String Renvoie la valeur de cet élément dans la liste de sélection isActive Boolean Renvoie true si l'élément doit être affiché dans la liste déroulante du champ de liste de sélection dans l'interface utilisateur, sinon renvoie false isDefaultValue Boolean Renvoie true si l'élément est la valeur par défaut de la liste de sélection, sinon renvoie false. Un seul élément d'une liste de sélection peut être désigné comme valeur par défaut Schema.sObjectField Un objet Schema.sObjectField est renvoyé depuis le résultat de description de champ en utilisant les méthodes getControler et getSObjectField. Par exemple : Schema.DescribeFieldResult F = Account.Industry.getDescribe(); Schema.sObjectField T = F.getSObjectField(); Le tableau suivant présente la méthode disponible avec l'objet sObjectField. Cette méthode ne prend aucun argument. Nom Type de données Description getDescribe Schema.DescribeFieldResult Renvoie le résultat de description de ce champ Schema.sObjectType Un objet Schema.sObjectType est renvoyé depuis le résultat de description de champ en utilisant la méthode getReferenceTo, ou depuis le résultat de description de sObject en utilisant la méthode getSObjectType. Par exemple : Schema.DescribeFieldResult F = Account.Industry.getDescribe(); List<Schema.sObjectType> P = F.getReferenceTo(); Le tableau suivant présente les méthodes disponibles avec l'objet sObjectType. Nom Argument Type de données Description getDescribe Schema.DescribeSObjectResult Renvoie le résultat sObject description de ce champ newSObject sObject Construit un nouveau sObject de ce type. Pour consulter un exemple, reportez-vous à Création de sObjects de façon dynamique. Référence salesforce | Méthodes sObject Apex | 453 Nom Argument Type de données Description newSObject ID Id sObject Construit un nouveau sObject de ce type, avec l'ID spécifié. Pour cet argument, passez l'ID d'un enregistrement existant dans la base de données. Une fois le nouveau sObject créé, les champs du sObject renvoyé sont tous définis sur null. Vous pouvez définir tout champ qui peut être mis à jour avec les valeurs de votre choix, puis mettre à jour l'enregistrement dans la base de données. Seuls les champs pour lesquels vous définissez de nouvelles valeurs sont mis à jour. Les autres champs, qui ne sont pas des champs système, sont conservés. newSObject ID recordTypeId Boolean loadDefaults sObject Construit un nouveau sObject de ce type et, en option, avec l'ID du type d'enregistrement spécifié et des valeurs de champ personnalisé par défaut. L'argument recordTypeId spécifie l'ID de type d'enregistrement du sObject à créer. Si aucun type d'enregistrement n'existe pour ce sObject, utilisez null. Si le sObject a des types d'enregistrement et que vous spécifiez null, le type d'enregistrement par défaut est utilisé. L'argument loadDefaults spécifie si les champs personnalisés sont renseignés avec leurs valeurs par défaut prédéfinies (true) ou non (false). Remarques : • • • Pour les champs obligatoires ayant des valeurs par défaut, assurez-vous de fournir une valeur avant d'insérer le nouveau sObject, sinon l'insertion génère une erreur. Le champ Nom de compte ou un champ de relation principal-détails en sont des exemples. Les listes de sélection et les listes de sélection multiple peuvent prendre des valeurs par défaut spécifiées par type d'enregistrement. Par conséquent, cette méthode renseigne la valeur par défaut correspondant au type d'enregistrement spécifié. Si des champs n'ont aucune valeur par défaut prédéfinie et que l'argument loadDefaults est true, cette méthode Référence Nom salesforce | Méthodes sObject Apex | 454 Argument Type de données Description • • • crée le sObject avec des valeurs de champ null. Si l'argument loadDefaults est false, cette méthode crée le sObject avec des valeurs de champ null. Cette méthode renseigne les champs personnalisés en lecture seule du nouveau sObject avec les valeurs par défaut. Vous pouvez ensuite insérer le nouveau sObject avec les champs en lecture seule, même si ces champs ne sont pas modifiables une fois insérés. Si un champ personnalisé est marqué comme unique et qu'il fournit également une valeur par défaut, l'insertion de plusieurs nouveaux sObjects génère une exception d'exécution en raison des valeurs de champ dupliquées. Pour plus d'informations sur les valeurs de champ par défaut, reportez-vous à « À propos des valeurs de champ par défaut » dans l'aide en ligne de Salesforce. Pour un exemple, reportez-vous à Exemple : Création d'un nouveau sObject avec des valeurs par défaut. Exemple : Création d'un nouveau sObject avec des valeurs par défaut Cet exemple crée un compte avec toutes les valeurs par défaut renseignées pour ses champs personnalisés, s'il en contient, en utilisant la méthode newSObject. Il crée également un deuxième compte pour un type d'enregistrement spécifique. L'exemple définit le champ Nom (obligatoire et sans valeur par défaut) pour les deux comptes avant de les insérer. // Create an account with predefined default values Account acct = (Account)Account.sObjectType.newSObject(null, true); // Provide a value for Name acct.Name = 'Acme'; // Insert new account insert acct; // This is for record type RT1 of Account ID rtId = [SELECT Id FROM RecordType WHERE sObjectType='Account' AND Name='RT1'].Id; Account acct2 = (Account)Account.sObjectType.newSObject(rtId, true); // Provide a value for Name Référence salesforce | Méthodes sObject Apex | 455 acct2.Name = 'Acme2'; // Insert new account insert acct2; Valeurs enum Schema.SOAPType Une valeur d'énumération Schema.SOAPType est renvoyée par la méthode getSoapType de résultat de description de champ. Pour plus d'informations, reportez-vous à SOAPTypes dans le guide SOAP API Developer's Guide. Pour plus d'informations sur les méthodes partagées par toutes les énumérations, reportez-vous à Méthodes Enum à la page 431. Valeur de champ de type Contenu de l'objet champ anytype Toute valeur de type : String, Boolean, Integer, Double, ID, Date ou DateTime. base64binary Données binaires arbitraires codées en base64 (de type base64Binary) Boolean Valeurs booléennes (true ou false) Date Valeurs de date DateTime Valeurs de date/heure Double Valeurs de doublons ID Champ de clé primaire d'un objet Integer Valeurs de nombre entier String Valeurs de chaîne Time Valeurs temporelles Méthodes Schema.FieldSet Contient des méthodes de découverte et de récupération des détails des ensembles de champs créés dans des sObjects. Remarque: Cette édition contient une version bêta d'ensembles de champs prêts pour la production, mais qui inclut des limitations connues. Utilisation Utilisez les méthodes de la classe Schema.FieldSet pour découvrir les champs inclus dans un ensemble de champs, et récupérez des informations sur l'ensemble de champs lui-même, notamment le nom, l'espace de noms, l'étiquette, etc. L'exemple suivant montre comment obtenir une collection d'objets de résultat de description d'ensemble de champs pour un sObject. La clé du mappage renvoyé est le nom de l'ensemble de champs, et la valeur est le résultat de description d'ensemble de champs correspondant. Map<String, Schema.FieldSet> FsMap = Schema.SObjectType.Account.fieldSets.getMap(); Référence salesforce | Méthodes sObject Apex | 456 Les ensembles de champs sont également disponibles à partir des résultats de description de sObject. Les lignes de codes suivantes sont équivalentes à l'exemple précédent : Schema.DescribeSObjectResult d = Account.sObjectType.getDescribe(); Map<String, Schema.FieldSet> FsMap = d.fieldSets.getMap(); Pour travailler avec un ensemble de champs individuel, vous pouvez y accéder via le mappage d'ensembles de champs avec un sObject, ou en utilisant une référence explicite à l'ensemble de champs si vous connaissez son nom. Les deux lignes de code suivantes récupèrent le même ensemble de champs : Schema.FieldSet fs1 = Schema.SObjectType.Account.fieldSets.getMap().get('field_set_name'); Schema.FieldSet fs2 = Schema.SObjectType.Account.fieldSets.field_set_name; Méthodes Les méthodes suivantes sont les méthodes d'instance de la classe Schema.FieldSet. Aucune méthode ne prend d'argument. Méthode Type de renvoi Description getDescription String Renvoie la description de l'ensemble de champs. Description est un champ obligatoire d'un ensemble de champs, qui indique le contexte et le contenu de l'ensemble de champs. Il est souvent destiné à l'administrateur qui configure un ensemble de champs défini dans un package géré, plutôt qu'à l'utilisateur final. getFields List<FieldSetMember> Renvoie une liste d'objets Schema.FieldSetMember pour les champs qui composent l'ensemble de champs. getLabel String Renvoie l'étiquette du texte affiché en regard du champ dans l'interface utilisateur de Salesforce. getName String Renvoie le nom de l'ensemble de champs. getNamespace String Renvoie l'espace de noms de l'ensemble de champs. L'espace de noms est une chaîne vide si votre organisation n'a pas défini d'espace de noms et que l'ensemble de champs est défini dans votre organisation. Sinon, il correspond à l'espace de noms de votre organisation ou à celui du package géré contenant l'ensemble de champs. getSObjectType SObjectType Renvoie le Schema.sObjectType du sObject qui contient la définition de l'ensemble de champs. Méthodes Schema.FieldSetMember Contient les méthodes d'accès aux métadonnées des champs de membre d'ensemble de champs. Utilisation Utilisez la méthode de la classe Schema.FieldSetMember pour obtenir des détails sur les champs contenus dans un ensemble de champs, notamment l'étiquette du champ, le type, un chemin de champ prêt pour SOQL, etc. L'exemple suivant montre Référence salesforce | Méthodes sObject Apex | 457 comment obtenir une collection d'objets de résultat de description de membre pour un ensemble de champs spécifique dans un sObject : List<Schema.FieldSetMember> fields = Schema.SObjectType.Account.fieldSets.getMap().get('field_set_name').getFields(); Si vous connaissez le nom de l'ensemble de champs, vous pouvez accéder directement aux champs en utilisant une référence explicite à l'ensemble de champs : List<Schema.FieldSetMember> fields = Schema.SObjectType.Account.fieldSets.field_set_name.getFields(); Méthodes Les méthodes suivantes sont les méthodes d'instance de la classe Schema.FieldSetMember. Aucune méthode ne prend d'argument. Méthode Type de renvoi Description getDBRequired Boolean Renvoie true si le champ est requis par la définition du champ dans ce sObject, sinon renvoie false. getFieldPath String Renvoie une chaîne de chemin de champ sous un format prêt pour une requête SOQL dynamique. Pour consulter un exemple d'utilisation de cette méthode, reportez-vous à Affichage d'un ensemble de champs dans une page Visualforce. getLabel String Renvoie l'étiquette du texte affiché en regard du champ dans l'interface utilisateur de Salesforce. getRequired Boolean Renvoie true si le champ est requis par l'ensemble de champs, sinon renvoie false. getType Schema.DisplayType Renvoie le type de données Apex du champ. Exemple : Affichage d'un ensemble de champs dans une page Visualforce Cet exemple utilise les méthodes Schema.FieldSet et Schema.FieldSetMember pour obtenir dynamiquement tous les champs de l'ensemble de champs Dimensions de l'objet personnalisé Merchandise. La liste de champs est ensuite utilisée pour construire une requête SOQL qui vérifie que ces champs peuvent être affichés. La page Visualforce utilise la classe MerchandiseDetails en tant que contrôleur. public class MerchandiseDetails { public Merchandise__c merch { get; set; } public MerchandiseDetails() { this.merch = getMerchandise(); } Référence salesforce | Méthodes sObject Apex | 458 public List<Schema.FieldSetMember> getFields() { return SObjectType.Merchandise__c.FieldSets.Dimensions.getFields(); } private Merchandise__c getMerchandise() { String query = 'SELECT '; for(Schema.FieldSetMember f : this.getFields()) { query += f.getFieldPath() + ', '; } query += 'Id, Name FROM Merchandise__c LIMIT 1'; return Database.query(query); } } La page Visualforce utilisant le contrôleur ci-dessus est simple : <apex:page controller="MerchandiseDetails"> <apex:form > <apex:pageBlock title="Product Details"> <apex:pageBlockSection title="Product"> <apex:inputField value="{!merch.Name}"/> </apex:pageBlockSection> <apex:pageBlockSection title="Dimensions"> <apex:repeat value="{!fields}" var="f"> <apex:inputField value="{!merch[f.fieldPath]}" required="{!OR(f.required, f.dbrequired)}"/> </apex:repeat> </apex:pageBlockSection> </apex:pageBlock> </apex:form> Référence salesforce | Méthodes sObject Apex | 459 </apex:page> Dans la construction ci-dessus, notez l'expression utilisée pour déterminer si un champ du formulaire doit être défini comme obligatoire. Un champ d'un ensemble peut être requis par la définition d'ensemble de champs ou par la définition de propriétaire du champ. L'expression gère ces deux cas. Méthodes Custom Settings Les méthodes Custom Settings (paramètres personnalisés) sont toutes des méthodes d'instance, c.-à-d. qu'elles sont appelées par un opérateur dans une instance particulière d'un paramètre personnalisé. Il existe deux types de paramètres personnalisés : hiérarchie et liste. Les méthodes sont divisées entre celles qui fonctionnent avec des paramètres personnalisés de liste et celles qui fonctionnent avec des paramètres personnalisés de hiérarchie. Les méthodes ci-dessous sont les méthodes d'instance pour List Custom Settings. Tableau 1: Méthodes List Custom Settings Nom Arguments getAll Type de renvoi Description Map<String Renvoie un mappage des ensembles de données définis pour le paramètre personnalisé. Data_set_name, CustomSetting__c> Si aucun ensemble de données n'est défini, cette méthode renvoie un mappage vide. Remarque: Pour Apex enregistré en utilisant l'API Salesforce.com version 20.0 ou antérieure, les noms d'ensemble de données, qui sont les clés du mappage renvoyé, sont convertis en minuscules. Pour Apex enregistré en utilisant l'API Salesforce.com versions 21.0 et ultérieures, la casse des noms d'ensemble de données dans les clés de mappage renvoyées n'est pas modifiée et la casse d'origine est conservée. getInstance String CustomSetting__c dataset_name Renvoie l'enregistrement d'ensemble de données pour le paramètre personnalisé dataset_name spécifié. Cette méthode renvoie exactement le même objet que getValues(dataset_name). Si aucune donnée n'est définie pour l'ensemble de données spécifié, cette méthode renvoie null. getValues String dataset_name CustomSetting__c Renvoie l'enregistrement d'ensemble de données pour le paramètre personnalisé dataset_name spécifié. Cette méthode renvoie exactement le même objet que getInstance(dataset_name). Si aucune donnée n'est définie pour l'ensemble de données spécifié, cette méthode renvoie null. Référence salesforce | Méthodes sObject Apex | 460 Les méthodes ci-dessous sont les méthodes d'instance pour Hierarchy Custom Settings. Tableau 2: Méthodes Hierarchy Custom Settings Nom getInstance Arguments Type de renvoi Description CustomSetting__c Renvoie un enregistrement d'ensemble de données de paramètre personnalisé de l'utilisateur actif. Les champs renvoyés dans l'enregistrement de paramètre personnalisé sont fusionnés sur la base des champs au niveau le plus bas de la hiérarchie. Si aucune donnée de paramètre personnalisé n'est définie pour l'utilisateur, cette méthode renvoie un nouvel objet de paramètre personnalisé avec l'ID défini sur un null, et avec des champs fusionnés plus haut dans la hiérarchie. Vous pouvez ajouter ce nouvel enregistrement de paramètre personnalisé pour l'utilisateur en utilisant insert ou upsert. Si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, le paramètre personnalisé renvoyé contient des champs vides, à l'exception du champ SetupOwnerId qui contient l'ID utilisateur. Remarque: Pour Apex enregistré en utilisant l'API Salesforce.com version 21.0 ou antérieure, cette méthode renvoie l'enregistrement d'ensemble de données de paramètre personnalisé, avec des champs fusionnés à partir de valeurs de champ définies au plus bas niveau de la hiérarchie, en commençant par l'utilisateur. En outre, si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, cette méthode renvoie null. Exemples : • • Ensemble de données de paramètre personnalisé défini pour l'utilisateur : si vous avez un ensemble de données de paramètre personnalisé défini pour l'utilisateur « Uriel Jones », pour le profil « Administrateur système » et pour l'organisation dans son ensemble, et que l'utilisateur exécutant le code est Uriel Jones, cette méthode renvoie l'enregistrement de paramètre personnalisé défini pour Uriel Jones. Champs fusionnés : si vous avez un ensemble de données de paramètre personnalisé avec des champs A et B pour l'utilisateur « Uriel Jones » et pour le profil « Administrateur système », et que le champ A est défini pour Uriel Jones, que le champ B est Référence Nom salesforce | Méthodes sObject Apex | 461 Arguments Type de renvoi Description null, mais défini pour le profil Administrateur • système, cette méthode renvoie l'enregistrement de paramètre personnalisé de Uriel Jones, avec le champ A d'Uriel Jones et le champ B du profil Administrateur système. Aucun ensemble de données de paramètre personnalisé défini pour l'utilisateur : si l'utilisateur actuel est « Barbara Mahonie », qui partage également le profil « Administrateur système », mais qu'aucune donnée n'est définie pour Barbara en tant qu'utilisatrice, cette méthode renvoie un nouvel enregistrement de paramètre personnalisé avec l'ID défini sur null, et avec des champs fusionnés sur la base des champs définis au niveau le plus bas de la hiérarchie. Cette méthode est équivalente à un appel de méthode de getInstance(User_Id) pour l'utilisateur actuel. getInstance ID User_Id CustomSetting__c Renvoie l'enregistrement d'ensemble de données pour le paramètre personnalisé User_Id spécifié. L'enregistrement de paramètre personnalisé et les champs au niveau le plus bas sont renvoyés. Utilisez-la afin de récupérer explicitement des données pour le paramètre personnalisé au niveau de l'utilisateur. Si aucune donnée de paramètre personnalisé n'est définie pour l'utilisateur, cette méthode renvoie un nouvel objet de paramètre personnalisé avec l'ID défini sur un null, et avec des champs fusionnés plus haut dans la hiérarchie. Vous pouvez ajouter ce nouvel enregistrement de paramètre personnalisé pour l'utilisateur en utilisant insert ou upsert. Si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, le paramètre personnalisé renvoyé contient des champs vides, à l'exception du champ SetupOwnerId qui contient l'ID utilisateur. Remarque: Pour Apex enregistré en utilisant l'API Salesforce.com version 21.0 ou antérieure, cette méthode renvoie l'enregistrement d'ensemble de données de paramètre personnalisé, avec des champs fusionnés à partir de valeurs de champ définies au plus bas niveau de la hiérarchie, en commençant par l'utilisateur. En outre, si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, cette méthode renvoie null. Référence salesforce | Méthodes sObject Apex | 462 Nom Arguments Type de renvoi Description getInstance ID Profile_Id CustomSetting__c Renvoie l'enregistrement d'ensemble de données pour le paramètre personnalisé Profile_Id spécifié. L'enregistrement de paramètre personnalisé et les champs au niveau le plus bas sont renvoyés. Utilisez-la afin de récupérer explicitement des données pour le paramètre personnalisé au niveau du profil. Si aucune donnée de paramètre personnalisé n'est définie pour le profil, cette méthode renvoie un nouvel enregistrement de paramètre personnalisé avec l'ID défini sur un null, et avec des champs fusionnés à partir des valeurs par défaut de votre organisation. Vous pouvez ajouter ce nouveau paramètre personnalisé pour le profil en utilisant insert ou upsert. Si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, le paramètre personnalisé renvoyé contient des champs vides, à l'exception du champ SetupOwnerId qui contient l'ID de profil. Remarque: Pour Apex enregistré en utilisant l'API Salesforce.com version 21.0 ou antérieure, cette méthode renvoie l'enregistrement d'ensemble de données de paramètre personnalisé, avec des champs fusionnés à partir de valeurs de champ définies au plus bas niveau de la hiérarchie, en commençant par le profil. En outre, si aucune donnée de paramètre personnalisé n'est définie dans la hiérarchie, cette méthode renvoie null. CustomSetting__c getOrgDefaults Renvoie l'enregistrement d'ensemble de données de paramètre personnalisé pour l'organisation. Si aucune donnée de paramètre personnalisé n'est définie pour l'organisation, cette méthode renvoie un objet de paramètre personnalisé vide. Remarque: Pour Apex enregistré en utilisant l'API Salesforce.com version 21.0 ou antérieure, cette méthode renvoie null si aucune donnée de paramètre personnalisé n'est définie pour l'organisation. getValues ID User_Id CustomSetting__c Renvoie l'enregistrement d'ensemble de données pour le paramètre personnalisé User_Id spécifié. Utilisez-la si vous souhaitez uniquement le sous-ensemble de données de paramètre personnalisé qui a été défini au niveau de l'utilisateur. Par exemple, supposons que vous Référence salesforce | Méthodes sObject Apex | 463 Nom Arguments Type de renvoi Description avez un champ de paramètre personnalisé auquel une valeur « foo » a été attribuée au niveau de l'organisation, mais qui n'a aucune valeur attribuée au niveau de l'utilisateur ou du profil. L'utilisation de getValues(User_Id) renvoie null pour ce champ de paramètre personnalisé. getValues ID Profile_Id CustomSetting__c Renvoie l'ensemble de données pour le paramètre personnalisé Profile_Id spécifié. Utilisez-la si vous souhaitez uniquement le sous-ensemble de données de paramètre personnalisé qui a été défini au niveau du profil. Par exemple, supposons que vous avez un champ de paramètre personnalisé auquel une valeur « foo » a été attribuée au niveau de l'organisation, mais qui n'a aucune valeur attribuée au niveau de l'utilisateur ou du profil. L'utilisation de getValues(Profile_Id) renvoie null pour ce champ de paramètre personnalisé. Pour plus d'informations sur les paramètres personnalisés, reportez-vous à « Présentation des paramètres personnalisés » dans l'aide en ligne de Salesforce. Remarque: Toutes les données des paramètres personnalisés sont exposées dans la mémoire cache de l'application, ce qui optimise l'accès sans les coûts inhérents aux requêtes répétées vers la base de données. Cependant, une demande de données de paramètres personnalisés à l'aide de SOQL (Standard Object Query Language) n'utilise pas le cache de l'application et est similaire à une demande d'objet personnalisé. Pour bénéficier de la mise en cache, utilisez d'autres méthodes d'accès aux données de paramètres personnalisés, telles que les méthodes de paramètres personnalisés Apex. Exemples de paramètres personnalisés L'exemple suivant utilise une liste de paramètres personnalisés appelée Games. Games possède un champ appelé GameType. Cet exemple détermine si la valeur du premier ensemble de données est égal à la chaîne PC. List<Games__C> mcs = Games__c.getall().values(); boolean textField = null; if (mcs[0].GameType__c == 'PC') { textField = true; } system.assertEquals(textField, true); L'exemple suivant utilise un paramètre personnalisé depuis Exemple de paramètres personnalisés de code de pays et de région. Cet exemple montre que le paramètre personnalisé de liste des méthodes getValues et getInstance renvoie des valeurs identiques. Foundation_Countries__c myCS1 = Foundation_Countries__c.getValues('United States'); String myCCVal = myCS1.Country_code__c; Référence salesforce | Méthodes sObject Apex | 464 Foundation_Countries__c myCS2 = Foundation_Countries__c.getInstance('United States'); String myCCInst = myCS2.Country_code__c; system.assertEquals(myCCinst, myCCVal); Exemples de paramètres personnalisés de hiérarchie Dans l'exemple suivant, le paramètre personnalisé de hiérarchie GamesSupport possède un champ appelé Corporate_number. Le code renvoie la valeur pour le profil spécifié par pid. GamesSupport__c mhc = GamesSupport__c.getInstance(pid); string mPhone = mhc.Corporate_number__c; L'exemple est identique si vous choisissez d'utiliser la méthode getValues. L'exemple suivant montre comment utiliser les méthodes de paramètres personnalisés de hiérarchie. Pour getInstance, l'exemple montre comment les valeurs de champ qui ne sont pas définies pour un utilisateur ou un profil spécifique sont renvoyées depuis les champs définis au niveau le plus bas suivant de la hiérarchie. L'exemple montre également comment utiliser getOrgDefaults. Enfin, l'exemple montre comment getValues renvoie des champs de l'enregistrement de paramètre personnalisé uniquement pour l'utilisateur ou le profil spécifique, sans fusionner de valeurs d'autres niveaux de la hiérarchie. À la place, getValues renvoie null pour tous les champs non définis. Cet exemple utilise un paramètre personnalisé de hiérarchie appelé Hierarchy. Hierarchy possède deux champs : OverrideMe et DontOverrideMe. De plus, un utilisateur appelé Robert a un profil d'administrateur système. Les paramètres d'organisation, de profil et d'utilisateur de cet exemple sont les suivants : Paramètres d'organisation OverrideMe: Hello DontOverrideMe: World Paramètres de profil OverrideMe: Goodbye DontOverrideMe n'est pas défini. Paramètres d'utilisateur OverrideMe: Fluffy DontOverrideMe n'est pas défini. L'exemple suivant montre le résultat de la méthode getInstance si Robert l'appelle dans son organisation : Hierarchy__c CS = Hierarchy__c.getInstance(); System.Assert(CS.OverrideMe__c == 'Fluffy'); System.assert(CS.DontOverrideMe__c == 'World'); Si Robert transmet son ID utilisateur spécifié par RobertId à getInstance, les résultats sont identiques. En effet, le niveau le plus bas des données dans le paramètre personnalisé est spécifié au niveau de l'utilisateur. Hierarchy__c CS = Hierarchy__c.getInstance(RobertId); System.Assert(CS.OverrideMe__c == 'Fluffy'); Référence salesforce | Méthodes sObject Apex | 465 System.assert(CS.DontOverrideMe__c == 'World'); Si Robert transmet l'ID de profil Administrateur système spécifié par SysAdminID à getInstance, le résultat est différent. Les données spécifiées pour le profil sont renvoyées : Hierarchy__c CS = Hierarchy__c.getInstance(SysAdminID); System.Assert(CS.OverrideMe__c == 'Goodbye'); System.assert(CS.DontOverrideMe__c == 'World'); Lorsque Robert essaie de renvoyer l'ensemble de données de l'organisation en utilisant getOrgDefaults, le résultat est : Hierarchy__c CS = Hierarchy__c.getOrgDefaults(); System.Assert(CS.OverrideMe__c == 'Hello'); System.assert(CS.DontOverrideMe__c == 'World'); En utilisant la méthode getValues, Robert peut obtenir les valeurs de paramètre personnalisé de hiérarchie spécifiques à ces paramètres d'utilisateur et de profil. Par exemple, si Robert transmet son ID d'utilisateur RobertId à getValues, le résultat est : Hierarchy__c CS = Hierarchy__c.getValues(RobertId); System.Assert(CS.OverrideMe__c == 'Fluffy'); // Note how this value is null, because you are returning // data specific for the user System.assert(CS.DontOverrideMe__c == null); Si Robert transmet son ID de profil Administrateur système SysAdminID à getValues, le résultat est : Hierarchy__c CS = Hierarchy__c.getValues(SysAdminID); System.Assert(CS.OverrideMe__c == 'Goodbye'); // Note how this value is null, because you are returning // data specific for the profile System.assert(CS.DontOverrideMe__c == null); Exemple de paramètres personnalisés de code de pays et de région Cet exemple illustre l'utilisation de deux objets de paramètre personnalisé pour le stockage d'informations associées, et d'une page Visualforce pour l'affichage des données dans un ensemble de listes de sélection associées. Dans l'exemple suivant, les codes de pays et de région sont stockés dans deux paramètres personnalisés différents : Foundation_Countries et Foundation_States. Le paramètre personnalisé Foundation_Countries est un paramètre personnalisé de type de liste et possède un champ unique, Country_Code. Référence salesforce | Méthodes sObject Apex | 466 Le paramètre personnalisé Foundation_States est également un paramètre personnalisé de type de liste et possède les champs suivants : • • • Country Code State Code State Name La page Visualforce affiche deux listes de sélection : une pour le pays et une pour la région. Référence salesforce | Méthodes sObject Apex | 467 <apex:page controller="CountryStatePicker"> <apex:form > <apex:actionFunction name="rerenderStates" rerender="statesSelectList" > <apex:param name="firstParam" assignTo="{!country}" value="" /> </apex:actionFunction> <table><tbody> <tr> <th>Country</th> <td> <apex:selectList id="country" styleclass="std" size="1" value="{!country}" onChange="rerenderStates(this.value)"> <apex:selectOptions value="{!countriesSelectList}"/> </apex:selectList> </td> </tr> <tr id="state_input"> <th>State/Province</th> <td> <apex:selectList id="statesSelectList" styleclass="std" size="1" value="{!state}"> <apex:selectOptions value="{!statesSelectList}"/> Référence salesforce | Méthodes sObject Apex | 468 </apex:selectList> </td> </tr> </tbody></table> </apex:form> </apex:page> Le contrôleur Apex CountryStatePicker récupère les valeurs saisies dans les paramètres personnalisés, puis les renvoie vers la page Visualforce. public with sharing class CountryStatePicker { // Variables to store country and state selected by user public String state { get; set; } public String country {get; set;} // Generates country dropdown from country settings public List<SelectOption> getCountriesSelectList() { List<SelectOption> options = new List<SelectOption>(); options.add(new SelectOption('', '-- Select One --')); // Find all the countries in the custom setting Map<String, Foundation_Countries__c> countries = Foundation_Countries__c.getAll(); // Sort them by name List<String> countryNames = new List<String>(); countryNames.addAll(countries.keySet()); countryNames.sort(); // Create the Select Options. for (String countryName : countryNames) { Foundation_Countries__c country = countries.get(countryName); options.add(new SelectOption(country.country_code__c, country.Name)); } Référence salesforce | Méthodes sObject Apex | 469 return options; } // To generate the states picklist based on the country selected by user. public List<SelectOption> getStatesSelectList() { List<SelectOption> options = new List<SelectOption>(); // Find all the states we have in custom settings. Map<String, Foundation_States__c> allstates = Foundation_States__c.getAll(); // Filter states that belong to the selected country Map<String, Foundation_States__c> states = new Map<String, Foundation_States__c>(); for(Foundation_States__c state : allstates.values()) { if (state.country_code__c == this.country) { states.put(state.name, state); } } // Sort the states based on their names List<String> stateNames = new List<String>(); stateNames.addAll(states.keySet()); stateNames.sort(); // Generate the Select Options based on the final sorted list for (String stateName : stateNames) { Foundation_States__c state = states.get(stateName); options.add(new SelectOption(state.state_code__c, state.state_name__c)); } // If no states are found, just say not required in the dropdown. if (options.size() > 0) { options.add(0, new SelectOption('', '-- Select One --')); } else { options.add(new SelectOption('', 'Not Required')); Référence salesforce | Méthodes Système Apex | 470 } return options; } } Méthodes Système Apex Les classes suivantes sont les classes système Apex. • • • ApexPages Approval Database ◊ ◊ ◊ ◊ • Database Batch Database DMLOptions Database EmptyRecycleBinResult Database Error JSON Support ◊ Méthodes JSON ◊ Méthodes JSONGenerator ◊ Méthodes JSONParser • • • • Limits Math MultiStaticResourceCalloutMock Apex REST ◊ Méthodes RestContext ◊ Méthodes RestRequest ◊ Méthodes RestResponse • • • • • • • • • Search StaticResourceCalloutMock System Test TimeZone Type URL UserInfo Version Référence salesforce | Méthodes Système Apex | 471 Méthodes ApexPages Utilisez ApexPages pour ajouter et consulter les messages associés à la page actuelle, et pour référencer cette page. Les méthodes ApexPages sont également utilisées en tant qu'espace de noms pour des classes PageReference et Message. Le tableau suivant répertorie les méthodes ApexPages : Nom Arguments Type de renvoi Description addMessage sObject Void Ajoute un message au contexte de page actuel. Void Ajoute une liste de messages au contexte de page actuel sur la base d'une exception levée. ApexPages.Message addMessages Exception ex getMessages ApexPages.Message[] Renvoie une liste des messages associés au contexte actuel. hasMessages Boolean Renvoie true si des messages sont associés au contexte actuel, sinon renvoie false. Boolean Renvoie true si des messages de la sévérité spécifiée existent, sinon renvoie false. hasMessages ApexPages.Severity Méthodes Approval Le tableau suivant répertorie les méthodes statiques Approval. Approval est également utilisée comme espace de noms pour les classes ProcessRequest et ProcessResult. Nom Arguments Type de renvoi Description process Approval.ProcessRequest Approval.ProcessResult Soumet une nouvelle requête d'approbation et approuve ProcessRequest ou refuse les requêtes d'approbation existantes. Par exemple : // Insert an account Account a = new Account(Name='Test', annualRevenue=100.0); insert a; // Create an approval request for the account Approval.ProcessSubmitRequest req1 = new Référence Nom salesforce | Méthodes Système Apex | 472 Arguments Type de renvoi Description Approval.ProcessSubmitRequest(); req1.setObjectId(a.id); // Submit the approval request for the account Approval.ProcessResult result = Approval.process(req1); process Approval.ProcessRequest Approval.ProcessResult Soumet une nouvelle requête d'approbation et approuve ProcessRequests ou refuse les requêtes d'approbation existantes. Boolean Le paramètre facultatif opt_allOrNone spécifie si l'opération autorise un succès partiel. Si vous spécifiez false pour ce paramètre et qu'une approbation échoue, le reste des processus d'approbation peut réussir. opt_allOrNone process Approval.ProcessRequest Approval.ProcessResult Soumet une liste de nouvelles requêtes d'approbation et [] [] approuve ou refuse les requêtes d'approbation existantes. ProcessRequests process Approval.ProcessRequest Approval.ProcessResult Soumet une liste de nouvelles requêtes d'approbation et [] [] approuve ou refuse les requêtes d'approbation existantes. ProcessRequests Le paramètre facultatif opt_allOrNone spécifie si l'opération autorise un succès partiel. Si vous spécifiez false pour ce paramètre et qu'une approbation échoue, le reste des processus d'approbation peut réussir. Boolean opt_allOrNone Pour plus d'informations sur le processus approbation Apex, reportez-vous à Classes de gestion des approbations Apex à la page 674. Méthodes Database Les méthodes ci-dessous sont les méthodes statiques système pour Database. Nom Arguments Type de renvoi convertLead LeadConvert Database. Convertit une piste en compte et en contact, et LeadConvertResult (facultatif) en opportunité. leadToConvert, Boolean opt_allOrNone Description Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de Référence Nom salesforce | Méthodes Système Apex | 473 Arguments Type de renvoi Description résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode convertLead exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. convertLead LeadConvert[] leadsToConvert Boolean opt_allOrNone Database. LeadConvert Result[] Convertit une liste d'objets LeadConvert en comptes et en contacts, et (facultatif) en opportunités. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode convertLead exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. countQuery String query Integer Renvoie le nombre d'enregistrements qu'une requête SOQL dynamique renverrait à l'exécution. Par exemple : String QueryString = 'SELECT count() FROM Account'; Integer i = Database.countQuery(QueryString); Pour plus d'informations, reportez-vous à SOQL dynamique à la page 217. Chaque méthode convertQuery exécutée est prise en compte dans les limitations du gouverneur pour les requêtes SOQL. delete SObject recordToDelete Boolean opt_allOrNone DeleteResult Supprime un enregistrement de sObject existant, tel qu'un compte ou un contact individuel, des données de votre organisation. delete est semblable à l'instruction delete() dans l'API SOAP. Référence Nom salesforce | Méthodes Système Apex | 474 Arguments Type de renvoi Description Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode delete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. delete SObject[] recordsToDelete DeleteResult[] Boolean opt_allOrNone Supprime une liste d'enregistrements de sObject existants, tels que des comptes ou des contacts individuels, des données de votre organisation.delete est semblable à l'instruction delete() dans l'API SOAP. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode delete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. delete RecordID ID Boolean opt_allOrNone DeleteResult Supprime des enregistrements de sObject existants, tels que des comptes ou des contacts individuels, des données de votre organisation.delete est semblable à l'instruction delete() dans l'API SOAP. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode delete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. Référence salesforce | Méthodes Système Apex | 475 Nom Arguments Type de renvoi Description delete RecordIDs []IDs DeleteResult[] Supprime une liste d'enregistrements de sObject existants, tels que des comptes ou des contacts individuels, des données de votre organisation.delete est semblable à l'instruction delete() dans l'API SOAP. Boolean opt_allOrNone Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode delete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. emptyRecycleBin RecordIds []Ids Database. EmptyRecycleBin Result[] Supprime définitivement les enregistrements spécifiés de la corbeille. Notez les points suivants : • Les enregistrements supprimés avec cette méthode ne peuvent pas être restaurés. • Seuls 10 000 enregistrements peuvent être spécifiés pour la suppression. • L'utilisateur connecté peut supprimer tout enregistrement qu'il peut demander dans sa Corbeille ou dans celle de ses subordonnés. Si l'utilisateur connecté dispose de l'autorisation « Modifier toutes les données », il peut demander et supprimer des enregistrements dans n'importe quelle Corbeille de l'organisation. • Les ID d'enregistrements de suppression en cascade ne doivent pas être inclus dans la liste des ID, sinon une erreur est générée. Par exemple, si un enregistrement de compte est supprimé, tous les contacts, opportunités, contrats, etc., associés sont également supprimés. Incluez uniquement l'ID du compte de niveau supérieur. Tous les enregistrements associés sont automatiquement supprimés. • Les éléments supprimés sont ajoutés au nombre d'éléments traités par une instruction DML, et l'appel de méthode est ajouté au nombre total d'instructions DML générées. Chaque méthode emptyRecycleBin exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. Référence Nom salesforce | Méthodes Système Apex | 476 Arguments emptyRecycleBin sObject sObject Type de renvoi Description Database. EmptyRecycleBin Result Supprime définitivement le sObject spécifié de la corbeille. Notez les points suivants : • Un sObject supprimé avec cette méthode ne peut pas être restauré. • Seuls 10 000 sObjects peuvent être spécifiés pour être supprimés. • L'utilisateur connecté peut supprimer tout sObject qu'il peut demander dans sa Corbeille ou dans celle de ses subordonnés. Si l'utilisateur connecté dispose de l'autorisation « Modifier toutes les données », il peut demander et supprimer des sObjects dans n'importe quelle Corbeille de l'organisation. • N'incluez pas un sObject qui a été supprimé suite à une suppression en cascade, sinon une erreur est générée. Par exemple, si un compte est supprimé, tous les contacts, opportunités, contrats, etc., associés sont également supprimés. Incluez uniquement les sObjects du compte de niveau supérieur. Tous les sObjects associés sont automatiquement supprimés. • Les éléments supprimés sont ajoutés au nombre d'éléments traités par une instruction DML, et l'appel de méthode est ajouté au nombre total d'instructions DML générées. Chaque méthode emptyRecycleBin exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. emptyRecycleBin sObjects []listOfSObjects Database. EmptyRecycleBin Result[] Supprime définitivement les sObjects spécifiés de la corbeille. Notez les points suivants : • Un sObject supprimé avec cette méthode ne peut pas être restauré. • Seuls 10 000 sObjects peuvent être spécifiés pour être supprimés. • L'utilisateur connecté peut supprimer tout sObject qu'il peut demander dans sa Corbeille ou dans celle de ses subordonnés. Si l'utilisateur connecté dispose de l'autorisation « Modifier toutes les données », il peut demander et supprimer des sObjects dans n'importe quelle Corbeille de l'organisation. • N'incluez pas un sObject qui a été supprimé suite à une suppression en cascade, sinon une erreur est générée. Par exemple, si un compte est supprimé, tous les contacts, opportunités, Référence Nom salesforce | Méthodes Système Apex | 477 Arguments Type de renvoi Description • executeBatch sObject className ID contrats, etc., associés sont également supprimés. Incluez uniquement les sObjects du compte de niveau supérieur. Tous les sObjects associés sont automatiquement supprimés. Les éléments supprimés sont ajoutés au nombre d'éléments traités par une instruction DML, et l'appel de méthode est ajouté au nombre total d'instructions DML générées. Chaque méthode emptyRecycleBin exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. Exécute la classe spécifiée en tant que tâche Apex par lot. Pour plus d'informations, reportez-vous à Utilisation d'un code Apex par lot à la page 224. Remarque: La classe appelée par la méthode executeBatch met en oeuvre la méthode execute. executeBatch sObject className, Integer ID scope Exécute la classe spécifiée en tant que tâche Apex par lot. La valeur de scope doit être supérieure à 0. Pour plus d'informations, reportez-vous à Utilisation d'un code Apex par lot à la page 224. Remarque: La classe appelée par la méthode executeBatch met en oeuvre la méthode execute. getQueryLocator sObject [] listOfQueries QueryLocator Crée un objet QueryLocator utilisé dans un code Apex par lot ou une page Visualforce. Pour plus d'informations, reportez-vous à Objets et méthodes Apex de base de données par lot, Compréhension du partage géré Apex et Classe StandardSetController. Vous ne pouvez utiliser getQueryLocator avec aucune requête contenant une fonction agrégée. Chaque méthode getQueryLocator exécutée est prise en compte dans les limitations du gouverneur de 10 000 enregistrements au total récupérés et en nombre total de requêtes SOQL générées. getQueryLocator String query QueryLocator Crée un objet QueryLocator utilisé dans un code Apex par lot ou une page Visualforce. Pour plus d'informations, reportez-vous à Objets et méthodes Apex de base de données par lot, Compréhension Référence Nom salesforce | Méthodes Système Apex | 478 Arguments Type de renvoi Description du partage géré Apex et Classe StandardSetController. Vous ne pouvez utiliser getQueryLocator avec aucune requête contenant une fonction agrégée. Chaque méthode getQueryLocator exécutée est prise en compte dans les limitations du gouverneur de 10 000 enregistrements au total récupérés et en nombre total de requêtes SOQL générées. insert sObject recordToInsert SaveResult Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions Ajoute un sObject, tel qu'un compte ou un contact individuel, aux données de votre organisation. insert est semblable à l'instruction INSERT dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Chaque méthode insert exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. insert sObject [] recordsToInsert SaveResult[] Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions Ajoute un ou plusieurs sObjects, tels que des comptes ou des contacts individuels, aux données de votre organisation.insert est semblable à l'instruction INSERT dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML Référence Nom salesforce | Méthodes Système Apex | 479 Arguments Type de renvoi Description peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Chaque méthode insert exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. query String query sObject[] Crée une requête SOQL dynamique à l'exécution. Cette méthode peut être utilisée partout où une requête SOQL statique est utilisable, notamment dans des instructions d'attribution régulières et des boucles for. Pour plus d'informations, reportez-vous à SOQL dynamique à la page 217. Chaque méthode query exécutée est prise en compte dans les limitations du gouverneur pour les requêtes SOQL. rollback System.Savepoint sp Void Restaure la base de données à l'état spécifié par la variable de point de sauvegarde. Tous les e-mails soumis depuis le dernier point de sauvegarde sont également restaurés et pas envoyés. Notez les points suivants : • Les variables statiques ne sont pas restaurées durant une restauration. Si vous essayez de réexécuter le déclencheur, les variables statiques conservent les valeurs de la première exécution. • Chaque annulation est prise en compte dans les limitations du gouverneur pour les instructions DML. Vous obtenez une erreur d'exécution si vous essayez de restaurer la base de données un nombre de fois excessif. Référence Nom salesforce | Méthodes Système Apex | 480 Arguments Type de renvoi Description • L'ID dans un sObject inséré après la définition d'un point enregistrement n'est pas effacé après une annulation. Créez un nouveau sObject à insérer après une annulation. Une tentative d'insertion du sObject en utilisant la même variable créée avant l'annulation échoue, car la variable du sObject inclut un ID. La mise à jour ou la mise à jour/insertion du sObject en utilisant la même variable échoue également, car le sObject ne figure pas dans la base de données et, par conséquent, ne peut pas être mis à jour. Pour consulter un exemple, reportez-vous à Contrôle des transactions. setSavepoint System.Savepoint Renvoie une variable de point de sauvegarde qui peut être stockée en tant que variable locale, puis utilisée avec la méthode rollback pour restaurer la base de données à ce point. Notez les points suivants : • • • Si vous définissez plusieurs points d'enregistrement, puis restaurez un point d'enregistrement qui ne correspond pas au dernier point d'enregistrement généré, les variables des points d'enregistrement ultérieurs ne sont plus valides. Par exemple, si vous générez un premier point d'enregistrement SP1, puis un deuxième point d'enregistrement SP2, et que vous restaurez vers le point d'enregistrement SP1, la variable SP2 n'est plus valide. Vous recevez une erreur d'exécution lorsque vous essayez de l'utiliser. Les références à des points d'enregistrements ne peuvent pas croiser des invocations de déclencheur, car chaque invocation de déclencheur correspond à un nouveau contexte d'exécution. Si vous déclarez un point d'enregistrement en tant que variable statique, puis essayez de l'utiliser à travers des contextes de déclencheur, vous obtenez une erreur d'exécution. Chaque point d'enregistrement que vous définissez est prise en compte dans les limitations du gouverneur pour les instructions DML. Référence Nom salesforce | Méthodes Système Apex | 481 Arguments Type de renvoi Description Pour consulter un exemple, reportez-vous à Contrôle des transactions. undelete sObject recordToUndelete UndeleteResult Boolean opt_allOrNone Restaure un enregistrement de sObject existant, tel qu'un compte ou un contact individuel, depuis la corbeille de votre organisation. undelete est semblable à l'instruction UNDELETE dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode undelete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. undelete sObject [] UndeleteResult[] recordsToUndelete Boolean opt_allOrNone Restaure un ou plusieurs enregistrements de sObjects existants, tels que des comptes ou des contacts individuels, depuis la Corbeille de votre organisation.undelete est semblable à l'instruction UNDELETE dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode undelete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. undelete RecordID ID Boolean opt_allOrNone UndeleteResult Restaure un enregistrement de sObject existant, tel qu'un compte ou un contact individuel, depuis la corbeille de votre organisation. undelete est semblable à l'instruction UNDELETE dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de Référence Nom salesforce | Méthodes Système Apex | 482 Arguments Type de renvoi Description résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode undelete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. undelete RecordIDs[] ID Boolean opt_allOrNone UndeleteResult [] Restaure un ou plusieurs enregistrements de sObjects existants, tels que des comptes ou des contacts individuels, depuis la Corbeille de votre organisation.undelete est semblable à l'instruction UNDELETE dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Chaque méthode undelete exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. update sObject recordToUpdate Boolean opt_allOrNone | database.DMLOptions opt_DMLOptions Database.SaveResult Modifie un enregistrement de sObject existant, tel qu'un compte ou un contact individuel, dans les données de votre organisation. update est semblable à l'instruction UPDATE dans SQL. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous Référence Nom salesforce | Méthodes Système Apex | 483 Arguments Type de renvoi Description attribuez une valeur String trop longue pour le champ. Chaque méthode update exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. update sObject [] recordsToUpdate Database.SaveResult Modifie un ou plusieurs enregistrements de sObject [] existants, tels que des relevés de compte de comptes Boolean opt_allOrNone ou contacts individuels, dans les données de votre | organisation. update est semblable à l'instruction UPDATE dans SQL. database.DMLOptions opt_DMLOptions Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Le paramètre facultatif opt_DMLOptions spécifique des données supplémentaires pour la transaction, telles que des informations sur une règle d'attribution ou un comportement d'annulation lorsque des erreurs se produisent durant l'insertion d'enregistrements. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Chaque méthode update exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. upsert sObject recordToUpsert Schema.SObjectField External_ID_Field Boolean opt_allOrNone Database.UpsertResult Crée un enregistrement de sObject ou met à jour un enregistrement de sObject existant dans une instruction unique à l'aide d'un champ personnalisé facultatif afin de déterminer la présence d'objets existants. Le External_ID_Field est de type Schema.SObjectField, c.-à-d. un jeton de champ. Recherchez le jeton du champ en utilisant la méthode spéciale fields. Par exemple, Schema.SObjectField f = Account.Fields.MyExternalId. Référence Nom salesforce | Méthodes Système Apex | 484 Arguments Type de renvoi Description Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Chaque méthode upsert exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. upsert sObject [] recordsToUpsert Database.UpsertResult Utilisation d'un champ personnalisé facultatif pour [] déterminer la présence d'objets existants. Schema.SObjectField External_ID_Field Le External_ID_Field est de type Schema.SObjectField, c.-à-d. un jeton de champ. Boolean opt_allOrNone Recherchez le jeton du champ en utilisant la méthode spéciale fields. Par exemple, Schema.SObjectField f = Account.Fields.MyExternalId. Le paramètre facultatif opt_allOrNone indique si l'opération autorise le succès partiel. Si vous spécifiez false pour ce paramètre et qu'un enregistrement échoue, le reste de l'opération DML peut réussir. Cette méthode renvoie un objet de résultat qui permet de déterminer quels enregistrements ont réussi, quels enregistrements ont échoué, et pour quelles raisons. Les classes et les déclencheurs Apex sauvegardés (compilés) en utilisant l'API versions 15.0 et supérieures, génèrent une erreur d'exécution si vous attribuez une valeur String trop longue pour le champ. Référence salesforce | Méthodes Système Apex | 485 Nom Arguments Type de renvoi Description Chaque méthode upsert exécutée est prise en compte dans les limitations du gouverneur pour les instructions DML. Voir aussi : Opérations DML (langage de manipulation de données) Apex Compréhension des limitations et des gouverneurs d'exécution Objets et méthodes Database Batch Apex Méthode Database.QueryLocator Le tableau suivant indique la méthode de l'objet Database.QueryLocator : Nom Arguments getQuery Type de renvoi Description String Renvoie la requête utilisée pour instancier l'objet Database.QueryLocator. Elle est utile lors d'un test de la méthode start. Par exemple : System.assertEquals(QLReturnedFromStart. getQuery(), Database.getQueryLocator([SELECT Id FROM Account]).getQuery() ); Vous ne pouvez pas utiliser les mots clésFOR UPDATE avec une requête getQueryLocator pour verrouiller un ensemble d'enregistrements. La méthode start verrouille automatiquement l'ensemble d'enregistrements dans le lot. Propriétés Database DMLOptions Utilisez la classe Database.DMLOptions pour fournir des informations supplémentaires durant une transaction, par exemple en spécifiant des informations sur le comportement de troncature des champs ou sur les règles d'attribution. DMLOptions est disponible uniquement pour un code Apex enregistré en utilisant l'API versions 15.0 et supérieures. La classe Database.DMLOptions a les propriétés suivantes : • • • • Propriété allowFieldTruncation Propriété assignmentRuleHeader Propriété emailHeader Propriété localeOptions Référence • salesforce | Méthodes Système Apex | 486 Propriété optAllOrNone Propriété allowFieldTruncation La propriété allowFieldTruncation spécifie le comportement de troncature des chaînes. Dans un code Apex enregistré en utilisant l'API de versions antérieures à 15.0, si vous spécifiez une valeur de chaîne trop grande, elle est tronquée. Avec l'API versions 15.0 et ultérieures, si une valeur spécifiée est trop grande, l'opération échoue et un message d'erreur est renvoyé. La propriété allowFieldTruncation permet de spécifier que le comportement précédent, la troncature, doit être utilisée au lieu du nouveau comportement dans un code Apex enregistré en utilisant l'API versions 15.0 et ultérieures. La propriété allowFieldTruncation prend une valeur booléenne. Si true, la propriété tronque les valeurs de chaîne trop longues, ce qui correspond au comportement de l'API versions 14.0 et antérieures. Par exemple : Database.DMLOptions dml = new Database.DMLOptions(); dml.allowFieldTruncation = true; Propriété assignmentRuleHeader La propriété assignmentRuleHeader spécifie la règle d'attribution à utiliser lors de la création d'une requête ou d'une piste. Remarque: L'objet database.DMLOptions prend en charge les règles d'attribution pour les requêtes et les pistes, mais pas pour les comptes ou la gestion des territoires. Les options suivantes peuvent être définies avec assignmentRuleHeader : Nom Type Description assignmentRuleID ID Spécifie l'ID d'une règle d'attribution spécifique à exécuter pour la requête ou la piste. La règle d'attribution peut être active ou inactive. L'ID peut être récupéré en demandant le sObject AssignmentRule. Si l'ID est spécifié, ne définissez pas useDefaultRule. Si la valeur ne respecte pas le format d'ID correct (ID Salesforce à 15 ou 18 caractères), l'appel échoue et une exception est renvoyée. useDefaultRule Boolean Si spécifié comme true pour une requête ou une piste, le système utilise la règle d'attribution (active) par défaut pour la requête ou la piste. Si cette propriété est spécifiée, ne définissez pas assignmentRuleId. L'exemple suivant utilise l'option useDefaultRule : Database.DMLOptions dmo = new Database.DMLOptions(); dmo.assignmentRuleHeader.useDefaultRule= true; Lead l = new Lead(company='ABC', lastname='Smith'); l.setOptions(dmo); Référence salesforce | Méthodes Système Apex | 487 insert l; L'exemple suivant utilise l'option assignmentRuleID : Database.DMLOptions dmo = new Database.DMLOptions(); dmo.assignmentRuleHeader.assignmentRuleId= '01QD0000000EqAn'; Lead l = new Lead(company='ABC', lastname='Smith'); l.setOptions(dmo); insert l; Propriété emailHeader L'interface utilisateur de Salesforce permet de spécifier l'envoi ou non d'un e-mail lorsque les événements ci-dessous se produisent : • • • • • • Création d'un requête ou tâche Création d'un commentaire de requête Conversion d'un e-mail de requête en contact Nouvelle notification d'utilisateur par e-mail Notification par e-mail de file d'attente de piste Réinitialisation du mot de passe Dans un code Apex enregistré en utilisant l'API version 15.0 ou ultérieure, la propriété emailHeader Database.DMLOptions permet de spécifier des informations supplémentaires dans l'e-mail envoyé si l'exécution du code entraîne l'un des événements. Les options suivantes peuvent être définies avec la propriété emailHeader : Nom Type Description triggerAutoResponseEmail Boolean Indique de déclencher des règles de réponse automatique (true) ou non (false) pour des pistes et des requêtes. Dans l'interface utilisateur Salesforce, cet e-mail peut être déclenché automatiquement par plusieurs événements, par exemple lors de la création d'une requête ou la réinitialisation d'un mot de passe utilisateur. Si cette valeur est définie sur true, lors de la création d'une requête, si une adresse e-mail du contact est spécifiée dans ContactID, l'e-mail est envoyé à cette adresse. Sinon, l'e-mail est envoyé à l'adresse spécifiée dans SuppliedEmail. triggerOtherEmail Boolean Indique de déclencher un e-mail hors de l'organisation (true) ou non (false). Dans l'interface utilisateur de Salesforce, cet e-mail peut être déclenché automatiquement lors de la création, la modification ou la suppression d'un contact d'une requête. triggerUserEmail Boolean Indique de déclencher un e-mail envoyé aux utilisateurs dans l'organisation (true) ou non (false). Dans l'interface utilisateur de Salesforce, cet e-mail peut être déclenché automatiquement lors de plusieurs événements, notamment la Référence salesforce | Méthodes Système Apex | 488 Nom Type Description réinitialisation d'un mot de passe, la création d'un utilisateur, l'ajout de commentaires à une requête, la création ou la modification d'une tâche. Dans l'exemple suivant, l'option triggerAutoResponseEmail est spécifiée : Account a = new Account(name='Acme Plumbing'); insert a; Contact c = new Contact(email='[email protected]', firstname='Joe',lastname='Plumber', accountid=a.id); insert c; Database.DMLOptions dlo = new Database.DMLOptions(); dlo.EmailHeader.triggerAutoResponseEmail = true; Case ca = new Case(subject='Plumbing Problems', contactid=c.id); database.insert(ca, dlo); E-mail envoyé via Apex, car un événement de groupe inclut des comportements supplémentaires. Un événement de groupe est un événement pour lequel IsGroupEvent est true. L'objet EventAttendee suit les utilisateurs, les pistes ou les contacts qui sont invités dans un événement de groupe. Notez les comportements suivants pour un e-mail d'événement de groupe envoyé via Apex : • • • L'envoi d'une invitation d'événement de groupe à un utilisateur respecte l'option triggerUserEmail L'envoi d'une invitation d'événement de groupe à une piste ou un contact respecte l'option triggerOtherEmail Un e-mail envoyé lors de la mise à jour ou de la suppression d'un événement de groupe respecte également les options triggerUserEmail et triggerOtherEmail, selon le cas Propriété localeOptions La propriété localeOptions spécifie la langue de toutes les étiquettes qui sont renvoyées par Apex. La valeur doit être un paramètre régional valide de l'utilisateur (langue et pays), tel que fr_FR ou en_GB. La valeur est une chaîne d'une longueur de 2 à 5 caractères. Les deux premiers caractères correspondent toujours à un code de langue ISO, par exemple 'fr' ou 'en'. Si la valeur est ensuite définie avec un pays, la chaîne inclut également un trait de soulignement (_) et un autre code de pays ISO, par exemple 'FR' ou 'UK'. SiPar exemple, pour les États-Unis la chaîne est 'en_US', et pour le français canadien la chaîne est 'fr_CA'. Référence salesforce | Méthodes Système Apex | 489 Pour une liste des langues prises en charge par Salesforce, reportez-vous à Quelles sont les langues prises en charge par Salesforce ? dans l'aide en ligne de Salesforce. Propriété optAllOrNone La propriété optAllOrNone spécifie si l'opération autorise un succès partiel. Si optAllOrNone est défini sur true, toutes les modifications sont annulées dans tout enregistrement qui génère des erreurs. La valeur par défaut de cette propriété est false, c.-à-d. que les enregistrements traités avec succès sont transmis alors que ceux contenant des erreurs ne le sont pas. Cette propriété est disponible dans Apex enregistré en utilisant l'API Salesforce.com versions 20.0 et ultérieures. Méthodes Database EmptyRecycleBinResult Une liste d'objets Database.EmptyRecycleBinResult est renvoyée par la méthode Database.emptyRecycleBin. Chaque objet de la liste correspond à un ID d'enregistrement ou à un sObject passé comme paramètre dans la méthode Database.emptyRecycleBin. Le premier index dans la liste EmptyRecycleBinResult correspond au premier enregistrement ou sObject spécifié dans la liste, le deuxième au deuxième, et ainsi de suite. Les méthodes suivantes sont toutes des méthodes d'instance, c.-à-d. qu'elles fonctionnent dans une instance spécifique d'un objet EmptyRecyclelBinResult. Aucune méthode ne prend d'argument. Nom Type de renvoi Description getErrors Database.Errors [] Si une erreur se produit durant la suppression de cet enregistrement ou sObject, une liste d'un ou de plusieurs objets Database.Error est renvoyée. Si aucune erreur ne se produit, cette liste est vide. getId ID Renvoie l'ID de l'enregistrement ou sObject que vous avez tenté de supprimer. isSuccess Boolean Renvoie true si l'enregistrement ou le sObject a été supprimé avec succès de la Corbeille, sinon renvoie false. Méthodes Database Error Object Un objet Database.error contient les informations sur une erreur survenue au cours d'une opération DML ou d'une autre opération. Toutes les opérations DML qui sont exécutées avec leur forme de méthode système de base de données renvoient un objet d'erreur si elles échouent. Tous les objets d'erreur ont accès aux méthodes suivantes : Nom Arguments Type de renvoi Description getMessage String Renvoie le texte du message d'erreur. getStatusCode StatusCode Renvoie un code qui caractérise l'erreur. La liste complète des codes de statut est disponible dans le fichier WSDL de votre organisation (reportez-vous à Téléchargement des certificats d'authentification client et des WSDL de Salesforce dans l'aide en ligne de Salesforce). Référence salesforce | Méthodes Système Apex | 490 Prise en charge JSON La prise en charge JSON (JavaScript Object Notation) dans le langage Apex active la sérialisation d'objets Apex sous le format JSON et la désérialisation de contenu JSON sérialisé. Le langage Apex fournit un ensemble de classes qui exposent des méthodes pour la sérialisation et la désérialisation JSON. Le tableau suivant présente les classes disponibles. Classe Description System.JSON Contient les méthodes pour la sérialisation d'objets Apex sous le format JSON et la désérialisation de contenu JSON qui a été sérialisé à l'aide de la méthode serialize dans cette classe. System.JSONGenerator Contient les méthodes utilisées pour sérialiser des objets Apex dans un contenu JSON à l'aide du codage JSON standard. System.JSONParser Représente un analyseur de contenu codé en JSON. L'énumération System.JSONToken contient les jetons utilisés pour l'analyse JSON. Les méthodes de ces classes lèvent une exception JSONException en cas de problème durant l'exécution. Considérations sur la prise en charge JSON • • • • • • • La prise en charge de la sérialisation et de la désérialisation JSON est disponible pour les sObjects (objets standard et objets personnalisés), les types primitive et collection Apex, les types de renvoi des méthodes Database (tels que SaveResult, DeleteResult, etc.), et les instances de vos classes Apex. Seuls les objets personnalisés, qui sont des types de sObject, de packages gérés peuvent être sérialisés à partir d'un code extérieur au package géré. Les objets qui sont des instances de classes Apex définies dans le package géré ne peuvent pas être sérialisés. Les objets Map désérialisés dont les clés ne sont pas des chaînes ne correspondent pas à leur objet Map respectif avant la sérialisation. Les valeurs de clé sont converties en chaînes durant la sérialisation, et leur type change lors de la désérialisation. Par exemple, Map<Object, sObject> devient Map<String, sObject>. Lorsqu'un objet est déclaré comme type parent, mais défini sur une instance du sous-type, certaines données peuvent être perdues. Les objets sont sérialisés et désérialisés en tant que type parent et tous les champs spécifiques au sous-type sont perdus. Un objet qui se référence lui-même n'est pas sérialisé et entraîne une exception JSONException. Les graphiques de référence qui référencent deux fois le même objet sont désérialisés et entraînent la génération de plusieurs copies de l'objet référencé. Le type de données System.JSONParser ne peut pas être sérialisé. Si vous avez une classe sérialisable, telle qu'un contrôleur Visualforce, qui inclut une variable de membre de type System.JSONParser et que vous essayez de créer cet objet, vous recevez une exception. Pour utiliser JSONParser dans une classe sérialisable, utilisez plutôt une variable locale dans votre méthode. Méthodes JSON Contient des méthodes pour la sérialisation d'objets Apex sous le format JSON, et la désérialisation d'un contenu JSON sérialisé en utilisant la méthode serialize dans cette classe. Référence salesforce | Méthodes Système Apex | 491 Utilisation Utilisez les méthodes de la classe System.JSON pour exécuter des allers-retours de sérialisation et désérialisation JSON d'objets Apex. Méthodes Les méthodes suivantes sont des méthodes statiques de la classe Schema.JSON. Méthode Arguments createGenerator Boolean pretty createParser String Type de renvoi System.JSONGenerator Renvoie un nouveau générateur JSON. L'argument pretty détermine si le générateur JSON crée un contenu JSON sous le format d'impression automatique avec le contenu prévu. Définissez sur true pour créer un contenu intentionnel. System.JSONParser Renvoie un nouvel analyseur JSON. jsonString deserialize String Description L'argument jsonString est le contenu JSON à analyser. N'importe quel type jsonString Désérialise la chaîne JSON spécifiée dans un objet Apex du type spécifié. L'argument jsonString est le contenu JSON à désérialiser. System.Type apexType L'argument apexType est le type Apex de l'objet que crée cette méthode après la désérialisation du contenu JSON. Si le contenu JSON à analyser contient des attributs absents dans le type Apex spécifié dans l'argument, tel qu'un champ ou un objet manquant, cette méthode ignore ces attributs et analyse le reste du contenu JSON. Cependant, pour un code Apex enregistré en utilisant l'API Salesforce.com version 24.0 ou antérieure, cette méthode lève une exception à l'exécution pour les attributs manquants. L'exemple suivant désérialise une valeur Decimal. Decimal n = (Decimal)JSON.deserialize( '100.1', Decimal.class); System.assertEquals(n, 100.1); deserializeStrict String jsonString System.Type apexType N'importe quel type Désérialise la chaîne JSON spécifiée dans un objet Apex du type spécifié. Tous les attributs de la chaîne JSON doivent être présents dans le type spécifié. L'argument jsonString est le contenu JSON à désérialiser. L'argument apexType est le type Apex de l'objet que crée cette méthode après la désérialisation du contenu JSON. Si le contenu JSON à analyser contient des attributs absents dans le type Apex spécifié dans l'argument, tel qu'un champ ou un objet manquant, cette méthode lève une exception à l'exécution. Référence Méthode salesforce | Méthodes Système Apex | 492 Arguments Type de renvoi Description L'exemple suivant désérialise une chaîne JSON dans un objet d'un type défini par l'utilisateur représenté par la classe Car, que cet exemple définit également. public class Car { public String make; public String year; } public void parse() { Car c = (Car)JSON.deserializeStrict( '{"make":"SFDC","year":"2020"}', Car.class); System.assertEquals(c.make, 'SFDC'); System.assertEquals(c.year, '2020'); } deserializeUntyped String jsonString N'importe quel type Désérialise la chaîne JSON spécifiée dans des collections de types de données primitifs. L'argument jsonString est le contenu JSON à désérialiser. L'exemple suivant désérialise une représentation JSON d'un objet appliance dans un mappage contenant des types de données primitifs et d'autres collections de types primitifs. Il vérifie ensuite les valeurs désérialisées. String jsonInput = '{\n' + ' "description" :"An appliance",\n' + ' "accessories" : [ "powerCord", ' + '{ "right":"door handle1", ' + '"left":"door handle2" } ],\n' + ' "dimensions" : ' + '{ "height" : 5.5 , ' + '"width" : 3.0 , ' + '"depth" : 2.2 },\n' + ' "type" : null,\n' + ' "inventory" : 2000,\n' + Référence Méthode salesforce | Méthodes Système Apex | 493 Arguments Type de renvoi Description ' "price" : 1023.45,\n' + ' "isShipped" : true,\n' + ' "modelNumber" : "123"\n' + '}'; Map<String, Object> m = (Map<String, Object>) JSON.deserializeUntyped(jsonInput); System.assertEquals( 'An appliance', m.get('description')); List<Object> a = (List<Object>)m.get('accessories'); System.assertEquals('powerCord', a[0]); Map<String, Object> a2 = (Map<String, Object>)a[1]; System.assertEquals( 'door handle1', a2.get('right')); System.assertEquals( 'door handle2', a2.get('left')); Map<String, Object> dim = (Map<String, Object>)m.get('dimensions'); System.assertEquals( 5.5, dim.get('height')); System.assertEquals( 3.0, dim.get('width')); System.assertEquals( 2.2, dim.get('depth')); System.assertEquals(null, m.get('type')); Référence salesforce | Méthodes Système Apex | 494 Méthode Arguments Type de renvoi Description System.assertEquals( 2000, m.get('inventory')); System.assertEquals( 1023.45, m.get('price')); System.assertEquals( true, m.get('isShipped')); System.assertEquals( '123', m.get('modelNumber')); serialize N'importe quel String type object Sérialise des objets Apex dans un contenu JSON. L'argument object est l'objet Apex à sérialiser. L'exemple suivant sérialise une nouvelle valeur Datetime. Datetime dt = Datetime.newInstance( Date.newInstance( 2011, 3, 22), Time.newInstance( 1, 15, 18, 0)); String str = JSON.serialize(dt); System.assertEquals( '"2011-03-22T08:15:18.000Z"', str); serializePretty N'importe quel String type object Sérialise des objets Apex dans un contenu JSON et génère un contenu prévu à l'aide du format d'impression automatique. L'argument object est l'objet Apex à sérialiser. Exemple : Sérialisation et désérialisation d'une liste de relevés de facture Cet exemple crée une liste d'objets InvoiceStatement et sérialise la liste. Ensuite, la chaîne JSON sérialisée est utilisée pour désérialiser de nouveau la liste, et l'exemple vérifie que la nouvelle liste contient les mêmes factures que dans la liste d'origine. public class JSONRoundTripSample { public class InvoiceStatement { Référence salesforce | Méthodes Système Apex | 495 Long invoiceNumber; Datetime statementDate; Decimal totalPrice; public InvoiceStatement(Long i, Datetime dt, Decimal price) { invoiceNumber = i; statementDate = dt; totalPrice = price; } } public static void SerializeRoundtrip() { Datetime dt = Datetime.now(); // Create a few invoices. InvoiceStatement inv1 = new InvoiceStatement(1,Datetime.valueOf(dt),1000); InvoiceStatement inv2 = new InvoiceStatement(2,Datetime.valueOf(dt),500); // Add the invoices to a list. List<InvoiceStatement> invoices = new List<InvoiceStatement>(); invoices.add(inv1); invoices.add(inv2); // Serialize the list of InvoiceStatement objects. String JSONString = JSON.serialize(invoices); System.debug('Serialized list of invoices into JSON format: ' + JSONString); // Deserialize the list of invoices from the JSON string. List<InvoiceStatement> deserializedInvoices = (List<InvoiceStatement>)JSON.deserialize(JSONString, List<InvoiceStatement>.class); System.assertEquals(invoices.size(), deserializedInvoices.size()); Integer i=0; for (InvoiceStatement deserializedInvoice :deserializedInvoices) { system.debug('Deserialized:' + deserializedInvoice.invoiceNumber + ',' Référence salesforce | Méthodes Système Apex | 496 + deserializedInvoice.statementDate.formatGmt('MM/dd/yyyy HH:mm:ss.SSS') + ', ' + deserializedInvoice.totalPrice); system.debug('Original:' + invoices[i].invoiceNumber + ',' + invoices[i].statementDate.formatGmt('MM/dd/yyyy HH:mm:ss.SSS') + ', ' + invoices[i].totalPrice); i++; } } } Voir aussi : Méthodes Type Méthodes JSONGenerator Contient les méthodes utilisées pour sérialiser des objets Apex dans un contenu JSON à l'aide du codage JSON standard. Utilisation Dans certains cas, le codage JSON généré par Apex via la méthode de sérialisation dans la classe System.JSON est différente du codage JSON standard. Par conséquent, la classe System.JSONGenerator est fournie pour activer la création d'un contenu codé en JSON standard. Méthodes Les méthodes suivantes sont les méthodes d'instance de la classe Schema.JSONGenerator. Méthode Arguments close Type de renvoi Description Void Ferme le générateur JSON. Aucun contenu ne peut être écrit après la fermeture du générateur JSON. String getAsString Renvoie le contenu JSON créé. De plus, cette méthode ferme le générateur JSON si ce n'est déjà fait. Boolean isClosed Renvoie true si le générateur JSON est fermé, sinon renvoie false. writeBlob Blob blobValue Void Écrit la valeur Blob spécifiée en tant que chaîne codée en base64. writeBlobField String fieldName Void Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur Blob spécifiés. Blob blobValue Référence salesforce | Méthodes Système Apex | 497 Méthode Arguments Type de renvoi Description writeBoolean Boolean Void Écrit la valeur booléenne spécifiée. blobValue writeBooleanField String fieldName Void Boolean Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur Boolean spécifiés. booleanValue writeDate Date dateValue Void Écrit la valeur de date spécifiée sous le format ISO-8601. writeDateField String fieldName Void Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur spécifiés. La valeur de date est écrite sous le format ISO-8601. Date dateValue writeDateTime Datetime Void datetimeValue writeDateTimeField String fieldName Void Datetime datetimeValue Écrit la valeur de date et d'heure spécifiée sous le format ISO-8601. Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la date/heure spécifiés. La valeur de date/heure est écrite sous le format ISO-8601. writeEndArray Void Écrit le marqueur final d'un tableau JSON (']'). writeEndObject Void Écrit le marqueur final d'un objet JSON ('}'). writeFieldName String fieldName Void Écrit un nom de champ. writeId ID identifier Écrit la valeur d'ID spécifiée. writeIdField String fieldName Void Void Id identifier Void writeNull Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur d'identificateur spécifiés. Écrit la valeur littérale null JSON. writeNullField String fieldName Void Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur littérale null JSON spécifiés. writeNumber Decimal number Void Écrit la valeur décimale spécifiée. writeNumber Double number Void Écrit la valeur double spécifiée. writeNumber Integer number Void Écrit la valeur d'entier spécifiée. writeNumber Long number Void Écrit la valeur de longueur spécifiée. writeNumberField String fieldName Void Decimal number writeNumberField String fieldName Void Double number writeNumberField String fieldName Void Integer number Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur de décimale spécifiés. Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur de double spécifiés. Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur d'entier spécifiés. Référence salesforce | Méthodes Système Apex | 498 Méthode Arguments Type de renvoi writeNumberField String fieldName Void Long number writeObject N'importe quel type object Void writeObjectField String fieldName Void N'importe quel type object Description Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur de longueur spécifiés. Écrit l'objet Apex spécifié sous le format JSON. Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de l'objet Apex spécifiés. writeStartArray Void Écrit le marqueur de début d'un tableau JSON ('['). writeStartObject Void Écrit le marqueur de début d'un objet JSON ('{'). Void Écrit la valeur de chaîne spécifiée. writeString String stringValue writeStringField String fieldName Void String Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur de chaîne spécifiés. stringValue writeTime Time timeValue Void Écrit la valeur d'heure spécifiée sous le format ISO-8601. writeTimeField String fieldName Void Écrit une paire nom de champ - valeur de champ à l'aide du nom de champ et de la valeur d'heure sous le format ISO-8601 spécifiés. Time timeValue Exemple de JSONGenerator Cet exemple crée une chaîne JSON en utilisant les méthodes de JSONGenerator. public class JSONGeneratorSample{ public class A { String str; public A(String s) { str = s; } } static void generateJSONContent() { // Create a JSONGenerator object. // Pass true to the constructor for pretty print formatting. JSONGenerator gen = JSON.createGenerator(true); Référence salesforce | Méthodes Système Apex | 499 // Create a list of integers to write to the JSON string. List<integer> intlist = new List<integer>(); intlist.add(1); intlist.add(2); intlist.add(3); // Create an object to write to the JSON string. A x = new A('X'); // Write data to the JSON string. gen.writeStartObject(); gen.writeNumberField('abc', 1.21); gen.writeStringField('def', 'xyz'); gen.writeFieldName('ghi'); gen.writeStartObject(); gen.writeObjectField('aaa', intlist); gen.writeEndObject(); gen.writeFieldName('Object A'); gen.writeObject(x); gen.writeEndObject(); // Get the JSON string. String pretty = gen.getAsString(); System.assertEquals('{\n' + ' "abc" : 1.21,\n' + ' "def" : "xyz",\n' + ' "ghi" : {\n' + Référence salesforce | Méthodes Système Apex | 500 ' "aaa" : [ 1, 2, 3 ]\n' + ' },\n' + ' "Object A" : {\n' + ' "str" : "X"\n' + ' }\n' + '}', pretty); } } Méthodes JSONParser Représente un analyseur de contenu codé en JSON. Utilisation Utilisez les méthodes System.JSONParser pour analyser une réponse renvoyée depuis un appel à un service externe sous le format JSON, telle qu'une réponse codée en JSON d'un appel de service Web. Méthodes Les méthodes suivantes sont des méthodes d'instance de la classe Schema.JSONParser. Méthode clearCurrentToken Arguments Type de renvoi Description Void Supprime le jeton actuel. Lorsque cette méthode est appelée, un appel à hasCurrentToken renvoie false et un appel à getCurrentToken renvoie null. Vous pouvez récupérer le jeton effacé en appelant getLastClearedToken. getBlobValue Blob Renvoie le jeton actuel en tant que valeur BLOB. Le jeton actuel doit être de type JSONToken.VALUE_STRING et doit être codé en base64. getBooleanValue Boolean Renvoie le jeton actuel en tant que valeur booléenne. Le jeton actuel doit être de type JSONToken.VALUE_TRUE ou JSONToken.VALUE_FALSE. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur booléenne. String JSONContent = '{"isActive":true}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. Référence Méthode salesforce | Méthodes Système Apex | 501 Arguments Type de renvoi Description parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the Boolean value. Boolean isActive = parser.getBooleanValue(); getCurrentName String Renvoie le nom associé au jeton actuel. Si le jeton actuel est de type JSONToken.FIELD_NAME, cette méthode renvoie la même valeur que getText. Si le jeton actuel est une valeur, cette méthode renvoie le nom de champ qui précède ce jeton. Pour d'autres valeurs, telles que des valeurs de tableau ou des valeurs de niveau racine, cette méthode renvoie null. L'exemple suivant analyse un exemple de chaîne JSON. Il progresse vers la valeur de champ et récupère son nom de champ correspondant. String JSONContent = '{"firstName":"John"}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the field name for the current value. String fieldName = parser.getCurrentName(); // Get the textual representation // of the value. String fieldValue = parser.getText(); getCurrentToken System.JSONToken Renvoie le jeton vers lequel l'analyseur pointe actuellement ou null s'il n'existe actuellement aucun jeton. L'exemple suivant itère dans tous les jetons dans un exemple de chaîne JSON. String JSONContent = '{"firstName":"John"}'; JSONParser parser = Référence Méthode salesforce | Méthodes Système Apex | 502 Arguments Type de renvoi Description JSON.createParser(JSONContent); // Advance to the next token. while (parser.nextToken() != null) { System.debug('Current token: ' + parser.getCurrentToken()); } getDatetimeValue Datetime Renvoie le jeton actuel en tant que valeur de date et d'heure. Le jeton actuel doit être de type JSONToken.VALUE_STRING et doit représenter une valeur Datetime sous le format ISO-8601. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de date/heure. String JSONContent = '{"transactionDate":"2011-03-22T13:01:23"}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the transaction date. Datetime transactionDate = parser.getDatetimeValue(); getDateValue Date Renvoie le jeton actuel en tant que valeur de date. Le jeton actuel doit être de type JSONToken.VALUE_STRING et doit représenter une valeur Date sous le format ISO-8601. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de date. String JSONContent = '{"dateOfBirth":"2011-03-22"}'; JSONParser parser = JSON.createParser(JSONContent); Référence Méthode salesforce | Méthodes Système Apex | 503 Arguments Type de renvoi Description // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the date of birth. Date dob = parser.getDateValue(); getDecimalValue Decimal Renvoie le jeton actuel en tant que valeur décimale. Le jeton actuel doit être de type JSONToken.VALUE_NUMBER_FLOAT ou JSONToken.VALUE_NUMBER_INT, et est une valeur numérique qui peut être convertie en valeur de type Decimal. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur décimale. String JSONContent = '{"GPA":3.8}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the GPA score. Decimal gpa = parser.getDecimalValue(); getDoubleValue Double Renvoie le jeton actuel en tant que valeur double. Le jeton actuel doit être de type JSONToken.VALUE_NUMBER_FLOAT, et est une valeur numérique qui peut être convertie en valeur de type Double. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur double. String JSONContent = '{"GPA":3.8}'; JSONParser parser = Référence Méthode salesforce | Méthodes Système Apex | 504 Arguments Type de renvoi Description JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the GPA score. Double gpa = parser.getDoubleValue(); getIdValue ID Renvoie le jeton actuel en tant que valeur d'ID. Le jeton actuel doit être de type JSONToken.VALUE_STRING et être un ID valide. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur d'ID. String JSONContent = '{"recordId":"001R0000002nO6H"}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the record ID. ID recordID = parser.getIdValue(); getIntegerValue Integer Renvoie le jeton actuel en tant que valeur de nombre entier. Le jeton actuel doit être de type JSONToken.VALUE_NUMBER_INT et doit représenter un Integer valide. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de nombre entier. String JSONContent = '{"recordCount":10}'; JSONParser parser = Référence Méthode salesforce | Méthodes Système Apex | 505 Arguments Type de renvoi Description JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the record count. Integer count = parser.getIntegerValue(); getLastClearedToken System.JSONToken Renvoie le dernier jeton effacé par la méthode clearCurrentToken. getLongValue Long Renvoie le jeton actuel en tant que valeur de longueur. Le jeton actuel doit être de type JSONToken.VALUE_NUMBER_INT, et est une valeur numérique qui peut être convertie en valeur de type Long. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de longueur. String JSONContent = '{"recordCount":2097531021}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the record count. Long count = parser.getLongValue(); getText String Renvoie la représentation textuelle du jeton actuel ou null s'il n'existe actuellement aucun jeton. Aucun jeton actuel n'existe, par conséquent cette méthode renvoie null, si nextToken n'a pas encore été appelé une première fois ou si l'analyseur a atteint la fin du flux d'entrée. Pour consulter un exemple, reportez-vous à getCurrentName à la page 501. Référence Méthode getTimeValue salesforce | Méthodes Système Apex | 506 Arguments Type de renvoi Description Time Renvoie le jeton actuel en tant que valeur d'heure. Le jeton actuel doit être de type JSONToken.VALUE_STRING et représenter une valeur Time sous le format ISO-8601. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de date/heure. String JSONContent = '{"arrivalTime":"18:05"}'; JSONParser parser = JSON.createParser(JSONContent); // Advance to the start object marker. parser.nextToken(); // Advance to the next value. parser.nextValue(); // Get the arrival time. Time arrivalTime = parser.getTimeValue(); hasCurrentToken Boolean Renvoie true si l'analyseur pointe actuellement vers un jeton, sinon renvoie false. nextToken System.JSONToken Renvoie le jeton suivant ou null si l'analyseur a atteint la fin du flux d'entrée. Progresse suffisamment dans le flux pour déterminer le type du jeton suivant, s'il existe. Pour consulter un exemple, reportez-vous à getCurrentName à la page 501. nextValue System.JSONToken Renvoie le jeton suivant qui est un type de valeur ou null si l'analyseur a atteint la fin du flux d'entrée. Progresse suffisamment dans le flux pour déterminer le type du jeton suivant qui, s'il existe, est un type de valeur comprenant un tableau JSON et des marqueurs de début et de fin. Pour consulter un exemple, reportez-vous à getCurrentName à la page 501. readValueAs System.Type N'importe quel type apexType Désérialise un contenu JSON dans un objet du type Apex spécifié et renvoie l'objet désérialisé. L'argument apexType spécifie le type de l'objet que cette méthode renvoie après la désérialisation de la valeur actuelle. Si le contenu JSON à analyser contient des attributs absents dans le type Apex spécifié dans l'argument, tels qu'un champ Référence Méthode salesforce | Méthodes Système Apex | 507 Arguments Type de renvoi Description ou un objet manquant, cette méthode ignore ces attributs et analyse le reste du contenu JSON. Cependant, pour un code Apex enregistré en utilisant l'API Salesforce.com version 24.0 ou antérieure, cette méthode lève une exception à l'exécution pour les attributs manquants. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de date/heure. Pour pouvoir exécuter cet exemple, vous devez créer une classe Apex comme suit : public class Person { public String name; public String phone; } Insérez ensuite l'exemple suivant dans une méthode de classe : // JSON string that contains a Person object. String JSONContent = '{"person":{' + '"name":"John Smith",' + '"phone":"555-1212"}}'; JSONParser parser = JSON.createParser(JSONContent); // Make calls to nextToken() // to point to the second // start object marker. parser.nextToken(); parser.nextToken(); parser.nextToken(); // Retrieve the Person object // from the JSON string. Person obj = (Person)parser.readValueAs( Person.class); System.assertEquals( obj.name, 'John Smith'); Référence Méthode salesforce | Méthodes Système Apex | 508 Arguments Type de renvoi Description System.assertEquals( obj.phone, '555-1212'); readValueAsStrict System.Type N'importe quel type apexType Désérialise un contenu JSON dans un objet du type Apex spécifié et renvoie l'objet désérialisé. Tous les attributs dans le contenu JSON doivent être présents dans le type spécifié. L'argument apexType spécifie le type de l'objet que cette méthode renvoie après la désérialisation de la valeur actuelle. Si le contenu JSON à analyser contient des attributs absents dans le type Apex spécifié dans l'argument, tel qu'un champ ou un objet manquant, cette méthode lève une exception à l'exécution. L'exemple suivant analyse un exemple de chaîne JSON et récupère une valeur de date/heure. Pour pouvoir exécuter cet exemple, vous devez créer une classe Apex comme suit : public class Person { public String name; public String phone; } Insérez ensuite l'exemple suivant dans une méthode de classe : // JSON string that contains a Person object. String JSONContent = '{"person":{' + '"name":"John Smith",' + '"phone":"555-1212"}}'; JSONParser parser = JSON.createParser(JSONContent); // Make calls to nextToken() // to point to the second // start object marker. parser.nextToken(); parser.nextToken(); parser.nextToken(); // Retrieve the Person object // from the JSON string. Référence salesforce | Méthodes Système Apex | 509 Méthode Arguments Type de renvoi Description Person obj = (Person)parser.readValueAsStrict( Person.class); System.assertEquals( obj.name, 'John Smith'); System.assertEquals( obj.phone, '555-1212'); skipChildren Void Ignore tous les jetons enfants de type JSONToken.START_ARRAY et JSONToken.START_OBJECT vers lesquels l'analyseur pointe actuellement. Exemple : Analyse d'une réponse JSON à partir d'un appel de service Web Cet exemple montre comment analyser une réponse formatée en JSON en utilisant des méthodes JSONParser. Il émet un appel vers un service Web qui renvoie une réponse sous le format JSON. La réponse est ensuite analysée pour obtenir toutes les valeurs de champ totalPrice, puis le prix total est calculé. Pour pouvoir exécuter cet exemple, vous devez ajouter l'URL de point de destination du service Web à la liste des sites distants autorisés dans l'interface utilisateur de Salesforce. Pour cela, connectez-vous à Salesforce, puis sélectionnez Votre nom > Configuration > Contrôles de sécurité > Paramètres de site distant. public class JSONParserUtil { @future(callout=true) public static void parseJSONResponse() { Http httpProtocol = new Http(); // Create HTTP request to send. HttpRequest request = new HttpRequest(); // Set the endpoint URL. String endpoint = 'http://www.cheenath.com/tutorial/sfdc/sample1/response.php'; request.setEndPoint(endpoint); // Set the HTTP verb to GET. request.setMethod('GET'); // Send the HTTP request and get the response. // The response is in JSON format. HttpResponse response = httpProtocol.send(request); System.debug(response.getBody()); /* The JSON response returned is the following: Référence salesforce | Méthodes Système Apex | 510 String s = '{"invoiceList":[' + '{"totalPrice":5.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' + '{"UnitPrice":1.0,"Quantity":5.0,"ProductName":"Pencil"},' + '{"UnitPrice":0.5,"Quantity":1.0,"ProductName":"Eraser"}],' + '"invoiceNumber":1},' + '{"totalPrice":11.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' + '{"UnitPrice":6.0,"Quantity":1.0,"ProductName":"Notebook"},' + '{"UnitPrice":2.5,"Quantity":1.0,"ProductName":"Ruler"},' + '{"UnitPrice":1.5,"Quantity":2.0,"ProductName":"Pen"}],"invoiceNumber":2}' + ']}'; */ // Parse JSON response to get all the totalPrice field values. JSONParser parser = JSON.createParser(response.getBody()); Double grandTotal = 0.0; while (parser.nextToken() != null) { if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && (parser.getText() == 'totalPrice')) { // Get the value. parser.nextToken(); // Compute the grand total price for all invoices. grandTotal += parser.getDoubleValue(); } } system.debug('Grand total=' + grandTotal); } } Exemple : Analyse d'une chaîne JSON et désérialisation de cette chaîne en objets Cet exemple utilise une chaîne JSON codée en dur, qui est la même chaîne JSON renvoyée par l'appel dans l'exemple précédent. Dans cet exemple, la chaîne entière est analysée dans des objets Invoice en utilisant la méthode readValueAs. Il utilise également la méthode skipChildren pour ignorer le tableau enfant et les objets enfants, et peut analyser la facture semblable suivante dans la liste. Les objets analysés sont des instances de la classe Invoice qui est définie en tant que classe interne. Référence salesforce | Méthodes Système Apex | 511 Puisque chaque facture contient des éléments de ligne, la classe qui représente le type d'élément de ligne correspondant, LineItem, est également définie en tant que classe interne. Ajoutez cet exemple de code à une classe pour l'utiliser. public static void parseJSONString() { String jsonStr = '{"invoiceList":[' + '{"totalPrice":5.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' + '{"UnitPrice":1.0,"Quantity":5.0,"ProductName":"Pencil"},' + '{"UnitPrice":0.5,"Quantity":1.0,"ProductName":"Eraser"}],' + '"invoiceNumber":1},' + '{"totalPrice":11.5,"statementDate":"2011-10-04T16:58:54.858Z","lineItems":[' + '{"UnitPrice":6.0,"Quantity":1.0,"ProductName":"Notebook"},' + '{"UnitPrice":2.5,"Quantity":1.0,"ProductName":"Ruler"},' + '{"UnitPrice":1.5,"Quantity":2.0,"ProductName":"Pen"}],"invoiceNumber":2}' + ']}'; // Parse entire JSON response. JSONParser parser = JSON.createParser(jsonStr); while (parser.nextToken() != null) { // Start at the array of invoices. if (parser.getCurrentToken() == JSONToken.START_ARRAY) { while (parser.nextToken() != null) { // Advance to the start object marker to // find next invoice statement object. if (parser.getCurrentToken() == JSONToken.START_OBJECT) { // Read entire invoice object, including its array of line items. Invoice inv = (Invoice)parser.readValueAs(Invoice.class); system.debug('Invoice number: ' + inv.invoiceNumber); system.debug('Size of list items: ' + inv.lineItems.size()); // For debugging purposes, serialize again to verify what was parsed. String s = JSON.serialize(inv); system.debug('Serialized invoice: ' + s); // Skip the child start array and start object markers. parser.skipChildren(); Référence salesforce | Méthodes Système Apex | 512 } } } } } // Inner classes used for serialization by readValuesAs(). public class Invoice { public Double totalPrice; public DateTime statementDate; public Long invoiceNumber; List<LineItem> lineItems; public Invoice(Double price, DateTime dt, Long invNumber, List<LineItem> liList) { totalPrice = price; statementDate = dt; invoiceNumber = invNumber; lineItems = liList.clone(); } } public class LineItem { public Double unitPrice; public Double quantity; public String productName; } L'énumération System.JSONToken Valeur Enum Description END_ARRAY La fin d'une valeur de tableau. Ce jeton est renvoyé lorsque ']' est rencontré. END_OBJECT La fin d'une valeur d'objet. Ce jeton est renvoyé lorsque '}' est rencontré. Référence salesforce | Méthodes Système Apex | 513 Valeur Enum Description FIELD_NAME Un jeton de chaîne qui est un nom de champ. NOT_AVAILABLE Le jeton requis n'est pas disponible. START_ARRAY Le début d'une valeur de tableau. Ce jeton est renvoyé lorsque '[' est rencontré. START_OBJECT Le début d'une valeur d'objet. Ce jeton est renvoyé lorsque '{' est rencontré. VALUE_EMBEDDED_OBJECT Un objet incorporé qui n'est pas accessible en tant que structure d'objet classique et qui comprend les jetons d'objet de début et de fin START_OBJECT et END_OBJECT, mais qui est représenté en tant qu'objet brut. VALUE_FALSE La valeur « false » littérale. VALUE_NULL La valeur « null » littérale. VALUE_NUMBER_FLOAT Une valeur de flottement. VALUE_NUMBER_INT Une valeur de nombre entier. VALUE_STRING Une valeur de chaîne. VALUE_TRUE Une valeur correspondant à la littérale de chaîne « true ». Voir aussi : Méthodes Type Méthodes Limits Comme le langage Apex est exécuté dans un environnement mutualisé, le moteur d'exécution Apex applique une limitation stricte pour empêcher qu'un emballement de code Apex ne monopolise des ressources partagées. Les méthodes Limits renvoient la limite spécifique du gouverneur particulier, notamment le nombre d'appels d'une méthode ou la taille du segment mémoire restant. Aucune méthodes Limits ne prend d'argument. Le format des méth