Weave Code
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

Safe Salesforce Saver Bundle Laravel Package

comsave/safe-salesforce-saver-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package

    composer require comsave/safe-salesforce-saver-bundle
    

    For Laravel (Symfony-based), register the bundle in config/app.php under the providers array:

    Comsave\SafeSalesforceSaverBundle\ComsaveSafeSalesforceSaverBundle::class,
    
  2. Configure RabbitMQ Add the following to config/packages/old_sound_rabbit_mq.yaml (or equivalent Laravel queue config):

    old_sound_rabbit_mq:
        producers:
            sss_async_processor:
                class: Comsave\SafeSalesforceSaverBundle\Producer\AsyncSfSaverProducer
                connection: default
                exchange_options: { name: 'sss_async_queue', type: direct }
        consumers:
            sss_async_processor:
                connection: default
                exchange_options: { name: 'sss_async_queue', type: direct }
                queue_options: { name: 'sss_async_queue' }
                qos_options: { prefetch_count: 1, global: false }
                callback: Comsave\SafeSalesforceSaverBundle\Consumers\AsyncSfSaveConsumer
        rpc_clients:
            parallel:
                connection: default
                expect_serialized_response: false
        rpc_servers:
            safe_salesforce_saver_server:
                connection: default
                callback: Comsave\SafeSalesforceSaverBundle\Consumers\SafeSalesforceSaverServer
                qos_options: { prefetch_count: 1, global: false }
                queue_options: { name: sss_rpc_queue, durable: true, auto_delete: false }
    
  3. Annotate Models Ensure your Salesforce models are annotated using salesforce-mapper-bundle. Example:

    /**
     * @SalesforceObject("Account")
     */
    class Account {}
    
  4. First Use Case Inject SafeSalesforceSaver into a service and save an object synchronously:

    use Comsave\SafeSalesforceSaverBundle\Services\SafeSalesforceSaver;
    
    class AccountService {
        public function __construct(private SafeSalesforceSaver $saver) {}
    
        public function createAccount(array $data): string {
            $account = new Account($data);
            $this->saver->save($account); // Blocks until Salesforce responds
            return $account->getId();
        }
    }
    

Implementation Patterns

Core Workflows

  1. Synchronous Saves (Blocking) Use when you need immediate feedback (e.g., redirecting to a detail page after creation):

    $this->saver->save($object); // Waits for Salesforce response
    $id = $object->getId(); // Safe to use after save()
    
  2. Asynchronous Saves (Non-Blocking) Use for background processing (e.g., bulk imports, notifications):

    $this->saver->aSyncSave($object); // Fire-and-forget
    
  3. Bulk Operations Batch saves to reduce queue overhead:

    $this->saver->save([$obj1, $obj2, $obj3]); // Synchronous bulk
    $this->saver->aSyncSave([$obj1, $obj2]);    // Asynchronous bulk
    

Integration Tips

  • Queue Workers Start consumers with:

    php bin/console rabbitmq:consumer sss_async_processor
    

    For Laravel, use the queue:work command:

    php artisan queue:work --queue=salesforce
    
  • Error Handling The bundle auto-requeues failed jobs with exponential backoff. Customize retry logic via config/packages/comsave_safe_salesforce_saver.yaml:

    comsave_safe_salesforce_saver:
        max_retries: 5
        retry_delay: 300 # seconds
    
  • Testing Mock the SafeSalesforceSaver in unit tests:

    $mockSaver = $this->createMock(SafeSalesforceSaver::class);
    $mockSaver->expects($this->once())->method('save')->with($object);
    
  • Monitoring Track queue health with:

    php bin/console rabbitmq:status
    

    Or log consumer metrics:

    $this->saver->setLogger($logger); // Inject PSR-3 logger
    

Gotchas and Tips

Pitfalls

  1. Queue Naming Issue: Changing queue/exchange names (e.g., sss_async_queue) breaks consumers. Fix: Use the exact names from the config. For customization, extend the bundle:

    // config/packages/comsave_safe_salesforce_saver.yaml
    comsave_safe_salesforce_saver:
        queue_name: custom_salesforce_queue
    
  2. Serialization Errors Issue: Complex objects (e.g., with closures) may fail to serialize for async saves. Fix: Implement __serialize()/__unserialize() or use aSyncSave() only with simple DTOs.

  3. Salesforce API Limits Issue: Bulk saves may hit Salesforce’s 24-hour batch limit. Fix: Split large arrays into chunks:

    $chunkSize = 100;
    array_chunk($objects, $chunkSize, function($chunk) {
        $this->saver->aSyncSave($chunk);
    });
    
  4. Consumer Lag Issue: Slow consumers cause queue backlog. Fix: Monitor rabbitmq:status and scale workers:

    php artisan queue:work --queue=salesforce --daemon
    

Debugging

  • Failed Jobs Check the failed_jobs table (Laravel) or RabbitMQ dead-letter queue:

    php bin/console rabbitmq:consumer dead_letter_queue
    
  • Logging Enable debug logs in config/packages/monolog.yaml:

    handlers:
        salesforce:
            type: stream
            path: "%kernel.logs_dir%/salesforce.log"
            level: debug
            channels: ["salesforce"]
    
  • Common Errors

    Error Cause Solution
    Connection refused RabbitMQ not running Start services: docker-compose up
    Invalid object type Missing @SalesforceObject Annotate models correctly
    Max retries exceeded Persistent Salesforce error Check failed_jobs table
    Serialization of 'Closure' is not allowed Async save with closures Use synchronous saves or DTOs

Extension Points

  1. Custom Retry Logic Override the consumer to add business logic:

    // src/Consumer/CustomSfSaveConsumer.php
    class CustomSfSaveConsumer extends AsyncSfSaveConsumer {
        protected function handleException(Throwable $e, $object) {
            if ($e instanceof SalesforceException && $e->isRateLimited()) {
                $this->retryAfter(60); // Custom delay
            }
            parent::handleException($e, $object);
        }
    }
    

    Update the config to use your consumer:

    consumers:
        sss_async_processor:
            callback: App\Consumer\CustomSfSaveConsumer
    
  2. Pre/Post-Save Hooks Extend the saver to add validation or transformations:

    class ExtendedSafeSalesforceSaver extends SafeSalesforceSaver {
        protected function beforeSave($object) {
            if (!$object->isValid()) {
                throw new \RuntimeException("Invalid object");
            }
        }
    }
    

    Bind it in config/services.yaml:

    services:
        Comsave\SafeSalesforceSaverBundle\Services\SafeSalesforceSaver:
            class: App\Services\ExtendedSafeSalesforceSaver
    
  3. Dead-Letter Queue Configure a DLQ in old_sound_rabbit_mq.yaml:

    consumers:
        sss_async_processor:
            queue_options:
                name: sss_async_queue
                dead_letter_exchange: salesforce_dlq
    

Performance Tips

  • Batch Size: Test with 100–500 objects per batch to avoid Salesforce timeouts.
  • Worker Concurrency: Limit to 1–2 workers per queue to prevent API throttling.
  • Async Overhead: For low-latency needs, prefer synchronous saves unless processing >100 objects.
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.
graham-campbell/flysystem
bugban/symfony
beyonder-capi/workflow-extensions-bundle
beyonder-capi/job-queue-bundle
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php