Apex Trailhead

 






Apex Programing Language














Prepared for [Client company]

Created by [Consultant company]

[Company]

Apex is a strongly typed,object oriented program language that allows developers to execute flow and transaction control statements on the salesforce platform.

1.Unlike other object-oriented programming languages, Apex supports:

  • Cloud development as Apex is stored, compiled, and executed in the cloud.

  • Triggers, which are similar to triggers in database systems.

  • Database statements that allow you to make direct database calls and query languages to query and search data.

  • Transactions and rollbacks.

  • The global access modifier, which is more permissive than the public modifier and allows access across namespaces and applications.

  • Versioning of custom code.


Data type in Apex - 

A primitive, such as an Integer, Double, Long, Date, Datetime, String, ID, Boolean

An sObject, either as a generic sObject or as a specific sObject, such as an Account, Contact, or MyCustomObject__c

  • A list (or array) of primitives, sObjects, user defined objects, objects created from Apex classes, or collections

  • A set of primitives, sObjects, user defined objects, objects created from Apex classes, or collections

  • A map from a primitive to a primitive, sObject, or collection

  • A typed list of values, also known as an enum

  • User-defined Apex classes

  • System-supplied Apex classes


Apex Collection : List

Lists hold an ordered collection of objects. Lists in Apex are synonymous with arrays and the two can be used interchangeably.


The following two declaration are equivalent 

List<String> colors = new List<String>();

String[] colors = new List<String>();



Each Salesforce record is represented as an sObject before it is inserted into Salesforce. Likewise, when persisted records are retrieved from Salesforce, they’re stored in an sObject variable.

Account acct = new Account(Name='Acme');

For custom objects and custom fields, the API name always ends with the __c suffix. For custom relationship fields, the API name ends with the __r suffix. For example:


Before you can insert a Salesforce record, you must create it in memory first as an sObject. Like with any other object, sObjects are created with the new operator:



The account referenced by the acct variable is empty because we haven’t populated any of its fields yet. There are two ways to add fields: through the constructor or by using dot notation.

Account acct = new Account();

  1. Account acct = new Account(Name='Acme', Phone='(415)555-1212', NumberOfEmployees=100);

  2. Account acct = new Account();

      acct.Name = 'Acme';

      acct.Phone = '(415)555-1212';

      acct.NumberOfEmployees = 100;

This example shows how any Salesforce object, such as an account or a custom object called Book__c, can be assigned to a generic sObject variable.


sObject sobj1 = new Account(Name='Trailhead');

sObject sobj2 = new Book__c(Name='Workbook 1');


// Doubt  cleared 

Link - https://trailhead.salesforce.com/content/learn/modules/apex_database/apex_database_sobjects?trailmix_creator_id=journeytosalesforce&trailmix_slug=trailmix-4



// Cast a generic sObject to an Account

Account acct = (Account)myGenericSObject;

// Now, you can use the dot notation to access fields on Account

String name = acct.Name;

String phone = acct.Phone;


// 



DML Statements

The following DML statements are available.

  • insert

  • update

  • upsert

  • delete

  • undelete

  • merge

The upsert DML operation creates new records and updates sObject records within a single statement, using a specified field to determine the presence of existing objects, or the ID field if no field is specified.

The merge statement merges up to three records of the same sObject type into one of the records, deleting the others, and re-parenting any related records.


Upsert uses the sObject record's primary key (the ID), an idLookup field, or an external ID field to determine whether it should create a new record or update an existing one:



If insert was used in this example instead of upsert, a duplicate Jane Smith contact would have been inserted.

  1. Execute this snippet in the Execute Anonymous window of the Developer Console.


  2. Contact jane = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Contact of the day'); insert jane; // 1. Upsert using an idLookup field // Create a second sObject variable. // This variable doesn’t have any ID set. Contact jane2 = new Contact(FirstName='Jane', LastName='Smith', Email='jane.smith@example.com', Description='Prefers to be contacted by email.'); // Upsert the contact by using the idLookup field for matching. upsert jane2 Contact.fields.Email; // Verify that the contact has been updated System.assertEquals('Prefers to be contacted by email.', [SELECT Description FROM Contact WHERE Id=:jane.Id].Description);

  3. Copy

