Skip to main content
Build the future with Agentforce at TDX in San Francisco or on Salesforce+ on March 5–6. Register now.

Maintain Your Platform Developer I Certification for Winter ’24

Learning Objectives

After completing this unit, you’ll be able to:

  • Secure Apex code with user mode database operations.
  • Create custom error messages in record-triggered flows.
  • Use improved conditional directives in Lightning web components.
  • Iterate within For Loops more easily with Iterable.
  • Use the Comparator interface and Collator class for sorting.

Salesforce Certification

If you hold the Platform Developer I credential, keep in mind that you need to complete this module by the due date to maintain your certification. Another important part of maintaining your credential is ensuring your Trailhead and Webassessor accounts are linked.

Interested in learning more about getting certified? Check out the Salesforce Platform Developer I credential.

Note: While anyone can earn this badge, this module is designed for those who hold the Platform Developer I certification.

Protect the Integrity of Your Certification

The quality of our certification exams and the value our credentials provide are our highest priority. Protecting the security and confidentiality of our exams is essential to providing our customers with credentials that are respected and industry-leading.

As a participant of the Salesforce Certification Program, you’re required to accept the terms of the Salesforce Credential and Certification Program Agreement. Please review the Salesforce certification exam-taking policies in the Salesforce Credential and Certification Program Agreement and Code of Conduct Salesforce Help article for more details.

Salesforce introduced a ton of great feature enhancements over the past year. Let’s take a look at some of the more important ones.

Secure Apex Code with User Mode Database Operations

The new Database and Search methods support an accessLevel parameter that lets you run database and search operations in user mode instead of in the default system mode. Apex code runs in system mode by default, which means that it runs with substantially elevated permissions over the user running the code. To enhance the security context of Apex, you can specify user mode access for database operations. Field-level security (FLS) and object permissions of the running user are respected in user mode and it always applies sharing rules. In system mode, the class sharing keywords control the sharing rules.

Where: This change applies to Lightning Experience and Salesforce Classic in Enterprise, Performance, Unlimited, and Developer editions.

How: You can indicate the mode of the operation by using WITH USER_MODE or WITH SYSTEM_MODE in your SOQL query. This example specifies user mode.

List<Account> acc = [SELECT Id FROM Account WITH USER_MODE];

Database operations can specify user or system mode. This example inserts a new account in user mode.

Account acc = new Account(Name='test');insert as user acc;

The new AccessLevel class represents the two modes in which Apex runs database operations. Use this new class to define the execution mode as user mode or system mode.

Create Custom Error Messages in Record-Triggered Flows

Use the new Custom Error Message element to create targeted error messages for your end users to explain what went wrong or how to correct it. The error messages display in a window on the overall record page or as an inline error on a specific field. The associated record change is rolled back. Error messages can be created for before-save and after-save flows. We delivered this feature thanks to your ideas on IdeaExchange.

Where: This change applies to Lightning Experience and Salesforce Classic in Essentials, Professional, Enterprise, Performance, Unlimited, and Developer editions.

Why: When a user performs an action like deleting a record that triggers a flow, sometimes the flow runs into an error. Before, when the flow failed, it wasn’t possible to display a specific error message to the user. Now you can inform the user exactly what caused their operation to fail, so they can fix the problem and try again.

How: Add a Custom Error Message element (1), choose where to display the error message (2), and enter the error message text (3).

Flow Canvas with the Custom Error message element and the details pane to configure the element.

Use Improved Conditional Directives in Lightning Web Components

The lwc:if, lwc:elseif, and lwc:else conditional directives supersede the legacy if:true and if:false directives.

Where: This change applies to custom Lightning web components in Lightning Experience, Experience Builder sites, and all versions of the Salesforce mobile app. This change also applies to Lightning web components in Open Source.

How: With the lwc:if, lwc:elseif, and lwc:else conditional directives, the property getters are accessed only one time per instance of the directive.

<!-- conditionalExample.html -->
<template>
    <template lwc:if={isTemplateOne}>
        This is template one.
    </template>
    <template lwc:else>
        This is template two.
    </template>
</template>

Both lwc:elseif and lwc:else must be immediately preceded by a sibling lwc:if or lwc:elseif.

Pass in an expression to lwc:if and lwc:elseif, but not to lwc:else.

If expression1 returns a truthy, none of the other property getters are accessed.

<!-- example.html -->
<template>
    <template lwc:if={expression1}>
        Statement 1
    </template>
    <template lwc:elseif={expression2}>
        Statement 2
    </template>
    <template lwc:else>
        Statement 3
    </template>
</template>

Consider these guidelines when working with the lwc:if, lwc:elseif, and lwc:else conditional directives.

  • Use the conditional directives on nested <template> tags, <div> tags, or other HTML elements, and on your custom components tags like <c-custom-cmp>.
  • You can’t precede lwc:elseif or lwc:else with text or another element. Whitespace is ignored between the tags when the whitespace is a sibling of the conditional directive. For example, you can’t have a <div> tag that comes after lwc:if and before lwc:else.
  • Complex expressions like !condition or object?.property?.condition aren’t supported. To evaluate complex expressions, use a getter in your JavaScript class.

Iterate Within for Loops More Easily with Iterable

You can now easily iterate through lists or sets using an Iterable variable in a for loop.

