An Introduction to Using Salesforce's REST API with PHP

Salesforce is one of the biggest CRM services about. CRM stands for customer relationship management if you didn’t know. It comes with a powerful API that allow us to tap into Salesforce to create, retrieve, update and delete records. In this blog post we’ll take an introductory look at how we can use its REST API with PHP.

To keep things simple I am going to be showing examples using Guzzle.

Authentication

Before we can do anything we need to authenticate so that we have an access token for making API calls with. Salesforce gives us several options here:-

  • Web Server flow
  • User-agent flow
  • Username-password flow

For this post we will focus on using the Username-password flow.

There are two parts to authenticating against the Salesforce API. We need to request an access token and then we need to validate it. Let’s start by requesting the access token:

use GuzzleHttp\{Client, RequestOptions};

$apiCredentials = [
    'client_id' => 'YOUR SALESFORCE CLIENT ID',
    'client_secret' => 'YOUR SALESFORCE CLIENT SECRET',
    'security_token' => 'YOUR SALESFORCE SECURITY TOKEN',
];
$userCredentials = [
    'username' => 'YOUR SALESFORCE USERNAME',
    'password' => 'YOUR SALESFORCE PASSWORD',
];

$client = new Client(['base_uri' => 'YOUR SALESFORCE URL']);
try {
    $response = $client->post('services/oauth2/token', [
        RequestOptions::FORM_PARAMS => [
            'grant_type' => 'password',
            'client_id' => $apiCredentials['client_id'],
            'client_secret' => $apiCredentials['client_secret'],
            'username' => $userCredentials['username'],
            'password' => $userCredentials['password'] . $userCredentials['security_token'],
        ]
    ]);

    $data = json_decode($response->getBody());
} catch (\Exception $exception) {
    throw new \SalesforceException('Unable to connect to Salesforce');
}

Notice that we have to combine the user password with the security token when requesting the access token. If something goes wrong with this request we’re going to throw a custom SalesforceException (I’m not defining that in this post, I’ll leave that part up to you).

The access token should be returned as a property of the returned $data object. We will want to check that it is genuine before we proceed with interacting with Salesforce.

$hash = hash_hmac(
    'sha256', 
    $data->id . $data->issued_at, 
    $apiCredentials['client_secret'], 
    true
);
if (base64_encode($hash) !== $data->signature) {
    throw new \SalesforceException('Access token is invalid');
}
$accessToken = $data->access_token; // Valid access token

If everything has gone right we should now have a valid access token. In a real application you would probably want to cache this for reuse to avoid hammering Salesforce with authentication requests.

Describing Objects

Now that we’ve authenticated and validated our access token we can start getting information out of Salesforce. Let’s start by describing an object. This will enable us to determine what fields we can use when working with an object.

try {
    $response = $client->request()->get('services/data/v45.0/sobjects/Account/describe', [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
    ]);
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to describe Account object');
}

$accountObject = json_decode($response->getBody());

Here we’ve used our access token obtained in the previous part and used it to retrieve information about our Account object. For example, $accountObject->fields will give us a list of the fields on the object.

The object we are describing is in the request URL we pass to Guzzle’s get() method. So if we wanted to describe the User object instead of the Account object we’d change the URL from:

services/data/v45.0/sobjects/Account/describe

To:

services/data/v45.0/sobjects/User/describe

Creating an Account

We now know how to use the REST API to describe an object. We’ll continue with the Account object and create a new record. We need to make a post request for this.

$data = [
    'FirstName' => 'Grace',
    'LastName' => 'Hopper',
];
try {
    $response = $client->request()->post('services/data/v45.0/sobjects/Account/', [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
        RequestOptions::JSON => $data,
    ]);
    $newRecord = json_decode($response->getBody());
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to create Account record');
}

$accountId = $newRecord->id ?? null;

As long as there are no validation errors with the fields being used to create the new record the request will return the casesafe ID for our new account. We will want to use this casesafe ID whenever we want to get, update or delete a record.

Finding Accounts

It’s useful to know how to query Salesforce’s REST API to search for records. To do this we will want to use SOQL, Salesforce’s object query language. It looks very similar to SQL, especially when dealing with simple queries. Things start to change a bit when dealing with relations (working with multiple objects in a single query), but that is beyond the scope of this article.

In our previous example code we created a new record for ‘Grace Hopper’. Now we’ll use SOQL to find this record and any other accounts with the same last name. This will be the SOQL query we use for this:

SELECT Id, FirstName, LastName FROM Account WHERE LastName = 'Hopper'

Notice that we are specifying every field that we want returning in the query results. SOQL has no concept of a ‘select all’ option like SQL’s SELECT *. We must tell SOQL the fields we want.

We want to pass the SOQL query to Salesforce’s query resource URL via a get request:

$query = 'SELECT Id, FirstName, LastName FROM Account WHERE LastName = \'Hopper\'';
try {
    $response = $client->request()->get('services/data/v45.0/query', [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
        RequestOptions::QUERY => [
            'q' => $query
        ]
    ]);
    $accounts = json_decode($response->getBody());
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to query Salesforce Accounts');
}

$resultsFound = $accounts->totalSize;
$results = $accounts->records;

The response will contain the number of found records, $accounts->totalSize, and the records themselves, $accounts->records.

Getting an Account

If we know the casesafe ID of a record we can easily get the complete record without the need for a SOQL query:

try {
    $response = $client->request()->get('services/data/v45.0/sobjects/Account/' . $accountId, [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
    ]);
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to get Account record');
}

$account = json_decode($response->getBody());

Updating an Account

We can update a record using the casesafe ID:

$data = [
    'LastName' => 'Murray',
];
try {
    $response = $client->request()->patch('services/data/v45.0/sobjects/Account/' . $accountId, [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
        RequestOptions::JSON => $data,
    ]);
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to update Account record');
}

Note that this is a patch request and the record’s ID is being passed as part of the request URL.

Deleting an Account

Finally, if we want to delete a record:

try {
    $response = $client->request()->delete('services/data/v45.0/sobjects/Account/' . $accountId, [
        RequestOptions::HEADERS => [
            'Authorization' => 'Bearer ' . $accessToken,
            'X-PrettyPrint' => 1,
        ],
    ]);
} catch (\Exception $exception) {
    throw new SalesforceException('Unable to delete Account record');
}

Note that this is a delete request.

Further Reading

I hope this introduction has helped. We’ve covered the basics of using Salesforce’s REST API here, but for further information check out the official documentation. Using the API to create, retrieve, update and delete records is relatively simple. Using SOQL is a little more complicated, you can find the complete reference to it on the Salesforce website.

Related Content

Published on