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

Laravel5 Salesforce Laravel Package

davispeixoto/laravel5-salesforce

Laravel 5 Salesforce integrates Salesforce REST API access into your Laravel app, giving you a simple way to authenticate and query, create, update, or delete Salesforce records through an Eloquent-like interface.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin Laravel service-provider/facade wrapper around the (legacy) Salesforce Force.com PHP Toolkit, so everything you do is SOAP against the Enterprise or Partner WSDL. Get the connection working end-to-end before writing any domain code.

Minimal setup

  1. Install and pin (the package targets Laravel 5.x; check the illuminate/* constraint before dropping it into a modern app):

    composer require davispeixoto/laravel5-salesforce
    
  2. Register the provider and alias (config/app.php) on Laravel ≤ 5.4; 5.5+ auto-discovers but the alias still needs to be added if you want the facade short name:

    'providers' => [
        Davispeixoto\Laravel5Salesforce\SalesforceServiceProvider::class,
    ],
    'aliases' => [
        'Salesforce' => Davispeixoto\Laravel5Salesforce\Facades\Salesforce::class,
    ],
    
  3. Publish the config and drop your WSDL somewhere stable (do not commit a production WSDL with secrets baked into endpoints — it's org-specific for Enterprise):

    php artisan vendor:publish --provider="Davispeixoto\Laravel5Salesforce\SalesforceServiceProvider"
    
    [email protected]
    SALESFORCE_PASSWORD=secret
    SALESFORCE_TOKEN=securityTokenFromSetup
    SALESFORCE_WSDL=/var/www/app/resources/salesforce/enterprise.wsdl
    
  4. First use case — authenticate and read one object. This is the smoke test that proves credentials, token, IP allow-listing and WSDL path are all correct:

    use Davispeixoto\Laravel5Salesforce\Facades\Salesforce;
    
    Salesforce::login();
    
    $result = Salesforce::query(
        "SELECT Id, Name, BillingCity FROM Account LIMIT 5"
    );
    
    foreach ($result->records as $account) {
        dump($account->Id, $account->Name);
    }
    

Where to look first

  • config/salesforce.php after publishing — confirm the exact key names (username, password, token, wsdl, login URL) since they vary between releases of this wrapper.
  • The service provider's register()/singleton() binding — that tells you what class the facade resolves to and whether it logs in eagerly.
  • The bundled Force.com toolkit under vendor/davispeixoto/.../lib — the wrapper exposes very little, so the real API surface (query, queryMore, create, update, upsert, delete, describeSObjects, retrieve) lives in SforceEnterpriseClient / SforcePartnerClient.
  • README for the facade method list; anything not proxied you reach through the underlying client instance.

Choose your WSDL deliberately. Enterprise WSDL is generated per-org and gives typed SObjects but must be regenerated whenever you add fields; Partner WSDL is generic and stable but everything comes back as stdClass with fieldsToNull semantics. Most day-to-day code is easier against Partner, unless you're doing heavy typed mapping.

Implementation Patterns

Wrap it, don't sprinkle the facade. Keep the facade out of controllers and models. Bind a repository or a small client service and inject it:

class SalesforceAccountRepository
{
    public function __construct(private \SforcePartnerClient $client) {}

    public function find(string $id): ?object
    {
        $r = $this->client->retrieve('Id, Name, BillingCity', 'Account', $id);
        return $r[0] ?? null;
    }
}

Then in a service provider, bind your interface to the Salesforce-backed implementation and swap it for an in-memory fake in tests.

Login once per request, not per call. The toolkit stores the session id on the client instance, and the service provider binds a singleton — so call Salesforce::login() in a middleware or a base service, and let the singleton carry the session. Re-authenticating on every method is the most common cause of hitting login rate limits.

Pagination is manual. SOQL returns at most ~2,000 records per batch; anything past that requires queryMore on the queryLocator:

$response = Salesforce::query("SELECT Id, Name FROM Contact WHERE LastModifiedDate > 2024-01-01T00:00:00Z");

while (true) {
    foreach ($response->records as $record) {
        // handle
    }
    if ($response->done) break;
    $response = Salesforce::queryMore($response->queryLocator);
}

Always key sync jobs off LastModifiedDate (and SystemModstamp for replication) rather than polling everything.

Batch CRUD with SaveResult inspection. create/update/delete accept arrays and return one result per record, and Salesforce does not roll back the whole batch on a single failure:

$results = Salesforce::create($sobjects); // array of stdClass

foreach ($results as $i => $result) {
    if (!$result->success) {
        Log::warning('SF create failed', [
            'index'   => $i,
            'errors'  => $result->errors,
        ]);
    }
}

Treat every write as partially successful and reconcile failures in a queue job with retry/backoff.

Upsert on an external id is the cleanest way to make integration idempotent:

$client->upsert('External_Id__c', $payloads);

Map SObjects to DTOs immediately. SOAP records are stdClass with Id, fieldsToNull, any and nested relationship objects. Convert to Eloquent models or value objects at the boundary; never let stdClass leak into views.

Queue the heavy work. Sync jobs belong on a queue with tries, backoff, and a failed() hook that stores the SOAP fault. Wrap in DB::transaction only for your local writes — Salesforce calls are not transactional with your database.

Gotchas and Tips

  • Security token + IP allow-list. The token is required unless your server IP is trusted in Setup. Appending it to the password is a common manual workaround; if you do, keep it out of the config file and rotate it when passwords change. "INVALID_LOGIN" is almost always a stale token or wrong endpoint.
  • Sandbox vs production endpoint. Pointing a sandbox org at login.salesforce.com (or vice versa) produces confusing auth failures. Verify the login URL in config and that the WSDL matches the org.
  • API version is frozen in the WSDL. Old API versions are retired by Salesforce on a rolling schedule. If you start seeing INVALID_VERSION or unexpected schema errors, regenerate the WSDL — no code change required, just a redeploy of the file.
  • INVALID_SESSION_ID mid-request. Sessions expire. Catch it, re-login once, and retry the operation; don't retry blindly in a loop or you'll hammer the login endpoint.
  • SOQL injection. Never concatenate user input into a query string. Escape quotes (\') and validate Ids with a regex/SforceId check before interpolating.
  • Large extracts are the wrong tool. This is SOAP, one object at a time, ~2k per page. For backfills or nightly full syncs, use the Bulk API 2.0 (or a dedicated package), then use this client for point lookups and small writes.
  • PHP version compatibility. The bundled Force.com toolkit predates PHP 8 and uses constructs that emit deprecations or fatal errors on newer runtimes (dynamic properties, old each()-era idioms). Test on 7.4 before upgrading, and vendor/fork the toolkit if you need to patch it.
  • Errors are SoapFault, not exceptions you'll recognise. Wrap calls in try/catch (\SoapFault $e) and log $e->getMessage() plus $e->detail — the useful Salesforce error code lives in the detail, not the message.
  • Debugging transport issues. Enable SoapClient tracing (or a __getLastRequest/__getLastResponse dump) behind a config flag; the most common failures are TLS/cURL in the container, a relative WSDL path that breaks under config:cache, or a firewall blocking *.salesforce.com.
  • php artisan config:cache. Env-backed config works fine, but any code that reads env() directly inside the package will return null once config is cached. Always read through config('salesforce.*').
  • It is unmaintained. Zero dependents, ~47 stars, last meaningful activity years ago. For a greenfield integration prefer a maintained REST client (Saloon-based, omniphx/forrest, or the official REST/Bulk APIs); keep this package only where the SOAP toolkit is a hard requirement or you're maintaining an existing Laravel 5 app. If you keep it, fork-and-pin and own the toolkit patches yourself.
  • Extension points are limited. There's no event system or pluggable transport — the practical extension points are binding your own wrapper around the resolved client, overriding the service provider's singleton, and adding middleware/jobs around the facade. Plan for that rather than expecting to hook into the package internals.
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.
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
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource