genesis-global/salesforce-bundle
A Laravel package that streamlines Salesforce integration with configuration, service bindings, and helpers for authenticating, querying, and syncing records through the Salesforce REST API, reducing boilerplate for PHP apps.
Architecture fit
The headline finding for a Laravel shop: this is a Symfony bundle, not a Laravel package. Its value-add is Symfony-specific plumbing — Bundle registration in bundles.php, a DependencyInjection/Extension with a Configuration tree, tagged services, and autowired handlers. Laravel's container reads none of this. The bundle is therefore best understood as two layers:
Layer 2 is worthless to us. Layer 1 may be usable if it has no hard Symfony coupling (likely candidates: Guzzle + PSR-7, possibly symfony/http-client, possibly symfony/options-resolver). The assessment hinges on a source read of composer.json and the client classes — not on the README, which is thin.
Integration feasibility
Feasible but not turnkey. Two paths:
SalesforceServiceProvider with a config/salesforce.php. Roughly 1–3 days of engineering plus a sandbox PoC. This discards the only part of the package that was "productized."Technical risk — elevated
--ignore-platform-reqs, runtime deprecations at minimum.400 INVALID_VERSION. This is not a hypothetical — it is a scheduled outage.Key questions to resolve before committing
omniphx/forrest (Laravel-native, comparable maturity) or phpforce/salesforce-rest-api materially better, or is a 200-line Saloon/Guzzle client with contract tests the honest answer?Stack fit
Poor out of the box, acceptable after stripping the bundle layer. Assume Laravel 11/12, PHP 8.3, Composer 2. Expect the package's symfony/* constraints to conflict with Laravel's. Plan for a fork rather than a composer require.
Migration path
ourorg/salesforce-client (MIT permits this). Delete the DependencyInjection, Resources/config, and *Bundle classes; keep the client, auth, and query builder.App\Providers\SalesforceServiceProvider binding a SalesforceClient interface; config in config/salesforce.php, credentials in env / secrets manager.Compatibility
E_ALL; expect dynamic-property and null-to-non-nullable deprecations in a 2020 codebase.Sequencing
| Phase | Duration | Exit criterion |
|---|---|---|
| Spike: fork, install, authenticate to sandbox, one SOQL + one upsert | 2–3 days | Green in CI, no Symfony boot |
| Requirements lock: objects, operations, volumes, sync vs async | 3 days | Written scope, Bulk API decision made |
| Build: adapter, token cache, retry/backoff, queued jobs | 1–2 weeks | Contract tests pass against sandbox |
| Shadow mode: read-only in production alongside manual process | 1–2 weeks | Field-level parity confirmed |
| Cutover: writes enabled, manual process retired | 1 week | Rollback runbook exercised |
Realistic total: 5–8 weeks for one engineer, of which the package saves perhaps one week.
Maintenance
Effectively we own this code from day one. There is no upstream to patch. Budget 1–2 engineer-days per quarter for dependency bumps, Salesforce API version advancement, and PHP upgrades — plus a hard deadline each time Salesforce announces a retirement. Treat the fork as first-class internal code: owned in CODEOWNERS, covered by tests, included in the PHP upgrade rotation.
Support
No vendor, no community (0 dependents, 11 stars). Escalation path is "the engineer who read the source." Mitigate with a written ADR capturing why we forked, what we changed, and what would trigger a replacement. Avoid single-person dependency — two engineers should be able to read the client.
Scaling
This is where the package is most likely to fail us, because a synchronous per-record REST client does not scale against Salesforce governor limits:
REQUEST_LIMIT_EXCEEDED.Redis::throttle), not request-cycle calls.Failure modes to design for
| Failure | Symptom | Mitigation |
|---|---|---|
| API version retirement | Hard 400 INVALID_VERSION across all calls, no warning |
Config-driven version, CI canary, calendar reminder |
| Token expiry mid-batch | Partial job failure, ambiguous write state | Refresh ahead of expiry; idempotent upserts |
| Governor limit hit | REQUEST_LIMIT_EXCEEDED, backpressure |
Throttled queues, exponential backoff, dead-letter queue |
| Partial batch success | Some records written, job marked failed | Per-record result parsing + reconciliation report |
| Silent field truncation / validation rules | Data loss with 200 OK | Explicit field allowlist, post-write verification |
| Retry-induced duplicates | Duplicate leads/contacts in CRM | External ID upsert keyed on our system of record |
| No CDC/streaming | Polling only, latency and quota cost | Confirm whether real-time is required before scope lock |
Ramp-up
A mid-level PHP engineer reaches productivity in 1–2 weeks, most of it spent on Salesforce's auth and limits model rather than the package itself. The TPM-level cost is the discovery work — sandbox provisioning, API entitlement confirmation with the Salesforce admin, and the build-vs-adopt decision — which should be front-loaded into the spike.
Recommendation: Do not adopt this package as-is. Use it as a reference implementation and possible source for the client layer, forked and narrowed, behind our own interface. The low score and low opportunity signal (1.52 / 21.84) are consistent with what the code review will show: a small, dated, unmaintained wrapper whose only integrated feature is the one we cannot use. The decision that actually matters is build-vs-adopt at the client layer, and that should be settled by the two-day sandbox spike, not by the star count.
How can I help you explore Laravel packages today?