[SalesForce] Calling a Class from a Trigger (calls external service)

I am at my wits end with Future Methods coding and would be very grateful for a working example. I've been reading through the Future Methods documentation here: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_invoking_future_methods.htm.

I am using the following class (below) from the Salesforce Developer docs. I need to understand how to call this exact method and add in the new Account Name 'Test' into the record. Please provide the most basic Trigger to do so. Thanks!

global class FutureMethodExample {

    @future(callout=true)
    public static void getStockQuotes(String acctName) {  

         // Perform a callout to an external service
         acctName.Name = 'Test'; // this is the only line I've added from the doc example
    }
}

Best Answer

Basically if you want to call the above method from a trigger below is how you will do it.

trigger accountTestTrggr on Account (before insert, before update) {
  FutureMethodExample.getStockQuotes(Trigger.new[0].Name); // here the issue isonly the first account will be processed
}

But the issue with your code is its not bulkified(structured to handle processing of multiple records).

The term bulkifying Apex code refers to the concept of making sure the code properly handles more than one record at a time. When a batch of records initiate Apex, a single instance of that Apex code is executed, but it needs to handle all of the records in that given batch. For example, a trigger could be invoked by an Force.com Web Services API call that inserted a batch of records. Or a custom Apex Web Service. So if a batch of records invokes the the same Apex code, all of those records need to be processed as a bulk, in order to write scalable code and avoid hitting governor limits.

Here is an example of poorly written code that only handles one record:

trigger accountTestTrggr on Account (before insert, before update) {

   //This only handles the first record in the Trigger.new collection
   //But if more than 1 Account initiated this trigger, those additional records
   //will not be processed
   Account acct = Trigger.new[0];
   List<Contact> contacts = [select id, salutation, firstname, lastname, email 
              from Contact where accountId = :acct.Id];

}

The issue is that only one Account record is handled because the code explicitly accesses only the first record in the Trigger.new collection by using the syntax Trigger.new[0]. Instead, the trigger should properly handle the entire collection of Accounts in the Trigger.new collection.

Here is a sample of how to handle all incoming records:

trigger accountTestTrggr on Account (before insert, before update) {

   List<String> accountNames = new List<String>{};

   //Loop through all records in the Trigger.new collection
   for(Account a: Trigger.new){
      //Concatenate the Name and billingState into the Description field
      a.Description = a.Name + ':' + a.BillingState
   }

}

Now to bulkify your future method you will do something like below.

global class FutureMethodExample {
  @future
  public static void getStockQuotes(List<Id> recordIds) {
    List<Account> accounts = [Select Id, Name from Account Where Id IN :recordIds];
    // process account records to do awesome stuff may be callouts
  }
}

Now your trigger will look something like below.

trigger accountTestTrggr on Account (after insert, after update) { // after because you want to have the new values preserved for you when you query database and make a callout
  FutureMethodExample.getStockQuotes(Trigger.newMap.keySet()); // Your passing all the Id's instead of one.
}

Look into how to use future methods and definetly take into considerations made by @DavidReed on the other answer in terms of limits and robustness because there is very good chance of blowing up your callout limits with this approach