niclas-van-eyk / laravel-transactional-controllers
轻松将数据库事务包装到控制器操作中。
Requires
- php: ^8.1
- illuminate/contracts: ^9.0 || ^10.0
- illuminate/database: ^9.0 || ^10.0
- illuminate/routing: ^9.5 || ^10.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.8
- nunomaduro/collision: ^6.0
- nunomaduro/larastan: ^2.0.1
- orchestra/testbench: ^7.0
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-09 03:10:07 UTC
README
轻松将数据库事务包装到控制器操作中。
class ExampleUsageController { #[Transactional] public function demo(Request $request) { User::create($request->all()); User::create($request->all()); throw new Exception("Everything will be rolled back!"); } }
安装
您可以通过composer安装此包
composer require niclas-van-eyk/laravel-transactional-controllers
背景
如果您想对数据库进行一系列编辑,要么 全部 同时发生,要么 完全不发生,通常您会使用数据库事务。这里我们使用的例子是一个用户($author
)向另一个用户($receiver
)转移一定的$amount
。我们还想在单独的模型(TransferLog
)中保存这个转账已经发生的事实。
用法
在之前,您可能已经编写了如下代码
namespace App\Http\Controllers; use App\Http\Requests\TransferMoneyRequest; use App\Models\TransferLog; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class BankAccountController { public function transferMoney(TransferMoneyRequest $request) { return DB::transaction(function () use ($request) { $request->author->balance->decrement($request->amount); $request->receiver->balance->increment($request->amount); return TransferLog::createFromTransferRequest($request); }) } }
您必须将整个代码包裹在一个大的闭包中,显式地 use
所有注入的参数,如果您想在事务闭包的 内部 返回某些东西,您最终会得到这种双重返回,使得代码更难以阅读,并让您的IDE感到不满。
laravel-transactional-controllers
通过消除需要在闭包中包装代码的需要,并且通过在控制器方法上添加 Transactional
属性来解决此问题
namespace App\Http\Controllers; use App\Http\Requests\TransferMoneyRequest; use App\Models\TransferLog; use Illuminate\Http\Request; use NiclasVanEyk\TransactionalControllers\Transactional; // <-- from this package class BankAccountController { #[Transactional] public function transferMoney(TransferMoneyRequest $request): TransferLog { $request->author->balance->decrement($request->amount); $request->receiver->balance->increment($request->amount); return TransferLog::createFromTransferRequest($request); } }
不再需要 use
、双重 return
或您的IDE对无法保证正确返回类型的抱怨!
您还可以显式指定用于运行事务的数据库连接(默认使用 config('database.default')
)
#[Transactional(connection: 'other')] public function store() {}
限制
此功能仅在控制器中使用时有效
use NiclasVanEyk\TransactionalControllers\Transactional; // Works ✅ class RegularController { #[Transactional] public function store() {} } Route::post('/regular-store', [RegularController::class, 'store']); // Works ✅ class InvokableController { #[Transactional] public function __invoke() {} } Route::post('/invokable-store', InvokableController::class); // Does not work ❌ Route::post( '/invokable-store', #[Transactional] function () { /* Will not open a transaction! */}, )
实现细节
此包使用Laravel的 ControllerDispatcher
组件,该组件确定控制器操作应该如何执行。这意味着我们可以将打开事务推迟到最后时刻,防止 打开不必要的交易!例如,如果 FormRequest
内部的验证失败,或者使用路由模型绑定找不到模型,则不会开始事务。
更新日志
有关最近更改的更多信息,请参阅 CHANGELOG
贡献
如果您有任何修改的想法,请随意提出问题、PR或分支项目。
本地开发
这假设您已经在本地安装了sqlite、PHP以及所有composer依赖。
运行测试
composer test
运行格式化器
composer fix-cs
运行分析
composer analyse
一次性运行上述所有操作
composer ci
致谢
许可
MIT许可(MIT)。有关更多信息,请参阅 许可文件