feser / request-objects
为 Symfony 定制的请求对象,旨在让生活更加轻松
Requires
- php: >=7.1.0
- symfony/http-foundation: ^3.3|^4.0
- symfony/validator: ^3.3|^4.0
Requires (Dev)
- phpunit/phpunit: ^5.3
- symfony/framework-bundle: ^3.3|^4.0
- symfony/var-dumper: ^4.0
This package is not auto-updated.
Last update: 2024-09-14 18:58:45 UTC
README
注意:此库在 1.0 版本发布之前不应被视为生产就绪。请提供您的反馈以使其成为现实!
为什么?
Symfony 表单组件是一个非常强大的处理表单的工具。但现在情况已经改变。复杂表单大多在客户端处理。对于简单表单,symfony/forms 有很大的开销。
在某些情况下,你可能根本不需要表单。例如,如果你正在开发 HTTP API,你可能只需要与请求数据交互。那么为什么不将请求数据封装在用户定义的对象中,并仅对其进行验证呢?这也鼓励了关注点的分离,并在 API 版本化方面帮助你。
使用方法
首先,我们需要通过 composer 安装此包
composer require fesor/request-objects
并注册捆绑包
public function registerBundles()
{
$bundles = [
// ...
new \Fesor\RequestObject\Bundle\RequestObjectBundle(),
];
}
捆绑包不需要任何额外的配置,但你也可以在捆绑包配置中指定错误响应提供者服务。我们将在“处理验证错误”部分回到这一点。
定义你的请求对象
所有用户定义的请求都应该扩展 Fesor\RequestObject\RequestObject
。让我们为用户注册操作创建一个简单的请求对象
use Fesor\RequestObject\RequestObject; use Symfony\Component\Validator\Constraints as Assert; class RegisterUserRequest extends RequestObject { public function rules() { return new Assert\Collection([ 'email' => new Assert\Email(['message' => 'Please fill in valid email']), 'password' => new Assert\Length(['min' => 4, 'minMessage' => 'Password is to short']), 'first_name' => new Assert\NotNull(['message' => 'Please provide your first name']), 'last_name' => new Assert\NotNull(['message' => 'Please provide your last name']) ]); } }
然后我们可以在我们的操作中使用它
public function registerUserAction(RegisterUserRequest $request) { // Do Stuff! Data is already validated! }
此捆绑包将验证后的请求对象绑定到 $request
参数。请求对象具有非常简单的数据交互接口。它与 Symfony 的请求对象非常相似,但默认情况下被认为是不可变的(尽管你可以添加一些设置器,如果需要的话)
// returns value from payload by specific key or default value if provided $request->get('key', 'default value'); // returns whole payload $request->all();
有效负载从哪里来?
此库实现了 PayloadResolver
接口的默认实现,其行为如下
-
如果请求可以有体(即它是 POST、PUT、PATCH 或任何带有体的请求),则它使用
$request->request->all()
和$request->files->all()
数组的并集作为有效负载。 -
如果请求不能有体(即 GET、HEAD 请求),则它使用
$request->query->all()
。
如果您希望对有效负载提取应用自定义逻辑,您可以在请求对象中实现 PayloadResolver
接口。
class CustomizedPayloadRequest extends RequestObject implements PayloadResolver { public function resolvePayload(Request $request) { $query = $request->query->all(); // turn string to array of relations if (isset($query['includes'])) { $query['includes'] = explode(',', $query['includes']); } return $query; } }
这将允许您对请求进行一些疯狂的操作,并大量重复使用代码。
验证有效负载
如前例所示,rules
方法应返回用于 symfony 验证器的验证规则。您的请求有效负载将与此进行验证,并在您的操作中获取有效数据。
如果您有一些依赖于有效负载数据的验证规则,则可以通过验证组来处理它。
请注意:由于 Collection
约束验证器的限制,使用组并不那么方便。因此,在具有对有效负载数据依赖的复杂情况下,建议使用 Callback
验证器。有关问题的详细信息,请参阅 示例。
您可以通过实现 validationGroup
方法提供验证组
public function validationGroup(array $payload) { return isset($payload['context']) ? ['Default', $payload['context']] : null; }
处理验证错误
如果验证数据无效,库将抛出异常,其中包含验证错误和请求对象。
但如果你不想通过 kernel.exception
监听器来处理它,你有几个选项。
首先,使用您的控制器操作来处理错误
public function registerUserAction(RegisterUserRequest $request, ConstraintViolationList $errors) { if (0 !== count($errors)) { // handle errors } }
但这并不方便,如果你只需要返回常见的错误响应,它还会破坏DRY原则。这就是为什么库为你提供了ErrorResponseProvider
接口。你可以在你的请求对象中实现它,并将此代码移动到getErrorResponse
方法。
public function getErrorResponse(ConstraintViolationListInterface $errors) { return new JsonResponse([ 'message' => 'Please check your data', 'errors' => array_map(function (ConstraintViolation $violation) { return [ 'path' => $violation->getPropertyPath(), 'message' => $violation->getMessage() ]; }, iterator_to_array($errors)) ], 400); }
更多示例
如果你仍然不确定它对你是否有用,请查看examples
目录以获取更多用例。没有找到你的情况?那么请在issues中分享你的用例!
贡献
请随时提供反馈、功能请求或提交issue。PR(Pull Request,拉取请求)欢迎!