Database Methods

Apex contains the built-in Database class, which provides methods that perform DML operations and mirror the DML statement counterparts.

These Database methods are static and are called on the class name.

  • Database.insert()

  • Database.update()

  • Database.upsert()

  • Database.delete()

  • Database.undelete()

  • Database.merge()




When this parameter is set to false, if errors occur on a partial set of records, the successful records will be committed and errors will be returned for the failed records. Also, no exceptions are thrown with the partial success option.



Database.insert(recordList, false); // 

By default, the allOrNone parameter is true, which means that the Database method behaves like its DML statement counterpart and will throw an exception if a failure is encountered


// Create a list of contacts

List<Contact> conList = new List<Contact> {

        new Contact(FirstName='Joe',LastName='Smith',Department='Finance'),

        new Contact(FirstName='Kathy',LastName='Smith',Department='Technology'),

        new Contact(FirstName='Caroline',LastName='Roth',Department='Finance'),

        new Contact()};

// Bulk insert all contacts with one DML call

Database.SaveResult[] srList = Database.insert(conList, false);

// Iterate through each returned result

for (Database.SaveResult sr : srList) {

    if (sr.isSuccess()) {

        // Operation was successful, so get the ID of the record that was processed

        System.debug('Successfully inserted contact. Contact ID: ' + sr.getId());

    } else {

        // Operation failed, so get all errors

        for(Database.Error err : sr.getErrors()) {

            System.debug('The following error has occurred.');

            System.debug(err.getStatusCode() + ': ' + err.getMessage());

            System.debug('Contact fields that affected this error: ' + err.getFields());

}

    }

}





DML operations execute within a transaction. All DML operations in a transaction either complete successfully, or if an error occurs in one operation, the entire transaction is rolled back and no data is committed to the database.


// SOQL


Account[] accts = [SELECT Name,Phone FROM Account];


Bind variable 

SOQL statements in Apex can reference Apex code variables and expressions if they are preceded by a colon (:). The use of a local variable within a SOQL statement is called a bind.

This example shows how to use the targetDepartment variable in the WHERE clause.


String targetDepartment = 'Wingo'; Contact[] techContacts = [SELECT FirstName,LastName FROM Contact WHERE Department=:targetDepartment];



Query Related Records

Child records from the SOQL result by using the Contacts relationship name on the sObject.


Account[] acctsWithContacts = [SELECT Name, (SELECT FirstName,LastName FROM Contacts) FROM Account WHERE Name = 'SFDC Computing']; // Get child records Contact[] cts = acctsWithContacts[0].Contacts; System.debug('Name of first associated contact: ' + cts[0].FirstName + ', ' + cts[0].LastName);

Parent record from the Child component-

Contact[] cts = [SELECT Account.Name FROM Contact 

                 WHERE FirstName = 'Carol' AND LastName='Ruiz'];

Contact carol = cts[0];

String acctName = carol.Account.Name;

System.debug('Carol\'s account name is ' + acctName);



// Salesforce Object Search Language (SOSL) is a Salesforce search language that is used to perform text searches in records


List<List<SObject>> searchList = [FIND 'SFDC' IN ALL FIELDS 

                                      RETURNING Account(Name), Contact(FirstName,LastName)];

FIND {SearchQuery} [IN SearchGroup] [RETURNING ObjectsAndFields]


String soslFindClause = 'Wingo OR SFDC';

List<List<sObject>> searchList = [FIND :soslFindClause IN ALL FIELDS

                    RETURNING 

  • ALL FIELDS

  • NAME FIELDS

  • EMAIL FIELDS

  • PHONE FIELDS

  • SIDEBAR FIELDS



Account(Name),Contact(FirstName,LastName,Department)];

Account[] searchAccounts = (Account[])searchList[0];

Contact[] searchContacts = (Contact[])searchList[1];

System.debug('Found the following accounts.');

for (Account a : searchAccounts) {

    System.debug(a.Name);

}

System.debug('Found the following contacts.');

for (Contact c : searchContacts) {

    Sy



Trigger syntax
trigger TriggerName on ObjectName (trigger_events) {

   code_block

}

ore or after insert, update, delete, and undelete operations, specify multiple trigger events in a comma-separated list. The events you can specify are:

before insert

before update

before delete

after insert

after update

after delete

after undelete

before or after insert, update, delete, and undelete operations, specify multiple trigger events in a comma-separated list. The events you can specify are:

before insert

before update

before delete

after insert

after update

after delete

after undelete

  • Before triggers are used to update or validate record values before they’re saved to the database.

  • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records. The records that fire the after trigger are read-only.

Trigger.new contains all the records that were inserted in insert or update triggers. Trigger.old provides the old version of sObjects before they were updated in update triggers, or a list of deleted sObjects in delete triggers.

trigger HelloWorldTrigger on Account (before insert) {

    for(Account a : Trigger.new) {

        a.Description = 'New description';

    }   

}




isExecuting

Returns true if the current context for the Apex code is a trigger, not a Visualforce page, a Web service, or an executeanonymous() API call.


operationType

Returns an enum of type System.TriggerOperation corresponding to the current operation.


Possible values of the System.TriggerOperation enum are: BEFORE_INSERT, BEFORE_UPDATE, BEFORE_DELETE, AFTER_INSERT, AFTER_UPDATE, AFTER_DELETE, and AFTER_UNDELETE. If you vary your programming logic based on different trigger types, consider using the switch statement with different permutations of unique trigger execution enum states.

size

The total number of records in a trigger invocation, both old and new.


To prevent saving records in a trigger, call the addError() method on the sObject in question. The addError() method throws a fatal error inside a trigger. The error message is displayed in the user interface and is logged.



 Apex calls to external Web services are referred to as callouts.

To make a callout from a trigger, call a class method that executes asynchronously. Such a method is called a future method and is annotated with @future(callout=true)




// Asynchronous 

In a nutshell, asynchronous Apex is used to run processes in a separate thread, at a later time.

Advantage -

User efficiency,Scalability,Higher Limits


Type

Overview

Common Scenarios

Future Methods

Run in their own thread, and do not start until resources are available.

Web service callout.

Batch Apex

Run large jobs that would exceed normal processing limits.

Data cleansing or archiving of records.

Queueable Apex

Similar to future methods, but provide additional job chaining and allow more complex data types to be used.

Performing sequential processing operations with external Web services.

Scheduled Apex

Schedule Apex to run at a specified time.

Daily or weekly tasks.






Future Method Syntax

Future methods must be static methods, and can only return a void type. The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types. 



public class SomeClass {

  @future

  public static void someFutureMethod(List<Id> recordIds) {

    List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds];

    // process account records to do awesome stuff

  }

}

Note - The reason why objects can’t be passed as arguments to future methods is because the object can change between the time you call the method and the time that it actually executes.

Test code cannot actually send callouts to external systems, so you’ll have to ‘mock’ the callout for test coverage.


// doubt in mock test class

https://trailhead.salesforce.com/content/learn/modules/asynchronous_apex/async_apex_future_methods

  • You can’t call a future method from a future method. Nor can you invoke a trigger that calls a future method while running a future method. See the link in the Resources for preventing recursive future method calls.

@isTest

public class SMSCalloutMock implements HttpCalloutMock {

    public HttpResponse respond(HttpRequest req) {

        // Create a fake response

        HttpResponse res = new HttpResponse();

        res.setHeader('Content-Type', 'application/json');

        res.setBody('{"status":"success"}');

        res.setStatusCode(200);

        return res;

    }

}


Batch -


