Work with SalesForce APIs via the Eloquent Model.
Install via composer
composer require rob-lester-jr04/eloquent-sales-force
*Note: This package is only supported Laravel 5.6 and newer.
**Note that this is optional and in most cases, the configuration here is not needed.
php artisan vendor:publish --provider="Lester\EloquentSalesForce\ServiceProvider" --tag="config"
- Log into to your Salesforce org
- Click on Setup in the upper right-hand menu
- Under Build click
Create > Apps
- Scroll to the bottom and click
New
under Connected Apps. - Enter the following details for the remote application:
- Connected App Name
- API Name
- Contact Email
- Enable OAuth Settings under the API dropdown
- Callback URL
- Select access scope (If you need a refresh token, specify it here)
- Click
Save
After saving, you will now be given a Consumer Key and Consumer Secret. Update your config file with values for consumerKey
, consumerSecret
, and loginURL
.
In your config/database.php
file, add the following driver to the connections array
'soql' => [
'driver' => 'soql',
'database' => null,
'consumerKey' => env('CONSUMER_KEY'),
'consumerSecret' => env('CONSUMER_SECRET'),
'loginURL' => env('LOGIN_URL'),
// Only required for UserPassword authentication:
'username' => env('USERNAME'),
// Security token might need to be ammended to password unless IP Address is whitelisted
'password' => env('PASSWORD')
],
If you need to modify any more settings for Forrest, publish the config file using the artisan
command:
php artisan vendor:publish
You can find the config file in: config/eloquent_sf.php
. Any of the same settings that Forrest recognizes will be available to set here.
Create a model for the object you want to use, example: artisan make:model Lead
Open the model file, it will look like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Lead extends Model
{
//
}
Replace the use
statement so your model looks like:
<?php
namespace App;
use Lester\EloquentSalesForce\Model;
class Lead extends Model
{
//
}
Now use it like you normally would!
$leads = Lead::where('email', '[email protected]')->get();
$lead = Lead::find('00Q1J00000cQ08eUAC');
Update properties like you normally would...
$lead->FirstName = 'Robert';
$lead->save();
By default, the selected columns for the record will be the compact layout defined in the SalesForce instance. This is usually enough. If you need to pull specific columns, you have some options.
// Per select
$object = Contact::select('Id', 'Name')->get();
You can also define on the model what columns you want to bring back with each record. Add public $columns
to the top of the model.
...
public $columns = [
'Name',
'Id',
'Email',
];
To return the columns currently available on a model, use the describe
method on the object.
$fields = Lead::describe();
To specify fields on the model that are read-only and to force them to be excluded from any update/insert requests, define the protected $readonly = []
array in the model
...
protected $readonly = [
'Name'
];
If you are using a field that is a picklist in SalesForce, you can capture the values of that picklist from this function on the model.
$statusValues = $lead->getPicklistValues('Status');
SalesForce has API limits. We know this. It sucks. For us at least. So now in the package, you can batch several queries and make a single API call to execute them, and get the results back in a handy collection object.
At the end of most queries we commonly call get()
to retrieve the results of our assembled query. We can queue up a batch call by calling batch()
instead of get()
. After queuing up the desired queries, you can call SObjects::runBatch()
and it will return the results of the batched queries in an array.
Lead::select(['Id', 'FirstName', 'Company'])->limit(100)->batch(); // instead of get.
Contact::select(['Id', 'FirstName', 'Phone'])->limit(50)->batch();
$batch = SObjects::runBatch();
$leads = $batch->results('Lead'); // get() also works here...
$contacts = $batch->results('Contact'); // ... and here.
By default, each batch query is tagged with the name of the model that is being queried. For example if you have a model class called Prospects
(even if it maps to the SF Lead object), the tag of the batch will be Prospects
. If you try and batch 3 queries on the same object without specifying a custom tag for each batch, only the last batch will actually be run. So we recommend tagging the batch when you queue it if you're batching multiple queries on the same object.
Lead::select(['Id', 'FirstName', 'Company'])->limit(100)->batch();
Lead::select(['Id', 'FirstName', 'Company'])->limit(30)->where('Company', 'Test')->batch('test_company');
$batch = SObjects::runBatch();
$firstCentLeads = $batch->get('Lead');
$testCompanyLeads = $batch->get('test_company');
The batch collection object can be used independently of the facade if you'd like to create a batch over time and then execute later. When using the batch()
method on the query builder, the assembled query builder is added to a collection on the facade. You can either run that batch collection by using the method SObjects::runBatch()
or you can access the collection by returning SObjects::getBatch()
. If you have the object stored in a variable, you can run it with ->run()
or you can add more query builders to it with ->batch
$batchCollection = new SOQLBatch();
$batchCollection->batch(Lead::where('FirstName', 'like', 'Test%'));
$batchCollection->run();
$lead = new Lead();
$lead->FirstName = 'Foo';
$lead->LastName = 'Bar';
$lead->Company = 'Test';
$lead->save();
OR:
$lead = Lead::create(['FirstName' => 'Foo', 'LastName' => 'Bar', 'Company' => 'Test Company']);
$leads = collect([
new Lead(['Email' => '[email protected]']),
new Lead(['Email' => '[email protected]'])
]);
Lead::insert($leads);
$lead = Lead::first();
$lead->LastName = 'Lester';
$lead->save();
OR:
$lead->update(['LastName' => 'Lester']);
The bulk update method is model agnostic - meaning that this capability, within salesforce, accepts a mix of object types in the collection that gets sent. So this method therefore exists in the new SObjects facade.
$accounts = Account::where('Company', 'Test Company')->get(); // collection of accounts.
$accounts = $accounts->map(function($account) {
$account->Company = 'New Company Name';
return $account;
});
SObjects::update($accounts); // Sends all these objects to SF with updates.
SalesForce will execute each update individually and will not fail the batch if any individual update fails. If you want success to be dependent on all updates succeeding (all or nothing), then you can pass true
as the second parameter. If this is set, the batch of updates must all succeed, or none will.
SObjects::update($accounts, true); // All or none.
By default, the object is loaded with the columns found in the primary compactLayout. If you'd like additional columns, you would use the select
method on the model. For example:
$leads = Lead::select('Id', 'Name', 'Email', 'Custom_Field__c')->limit(10)->get();
The where
and orderBy
methods work as usual as well.
$contacts = Contact::where('Email', '[email protected]')->first();
$contacts = Contact::where('Name', 'like', 'Donuts%')->get();
$contacts = Contact::limit(20)->orderBy('Name')->get();
Exactly as you'd expect.
$lead = Lead::first();
$lead->delete();
Relationships work the same way.
Create a model like above for Account
and Contact
In the Contact
model, add a method for a relationship like you normally would
## Contact.php
public function account()
{
return $this->belongsTo(Account::class);
}
So you can call now:
$contact = Contact::where('email', '[email protected]')->first();
$account = $contact->account;
And the reverse is true
## Account.php
public function contacts()
{
return $this->hasMany(Contact::class);
}
$contacts = $account->contacts;
You are also able to use manual joins
$account = Account::join('Contact', 'AccountId')->first();
These aren't as easy to work with as Relationships because the SalesForce API still returns the array nested in the records
property.
To use custom objects (or an object with a special object name, different from the model), set the protected $table
property in the model.
<?php
namespace App;
use Lester\EloquentSalesForce\Model;
class TouchPoint extends Model
{
protected $table = 'TouchPoint__c';
/** Any other overrides **/
}
This is a new feature to the package. The SObjects facade serves the purpose of exposing any global features not model specific, such as authentication and mass updates, but also it is a pass-thru mechanism for the Forrest facade.
Any methods such as get, post, patch, resources, etc will pass through to the Forrest facade and accept parameters respectively.
The authenticate()
method in the facade will return the token information that has been stored in cache/session.
Sometimes you want to grab a record from SalesForce casually without having to pre-generate a model for it. Now you can do that easily with the object
method on the facade. Example:
$queryResult = SObjects::query("Select Id, FirstName from Lead where Email like 'test@%'");
$leads = collect($queryResult['records'])->map(function($record) {
return SObjects::object($record);
});
The class used for each object returned will be Lester\EloquentSalesForce\SalesForceObject
.
You can get the possible pick list values from a dropdown by using this method on the facade.
$listValues = SObjects::getPicklistValues('Lead', 'Status');
You can set a different log channel for the SOQL actions by specifying SOQL_LOG=
in your .env
file.
The tests in this package are meant for contributors and have been written to be executed independently of a Laravel application. They will not work as part of the applications testing flow.
Create a .env
file that includes the SalesForce credentials for your test instance, or else the test will fail to execute. The .env
field should include these properties:
USERNAME=
PASSWORD=
CONSUMER_KEY=
CONSUMER_SECRET=
LOGIN_URL=https://login.salesforce.com
CACHE_DRIVER=array
LOG_CHANNEL=file
Dependencies are required, so execute composer install
To execute, run composer test
If you discover any security related issues, please email instead of using the issue tracker.
This package is bootstrapped with the help of melihovv/laravel-package-generator.