Where: This change applies to Lightning Experience and Salesforce Classic in Enterprise, Performance, Unlimited, and Developer editions.

How: This example iterates through a list of strings.

Iterable<String> stringIterator = new List<String>{'Hello', 'World!'};
for (String str : stringIterator) {
   System.debug(str);
}

This example implements an Iterable interface and then iterates through the strings in the returned set of strings.

public class MyIterable implements Iterable<String> {
   public Iterator<String> iterator() {
      return new Set<String>{'Hello', 'World!'}.iterator();
   }
}




for (String str : new MyIterable()) {
   System.debug(str);
}

Use the Comparator Interface and Collator Class for Sorting

The List class now supports the new Comparator interface, so you can implement different sort orders in your code by using List.sort() with a Comparator parameter. To perform locale-sensitive comparisons and sorting, use the getInstance method in the new Collator class. Because locale-sensitive sorting can produce different results depending on the user running the code, avoid using it in triggers or in code that expects a particular sort order.

Where: This change applies to Lightning Experience and Salesforce Classic in Enterprise, Performance, Unlimited, and Developer editions.

How: Implement the compare() method of the System.Comparator interface, and specify the Comparator as a parameter to List.sort(). Your implementation must explicitly handle null inputs in the compare() method to avoid a null pointer exception. This example implements two different ways of sorting employees.

public class Employee {
    private Long id;
    private String name;
    private Integer yearJoined;
    // Constructor
    public Employee(Long i, String n, Integer y) {
        id = i;
        name = n;
        yearJoined = y;
    }
    public String getName() { return name; }
    public Integer getYear() { return yearJoined; }
}




// Class to compare Employees by name
    public class NameCompare implements Comparator<Employee> {
        public Integer compare(Employee e1, Employee e2) {
            if(e1?.getName() == null && e2?.getName() == null) {
                return 0;
            } else if(e1?.getName() == null) {
                return -1;
            } else if(e2?.getName() == null) {
                return 1;
            }
            return e1.getName().compareTo(e2.getName());
        }
    }
    // Class to compare Employees by year joined
    public class YearCompare implements Comparator<Employee> {
        public Integer compare(Employee e1, Employee e2) {
            // Guard against null operands for ‘<’ or ‘>’ operators because
            // they will always return false and produce inconsistent sorting
            Integer result;
            if(e1?.getYear() == null && e2?.getYear() == null) {
                result = 0;
            } else if(e1?.getYear() == null) {
                  result = -1;
            } else if(e2?.getYear() == null) {
                  result = 1;
            } else if (e1.getYear() < e2.getYear()) {
                  result = -1;
            } else if (e1.getYear() > e2.getYear()) {
                  result = 1;
            } else {
                  result = 0;
            }
            return result;
        }
    }




@isTest
private class EmployeeSortingTest {
    @isTest
    static void sortWithComparators() {
        List<Employee> empList = new List<Employee>();
        empList.add(new Employee(101,'Joe Smith', 2020));
        empList.add(new Employee(102,'J. Smith', 2020));
        empList.add(new Employee(25,'Caragh Smith', 2021));
        empList.add(new Employee(105,'Mario Ruiz', 2019));
        // Sort by name
        NameCompare nameCompare = new NameCompare();
        empList.sort(nameCompare);
        // Expected order: Caragh Smith, J. Smith, Joe Smith, Mario Ruiz
        Assert.areEqual('Caragh Smith', empList.get(0).getName());
        // Sort by year joined
        YearCompare yearCompare = new YearCompare();
        empList.sort(yearCompare);
        // Expected order: Mario Ruiz, J. Smith, Joe Smith, Caragh Smith
        Assert.areEqual('Mario Ruiz', empList.get(0).getName());
    }
}

This example performs a default list sort and then uses Collator to sort based on the user locale.

@IsTest
    static void userLocaleSort() {
        string userLocale = 'fr_FR';
        User u = new User(Alias = 'standt', Email='standarduser@testorg.com',
        EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
        LocaleSidKey=userLocale, TimeZoneSidKey='America/Los_Angeles',
        ProfileId = [SELECT Id FROM Profile WHERE Name='Standard User'].Id,
        UserName='standarduser' + DateTime.now().getTime() + '@testorg.com');
        System.runAs(u) {
            List<String> shoppingList = new List<String> {
                'épaule désosé Agneau',
                'Juice',
                'à la mélasse Galette 5 kg',
                'Bread',
                'Grocery'
            };
            // Default sort
            shoppingList.sort();
            Assert.areEqual('Bread', shoppingList[0]);
            // Sort based on user Locale
            Collator myCollator = Collator.getInstance();
            shoppingList.sort(myCollator);
            Assert.areEqual('à la mélasse Galette 5 kg', shoppingList[0]);
            Assert.areEqual('Bread', shoppingList[1]);
            Assert.areEqual('épaule désosé Agneau', shoppingList[2]);
            Assert.areEqual('Grocery', shoppingList[3]);
            Assert.areEqual('Juice', shoppingList[4]);
        }
    }

Resources

Share your Trailhead feedback over on Salesforce Help.

We'd love to hear about your experience with Trailhead - you can now access the new feedback form anytime from the Salesforce Help site.

Learn More Continue to Share Feedback