shafimsp/laravel-actions
Composer 安装命令:
composer require shafimsp/laravel-actions
包简介
An action executor with middleware pipeline for Laravel applications.
README 文档
README
A lightweight action executor with middleware pipeline for Laravel applications. Implements the CQRS pattern with convention-based handler resolution, return type validation, caching, and first-class testing support.
Requirements
- PHP 8.1+
- Laravel 10.x, 11.x, or 12.x
Installation
composer require shafimsp/laravel-actions
The package auto-registers its service provider. Optionally publish the config:
php artisan vendor:publish --tag=actions-config
Quick Start
1. Define an Action
Actions are simple classes that implement the Action interface and declare their return type via a docblock:
use ShafiMsp\Actions\Contracts\Action; /** @implements Action<User> */ final class FindUserById implements Action { public function __construct( public readonly int $id, ) {} }
2. Create a Handler
By convention, the handler class is the action class name suffixed with Handler in the same namespace:
final class FindUserByIdHandler { public function handle(FindUserById $action): User { return User::findOrFail($action->id); } }
3. Execute the Action
// Using the global helper $user = execute(new FindUserById(1)); // Using the facade use ShafiMsp\Actions\Facades\ActionExecutor; $user = ActionExecutor::execute(new FindUserById(1));
Defining Actions
Declare the return type using the @implements docblock:
/** @implements Action<string> */ final class GetGreeting implements Action {} /** @implements Action<User|null> */ final class FindOptionalUser implements Action {} /** @implements Action<void> */ final class SendEmail implements Action {} /** @implements Action<ResultA|ResultB|null> */ final class GetFlexibleResult implements Action {}
Supported return types: scalars (string, int, float, bool, array), objects, nullable types, union types, void, and mixed.
The executor validates the handler's return value against the declared type at runtime and throws a RuntimeException on mismatch.
Handler Resolution
Convention-Based (Default)
Append Handler to the action class name in the same namespace:
| Action | Handler |
|---|---|
App\Actions\CreateUser |
App\Actions\CreateUserHandler |
App\Queries\FindUser |
App\Queries\FindUserHandler |
Attribute-Based
Use the #[HandledBy] attribute to specify a custom handler:
use ShafiMsp\Actions\Attributes\HandledBy; #[HandledBy(CustomHandler::class)] final class CreateUser implements Action {}
Middleware
Middleware wraps the action execution pipeline, enabling cross-cutting concerns like logging, authorization, or caching.
Class-Based Middleware
use ShafiMsp\Actions\Contracts\Action; use ShafiMsp\Actions\Contracts\Middleware; final class LoggingMiddleware implements Middleware { public function handle(Action $action, Closure $next): mixed { Log::info('Executing: ' . $action::class); $result = $next($action); Log::info('Completed: ' . $action::class); return $result; } }
Closure-Based Middleware
use ShafiMsp\Actions\Facades\ActionExecutor; ActionExecutor::pushMiddleware(function (Action $action, Closure $next) { // before $result = $next($action); // after return $result; });
Global Middleware
Register middleware for every action in config/actions.php:
return [ 'middleware' => [ \ShafiMsp\Actions\Middleware\CacheMiddleware::class, \App\Actions\Middleware\LoggingMiddleware::class, ], ];
Middleware can short-circuit the pipeline by returning early without calling $next(), or modify the action before passing it along.
Caching
Cache action results automatically using the #[Cacheable] attribute:
use ShafiMsp\Actions\Attributes\Cacheable; #[Cacheable(ttl: 3600)] final class GetDashboardStats implements Action {} #[Cacheable(ttl: 600, key: 'all_users')] final class ListAllUsers implements Action {}
Parameters:
ttl— Time to live in seconds (default:3600)key— Custom cache key (optional, auto-generated from action class and properties if omitted)
The CacheMiddleware is included in the default middleware stack. It uses cache tags when available and a distributed lock to prevent cache stampede.
Bootstrap Cache
For production, generate a bootstrap cache to skip reflection on every request:
php artisan actions:cache
This discovers all actions, resolves their handlers and return types, and writes a cache file to bootstrap/cache/actions.php.
To clear:
php artisan actions:clear
Configuration
// config/actions.php return [ // Global middleware applied to every action execution 'middleware' => [ \ShafiMsp\Actions\Middleware\CacheMiddleware::class, ], // Bootstrap cache settings 'cache' => [ 'enabled' => true, 'directories' => [app_path()], 'path' => null, // defaults to bootstrap/cache/actions.php ], ];
Testing
The package provides a FakeExecutor with a fluent assertion API.
Faking the Executor
use ShafiMsp\Actions\Facades\ActionExecutor; // Fake all actions (returns null by default) ActionExecutor::fake(); // Fake with specific return values ActionExecutor::fake([ FindUserById::class => User::factory()->create(), GetGreeting::class => 'Hello!', ]); // Fake with closures for dynamic values ActionExecutor::fake([ FindUserById::class => fn (FindUserById $action) => User::find($action->id), ]);
Assertions
// Assert an action was executed ActionExecutor::assertExecuted(FindUserById::class); // Assert with a truth test ActionExecutor::assertExecuted( FindUserById::class, fn (FindUserById $action) => $action->id === 1 ); // Assert executed exactly N times ActionExecutor::assertExecutedTimes(FindUserById::class, 2); // Assert an action was NOT executed ActionExecutor::assertNotExecuted(DeleteUser::class); // Assert nothing was executed at all ActionExecutor::assertNothingExecuted();
Full Test Example
public function test_show_returns_user(): void { $user = User::factory()->create(); ActionExecutor::fake([ FindUserById::class => $user, ]); $response = $this->get("/users/{$user->id}"); $response->assertOk(); ActionExecutor::assertExecuted( FindUserById::class, fn (FindUserById $action) => $action->id === $user->id ); }
License
MIT
shafimsp/laravel-actions 适用场景与选型建议
shafimsp/laravel-actions 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 25 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 shafimsp/laravel-actions 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 shafimsp/laravel-actions 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 2
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 20
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-25