truecrouton / retort
简单的 PHP 路由定义和请求验证
Requires
- php: >=8.0
- adhocore/cli: ^1.6
- mustache/mustache: ^2.14
- yosymfony/toml: ^1.0
Requires (Dev)
- phpunit/phpunit: ^10.3
This package is not auto-updated.
Last update: 2024-09-26 21:54:31 UTC
README
使用 PHP 8 中引入的属性轻松定义 PHP 路由和验证请求!删除了大量的路由样板代码,请求验证非常简单!
只需将 Route
属性添加到控制器类的方法中,以定义路由方法。这消除了重复的路由定义文件的需要。
#[Route('GET', '/thing/{id:\d+}')]
public function thingGet(ThingGetRequest $request): array {}
为了验证请求,将 Validation
属性添加到请求类的属性中,并使用 createObject()
验证和创建请求对象。确保路由方法具有所需请求参数。
use Retort\Validation\ValidNumber;
use Retort\Validation\ValidString;
class ThingGetRequest extends RetortRequest
{
#[ValidNumber(true, 1)] // $id will be a number >= 1 and is required (true)
public int $id;
}
$request = Validation::createObject(ThingGetRequest::class, ['id' => 0]); // throws Error
$request = Validation::createObject(ThingGetRequest::class, ['id' => 1]); // creates a ThingGetRequest object with id = 1
使用 Validation
属性属性,可以从 RetortRequest
对象自动生成用于前端使用的 TypeScript 或其他类型!(见下文)
定义和调用路由方法
使用 Route 属性在控制器类内定义路由方法。
use Retort\Mapping\Attributes\Route;
#[Route('GET', '/thing/{id:\d+}')]
public function thingGet(Id $request): array
{
// return a "thing"
return ['name' => 'A thing'];
}
使用 getRoutes()
辅助函数获取每个控制器类的路由。
use Retort\Mapping\Helper;
$routes = Helper::getRoutes(YourController::class, AnotherController::class);
在路由中使用路由信息。这里是一个使用 FastRoute 的示例,但它应该与许多不同的路由器一起工作。
$dispatcher = FastRoute\simpleDispatcher(function (FastRoute\RouteCollector $r) {
$routes = Helper::getRoutes(YourController::class);
foreach ($routes as $route) {
$r->addRoute($route['requestMethod'], $route['uri'], [
'class' => $route['class'],
'classMethod' => $route['classMethod'],
'requestType' => $route['requestType']
]);
}
});
使用路由信息调用路由方法。
// Validate the request (see below)
// $route['requestType'] specifies the request class
$request = Validation::createObject($route['requestType'], $payload);
$class = new $route['class'](); // $route['class'] specifies the controller class
$method = $route['classMethod']; // $route['classMethod] specifies the class method
header('Content-Type: application/json; charset=utf-8');
print json_encode($class->$method($request)); // call the class method
验证请求
使用 Validation::createObject()
验证请求。Validation::createObject()
将创建指定的对象,如果验证失败则抛出 Error
。
class ThingGetRequest extends RetortRequest
{
#[ValidNumber(true, 1)] // $id will be a number >= 1 and is required (true)
public int $id;
}
$request = Validation::createObject(ThingGetRequest::class, ['id' => 0]); // throws Error
$request = Validation::createObject(ThingGetRequest::class, ['id' => 1]); // creates a ThingGetRequest object with id = 1
ValidString
、ValidNumber
和 ValidObject
提供了 Validation
对象。检查它们的 类定义 以获取验证选项,例如必需、最小值、最大值等。
ValidObject
可用于嵌套对象和对象数组。
class GoingConcern extends RetortRequest
{
#[ValidObject(true, Address::class)]
public Address $address;
#[ValidObject(true, Employees::class)]
public array $employees; // use array type
}
参数可以是可选的。
#[ValidString(false, 1)] // $nickname will be a string of length >= 1 and is optional (false)
public ?string $nickname;
依赖注入
提供了一些 DependencyController
作为示例。例如,MysqlController
构造函数接受一个 mysqli
对象 $myDb
,然后可以在控制器中使用它,例如通过包含的 executeQuery()
方法。
use Retort\Controller\MysqlController;
class ThingController extends MysqlController
{
#[Route('GET', '/thing/post')]
public function thingPost(ThingPostRequest $request): array
{
$this->executeQuery('insert into things (name) values(?)', [$request->name]);
return ['thingId' => $this->myDb->insert_id];
}
}
$db = new mysqli('localhost', 'user', 'super_secure_password', 'database');
$class = new ThingController($db); // inject $db
生成类型
运行 vendor/bin/retort_typegen -c <file>
与您的配置文件一起生成类型。配置文件是一个 TOML 文件,其中定义了配置选项和类型定义模板。有关详细信息,请参阅示例 配置。模板以 mustache 格式定义。
提供要生成的类。类必须自动加载,并且是 RetortRequest
的实例。
# Generate type definitions for these PHP classes
classes = [
"Retort\\Test\\Helper\\Jacket"
]
设置模板,在这种情况下是一个 TypeScript 类模板。
# Sample template for Typescript type generation in mustache format
template = '''
interface {{class}} {
{{#definitions}}
{{name}}{{#nullable}}?{{/nullable}}: {{type}}{{#iterable}}[]{{/iterable}};
{{/definitions}}
}
'''
可以定义不同语言的不同模板。例如,这里是一个 flutter 类模板。
class {{class}} {
{{#definitions}}
final {{type}}{{#nullable}}?{{/nullable}} {{name}};
{{/definitions}}
const {{class}}({
{{#definitions}}
{{^nullable}}required {{/nullable}}this.{{name}},
{{/definitions}}
});
factory {{class}}.fromJson(Map<String, dynamic> json) {
return {{class}}(
{{#definitions}}
{{name}}: json['{{name}}'],
{{/definitions}}
);
}
factory {{class}}.fromObject(Map<String, dynamic> json) {
return {{class}}(
{{#definitions}}
{{name}}: json['{{name}}'],
{{/definitions}}
);
}
Map<String, dynamic> toJson() {
return {
{{#definitions}}
'{{name}}': {{name}},
{{/definitions}}
};
}
}
可以在 typeMap
下将 PHP 类型映射到其他语言。
# Type mappings, e.g., int (php) to number (ts)
[typeMap]
int = "number"