7

Since I started coding in Apex, I've gotten used to working with lists of IDs of objects not the objects themselves.

This time around, instead of creating a List<ID> of account ids, I created a List<Account>.

Now, I'm trying to add account records to this list, via a lookup field on a related custom object. However, when I do this, I get an error back telling me I'm trying to add an ID to an sObject list.

So what I'm wondering is, how would I best indicate that it's the sObject record as a whole I want to add, not just the ID indicated by the lookup field? And if it involves a SOQL query, wouldn't that be problematic in terms of hitting governors limits?

2 Answers 2

12

Without sample code, it's hard to be certain, but I think you are saying you have something like this:

//You have a custom object, Widget__c, with a lookup to account
Widget__c widget = [select id, name, Account__c from Widget where id = :someId];

List<Account> accounts = new List<Account>();
accounts.add(widget.account__c);  // <-- error here, because account__c is an id

Instead of account__c, use account__r to refer to the object:

account.add(widget.account__r); // this works.

Note however that the account in the list will only have loaded the field values loaded in the original query. So if you will need the account.name (for example), add account__r.name to the query.

Also - the syntax is a little different for standard lookup fields. For example, if you were getting the Account from a Contact object, accountid refers to the id, but account refers to the object. This is consistent for all standard lookup fields that end with "id".

1
  • Nail on the head! It was the account__r I needed, in order to reference the object as a whole, not just the id which is the field's value.
    – smohyee
    Commented Jan 31, 2014 at 19:49
5

You need to query the accounts if you want their data. This is usually done as a map, not a list, for purposes of bulkification. Here's a traditional aggregate-query-update pattern:

trigger XTrigger on X (before insert) {
    map<id, account> accounts = new map<id, account>();
    for(X record: Trigger.new) {
        accounts.put(record.Account__c, null);
    }
    accounts.remove(null); // remove null key
    accounts.putAll([SELECT Id, Name, AnnualRevenue FROM Account WHERE Id IN :accounts.keySet()]);
    for(X record: Trigger.new) {
        if(accounts.containskey(record.account__c)) {
            record.Account_Revenue__c = accounts.get(record.Account__c).AnnualRevenue;
        }
    }
}

This common pattern is simple, and doesn't usually get worked up about governor limits: 1 query is 1% of the allowed amount, at most 200 rows of data returned, or about .4% of the total number of queried records allowed in a transaction.

6
  • Though Jason Clarks answer above works directly with my current code, this is a good alternative to my current code, which loops through my list of custom object records returned by trigger, and adds the related account to a set of accounts (chose a set to ensure no duplicates). Thanks!
    – smohyee
    Commented Jan 31, 2014 at 19:51
  • 1
    @smohyee This also prevents duplicate accounts in memory, but gives you the bonus of being able to specify a specific account on demand, as demonstrated in the code. I would be wary of using Set<SObject>, because it can behave badly, and you're not necessarily guaranteed to be free of duplicates, and, for that matter, if you're not careful, you could end up with "missing" accounts.
    – sfdcfox
    Commented Jan 31, 2014 at 20:37
  • SFDCFox, question: why do you put NULL in the map initially? Why not use the record.account__r relationship instead? As in, accounts.put(record.Account__c, record.Account__r);
    – smohyee
    Commented Jan 31, 2014 at 20:50
  • 2
    @smohyee record.Account__r is initially null inside a trigger. Related information has to be queried in order to be available.
    – sfdcfox
    Commented Jan 31, 2014 at 21:02
  • Ahh ok, thanks for the clarification. Before you posted that I also read about the PutAll method, which if I'm understanding the documentation right would assign the correct sObjects based on the IDs entered for the keys. Would that work within the trigger?
    – smohyee
    Commented Jan 31, 2014 at 21:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .