Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Force.com Toolkit For Php Laravel Package

developerforce/force.com-toolkit-for-php

A PHP toolkit for integrating with Salesforce, providing wrappers for the REST, SOAP, Bulk, Metadata, and Streaming APIs plus OAuth authentication. It simplifies building Force.com/Salesforce apps and syncing data from PHP.

Deep Wiki
Context7

Getting Started

The toolkit is a thin, namespace-less wrapper around the Salesforce SOAP API (the REST layer is incidental). There is no Composer autoload in the 2015 release — you require the classes and point the client at a WSDL file.

composer require developerforce/force.com-toolkit-for-php
require_once 'vendor/developerforce/force.com-toolkit-for-php/soapclient/SforcePartnerClient.php';

$client = new SforcePartnerClient();
$client->createConnection(base_path('soapclient/partner.wsdl.xml'));
$client->login(config('salesforce.username'), config('salesforce.password') . config('salesforce.token'));

// first useful call: pull records into an Eloquent-friendly shape
$res = $client->query("SELECT Id, Name, BillingCity FROM Account LIMIT 200");
foreach ($res->records as $record) {
    Account::updateOrCreate(['sf_id' => $record->Id], ['name' => $record->Name]);
}

Where to look first:

  1. soapclient/partner.wsdl.xml — use the Partner WSDL, not Enterprise. It is schema-agnostic, so you can query/update any standard or custom object without regenerating WSDL when your org changes. Enterprise WSDL is only worth it if you want typed properties and IDE completion on a frozen schema.
  2. SforceBaseClient — the shared implementation of query, queryMore, create, update, upsert, delete, retrieve, describeSObjects, plus getLastRequest() / getLastResponse() (your primary debugging tool).
  3. SforceBaseClient::sObject — the generic record container. You set __c custom fields, Id, and type as public properties on it.

The fastest first use case is a one-way sync: form submit → create(new SforceLead(...)), or a nightly query() of opportunities into a local reporting table. Do that before attempting bidirectional sync; the toolkit has no change-tracking or delta helpers, so you must build SystemModstamp-based watermarks yourself.

Implementation Patterns

Wrap it in a singleton, not a per-request login. login() is an authenticated API call and counts against your org's login-call limit; calling it on every HTTP request will throttle you. In Laravel:

// SalesForceServiceProvider::register()
$this->app->singleton(SforcePartnerClient::class, function () {
    $client = new SforcePartnerClient();
    $client->createConnection(config('salesforce.wsdl'));

    if ($session = Cache::get('salesforce.session')) {
        $client->setSessionHeader($session['id']);
        $client->setEndpoint($session['url']);
        return $client;                       // skip login entirely
    }

    $client->login(config('salesforce.username'), config('salesforce.password').config('salesforce.token'));
    Cache::put('salesforce.session', [
        'id'  => $client->getSessionId(),
        'url' => $client->getLocation(),
    ], now()->addHours(6));

    return $client;
});

Catch SoapFault with INVALID_SESSION_ID, flush the cache key, and retry once in a middleware or a rescue() wrapper.

Use a repository layer, not the client in controllers. The client mutates global session state and returns raw stdClass; keep it behind a SalesforceRepository that maps arrays ↔ sObject:

public function upsertAccount(array $data): string
{
    $s = new SforceBaseClient::sObject();
    $s->type = 'Account';
    $s->External_Key__c = $data['external_key'];   // upsert key must be an externalId field
    foreach ($data as $field => $value) {
        $s->{$field} = $value;
    }
    $result = $this->client->upsert('External_Key__c', [$s]);
    return $result[0]->id;
}

Batch writes 200 at a time. create/update/upsert accept arrays and are processed as one API call. Chunk your queue jobs with array_chunk($records, 200) rather than looping single records — this is the single biggest throughput lever available in this package.

Paginate with queryMore, and always check done. query() returns at most 2,000 records per call (15,000 total for a SOQL query). Loop while !$res->done with $res->queryLocator. Never assume a single query() result is complete.

Incremental sync via SystemModstamp. Store the max SystemModstamp seen, then WHERE SystemModstamp > :ts on the next run. Because the toolkit has no Bulk API support, for anything over ~50k rows overnight, write the CSV and POST it to /services/async/.../job yourself with Guzzle — mixing the two is normal and expected.

Metadata changes go through SforceMetadataClient with a zipped package.xml + source; treat it as a deployment script, not runtime code.

Gotchas and Tips

  • SOAP extension required. php-soap is not enabled by default on many images; the client fails with an opaque "could not connect" if SoapClient is missing.
  • Nulls are dropped. Setting $s->Field = null will not clear a field. Use $s->fieldsToNull = ['Field__c'] on a Partner sObject, and remember it replaces the array on each write.
  • Picklists, booleans, dates. Pass booleans as PHP true/false (not 1/0), dates as Y-m-d, datetimes as Y-m-d\TH:i:s\Z (UTC). Picklist values are case-sensitive and must match describeSObjects() output — cache the describes, they're cheap but rate-limited.
  • SOQL injection. There's no parameter binding. Escape \ then ' on every interpolated string, and validate anything used as a field or object name against a whitelist.
  • Errors are a serialized array in the fault string. Always inspect $e->faultstring and $client->getLastRequest() / getLastResponse(); the fault message alone rarely identifies the offending field. Partial-success array responses from batch writes contain per-record success and errors — check them all, never just [0].
  • WSDL/API version drift. The bundled WSDLs are ~v30 (2015). Salesforce still honors old API versions but has a rolling minimum; the setEndpoint() trick lets you target a newer /services/Soap/u/58.0/... endpoint with the older WSDL for most CRUD, but new fields/objects won't describe correctly. Plan for a migration path (omniphx/forrest for REST, or hand-rolled REST + JWT OAuth) rather than betting on this package long-term.
  • No OAuth in the toolkit. It only does username/password + security token. If your org enforces SSO or IP restrictions, obtain the access_token via the JWT bearer flow yourself and call setSessionHeader($token) + setEndpoint($instanceUrl . '/services/Soap/u/58.0') to bypass login() entirely.
  • Archived since 2015. No PHP 8.x fixes; expect dynamic-property deprecations on sObject under PHP 8.2+. Add #[AllowDynamicProperties] via a subclass or silence with error_reporting. Also no security patches — pin it, vendor-review it, and isolate it behind your own interface so swapping it out is a one-class change.
  • Extension points are limited to subclassing SforceBaseClient (override setHeaders / _getClient) and the SforceBaseClient::$sObject container. That's it — there are no events, middleware, or plugins.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console