adhenrique / laravel-domain-oriented
在 Laravel 框架上构建面向领域的应用程序
Requires (Dev)
- orchestra/testbench: ^6.7
- phpunit/phpunit: ^9.5
README
此包构建面向领域 API 的结构(不是 DDD,它们是不同的事物)。具有搜索过滤器、验证和整洁的代码。
需求
- PHP 7.2+,8.0(新版本)
- Laravel 7.x,8(推荐稳定版)
介绍
我的需求很简单:以有组织且高效的方式构建结构。支持过滤器、验证和数据缓存(CQRS)的结构。
在继续之前,先看看最终的架构
app ├── ... ├── Domain │ └── Dummy │ ├── DummyFilterService.php │ ├── DummyPersistenceModel.php │ ├── DummyPersistenceService.php │ ├── DummyPolicy.php │ ├── DummyResource.php │ ├── DummySearchModel.php │ ├── DummySearchService.php │ └── DummyValidateService.php ├── Http │ ├── Controllers │ │ ├── ... │ │ └── DummyController.php ├── ... database ├── factories │ └── ... │ └── DummyFactory.php ├── migrations │ ├── ... │ └── 2021_01_06_193044_create_dummies_table.php └── seeders ├── DatabaseSeeder.php └── DummySeeder.php
你可能想知道
- 为什么不使用仓储模式?
A. 无法获得比 Eloquent 提供的更高级的数据库抽象。1 - 持久化模型和搜索模型的想法是什么?
A. 实际上,我更进一步。Eloquent 的模型实例不应返回。因此我们保证一个“只读”实例(它不用于数据库中的持久化)2 3 - 文件很多,我如何构建所有这些?
A. 很简单,来杯咖啡,我们开始吧...
设置
- 运行以下 Composer 命令以安装最新版本
$ composer require adhenrique/laravel-domain-oriented
- 如果您愿意,可以导出位置文件
php artisan vendor:publish --provider="LaravelDomainOriented\ServiceProvider" --tag="lang"
- 运行此命令以构建领域结构
$ php artisan domain:create Dummy
- 请保持冷静。如果结构已存在,控制台会询问您是否要重写它,除非您传递
--force
标志
$ php artisan domain:create Dummy --force
- 当然,如果您想删除结构,只需运行此命令即可
$ php artisan domain:remove Dummy
就这样,尽情享受吧!
配置
调整您的模型
我们的模型遵循Eloquent 模型约定
- 持久化模型:仅用于数据库中的持久化。定义您的字段、类型转换等...
- 搜索模型:用于搜索。您的关系很可能在这里。
调整您的迁移
我们的迁移遵循Laravel 迁移结构
调整您的种子文件和工厂文件
在这里,我们也遵循 Laravel 的做法
调整您的策略
再次,策略遵循Laravel 策略授权
注意:您不必担心注册策略,因为我们已经在幕后完成了。但是,在这里我们遵循类名约定。当创建领域时,您的类必须命名为 SomethingPolicy 并属于 App\Domain\Something 命名空间。
配置验证
ValidateService 位于 app/Domain/YourDomainName/*
use LaravelDomainOriented\Services\ValidateService; class DummyValidateService extends ValidateService { protected array $rules = [ // You can define general validation rules, which will be inherited // for all actions, or you can define validation rules for each action: // SHOW, STORE, UPDATE, DESTROY // General rules validation. // If any action validation rule is not defined, it will inherit from here. 'name' => 'required|string', // Specific action rules validation. If set, ignores general validations. self::SHOW => [ 'id' => 'required|integer', ], self::UPDATE => [ 'id' => 'required|integer', 'name' => 'required|string', ], self::DESTROY => [ 'id' => 'required|integer', ], ]; }
配置路由
我们遵循Laravel 路由模式。但由于我们处理的是 API,请修改 routes/api.php
文件,并添加以下路由
Route::get('dummies', 'App\Http\Controllers\DummyController@index'); Route::get('dummies/{id}', 'App\Http\Controllers\DummyController@show'); Route::post('dummies', 'App\Http\Controllers\DummyController@store'); Route::put('dummies/{id}', 'App\Http\Controllers\DummyController@update'); Route::delete('dummies/{id}', 'App\Http\Controllers\DummyController@destroy');
使用方法
在搜索过滤器之前
在SearchService类中,您有两个方法可以帮助您根据需求预先启动查询: beforeAll
和 beforeFindById
。每个方法接收2个参数:具有启动的Eloquent实例的builder
,以及包含用户会话的auth
(如果已登录)。您只需重写这些方法,但请确保返回Eloquent的Builder
。看看
class DummySearchService extends SearchService { protected SearchModel $model; protected FilterService $filterService; public function __construct(DummySearchModel $model, DummyFilterService $filterService) { $this->model = $model; $this->filterService = $filterService; } public function beforeAll(Builder $builder, Guard $auth): Builder { return $builder; } public function beforeFindById(Builder $builder, Guard $auth): Builder { return $builder; } }
在我的使用场景中,以管理员身份登录,我通常从用户列表中筛选出我自己的用户。
// ... public function beforeAll(Builder $builder, Guard $auth): Builder { return $this->removeLoggedFromSearches($builder, $auth); } private function removeLoggedFromSearches($builder, $auth) { $id = $auth->id(); return $builder->where('id', '<>', $id); }
使用筛选进行搜索
您可以在列表路由上筛选和分页数据。为此,请使用您喜欢的客户端在请求中发送负载。
简单的Where条件
{ "name": "adhenrique", "email": "eu@adhenrique.com.br" }
Where in条件
{ "id": [1,2,3] }
Where条件运算符(如,>,>=,<,<=,<>)
{ "name": { "operator": "like", "value": "%adhenrique%" } }
Where between条件
{ "birthdate": { "start": "1988-13-12", "end": "2021-01-01" } }
分页结果
{ "paginate": { "per_page": 1, "page": 1 } }
注意:您可以将筛选和分页一起使用。
待办事项
- CQRS
- 对旧Laravel版本的支持
- 或Where筛选
- 面向对象改进
- 添加beforeAll和beforeFindById测试
- 要求确认名称
- 添加测试策略的方法
测试
$ composer test
变更日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全性
如果您发现任何安全相关的问题,请通过电子邮件eu@adhenrique.com.br而不是使用问题跟踪器。
鸣谢
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。
阅读文章
[1] 请,停止讨论Eloquent的Repository模式
[2] 有用的Eloquent Repositories?
[3] 您理解Repository模式吗?您确定是这样吗?
Laravel — 为什么您一直在错误地使用Repository模式