PHPackages                             meius/laravel-transaction-orchestrator - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. meius/laravel-transaction-orchestrator

ActiveLibrary[Database &amp; ORM](/categories/database)

meius/laravel-transaction-orchestrator
======================================

Attribute-driven transactions and row-level locks for Laravel: retries, backoff, HTTP-aware rollbacks, and FOR UPDATE/SHARE on route model binding.

v0.1.0(8mo ago)51MITPHPPHP ^8.2CI passing

Since Sep 3Pushed 5mo agoCompare

[ Source](https://github.com/brann-meius/laravel-transaction-orchestrator)[ Packagist](https://packagist.org/packages/meius/laravel-transaction-orchestrator)[ Fund](https://www.buymeacoffee.com/bohdanmeiuv)[ RSS](/packages/meius-laravel-transaction-orchestrator/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (7)Versions (4)Used By (0)

Laravel Transaction Orchestrator
================================

[](#laravel-transaction-orchestrator)

[![Build Status](https://camo.githubusercontent.com/6d14e4d9387af75a282867786fd3aa6528289a8924ba390b05b8fa850ecd65d7/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6272616e6e2d6d656975732f6c61726176656c2d7472616e73616374696f6e2d6f7263686573747261746f722f63692e796d6c)](https://github.com/brann-meius/laravel-transaction-orchestrator/actions)[![License](https://camo.githubusercontent.com/9480cfa804a2afc7b64c43820a4adf989c673b084894188635b1c36c16ec23e2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6272616e6e2d6d656975732f6c61726176656c2d7472616e73616374696f6e2d6f7263686573747261746f72)](LICENSE)[![codecov](https://camo.githubusercontent.com/2e05e794ae4a317a3a3a12a8a8ce6cf2cde262293095072950eb4f6d76345bc6/68747470733a2f2f636f6465636f762e696f2f6769746875622f6272616e6e2d6d656975732f6c61726176656c2d7472616e73616374696f6e2d6f7263686573747261746f722f67726170682f62616467652e7376673f746f6b656e3d334d4f30373831543642)](https://codecov.io/github/brann-meius/laravel-transaction-orchestrator)[![Codacy Badge](https://camo.githubusercontent.com/f860fcac2992d3dd81748cfc463ff22147e422b3bc02f858795a47c6dbcefb1a/68747470733a2f2f6170702e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f3264386134636434383336643461343762373033363137626665643139396263)](https://app.codacy.com/gh/brann-meius/laravel-transaction-orchestrator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)[![PHP Version](https://camo.githubusercontent.com/2a38189ebddcd255237b8da46df2e1435923035f259b9e4b261fd98ad4e9cea2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344253230382e322d626c7565)](https://www.php.net/)[![Laravel Version](https://camo.githubusercontent.com/35271957d8a01f96d8e1fab00a8df30fe6207ce28767b11e2f61942d42d412c6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d25334525334425323031312e302d6f72616e6765)](https://laravel.com/)

---

Declarative transactions and row-level locks for Laravel 11+ via PHP attributes. Annotate controller methods with `#[Transactional]` — get transactions with retries/backoff and HTTP-aware rollback. Annotate parameters with `#[LockForUpdate]` / `#[SharedLock]` — get row-locks directly during route model binding. Zero boilerplate.

> Internally it uses the **standard Laravel/Eloquent API**: `lockForUpdate()` and `sharedLock()`. Support = whatever Laravel and your DB driver support.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Requirements &amp; DB Support](#requirements--db-support)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Attribute Reference](#attribute-reference)
    - [`#[Transactional]`](#transactional)
    - [`#[LockForUpdate]` and `#[SharedLock]`](#lockforupdate-and-sharedlock)
- [Retries &amp; Backoff](#retries--backoff)
- [HTTP-Aware Rollback](#http-aware-rollback)
- [Multiple Connections](#multiple-connections)
- [Nested Transactions](#nested-transactions)
- [Limitations](#limitations)
- [License](#license)

---

Features
--------

[](#features)

- `#[Transactional]` — wrap controller actions in a transaction, optionally with retries and backoff.
- Rollback policy based on HTTP response (4xx/5xx/specific codes).
- Exceptions that **do not** trigger rollback (`noRollbackOn`).
- `#[LockForUpdate]` / `#[SharedLock]` on action parameters — row-lock during route model binding.
- Zero config: service provider and router decoration auto-registered.

---

Requirements &amp; DB Support
-----------------------------

[](#requirements--db-support)

- PHP **8.2+**
- Laravel **11+**

**Row lock support** is fully delegated to Laravel/Eloquent:

- MySQL/MariaDB — `FOR UPDATE` / `FOR SHARE` (or `LOCK IN SHARE MODE` on older versions).
- PostgreSQL — `FOR UPDATE` / `FOR SHARE` (or `FOR KEY SHARE` depending on context).
- SQL Server — via hints (`UPDLOCK`, `ROWLOCK`), same as Laravel does.
- SQLite — no row-level lock for `SELECT` (effectively no-op).

If your driver/version doesn’t support the mode, behavior matches Laravel.

---

Installation
------------

[](#installation)

1. **Composer Installation:**
    Install the package using Composer:

    ```
    composer require meius/laravel-transaction-orchestrator
    ```
2. **Register the Service Provider:**
    Manually register the service provider by adding it to your `bootstrap/providers.php` file:

```
return [
    // Other service providers...
    Meius\LaravelTransactionOrchestrator\Providers\TransactionOrchestratorServiceProvider::class,
];
```

---

Quick Start
-----------

[](#quick-start)

```
use App\Models\Order;
use App\Repositories\OrderRepository;
use Meius\LaravelTransactionOrchestrator\Attributes\Locks\LockForUpdate;
use Meius\LaravelTransactionOrchestrator\Attributes\Transactional;
use Meius\LaravelTransactionOrchestrator\Enums\HttpRollbackPolicy;
use Symfony\Component\HttpFoundation\Response;

class OrderController extends Controller
{
    public function __construct(
        private readonly OrderRepository $orderRepository,
    ) {
        //
    }

    #[Transactional(
        connection: 'mysql',
        retries: 3,
        backoff: [50, 100, 200], // milliseconds
        noRollbackOn: [QueryException::class],
        rollbackOnHttpError: HttpRollbackPolicy::ROLLBACK_ON_5XX,
    )]
    public function destroy(#[LockForUpdate] Order $order): Response
    {
        try {
            $this->orderRepository->delete($order);
        } catch (\Throwable) {
            return response()->json([
                'message' => 'Unable to delete the order.',
            ], Response::HTTP_INTERNAL_SERVER_ERROR); // 5xx → rollback (per policy)
        }

        return response()->noContent(); // 204 → commit
    }
}
```

---

Attribute Reference
-------------------

[](#attribute-reference)

### `Transactional`

[](#transactional)

**Purpose:** run the controller method inside transaction(s).

**Parameters &amp; behavior:**

- `connection`: `null|string|string[]`.
    - `null` → default from `config/database.php`.
    - Normalized to array at runtime → `$connections`.
- `retries`: how many times to retry on **transient** DB errors (deadlock, lock timeout, disconnect, etc.).
- `backoff`: delay in ms before retry.
    - Single number = constant delay.
    - Array = per-attempt delay, last value repeats.
- `noRollbackOn`: list of exception FQCNs that **do not** trigger rollback.
- `rollbackOnHttpError`: rollback policy based on HTTP response: `ROLLBACK_NONE`, `ROLLBACK_ON_4XX`, `ROLLBACK_ON_5XX`, `ROLLBACK_ON_4XX_5XX`(default), or list of codes (`[409, 422]`).

**Validation (constructor enforces):**

- `backoff` as array → must be `list`.
- `rollbackOnHttpError` as array → must be `list`.
- `noRollbackOn` → must be `Throwable` subclasses.

**Transaction outcome:**

- Exceptions are **not** swallowed. Any unhandled exception → rollback (unless in `noRollbackOn`).
- If no exception: commit/rollback decided by the **Response** and policy.

---

### `LockForUpdate` and `SharedLock`

[](#lockforupdate-and-sharedlock)

**Purpose:** apply row-lock to action parameter during route model binding.

How it works:

- Before resolving the parameter, the router checks the attribute and applies the standard Eloquent lock (`lockForUpdate()` or `sharedLock()`) **just once**.
- Lock does **not** leak into other queries inside the method.
- SQL and semantics depend on your DB driver/version (see [Requirements](#requirements--db-support)).

**Example:**

```
use App\Exceptions\Products\CannotRemoveProductException;
use App\Exceptions\Products\ProductNotInOrderException;
use App\Http\Resources\OrderResource;
use App\Models\Order;
use App\Models\Product;
use App\Services\OrderService;
use Meius\LaravelTransactionOrchestrator\Attributes\Locks\LockForUpdate;
use Meius\LaravelTransactionOrchestrator\Attributes\Locks\SharedLock;
use Meius\LaravelTransactionOrchestrator\Attributes\Transactional;
use Symfony\Component\HttpFoundation\Response;

class OrderProductController extends Controller
{
    public function __construct(
        private readonly OrderService $orderService,
    ) {
        //
    }

    /**
     * Removes a product from the order.
     */
    #[Transactional]
    public function destroy(
        #[LockForUpdate] Order $order,
        #[SharedLock] Product $product
    ): Response {
        try {
            $order = $this->orderService->recalculate($order, $product);
        } catch (ProductNotInOrderException|CannotRemoveProductException $exception) {
            return response()->json([
                'error' => $exception->getMessage(),
            ], Response::HTTP_UNPROCESSABLE_ENTITY);
        } catch (\Throwable) {
            return response()->json([
                'error' => 'Unable to remove product from order.',
            ], Response::HTTP_INTERNAL_SERVER_ERROR);
        }

        return OrderResource::make($order)->response();
    }
}
```

---

Retries &amp; Backoff
---------------------

[](#retries--backoff)

- Enabled when `retries > 0`.
- Transient errors include common concurrency/connection issues.
- `backoff` in ms. Example `[10, 30, 70]` → values applied per attempt, last repeated.

> With retries enabled, make operations **idempotent** (or dedupe-safe). The package does not enforce idempotency.

---

HTTP-Aware Rollback
-------------------

[](#http-aware-rollback)

Rollback can be triggered by response status without exceptions:

- Validation → `422` → rollback.
- Resource conflict → `409` → rollback.
- Any `5xx` → rollback (default).

Customize via policy or code list:

```
#[Transactional(rollbackOnHttpError: [409, 422])]
```

Decision is made **after** action returns a `Response`, before sending body.

---

Multiple Connections
--------------------

[](#multiple-connections)

`connection` accepts an array:

```
#[Transactional(connection: ['mysql', 'pgsql'])]
```

- Opens a transaction for each connection.
- On error/rollback condition — **all** are rolled back.
- This is **not** 2PC (no cross-DB atomicity).

---

Nested Transactions
-------------------

[](#nested-transactions)

- If you call `DB::transaction()` inside, Laravel uses **savepoints** (if supported).
- Outer `#[Transactional]` decides final commit/rollback.
- Do not mix manual `commit()`/`rollBack()` with orchestrator — use `DB::transaction()`.

---

Limitations
-----------

[](#limitations)

- Lock behavior depends on DB driver/version; package follows Laravel exactly.
- Locks apply **only** during route model binding. Queries inside method are unaffected.
- No 2PC across DBs.
- Retries ≠ idempotency: duplicate side effects are your responsibility.

---

License
-------

[](#license)

This package is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

31

—

LowBetter than 68% of packages

Maintenance67

Regular maintenance activity

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

251d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9436066baff14a6bcf412f22489e9c476f7e792aed4d36db360bcc23ac1b42af?d=identicon)[brann-meius](/maintainers/brann-meius)

---

Top Contributors

[![brann-meius](https://avatars.githubusercontent.com/u/177737890?v=4)](https://github.com/brann-meius "brann-meius (7 commits)")

---

Tags

concurrencylaravelattributesretrytransactionsbackoffrow-level-lockslock-for-updateshared-lockhttp-rollback

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/meius-laravel-transaction-orchestrator/health.svg)

```
[![Health](https://phpackages.com/badges/meius-laravel-transaction-orchestrator/health.svg)](https://phpackages.com/packages/meius-laravel-transaction-orchestrator)
```

###  Alternatives

[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[watson/validating

Eloquent model validating trait.

9723.3M47](/packages/watson-validating)[cybercog/laravel-love

Make Laravel Eloquent models reactable with any type of emotions in a minutes!

1.2k302.7k1](/packages/cybercog-laravel-love)[cviebrock/eloquent-taggable

Easy ability to tag your Eloquent models in Laravel.

567694.8k3](/packages/cviebrock-eloquent-taggable)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[reedware/laravel-relation-joins

Adds the ability to join on a relationship by name.

2121.2M13](/packages/reedware-laravel-relation-joins)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
