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

Transaction Bundle Laravel Package

inneair/transaction-bundle

inneair/transaction-bundle is a Symfony bundle for managing database transactions. It provides reusable services and integration points to wrap application operations in transactional boundaries, helping keep persistence logic consistent and easier to maintain in Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

The first thing to establish: inneair/transaction-bundle is a Symfony bundle, not a Laravel package. There is no TransactionBundleServiceProvider, no facade, no config stub published by php artisan vendor:publish. It targets Symfony 2.x/3.x-era applications on top of Doctrine, its last tagged release is from July 2017, and the repository is archived. So the realistic "getting started" is one of two paths:

  1. You are maintaining an old Symfony app that already depends on it — run composer show inneair/transaction-bundle to confirm which version is locked, then look at the bundle's Resources/config/ directory first. That is where the service definitions live, and it is the fastest way to discover the actual service id and constructor signature of the transaction manager (naming has drifted between versions, so trust the YAML over the README). Register the bundle in app/AppKernel.php (or config/bundles.php on Symfony 4+) and check DependencyInjection/Configuration.php for any tunable options such as retry counts or connection names.
  2. You are a Laravel developer evaluating it for a port — do not attempt to install it. Its composer.json will constrain PHP and symfony/* to versions that conflict with any current Laravel skeleton. Instead read the source as a design reference: the interesting part is a thin service that wraps a unit of work in beginTransaction / commit / rollback and lets you nest. You can reproduce that in ~40 lines on top of Illuminate\Database\Connection.

First use case either way: take one multi-write application service (create order + decrement stock + write audit row) and put the whole call behind the transaction manager, rather than scattering beginTransaction calls through repositories. That is the pattern the bundle exists to encourage, and it is the pattern worth copying.

Implementation Patterns

Wrap the unit of work, not the query. The bundle's model is that an application/domain service is the transaction boundary. In Laravel terms this is:

DB::transaction(function () use ($dto) {
    $order = $this->orders->create($dto);
    $this->stock->decrement($dto->sku, $dto->qty);
    $this->audit->record($order);
}, attempts: 3);

The attempts argument is the Laravel equivalent of the retry wrapper a transaction bundle provides — it re-runs the closure on deadlock/serialization failure. If you port the bundle's idea, make retry a first-class option rather than something each caller remembers.

Nesting via savepoints. Doctrine emulates nested transactions with savepoints; Laravel does the same through nested DB::transaction() calls. Use nesting for composability — a PlaceOrder action calling a ReserveStock action should not need to know whether it is already inside a transaction. Keep nesting shallow (two levels is plenty); deep nesting makes rollback semantics hard to reason about.

Keep the boundary above the repository layer. Repositories that open their own transactions defeat the purpose. Inject the transaction manager (or just use the DB facade) in actions/handlers/jobs, and let repositories be dumb persistence.

Do not cross I/O boundaries inside the transaction. No HTTP calls, no queue pushes, no mail. If you need side effects, use Laravel's DB::afterCommit() (or the bundle's post-commit hook if it exposes one) so the effect only fires on successful commit. This is the single most valuable habit the pattern enforces.

Integration tip for jobs and console commands: a queued job that fails mid-transaction should be allowed to release back to the queue rather than swallow the exception. Let the transaction helper rethrow after rollback; catch at the job level, not inside the closure.

Gotchas and Tips

  • Abandonment is the headline risk. Archived since 2017, zero dependents, no security process. Treat it as vendored source, not a dependency. If you need the behaviour on a modern stack, copy the one relevant class into your own namespace and own it.
  • Composer will fight you. PHP and Symfony constraints from 2017 will not resolve against PHP 8.1+ / Symfony 6/7 without --ignore-platform-reqs, and ignoring those flags on a transaction package is a good way to ship a runtime fatal in production.
  • Doctrine's rollback-only flag. In Doctrine, once a nested transaction fails, the outer transaction is marked rollback-only and any subsequent commit() throws — even if your code caught the inner exception and continued. Laravel's savepoint handling has a similar trap: catching an exception inside the closure and returning normally will still commit whatever partial state exists at that savepoint. Never swallow exceptions inside the transaction body.
  • DDL breaks the guarantee. MySQL implicitly commits on CREATE TABLE/ALTER TABLE; migrations are not transactional on that engine. Do not assume the wrapper protects schema work.
  • Long transactions invite lock contention and connection timeouts. Set wait_timeout/innodb_lock_wait_timeout deliberately, and log transaction duration if you own the wrapper — it is the cheapest early warning you will get.
  • Debugging: enable query logging and grep for START TRANSACTION / SAVEPOINT / ROLLBACK around the failing request. On Doctrine, getTransactionNestingLevel() is the quickest way to spot a leaked beginTransaction that never committed — the classic cause of "connection pool exhausted" after a deploy.
  • Extension points, if you do port it: bind an interface (TransactionManager) in the container rather than referencing a concrete class, so you can later swap in a decorator that adds metrics, retry, or after-commit callbacks without touching call sites. In Laravel, that is a one-line binding in a service provider and is worth doing on day one.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource