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

Transactional Bundle Laravel Package

axsy/transactional-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require axsy/transactional-bundle:dev-master
    

    Replace dev-master with the latest stable version (e.g., 1.0.*).

  2. Register the Bundle: Add Axsy\TransactionalBundle\AxsyTransactionalBundle to config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):

    // config/bundles.php
    return [
        // ...
        Axsy\TransactionalBundle\AxsyTransactionalBundle::class => ['all' => true],
        // ...
    ];
    

    Ensure JMS\AopBundle\JMSAopBundle and JMS\DiExtraBundle\JMSDiExtraBundle are also registered (included by default in Symfony Standard Edition).

  3. Configure Defaults (optional): Add to config/packages/axsy_transactional.yaml:

    axsy_transactional:
        default_connection:   default
        default_isolation:    read_committed
    
  4. First Use Case: Annotate a controller action or service method:

    use Axsy\TransactionalBundle\Annotation\Transactionable;
    
    class SomeController extends AbstractController
    {
        /**
         * @Transactionable
         */
        public function createAction()
        {
            // Database operations here
            throw new \RuntimeException('Oops!');
            // Transaction rolls back automatically
        }
    }
    

Implementation Patterns

Common Workflows

  1. Controller-Level Transactions: Use @Transactionable on controller methods to wrap HTTP requests in transactions. Ideal for:

    • Form submissions (e.g., POST /orders).
    • API endpoints requiring atomicity (e.g., PATCH /users/{id}).
    /**
     * @Transactionable(noRollbackFor={"Symfony\Component\HttpKernel\Exception\NotFoundHttpException"})
     */
    public function updateUser(Request $request, User $user)
    {
        $user->update($request->request->all());
        // Auto-rollback on exceptions except NotFoundHttpException
    }
    
  2. Service-Level Transactions: Annotate service methods to ensure business logic runs transactionally. Example:

    class OrderService
    {
        /**
         * @Transactionable(connection="read_replica", isolation="repeatable_read")
         */
        public function processPayment(Order $order, Payment $payment)
        {
            $order->charge($payment);
            // Uses custom connection/isolation
        }
    }
    
  3. Class-Level Annotations: Apply @Transactionable at the class level to inherit settings across all methods:

    /**
     * @Transactionable(rollbackFor={"App\Exception\ValidationException"})
     */
    class InvoiceService
    {
        public function generate() { /* ... */ }
        public function send() { /* ... */ }
        // Both methods rollback only on ValidationException
    }
    
  4. Overriding Settings: Override class-level annotations on specific methods:

    /**
     * @Transactionable(noRollbackFor={"Symfony\Component\HttpKernel\Exception\NotFoundHttpException"})
     */
    class ProductService
    {
        public function updateStock() { /* ... */ }
    
        /**
         * @Transactionable(rollbackFor={"App\Exception\StockException"})
         */
        public function applyDiscount() { /* ... */ }
    }
    

Integration Tips

  • Doctrine ORM/DBAL: Works seamlessly with both. Ensure your database operations use the annotated connection.
  • Symfony Events: Combine with Symfony’s event system for pre/post-transaction hooks:
    $this->get('event_dispatcher')->dispatch(
        new TransactionEvent($this, TransactionEvent::PRE_COMMIT),
        TransactionEvent::PRE_COMMIT
    );
    
  • Testing: Mock transactions in PHPUnit by catching Doctrine\DBAL\ConnectionException or using Doctrine\DBAL\DriverManager with an in-memory database (e.g., SQLite).

Gotchas and Tips

Pitfalls

  1. Annotation Parsing:

    • Issue: Annotations may not apply if JMS\AopBundle is misconfigured or missing.
    • Fix: Verify JMSAopBundle is registered and jms.di_extra.autowire is enabled in config/packages/jms_di_extra.yaml:
      jms_di_extra:
          autowire: true
      
  2. Connection Isolation:

    • Issue: Unsupported isolation levels (e.g., serializable) may throw Doctrine\DBAL\Exception\InvalidArgumentException.
    • Fix: Use valid levels: read_uncommitted, read_committed, repeatable_read, or serializable.
  3. Nested Transactions:

    • Issue: The bundle does not support nested transactions natively. Calling a @Transactionable method from another may cause unexpected rollbacks.
    • Workaround: Use Doctrine\DBAL\Connection::beginTransaction() manually for nested logic.
  4. Exception Handling:

    • Issue: Non-caught exceptions (e.g., unhandled RuntimeException) will trigger rollbacks, but silent errors (e.g., E_ERROR) may not.
    • Fix: Wrap critical code in try-catch blocks or use noRollbackFor to exclude specific exceptions.
  5. Configuration Overrides:

    • Issue: Method-level annotations override class-level ones, but this can lead to inconsistent behavior if not documented.
    • Tip: Document overrides clearly in PHPDoc or method comments.

Debugging

  • Enable AOP Logging: Add to config/packages/dev/jms_aop.yaml:

    jms_aop:
        proxy_validator: '%kernel.debug%'
        logging: '%kernel.debug%'
    

    Check logs for AxsyTransactionalBundle entries during transactions.

  • Doctrine Events: Listen for postFlush/onFlush events to debug transaction boundaries:

    $entityManager->getEventManager()->addEventListener(
        \Doctrine\ORM\Events::postFlush,
        function() { \Log::debug('Transaction flushed'); }
    );
    

Extension Points

  1. Custom Annotations: Extend the bundle by creating a custom annotation processor. Implement Axsy\TransactionalBundle\Annotation\TransactionableInterface and register it via services.yaml:

    services:
        App\Annotation\CustomTransactionable:
            tags:
                - { name: 'axsy_transactional.annotation_processor' }
    
  2. Transaction Listeners: Subscribe to axsy_transactional.transaction.start/commit/rollback events:

    $dispatcher->addListener(
        'axsy_transactional.transaction.start',
        function() { \Log::info('Transaction started'); }
    );
    
  3. Connection Management: Dynamically switch connections by implementing Axsy\TransactionalBundle\Connection\ConnectionProviderInterface and binding it in services.yaml:

    services:
        App\Connection\DynamicConnectionProvider:
            arguments: ['@database_connection']
            tags: ['axsy_transactional.connection_provider']
    
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log