icanboogie / operation
专注于单一任务的丰富功能的控制器
Requires
- php: >=7.2
- icanboogie/bind-routing: ^5.0
- icanboogie/errors: ^2.0
Requires (Dev)
- icanboogie/icanboogie: ^5.0
- icanboogie/module: ^5.0
- phpunit/phpunit: ^8.5
README
操作是针对单一任务的丰富功能的控制器,通常用于创建/更新/删除记录。
前言
本文件中的事件通常以 ICanBoogie\Operation::<event_type>
引用,其中 <event_type>
是事件的类型。例如,ICanBoogie\Operation::rescue
是一个类型为 rescue
的事件,在 ICanBoogie\Operation
的实例上触发。现在考虑一个从 ICanBoogie\Operation
继承的 SaveOperation
类。在它的一个实例上也可以触发 rescue
事件,可以将事件钩子附加到 SaveOperation::rescue
来恢复操作。
由于 ICanBoogie 的事件系统基于类层次结构,附加到 SaveOperation::rescue
的事件钩子仅用于恢复 SaveOperation
及其子类的实例,而附加到 ICanBoogie\Operation::rescue
的事件钩子用于恢复 ICanBoogie\Operation
及其子类(包括 SaveOperation
)的实例。
因此,当您看到 ICanBoogie\Operation::rescue
时,请理解为“在 ICanBoogie\Operation 子类 I 想要监听的一个实例上触发了 'rescue' 类型的事件”。
请阅读 icanboogie/event 包的文档,了解更多关于事件系统的信息。
操作
Operation 的实例表示一个操作。尽管该类提供了许多控制方法和获取器,但操作的有效性和处理必须由子类根据其设计实现。
控制操作
在验证和处理操作之前,会运行控制。要运行的控制由操作定义。以下实现了控制,并可扩展:
CONTROL_AUTHENTICATION
:控制用户的身份验证。CONTROL_PERMISSION
:控制用户关于操作的权利。CONTROL_RECORD
:控制与操作关联的记录。使用获取器record
来检索记录。CONTROL_OWNERSHIP
:控制用户对记录的所有权。CONTROL_FORM
:控制与操作关联的表单。使用获取器form
来检索表单。《FormNotFound》可以在找不到与操作关联的表单时抛出。《FormHasExpired》可以抛出以指示与操作关联的表单已过期。
通过 controls
魔法属性获取控制定义
<?php use ICanBoogie\Module; use ICanBoogie\Operation; class SaveOperation extends Operation { protected function get_controls() { return [ self::CONTROL_PERMISSION => Module::PERMISSION_CREATE, self::CONTROL_RECORD => true, self::CONTROL_OWNERSHIP => true, self::CONTROL_FORM => true ] + parent::get_controls(); } }
以下事件在过程中触发
-
在通过
control()
方法控制操作之前,触发类 BeforeControlEvent 的ICanBoogie\Operation::control:before
事件。第三方可以使用此事件来更改要运行的控制,或者完全清除它们。 -
在控制之后,触发类 ControlEvent 的
ICanBoogie\Operation::control
事件。第三方可以使用此事件来更改控制的输出。 -
在失败时,会触发类 FailureEvent 的事件
ICanBoogie\Operation::failure
,其中其type
属性设置为control
。 -
如果未重写
form
获取器,则会触发类 GetFormEvent 的事件ICanBoogie\Operation::get_form
。这允许第三方提供表单以检查请求的参数。
验证操作
在处理操作之前,需要对其进行验证。通过调用 validate()
方法来验证操作。应在提供的 $errors
集合中收集错误。如果方法返回空值或定义了错误,则认为验证失败。
以下事件在过程中触发
-
在验证之前,会触发类 BeforeValidateEvent 的事件
ICanBoogie\Operation::validate:before
。第三方可以使用此事件来修改错误或验证状态。 -
在验证之后,会触发类 ValidateEvent 的事件
ICanBoogie\Operation::validate
。第三方可以使用此事件来修改错误或验证结果。 -
在失败时,会触发类 FailureEvent 的事件
ICanBoogie\Operation::failure
。
处理操作
在控制和验证之后,通过调用其 process()
方法最终处理操作。如果方法返回 null
或定义了错误,则认为操作处理失败。
以下事件在过程中触发
-
在处理之前,会触发类 BeforeProcessEvent 的事件
ICanBoogie\Operation::process:before
。第三方可以使用此事件来修改请求、响应或错误。 -
在处理之后,会触发类 ProcessEvent 的事件
ICanBoogie\Operation::process
。第三方可以使用此事件来修改结果、请求或响应。
处理失败
在处理(控制/验证/处理)过程中抛出的异常将被捕获并转换为 Failure 异常。可以使用 getPrevious()
方法或 previous
属性访问原始异常。使用异常代码和消息更新操作的响应。
如果响应中有客户端或服务器错误,也会抛出 Failure 异常,在这种情况下,异常会被触发而无需先前的异常。
注意:失败的操作可以被调度器恢复。
转发操作
当使用请求参数 Operation::DESTINATION 和 Operation::NAME 定义实际目标操作名称时,操作被视为 转发。请求的 URL 对转发操作无关紧要,并且无论成功或失败,调度过程都会继续。例如,这允许将表单提交到它们的 view URL(而不是操作 URL),并在发生错误时再次显示。
注意:具有
location
的成功响应不会被丢弃,它们将重定向请求。
注意:此功能目前是 icanboogie/module 包的基础,并且实际上是由该包处理转发操作。这可能会在未来发生变化。
<?php use ICanBoogie\HTTP\Request; use ICanBoogie\Operation; $request = Request::from([ 'path' => '/', 'request_params' => [ Operation::DESTINATION => 'form', Operation::NAME => 'post', // … ] ]); $operation = new SaveOperation; $response = $operation($request); $operation->is_forwarded; // true
响应
操作响应由一个 Response 实例表示。由 process()
方法返回的值设置为其 rc
属性。如果其值为 null
,则认为操作失败,此时将操作的状态设置为“400 操作失败”。
响应位置
使用 Location
头部让浏览器加载不同的网页。这通常用于在执行操作(例如创建/删除资源)后重定向用户。响应的 location
属性用于设置该头部。
响应位置和XHR
重定向XHR不是理想的行为,因为尽管我们可能希望重定向用户,但我们仍然需要首先获取请求的结果。在这种情况下,location
属性的值将移动到 redirect_to
字段,并将 location
属性设置为 null
。因此,禁用了浏览器重定向,返回响应,并由开发者决定是否应该遵守重定向。
分发器
该包提供了一个HTTP分发器来分发操作。它应该放在分发器链的顶部,在所有路由之前。分发器尝试从指定的请求创建一个 Operation
实例,如果失败则立即返回。
操作响应的处理
分发器丢弃转发操作的响应,除非请求是XHR或响应有位置。记住,失败的操作会抛出一个 Failure 异常,这可以被捕获。
捕获失败的操作
如果在操作的调度过程中抛出异常,分发器将尝试使用以下步骤来捕获它
- 触发 RescueEvent 类的
ICanBoogie\Operation::rescue
事件。附加到此事件的钩子事件可以替换异常或提供响应。如果提供了响应,则返回。 - 否则,如果异常不是 Failure 的实例,则重新抛出异常。
- 否则,如果请求是XHR,则返回操作响应。
- 否则,如果操作被转发,则将异常消息记录为错误,并返回方法。
- 否则,重新抛出异常。
总之,如果 ICanBoogie\Operation::rescue
事件期间提供了响应,或者稍后请求是XHR,则失败的操作将被捕获。尽管操作捕获可能成功,但返回的响应可以是错误响应。
注意:如果操作被转发并且操作无法被捕获,则请求调度过程将简单地继续。
定义操作
将操作定义为路由
因为操作是控制器,所以它们可以以相同的方式定义。
以下示例演示了 module Nodes 如何定义路由来设置/取消设置 is_online
属性
<?php namespace Icybee\Modules\Nodes\Operation; use ICanBoogie\HTTP\Request; use ICanBoogie\Operation; return [ 'api:nodes/online' => [ 'pattern' => '/api/:constructor/<nid:\d+>/is_online', 'controller' => OnlineOperation::class, 'via' => Request::METHOD_PUT, 'param_translation_list' => [ 'constructor' => Operation::DESTINATION, 'nid' => Operation::KEY ] ], 'api:nodes/offline' => [ 'pattern' => '/api/:constructor/<nid:\d+>/is_online', 'controller' => OfflineOperation::class, 'via' => Request::METHOD_DELETE, 'param_translation_list' => [ 'constructor' => Operation::DESTINATION, 'nid' => Operation::KEY ] ] ];
操作的类定义为路由的控制器。注意请求方法如何用于相同的路由来区分操作类型。
param_translation_list
数组用于定义在执行操作之前,如何将从 pathinfo 中捕获的参数进行翻译。这个实用的功能允许从记录格式化路由,同时提供对操作关键特征的映射。
<?php $node = $app->models['nodes']->one; $path = $app->url_for('api:nodes/online', $node);
异常
该包中定义的异常类实现了 ICanBoogie\Operation\Exception
接口,以便于轻松识别。
<?php try { // … } catch (\ICanBoogie\Operation\Exception $e) { // an Operation exception } catch (\Throwable $e) { // some other exception }
以下异常已定义:
- Failure:当操作失败时抛出的异常。
- FormHasExpired:当与操作关联的表单已过期时抛出的异常。
- FormNotFound:当无法找到与操作关联的表单时抛出的异常。
要求
该包需要 PHP 7.2 或更高版本。
安装
composer require icanboogie/operation
文档
该包作为 [ICanBoogie][] 框架的一部分进行了文档记录 文档。您可以使用 make doc
命令生成包及其依赖项的文档。文档生成在 build/docs
目录中。需要 ApiGen。该目录可以通过 make clean
命令进行清理。
测试
运行 make test-container
创建并登录到测试容器,然后运行 make test
运行测试套件。或者,运行 make test-coverage
以带有测试覆盖率的运行测试套件。打开 build/coverage/index.html
查看代码覆盖率的分解。
许可证
icanboogie/operation 在 New BSD License 下发布。