Every transaction starts with a new set of governor limits, making it easier to ensure that your code stays within the governor execution limits.

If one batch fails to process successfully, all other successful batch transactions aren’t rolled back.

Database.Batchable

  • start    ->

    • returns either a Database.QueryLocator object or an Iterable that contains the records   or objects passed to the job.

    • With the QueryLocator object, the governor limit for the total number of records retrieved by SOQL queries is bypassed and you can query up to 50 million records. However, with an Iterable, the governor limit for the total number of records retrieved by SOQL queries is still enforced.

  • execute

This method takes the following:

A reference to the Database.BatchableContext object.

A list of sObjects, such as List<sObject>, or a list of parameterized types. If you are using a Database.QueryLocator, use the returned list.


  • finish


—---------------------------------------------------------------

public class MyBatchClass implements Database.Batchable<sObject> {

    public (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContext bc) {

        // collect the batches of records or objects to be passed to execute

    }

    public void execute(Database.BatchableContext bc, List<P> records){

        // process each batch of records

    }

    public void finish(Database.BatchableContext bc){

        // execute any post-processing operations

    }

}

Invoking a Batch Class 

MyBatchClass myBatchObject = new MyBatchClass();

Id batchId = Database.executeBatch(myBatchObject);

// for limiti ng batch size

Id batchId = Database.executeBatch(myBatchObject, 100);


Each batch Apex invocation creates an AsyncApexJob record so that you can track the job’s progress. You can view the progress via SOQL or manage your job in the Apex Job Queue. We’ll talk about the Job Queue shortly.

AsyncApexJob job = [SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors FROM AsyncApexJob WHERE ID = :batchId ];



Using State in Batch Apex -

If you specify Database.Stateful in the class definition, you can maintain state across all transactions. When using Database.Stateful, only instance member variables retain their values between transactions.


public class UpdateContactAddresses implements

    Database.Batchable<sObject>, Database.Stateful {

    // instance member to retain state across transactions

    public Integer recordsProcessed = 0;

    public Database.QueryLocator start(Database.BatchableContext bc) {

        return Database.getQueryLocator(

            'SELECT ID, BillingStreet, BillingCity, BillingState, ' +

            'BillingPostalCode, (SELECT ID, MailingStreet, MailingCity, ' +

            'MailingState, MailingPostalCode FROM Contacts) FROM Account ' +

            'Where BillingCountry = \'USA\''

        );

    }

    public void execute(Database.BatchableContext bc, List<Account> scope){

        // process each batch of records

        List<Contact> contacts = new List<Contact>();

        for (Account account : scope) {

            for (Contact contact : account.contacts) {

                contact.MailingStreet = account.BillingStreet;

                contact.MailingCity = account.BillingCity;

                contact.MailingState = account.BillingState;

                contact.MailingPostalCode = account.BillingPostalCode;

                // add contact to list to be updated

                contacts.add(contact);

                // increment the instance member counter

                recordsProcessed = recordsProcessed + 1;

            }

        }

        update contacts;

    }

    public void finish(Database.BatchableContext bc){

        System.debug(recordsProcessed + ' records processed. Shazam!');

        AsyncApexJob job = [SELECT Id, Status, NumberOfErrors,

            JobItemsProcessed,

            TotalJobItems, CreatedBy.Email

            FROM AsyncApexJob

            WHERE Id = :bc.getJobId()];

        // call some utility to send email

        EmailUtils.sendMessage(job, recordsProcessed);

    }

}


Queueable Apex

  • Non-primitive types: Your Queueable class can contain member variables of non-primitive data types, such as sObjects or custom Apex types. Those objects can be accessed when the job executes.

  • Monitoring: When you submit your job by invoking the System.enqueueJob() method, the method returns the ID of the AsyncApexJob record. You can use this ID to identify your job and monitor its progress, either through the Salesforce user interface in the Apex Jobs page, or programmatically by querying your record from AsyncApexJob.

  • Chaining jobs: You can chain one job to another job by starting a second job from a running job. Chaining jobs is useful if you need to do some sequential processing.

