Download the PHP package daikazu/eloquent-salesforce-cache without Composer
On this page you can find all versions of the php package daikazu/eloquent-salesforce-cache. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download daikazu/eloquent-salesforce-cache
More information about daikazu/eloquent-salesforce-cache
Files in daikazu/eloquent-salesforce-cache
Package eloquent-salesforce-cache
Short Description Redis-backed caching layer for daikazu/eloquent-salesforce-objects
License MIT
Informations about the package eloquent-salesforce-cache
daikazu/eloquent-salesforce-cache
Beta — This package is under active development. APIs may change before the stable 1.0 release. Please report issues on GitHub.
Redis-backed caching layer for daikazu/eloquent-salesforce-objects. Transparently caches all SOQL queries and provides surgical cache invalidation via Artisan commands, HTTP API endpoints, and a programmatic service — with zero required changes to existing models.
Table of Contents
- Requirements
- Installation
- Quick Start
- Architecture Overview
- Layer 1: Automatic Adapter Caching
- Layer 2: Opt-In Per-Record Tagging
- Configuration Reference
- Cache Invalidation
- Programmatic Invalidation
- Artisan Commands
- HTTP API Endpoints
- API Authentication
- Events
- Error Handling and Graceful Degradation
- Logging
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | ^12.0 or ^13.0 |
| daikazu/eloquent-salesforce-objects | ^1.0 |
| Redis | Any version supporting cache tags |
Redis is required. The Laravel cache driver must be set to redis (or another tagged-cache-compatible driver) for the store this package targets. Laravel's file and database cache drivers do not support cache tags.
Installation
1. Require the package.
The service provider is registered automatically via Laravel package discovery.
2. Publish the configuration file.
This creates config/salesforce-cache.php in your application.
3. Configure your environment.
Add the following to your .env file:
4. Verify Redis is configured.
Ensure your config/database.php has a Redis connection defined, and that config/cache.php references it:
Quick Start
Once installed, caching is active immediately. Every SOQL query executed through daikazu/eloquent-salesforce-objects is cached automatically — no model changes are required.
To enable surgical per-record invalidation, add the CachesSalesforceQueries trait to any model:
Automatic Cascade Invalidation
When a child object is mutated, the package can automatically invalidate the specific parent records that contain stale related data — no manual invalidation calls required.
Add $invalidatesObjects to any model using the CachesSalesforceQueries trait:
Now when a LineItem is created, updated, or deleted, the specific parent Opportunity records tracked in the registry are surgically invalidated. Other Opportunity cache entries are untouched.
How it works:
- The model declares which object types to cascade to via
$invalidatesObjects. - When relationships are accessed, the package tracks parent-child mappings in Redis (via
$trackedRelationships). - On mutation, the adapter looks up the child record's parents in the reverse registry and invalidates only those specific parent records.
Requirements:
- The parent model must use
CachesSalesforceQuerieswith$trackedRelationshipsthat include the child relationship — this is how the parent-child mapping gets registered. - If no parent records are found in the registry (the relationship was never queried/cached), no cascade happens.
Architecture Overview
The package implements a two-layer caching strategy. Layer 1 operates automatically at the adapter level. Layer 2 is opt-in per model and enables granular, record-level invalidation.
Layer 1: Automatic Adapter Caching
CachedSalesforceAdapter extends SalesforceAdapter and overrides query() and queryAll(). It wraps every SOQL call in a Redis-backed cache lookup before delegating to Salesforce.
How a cache read works:
- The SOQL string is examined to extract the Salesforce object name from the
FROMclause. - If the object is excluded or caching is disabled, the query passes through to Salesforce directly.
- The SOQL string is hashed (
md5) to produce a cache key in the formatsf_cache:{type}:{hash}. - The cache is checked using tags
['salesforce', '{ObjectName}']. - On a cache miss, a distributed lock (
sf_lock:{cacheKey}) is acquired to prevent cache stampede — only one process queries Salesforce; others wait up to 5 seconds and then re-check the cache. - The result is stored with the configured TTL.
Write-through invalidation:
When any mutation method is called (create, update, delete, upsert, bulkCreate, bulkUpdate, bulkDelete), the adapter executes the operation first and then flushes the cache tag for that object type. All cached queries for that object are invalidated atomically.
Cache key format:
Cache tags:
Flushing the salesforce tag clears all Salesforce cache. Flushing the Opportunity tag clears only Opportunity queries.
Layer 2: Opt-In Per-Record Tagging
The CachesSalesforceQueries trait adds per-record tracking on top of Layer 1. When a model using this trait is hydrated from a query, its record ID is registered in the TagRegistry — a Redis-backed set that maps {ObjectType}:{Id} to the cache keys that contain that record.
This enables three capabilities not available at Layer 1:
- Record-level invalidation: Flush only the cache entries that contain a specific record ID, leaving all other cached queries intact.
- Relationship tracking: When a related model is accessed, the relationship is registered in the
TagRegistry. CallinginvalidateCacheWithRelationships()cascades invalidation to all related records automatically. - External invalidation: Because the API endpoint and
CacheInvalidatorservice work with object/ID pairs, external systems (such as Salesforce outbound messages) can trigger targeted invalidation of specific records without knowing which cache keys are involved.
TagRegistry internals:
The registry uses Redis sets with the following key structure:
Registry keys expire at 2x the configured cache TTL to ensure they outlive the cache entries they describe.
Trait properties (all optional):
| Property | Type | Default | Description |
|---|---|---|---|
$cacheable |
bool |
true |
Set to false to exclude this model from Layer 2 tracking |
$cacheTtl |
int |
1800 |
Per-model TTL in seconds (informational; does not override Layer 1 TTL) |
$trackedRelationships |
array |
[] (all) |
Relationship method names to track. Empty array tracks all relationships |
$invalidatesObjects |
array |
[] |
Object types to cascade-invalidate when this model is mutated |
Configuration Reference
Published to config/salesforce-cache.php.
Cache Settings
| Key | Type | Default | Env Variable | Description |
|---|---|---|---|---|
enabled |
bool |
true |
SALESFORCE_CACHE_ENABLED |
Master switch. When false, CachedSalesforceAdapter is not bound and all queries pass through to Salesforce directly. |
store |
string |
'redis' |
SALESFORCE_CACHE_STORE |
The Laravel cache store to use. Must support cache tags. |
ttl |
int |
1800 |
SALESFORCE_CACHE_TTL |
Default cache lifetime in seconds (30 minutes). |
exclude_objects |
array |
[] |
— | Salesforce object names to bypass caching entirely. |
registry_prefix |
string |
'sf_cache_registry' |
— | Redis key prefix for the per-record tag registry. |
log_enabled |
bool |
false |
SALESFORCE_CACHE_LOG_ENABLED |
Whether to log cache hits, misses, invalidations, and failures. |
log_channel |
string\|null |
null |
SALESFORCE_CACHE_LOG_CHANNEL |
Laravel log channel. When null, uses the default channel. |
model_paths |
array\|null |
null |
— | Directories to scan for Salesforce models. When null, scans all declared classes. |
Example — excluding specific objects:
api Section
Controls the HTTP cache invalidation endpoints.
| Key | Type | Default | Env Variable | Description |
|---|---|---|---|---|
api.enabled |
bool |
true |
SALESFORCE_CACHE_API_ENABLED |
Whether to register the invalidation HTTP routes. |
api.prefix |
string |
'api/salesforce-cache' |
— | URL prefix for all invalidation routes. |
api.middleware |
array |
['api'] |
— | Laravel middleware applied to all invalidation routes. |
api.api_key_header |
string |
'X-Salesforce-Cache-Key' |
— | HTTP header name for API key authentication. |
api.api_key |
string\|null |
null |
SALESFORCE_CACHE_API_KEY |
The expected API key value. If empty, all requests are rejected (fail closed). |
Cache Invalidation
Programmatic Invalidation
Resolve CacheInvalidator from the container and call one of its methods directly. All methods are safe to call even when Redis is unavailable — failures are logged and never thrown.
invalidateRecord(string $object, string $id): void
Flushes all cached queries that include the specified record. Also removes the record's entry from the tag registry.
invalidateRecordWithRelationships(string $object, string $id): void
Flushes the record and cascades to all related records tracked in the registry (populated by the CachesSalesforceQueries trait on models with $trackedRelationships defined).
invalidateObject(string $object): void
Flushes all cached queries for a given Salesforce object type. Equivalent to the write-through invalidation that happens automatically on mutations.
invalidateMany(array $records): void
Flushes multiple records in a single call. Each element must be an associative array with object and id keys.
invalidateAll(): void
Flushes the entire salesforce cache tag and clears all registry entries. Use with caution in production.
Trait-level invalidation methods:
When using the CachesSalesforceQueries trait, models also expose the following instance and static methods:
Artisan Commands
salesforce-cache:invalidate
Invalidates cache entries. Accepts flags for non-interactive use or runs an interactive prompt when called without flags.
Examples:
salesforce-cache:status
Displays the current configuration and a summary of tracked objects in the Layer 2 registry.
Example output:
HTTP API Endpoints
All endpoints are registered under the configured prefix (default: api/salesforce-cache) and protected by the VerifyInvalidationRequest middleware. See API Authentication for details on securing these endpoints.
Full documentation of the API, including curl examples, webhook integration, and authentication setup, is available in docs/invalidation-api.md.
| Method | Path | Description |
|---|---|---|
POST |
/api/salesforce-cache/invalidate |
Invalidate one or more specific records |
POST |
/api/salesforce-cache/invalidate/object |
Flush all cache for an object type |
POST |
/api/salesforce-cache/flush |
Flush all Salesforce cache |
GET |
/api/salesforce-cache/status |
Return cache configuration and tracked object summary |
API Authentication
All HTTP endpoints are protected by the VerifyInvalidationRequest middleware, which verifies an API key sent via HTTP header using a timing-safe comparison (hash_equals).
If SALESFORCE_CACHE_API_KEY is empty or unset, all requests are rejected (fail closed).
Events
Daikazu\EloquentSalesforceCache\Events\CacheInvalidated
Dispatched after every cache invalidation operation, regardless of whether it was triggered by the API, an Artisan command, the CacheInvalidator service, or an automatic write-through invalidation from the adapter.
Properties:
| Property | Type | Description |
|---|---|---|
$object |
string |
Salesforce object type (e.g., 'Opportunity'). Value is '*' when scope is all. |
$ids |
string[] |
Array of invalidated record IDs. Empty array when scope is object or all. |
$scope |
string |
Invalidation scope: 'record', 'object', or 'all'. |
Registering a listener:
In your EventServiceProvider:
Example listener:
Using a closure listener in AppServiceProvider::boot():
Error Handling and Graceful Degradation
All cache operations in this package are wrapped in try/catch blocks and degrade gracefully when Redis is unavailable. The application continues to function — it simply makes live requests to Salesforce instead of serving cached results.
Specific behaviors:
- Cache read failure: Returns
null(treated as a cache miss), causing a live Salesforce query. - Cache write failure: The Salesforce response is returned to the caller; the failure is logged at
warninglevel if logging is enabled. - Lock acquisition failure: The process queries Salesforce directly without caching. Stampede protection is best-effort.
- Lock release failure: The lock auto-expires (10-second TTL) and is ignored.
- Invalidation failure: The write operation already succeeded before invalidation was attempted. The failure is logged; no exception is thrown.
- Registry failure: Tag registration silently degrades. Layer 1 object-level caching continues to function.
No exception from any cache operation bubbles up to the caller. If you need to observe failures, enable logging (SALESFORCE_CACHE_LOG_ENABLED=true) or listen to application log events.
Logging
Cache events are logged at the debug level; failures are logged at the warning level. All log messages are prefixed with [SalesforceCache].
Logged events include:
| Event | Level | Context |
|---|---|---|
| Cache hit | debug |
key, object, type |
| Cache miss | debug |
key, object, type |
| Cache invalidated | debug |
object, tags |
| Cache read failed | warning |
error |
| Cache write failed | warning |
error, object |
| Cache lock unavailable | warning |
error |
| Invalidation failed | warning |
error, object |
| Registry operation failed | warning |
error |
License
MIT
All versions of eloquent-salesforce-cache with dependencies
laravel/framework Version ^12.0|^13.0
daikazu/eloquent-salesforce-objects Version ^1.0