phpforce/soap-client
PHP client for the Salesforce SOAP API. Query and manipulate org data via a builder-based client, with SOQL support, record iteration for large result sets, bulk save helpers to stay within API limits, timezone/date conversions, and event-based extensibility.
Installation:
composer require phpforce/soap-client
Note: The package is outdated (last release in 2015), so use dev-master for the latest changes:
composer require phpforce/soap-client dev-master
Basic Setup:
enterprise.wsdl from your Salesforce instance).config/salesforce/wsdl/)..env entry for credentials:
SALESFORCE_USERNAME=your_username
SALESFORCE_PASSWORD=your_password
SALESFORCE_TOKEN=your_security_token
SALESFORCE_WSDL_PATH=config/salesforce/wsdl/enterprise.wsdl
First Query:
Create a service class (e.g., app/Services/SalesforceService.php):
use Phpforce\SoapClient\ClientBuilder;
class SalesforceService {
public function __construct() {
$builder = new ClientBuilder(
config('salesforce.wsdl_path'),
config('salesforce.username'),
config('salesforce.password'),
config('salesforce.token')
);
$this->client = $builder->build();
}
public function getAccounts() {
return $this->client->query('SELECT Id, Name FROM Account LIMIT 10');
}
}
Register the Service:
In config/app.php, add the service to the providers array:
App\Services\SalesforceService::class,
Bind it in a service provider (e.g., AppServiceProvider):
$this->app->singleton(SalesforceService::class, function ($app) {
return new SalesforceService();
});
First Usage in a Controller:
use App\Services\SalesforceService;
class AccountController extends Controller {
public function index(SalesforceService $salesforce) {
$accounts = $salesforce->getAccounts();
return view('accounts.index', ['accounts' => $accounts]);
}
}
RecordIterator, BulkSaver).Phpforce\SoapClient\Event\BeforeRequest). Look for EventDispatcher integration in the codebase.Query Data:
Use the query() method to fetch records (e.g., accounts, contacts) and iterate over them:
$results = $salesforce->getAccounts();
foreach ($results as $account) {
echo $account->Name . ' (Last Modified: ' . $account->SystemModstamp->format('Y-m-d') . ')';
}
Handle Large Datasets:
The RecordIterator automatically handles pagination for queries returning >2000 records:
$largeQuery = $salesforce->query('SELECT Id, Name FROM Account');
foreach ($largeQuery as $account) {
// Process each account (handles pagination under the hood)
}
Display in Blade:
Pass the iterator to a view and use @foreach:
@foreach($accounts as $account)
<tr>
<td>{{ $account->Name }}</td>
<td>{{ $account->SystemModstamp->format('m/d/Y') }}</td>
</tr>
@endforeach
create() or update() methods with a sObject array:
$account = [
'Name' => 'Acme Corp',
'Description' => 'A test account',
'Type' => 'Customer'
];
$createdAccount = $salesforce->create('Account', [$account]);
$salesforce->delete('Account', $accountId);
Use BulkSaver to handle large datasets efficiently (avoids API limits):
$bulkSaver = $salesforce->getBulkSaver();
$bulkSaver->insert('Account', $accountsArray);
$bulkSaver->flush(); // Execute the bulk operation
Fetch related records in a single query:
$accounts = $salesforce->query('SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account LIMIT 5');
foreach ($accounts as $account) {
if (isset($account->Contacts)) {
foreach ($account->Contacts as $contact) {
echo "Contact: {$contact->Name}\n";
}
}
}
The package automatically converts Salesforce UTC times to your local timezone:
$account = $salesforce->query('SELECT Id, LastModifiedDate FROM Account LIMIT 1')->current();
echo $account->LastModifiedDate->format('Y-m-d H:i:s'); // Local timezone
Centralize Salesforce client initialization in a service provider:
// app/Providers/SalesforceServiceProvider.php
public function register() {
$this->app->singleton('salesforce', function ($app) {
$builder = new ClientBuilder(
config('salesforce.wsdl_path'),
config('salesforce.username'),
config('salesforce.password'),
config('salesforce.token')
);
return $builder->build();
});
}
Bind the client to a facade for easy access:
// app/Facades/Salesforce.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Salesforce extends Facade {
protected static function getFacadeAccessor() { return 'salesforce'; }
}
Usage:
use App\Facades\Salesforce;
$accounts = Salesforce::query('SELECT Id, Name FROM Account');
Publish the package’s config file:
php artisan vendor:publish --provider="Phpforce\SoapClient\ServiceProvider"
Customize config/salesforce.php:
return [
'wsdl_path' => env('SALESFORCE_WSDL_PATH', 'config/salesforce/wsdl/enterprise.wsdl'),
'username' => env('SALESFORCE_USERNAME'),
'password' => env('SALESFORCE_PASSWORD'),
'token' => env('SALESFORCE_TOKEN'),
'log_enabled' => env('SALESFORCE_LOG_ENABLED', false),
];
Integrate with Laravel’s logging system:
$log = \Log::getMonolog();
$builder = new ClientBuilder(/* ... */);
$client = $builder->withLog($log)->build();
Or use Monolog directly:
$log = new \Monolog\Logger('salesforce');
$log->pushHandler(new \Monolog\Handler\StreamHandler(storage_path('logs/salesforce.log')));
$client = $builder->withLog($log)->build();
Wrap Salesforce calls in try-catch blocks to handle SOAP faults:
try {
$accounts = $salesforce->query('SELECT Id FROM NonExistentObject');
} catch (\Phpforce\SoapClient\Fault $e) {
\Log::error('Salesforce error: ' . $e->getMessage());
return back()->with('error', 'Failed to fetch data from Salesforce.');
}
Cache frequent queries to reduce API calls:
$cacheKey = 'salesforce_accounts_' . $lastUpdated;
$accounts = \Cache::remember($cacheKey, now()->addHours(1), function () use ($salesforce) {
return $salesforce->query('SELECT Id, Name FROM Account');
});
Extend the package’s behavior via events (e.g., log all API calls):
// app/Listeners/LogSalesforceRequest.php
public function handle($event) {
\Log::info('Salesforce Request', [
'method' =>
How can I help you explore Laravel packages today?