This is a limitation of using Dynamic SOQL
The reason we need to execute UserInfo.getUserId()
prior to running the query is that dynamic SOQL (which is where you pass a query string into Database.query()
) can't evaluate complex variable binds.
With dynamic soql, we basically can't use any dots in a variable bind. So things like UserInfo.getUserId()
and myAccount.Id
would be invalid.
You can get around this in one of two ways (with dynamic SOQL):
- Execute
UserInfo.getUserId()
before the query, and store the result in a variable. You can then use that intermediate variable in a bind (as in Vitalii Dehnerys's and Balwinder Kumar's answers)
- Calculate the value while you're building your query string, and enclose it in single quotes
Since the other two answers cover the first option, I'll cover the second one.
Doing this without variable binds
In a nutshell, instead of
'WHERE OwnerId =:UserInfo.getUserId()'
you could do
'WHERE OwnerId = \'' + UserInfo.getUserId() + '\''
If you were to print the resulting query to the debug log, it'd end up looking like
WHERE OwnerId = '005000000000001'
Just the same as if it weren't a dynamic query (with the string value enclosed in quotes). Those extra \'
were necessary here to add the single quotes. It's a bit of a pain to do things this way, which is why most people use variable binds.
There is an easier way
You don't need to be using dynamic SOQL here. Since the only part of the query that involves run-time data is a value that you're comparing against in the WHERE
clause, you can just execute the query using the static [SELECT X FROM Y WHERE Z]
query approach.
public List<Contact> getContacts() {
// Static queries can
// 1: Span multiple lines just fine
// 2: Use complex variable binds (things that need dot-notation)
return [SELECT Id, FirstName, LastName, Title, Email, OwnerId
FROM Contact
WHERE OwnerId = :UserInfo.getUserId()
ORDER BY LastName ASC
LIMIT 10];
}
Though if you have an eye on making this more dynamic...
Something like changing the fields that you're querying, choosing one of many objects to query against, or changing the field that you're ordering by, then using dynamic SOQL makes sense.
My currently preferred method for building dynamic query strings is to use String.format()
. I find that it's easier to read and maintain than a bunch of string concatenation. It allows us to use numbered placeholders, which get substituted by the List<String>
or List<Object>
that we pass in as the second parameter.
Applied to your example
public class ContactsListController {
// The {get; set;} turn the variable into a "property"
// It tells Salesforce to create/use a default getter and setter
// so that we can use this in Visualforce.
// Without this, you would need to manually create `getSortOrder()`
// and `setSortOrder(String input)` to use this in Visualforce
// The downside of using the automatic getter/setter is that it's
// a bit of a pain to set a default value
public String sortField {get; set;}
// ...but we can just set the default value in a constructor
public ContactsListController(){
sortField = 'LastName';
}
public List<Contact> getContacts() {
String queryString = String.format(
// I'm going a bit overboard here, but it is useful to show
// how you can (fairly easily) defer the decision of which fields,
// object, and filters you use until runtime
'SELECT {0} FROM {1} WHERE {2} ORDER BY {3} LIMIT 10',
new List<String>{
// 0th placeholder here = fields
// You could just pass a string like 'Id, FirstName, LastName, ...'
// but if you're dynamically selecting fields, then you probably
// have a List<String>.
// You can use String.join(list, separator) to turn it into a single string
String.join(new List<String>{'Id', 'FirstName', 'LastName', 'Title', 'Email', 'OwnerId'}, ', '),
// 1st placeholder for us = object name
'Contact',
// 2nd placeholder for us = filters
// You could also use String.join() here if you have multiple filters
'OwnerId = \'' + UserInfo.getUserId() + '\'',
// 3rd placeholder for us = ordering field
sortField + ' ASC'
}
);
List<Contact> results database.query(queryString);
return results;
}
}
with the comments removed
public class ContactsListController {
public String sortField {get; set;}
public ContactsListController(){
sortField = 'LastName';
}
public List<Contact> getContacts() {
String queryString = String.format(
'SELECT {0} FROM {1} WHERE {2} ORDER BY {3} LIMIT 10',
new List<String>{
String.join(new List<String>{'Id', 'FirstName', 'LastName', 'Title', 'Email', 'OwnerId'}, ', '),
'Contact',
'OwnerId = \'' + UserInfo.getUserId() + '\'',
sortField + ' ASC'
}
);
List<Contact> results database.query(queryString);
return results;
}
}