public class SomeClass implements Queueable {

    public void execute(QueueableContext context) {

        // awesome code here

    }

}



///
public class UpdateParentAccount implements Queueable {

    private List<Account> accounts;

    private ID parent;

    public UpdateParentAccount(List<Account> records, ID id) {

        this.accounts = records;

        this.parent = id;

    }

    public void execute(QueueableContext context) {

        for (Account account : accounts) {

          account.parentId = parent;

          // perform other processing or callout

        }

        update accounts;

    }

}



// To add this class as a job on the queue, execute the following code:

// find all accounts in ‘NY’

List<Account> accounts = [select id from account where billingstate = ‘NY’];

// find a specific parent account for all records

Id parentId = [select id from account where name = 'ACME Corp'][0].Id;

// instantiate a new instance of the Queueable class

UpdateParentAccount updateJob = new UpdateParentAccount(accounts, parentId);

// enqueue the job for processing

ID jobID = System.enqueueJob(updateJob);


Chaining Jobs

public class FirstJob implements Queueable {

    public void execute(QueueableContext context) {

        // Awesome processing logic here

        // Chain this job to next job by submitting the next job

        System.enqueueJob(new SecondJob());

    }

}


// 

You can add up to 50 jobs to the queue with System.enqueueJob in a single transaction.

When chaining jobs, you can add only one job from an executing job with System.enqueueJob, which means that only one child job can exist for each parent queueable job. Starting multiple child jobs from the same queueable job is a no-no.

// 


clone(preserveId, isDeepClone, preserveReadonlyTimestamps, preserveAutonumber)


Schedule Jobs Using the Apex Scheduler -

To invoke Apex classes to run at specific times, first implement the Schedulable interface for the class. Then, schedule an instance of the class to run at a specific time using the System.schedule() method.


public class SomeClass implements Schedulable {

    public void execute(SchedulableContext ctx) {

        // awesome code here

    }

}

The parameter of this method is a SchedulableContext object. After a class has been scheduled, a CronTrigger object is created that represents the scheduled job. It provides a getTriggerId() method that returns the ID of a CronTrigger API object.



public class RemindOpptyOwners implements Schedulable {

    public void execute(SchedulableContext ctx) {

        List<Opportunity> opptys = [SELECT Id, Name, OwnerId, CloseDate

            FROM Opportunity

            WHERE IsClosed = False AND

            CloseDate < TODAY];

        // Create a task for each opportunity in the list

        TaskUtils.remindOwners(opptys);

    }

}



  • You can only have 100 scheduled Apex jobs at one time and there are maximum number of scheduled Apex executions per a 24-hour period. See Execution Governors and Limits in the Resources section for details.


RemindOpptyOwners reminder = new RemindOpptyOwners();

// Seconds Minutes Hours Day_of_month Month Day_of_week optional_year

String sch = '20 30 8 10 2 ?';

String jobID = System.schedule('Remind Opp Owners', sch, reminder);




























Apex Integration Services






























































//*  hello Word ,Variable and Heap

// System -> is a Class and debug -> is a method

Accept the string that can be printed in output

// accepts a string 

// Variable  -> heap is a size of RAm that is allocated to a particular program. 

String var = ‘abc’;

System.debug(var );

when you don’t assign a value to a variable it is always null

// Primitive Data Type -


  • Integer - 32 bit 

  • Long - 64 bit 

  • Decimal

  • Double

  • Date

  • Datetime

  • String

  • ID - 15 digit (Case Sensitive) and 18 digit (Case Insensitive)

  • Boolean

  • Blob - Binary Data - image,files


String greeting = 'Hello World';

System.debug(greeting);


Boolean amIAwake = true;

System.debug(amIAwake);


Integer rollNumber = 11008890;

System.debug(rollNumber);


Long worldPopulation = 7000000000L;

System.debug(worldPopulation);


Double lightSpeed = 93000000/186000;

System.debug(lightSpeed);


Date tDay = Date.newInstance(2020, 5, 18);

System.debug(tDay);


Time currentTime = Time.newInstance(3, 25, 0, 0);

System.debug(currentTime);


DateTime currentDateTime = DateTime.newInstance(2020, 5, 18, 3, 25, 0);

System.debug(currentDateTime);


//All null values

String greeting;

System.debug(greeting);


* // String Class Methods

String str = ' i am a string variable ';

System.debug('Actual String: '+str);


// capitalize string

System.debug('Capitalize String: '+str.capitalize());


// contains example

System.debug('Contains ring? :'+ str.contains('ring'));


// convert to upper case

System.debug('Upper case: '+str.toUpperCase());

// convert to lower case

System.debug('Lower case: '+str.toLowerCase());


// equals

System.debug('Is equal to ring?: '+str.equals('ring'));

String str1 = 'Manish';

String str2 = 'maNish';

System.debug('str1 equals str2: '+str1.equals(str2));

System.debug('str1 equals str2 ignore case: '+str1.toLowerCase().equals(str2.toLowerCase()));


// remove

System.debug('Remove ring: '+str.remove('ring'));


// replace

System.debug('Replace ring: '+str.replace('ring', 'rong'));


// split

System.debug('Split by space: '+str.split(' '));


// Escape Character 


You can use the backlash character to escape character in string.
e.g - single quotes


String str = 'My team\'s name is \'Voyager \'. \n We work on apex.';

System.debug(str);








// Adding Comments to your code -

Single line comments - //

Multiple line comments - /*    */



Collection -

List - Collection of elements of Same Data Type (Dynamic Data Type )-
A new keyword is used to create the instance of the class .

Store the data in indices - 0 -> n -1

List<Integer> rollNumbers = new List<Integer>{11008890, 11008100, 11007231};

System.debug(rollNumbers);


rollNumbers.add(89897767);

rollNumbers.add(89897764);

rollNumbers.add(89897765);


System.debug(rollNumbers);


// get item on index 1

Integer rollNum = rollNumbers.get(1);

System.debug(rollNum);

System.debug(rollNumbers.get(1));


// add item on index 4

rollNumbers.add(4, 99990000);

System.debug(rollNumbers);


// get the list size

System.debug(rollNumbers.size());


// remove the item on index 3

rollNumbers.remove(3);

System.debug(rollNumbers);

System.debug(rollNumbers.size());


// update item on index 1

rollNumbers.set(1, 44444444);

System.debug(rollNumbers);


// clear the list

rollNumbers.clear();

System.debug(rollNumbers);

System.debug(rollNumbers.size());


// below line will throw an error

rollNumbers.set(1, 44444444);

System.debug(rollNumbers);




// Set -
An unordered collection of elements of the same Data Type .(Unique)


Set<Integer> rollNumbers = new Set<Integer>{11008890, 11008100, 11007231};

System.debug(rollNumbers);


rollNumbers.add(89897767);

rollNumbers.add(89897764);

rollNumbers.add(89897765);


System.debug(rollNumbers);


// adding duplicate values - NOT ALLOWED

rollNumbers.add(89897767);

System.debug(rollNumbers);


// check if set has an item

System.debug(rollNumbers.contains(89897764));

System.debug(rollNumbers.contains(345345));


// delete an item

rollNumbers.remove(89897765);

System.debug(rollNumbers);


// get set size

System.debug(rollNumbers.size());


// check if set is empty

System.debug(rollNumbers.isEmpty());


// remove all items

rollNumbers.clear();

System.debug(rollNumbers.isEmpty());



// Map

A Key value pair based collection 

Key - Can’t have duplicates 

But Value can have duplicates.




Comments

Popular posts from this blog

Understanding Note and ContentNote Objects in Salesforce

OOPS in Apex

System administrator gets logged out when they use 'Login as User'