hashwallet / hyperf-skeleton
一个专注于超高速和灵活的协程框架,特别用于构建微服务和中间件。
Requires
- php: >=7.4
- ext-bcmath: *
- ext-json: *
- alibabacloud/client: ^1.5
- easyswoole/oss: ^1.0
- hashwallet/apidocs: dev-main
- hashwallet/dto: dev-main
- hyperf-ext/jwt: ^2.2.1
- hyperf/async-queue: ~2.2.0
- hyperf/cache: ~2.2.0
- hyperf/command: ~2.2.0
- hyperf/config: ~2.2.0
- hyperf/crontab: ~2.2.0
- hyperf/database: ~2.2.0
- hyperf/db-connection: ~2.2.0
- hyperf/framework: ~2.2.0
- hyperf/guzzle: ~2.2.0
- hyperf/http-server: ~2.2.0
- hyperf/logger: ~2.2.0
- hyperf/memory: ~2.2.0
- hyperf/process: ~2.2.0
- hyperf/redis: ~2.2.0
- hyperf/signal: ~2.2.0
- lengbin/hyperf-auth: dev-master
- lengbin/hyperf-common: 3.0.x-dev
- lengbin/hyperf-error-code: 2.0.x-dev
- lengbin/php-generator: dev-main
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- hyperf/devtool: ~2.2.0
- hyperf/ide-helper: ~2.2.0
- hyperf/testing: ~2.2.0
- mockery/mockery: ^1.0
- phpstan/phpstan: ^0.12
- swoole/ide-helper: ^4.5
Suggests
- ext-json: Required to use JSON.
- ext-openssl: Required to use HTTPS.
- ext-pdo: Required to use MySQL Client.
- ext-pdo_mysql: Required to use MySQL Client.
- ext-redis: Required to use Redis Client.
README
基于hyperf 框架封装的骨架
谈谈项目分层架构
如果不想看,直接跳到下面看使用方法
回顾我的开发历史,我经历了几种项目分层
v1.0 controller -> model -> view (MVC)
那时前端是 jQuery + Bootstrap 的天下,也是后端编写前端代码的痛苦时期。拿到前端同事写好的页面后,后端需要自己填写页面数据或通过 AJAX 渲染数据,但页面往往还需要前端再次调整。
那时除了公司定义的代码规范,还会要求“胖模型瘦控制器”。
当然有很多问题,我就不一一列举了,但至少有两点可以赞扬,那就是页面缓存和 SEO。
v2.0 controller -> service -> model
前后端饱受混合编码的折磨,后来就分化出前端使用 AJAX 来对接页面数据,这样各司其职,后端就安心地提供接口。
MSC 和 MVC 类似,只是把 view 换成了 service,把业务逻辑放到服务层去实现
v2.1 controller -> serviceInterface -> serviceImpl -> model
这个变异版本的原因是项目负责人是 Java 转到 PHP 的,把 Java 的一些思想带过来,所以搞得有点不伦不类
这个版本,美其名曰“面向接口开发”。说实话,还不如 MSC 写得简单
2.2 controller -> service -> dao -> model
随着项目业务复杂度的提高,MySQL 无法满足需求,加入了 ES、MongoDB 等其他数据库类型,dao 层也是参照 Java 来的,做了一层数据驱动接口
上述两个版本仍然存在一些问题
- 在单体应用、多端服务时,在共同使用 service 层后,各种兼容性问题
- service 层各种依赖注入,蜘蛛网般复杂,还可能出现死循环
- 接口文档只有请求参数,大部分没有返回参数
当然也有人会说,你给每个端各写一个 service,不就行了,自己控制业务逻辑就可以避免出现死循环,接口文档可以手动编写。马后炮,这是我经历的痛苦
3.0 controller -> logic -> service -> dao -> model
当前这个骨架就是基于此分层,支持多服务、多数据库类型。
logic 层为每个应用的独立业务逻辑层
service 层为公共服务层,serviceInterface 用于定义 RPC
同时定义请求实体和返回实体
请求实体用于验证请求字段,同时过滤不是请求实体的属性字典
返回实体用于实现接口文档返回数据,同时自动翻译字典数据
这个版本很好,就是创建文件有点多,10 多个文件,如果不使用模板生成,会把人写崩溃
接口我也经历了几个版本
- get,post
- RESTful API 接口规范
- 万能 post
当前这个骨架就是使用万能 post,如果不喜欢或不习惯,通过代码生成后,去控制把请求方式修改一下就可以了,对吧。
如何使用
安装
composer create-project hashwallet/hyperf-skeleton:3.1.x-dev demo
当前这个骨架是 hyperf 3.0 版本,php8
代码生成器命令
php bin/hyperf.php gen:code
1,代码生成器是基于数据库表实现的,所以执行此命令之前,先确认一下数据库是否创建了表。如果想使用我二次封装的 model,在表里面需要添加至少 3 个字段
- enable 是否能使用(类似软删字段)默认为 1
- create_at 创建时间戳,默认为 0
- update_at 更新时间戳,默认为 0
2,生成器配置文件在
config/autoload/generate.php
return [ // 定义应用端 'applications' => [ 'app', 'platform', ], // 是否通过 表名(下划线分割)来生成 ddd 目录结构, 'for_table_ddd' => false, // 模块,定义模块顺序 'modules' => [], ];3,
php bin/hyperf.php gen:code --help
查看命令参数4,会自动生成 CURD 代码,同时会生成一个常量访问接口
目录结构
普通目录
DDD 目录结构
自定义错误字典合并
composer error
项目启动
composer start // 会自动启动 swagger 服务,访问http://127.0.0.1:9501/swagger, 如果不需要 可以再 config/autoload/api_docs.php 关闭
比较常用用法提示
1,枚举
在没有使用 php8.1 的时候官方自带 enum,先使用
marc-mabe/php-enum
封装的枚举类对象
<?php declare(strict_types=1); namespace Lengbin\Hyperf\Common\Constants; use Lengbin\ErrorCode\AbstractEnum; use Lengbin\ErrorCode\Annotation\EnumMessage; /** * 基础状态 * @method static BaseStatus FROZEN() * @method static BaseStatus NORMAL() */ class BaseStatus extends AbstractEnum { /** * @Message("禁用") */ #[EnumMessage("禁用")] const FROZEN = 0; /** * @Message("正常") */ #[EnumMessage("正常")] const NORMAL = 1; } // 使用 $status = BaseStatus::NORMAL(); var_dump($status->getValue()); // 获取数字值 var_dump($status->getMessage()); // 获取关联信息 // 具体用法 查看 `marc-mabe/php-enum` 库
2 BaseObject
/** * Class SystemHelpItem. */ class SystemHelpItem extends BaseObject { #[ApiModelProperty('ID')] public int $id; #[ApiModelProperty('名称')] public string $name; #[ApiModelProperty('名称'), Integer] public BaseStatus $status; } // 使用 $item = new SystemHelpItem([ 'id' => 1, 'name' => "demo", 'status' => 1 ]); var_dump($item->status->getValue()) // 1 var_dump($item->toArray()); // ['id' => 1,'name' => "demo", 'status' => 1]
3 自定义注解
use Lengbin\Common\Annotation\EnumView; use Lengbin\Common\Annotation\ArrayType; /** * Class SystemHelpItem. */ class SystemHelpItem extends BaseObject { #[EnumView] public BaseStatus $status; } // 使用 $item = new SystemHelpItem([ 'status' => 1, ]); var_dump($item->toArray()) // ["status" => ["message" => "正常", "value" => 1]] // #[ArrayType(type: 'int')] // public array $item; // 等价于 // /** // * @var int[] // */ // #[ArrayType(className: SystemHelpItem::class)] // public array $item; // 等价于 // /** // * @var SystemHelpItem[] // */
简要说明,暂时到这,后面有问题提 issues,散会