c9s / webaction
Requires
- php: >=7
- corneltek/cascading-attribute: ^1
- corneltek/cliframework: @dev
- corneltek/codegen: @dev
- corneltek/fileutil: ^1.7
- corneltek/formkit: ^1.4
- corneltek/imagekit: ^1
- pekkis/mime-types: ^1
- phifty/locale: ^3
- pimple/pimple: ^3.0
- twig/twig: ^2
- universal/universal: 2.0.x-dev
Requires (Dev)
- corneltek/kendo: 4.0.x-dev
- corneltek/webserver-runner: dev-master
- maghead/maghead: 4.0.x-dev
- maghead/magsql: @dev
Conflicts
This package is auto-updated.
Last update: 2024-08-27 10:31:56 UTC
README
这是什么?
WebAction 是一个库,允许您从数据模式自上而下地跨控制器、页面、Ajax 请求共享业务逻辑。
为什么存在这个?
可能您需要在不同的控制器、页面、Ajax 请求中重用 CRUD 逻辑或业务逻辑,因此您可能需要坐下来编写一个共享的控制器类来共享通用代码。
这种方法对于小型应用程序可能效果不错。然而,当您的应用程序变得越来越大时,共享通用代码将非常复杂,并且难以维护。
解决方案
WebAction 提供了一种封装您的通用代码的方法,并使这些通用代码在应用程序的各个地方可重用。
您只需定义带有名称、类型、验证器、规范化和过滤器等参数,以处理表单请求、渲染表单小部件并集成到您的 ORM。
这也有助于减少、简化您的应用程序代码。
工作流程
操作就像 API(应用程序编程接口),可以从 HTTP 请求、Ajax 请求或后端触发,以下是工作流程
基本操作
最小操作骨架
use WebAction\Action; class YourAction extends Action { public function run() { } }
要使用操作,您至少应定义一个 run
方法,在此 run
方法中,您编写您的逻辑、操作,然后在最后返回结果。
要报告成功结果,您可以简单使用 success
方法
function run() { return $this->success('Success!!'); }
您还可以通过在数组中添加另一个参数来向操作结果传递数据
function run() { return $this->success('Success', ['user_id' => 1]); }
要报告错误
function run() { return $this->error('Error', ['user_id' => 1]); }
操作签名
要从前端触发操作,您可以在您的 HTML 表单中定义一个操作签名。
当提交此表单时,ActionRunner 使用此签名将您的操作调度到正确的地方。
约定规则如下
- 具有类似
App\Action\CreateUser
命名空间的类将被转换为签名App::Action::CreateUser
。
简单操作骨架
class YourAction extends \WebAction\Action { function schema() { $this->param('id') ->renderAs('HiddenInput'); $this->param('password') ->renderAs('PasswordInput'); $this->filterOut('hack','hack2','role'); } function beforeRun() { // do something } function run() { return $this->success( 'Success Helper (point to action result object)' ); return $this->error( 'Error Helper (point to action result object)' ); } function afterRun() { // do something } }
然后调用者
$act = new Action( $_REQUEST ); $act->invoke(); $rs = $a->getResult();
要执行操作,只需调用 invoke
方法来触发操作。
invoke
方法依次触发 runPreinit
、runInit
、beforeRun
、run
、afterRun
。
操作模式
run 方法
方法
您将在 run
中使用的方法
-
$this->arg(string $key)
: 通过键从操作中获取参数。 -
$this->setArgs(array $arguments)
设置操作参数。 -
$this->success(string $message, $data = array())
报告成功消息。
您还可以将数据传递到操作结果对象中。
使用此方法,操作对象创建一个操作结果对象,并使用操作签名作为键将其注册到操作结果池中。
@see WebAction\Runner
-
在 Ajax 模式下,操作结果将被转换为 JSON 格式,并且前端
Action.js
将获取操作结果数据,然后显示结果消息(或将消息分发给 jGrowl 插件) -
在 HTTP POST/GET 模式下,操作结果也将保存在操作结果池中,并且通过调用简单的 twig 宏,您可以将这些操作结果对象渲染成 HTML 格式的字符串。
@see bundles/CoreBundle/Templates/phifty/action_result.html
-
-
$this->error(string $message)
报告错误消息。
属性
您在 Action run
方法中需要的属性
-
请求:HttpRequest 对象,您可以通过简单的 API 获取 POST、GET、SESSION、SERVER。
$this->request->param('user') // is equal to isset($_REQUEST['user']) ? $_REQUEST['user'] : null $this->request->server->HTTP_HOST // is equal to isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null
操作参数方法
操作结果
执行操作后,操作在自身内部创建一个操作结果对象,您可以通过操作对象的 getResult()
方法来检索操作结果对象,以查看是否成功执行或遇到错误。
每个操作结果对象都保存在 ActionRunner 实例中。(操作结果池,是一个单例对象)
您还可以从 ActionRunner 中获取操作结果对象。
ActionResult
对象包含一个标志(成功或错误)、一个消息字符串和一个数据存储。
以下是一个简单的示例,用于检查结果错误
if( $result->success ) { } else { // error here }
从操作对象获取操作结果。
$rs = $action->getResult(); if( $rs->success ) { $msg = $rs->getMessage(); $data = $rs->getData(); }
从 ActionRunner 获取操作结果。
$runner = WebAction\Runner::getInstance(); if( $result = $runner->getResult( 'Login::...' ) ) { // check the action result }
RecordAction
Record Action 非常有用,用于将 ORM 与前端表单连接,Record Action 将参数传递给模型对象,并验证 HTTP 请求中的参数。
如果所有验证都通过,并且没有捕获到 PDOExcetion,则将生成操作结果并准备好发送到前端。
有 3 种类型的记录操作,它们映射到 CRUD 操作
- 创建
- 更新
- 删除
映射的操作类是
- CreateRecordAction
- UpdateRecordAction
- DeleteRecordAction
这 3 个记录操作类继承自 BaseRecordAction 类。
BaseRecordAction 类提供了粘合 ORM 接口方法和结果数据转换的大部分方法。
RecordAction 概述
namespace User\Action\UpdateAction; use WebAction\RecordAction\UpdateRecordAction; class UpdateAction extends UpdateRecordAction { function schema() { // For record actions, we can convert the record columns $this->useRecordSchema(); $this->param( 'username' ) ->label( _('Username') ) ->useSuggestion(); $this->param( 'password' ) ->validator(function($value) { if ($value) { return [false, "reason"]; } return [true, "success!!"]; }); $this->filterOut(array('auth_token')); } function validatePassword( $value , $args ) { return $this->valid( $message ); # or return $this->invalid( $message ); } function suggestUsername( $value , $args ) { return; # not to suggest return $this->suggest( "$value is used. use: " , array( ... ) ); } function completeCountry( $value , $args ) { ... } }
消息
BaseRecordAction
提供了默认的消息接口,要覆盖这些消息(无论是成功消息还是错误消息),您可以简单地重写方法
function successMessage(OperationResult $ret) { return _('Your Success Message'); } function errorMessage(OperationResult $ret) { return _('Your Error Message'); }
RecordAction 示例
创建新闻
namespace News\Action; use WebAction\RecordAction\CreateRecordAction; class CreateNews extends CreateRecordAction { public $recordClass = 'News\Model\News'; }
更新新闻
namespace News\Action; use WebAction\RecordAction\UpdateRecordAction; class UpdateNews extends UpdateRecordAction { public $recordClass = 'News\Model\News'; }
Record Action API
$record = new User\Model\User( 3 ); // primary key = 3 $a = new User\Action\UpdateUser(array( 'nickname' => 'New Name' ) , $record ); $a->invoke(); // which calls $record->update(array( 'nickname' => 'New Name' ) );
RecordAction 模式方法
- useRecordSchema
Record Action 生成器
CRUD 操作可以自动生成,也可以手动创建。
从模型类名生成 CreateRecordAction
$g = new WebAction\ActionGenerator; $code = $g->generateClassCode( 'App\Model\User' , 'Create' )->code;
从模型类名生成 UpdateRecordAction
$g = new WebAction\ActionGenerator; $code = $g->generateClassCode( 'App\Model\User' , 'Update' )->code;
生成自定义操作
$g = new WebAction\ActionGenerator; $g->register('template name','...template path...'); $g->generate('SortImage', 'template name', array( "base_class" => "SortRecordAction", "record_class" => "ProductBundle\\Model\\ProductImage", ... template variable... ));
或者更简短(???)
use WebAction\RecordAction\BaseRecordAction; $class = BaseRecordAction::createCRUDClass( 'App\Model\Post' , 'Create' );
或者从记录对象创建记录操作
$post = new Post; $update = $post->asUpdateAction(); $create = $post->asCreateAction(); $delete = $post->asDeleteAction();
操作小部件
操作小部件依赖于参数定义,默认的小部件类型是 TextInput。
$post = new Post; $update = $post->asUpdateAction(); $html = $update->widget('title')->render(); $html = $update->widget('title')->render( array( 'class' => '....' )); $html = $update->render('title',array( /* attributes.... */ )); $html = $update->render( null, array( /* attributes */ ) );
在操作模式中,您定义的参数可以自动生成表单小部件(使用 FormKit)。
您只需要在操作模式中为参数定义一个 renderAs
属性。
例如
class YourAction extends Action { function schema() { $this->param('name') ->renderAs('TextInput'); } }
然后,您可以通过 Action 对象获取表单小部件,您可以这样做
$action = new YourAction; $widget = $a->widget('name');
然后渲染它
$html = $widget->render(array( 'class' => 'extra-class' 'id' => 'field-id' ));
对于其他类型的小部件,如 SelectInput,您可以指定 options
$a->widget('user_type')->render(array( 'options' => array( 'Option 1' => '1' 'Option 2' => '2' 'Group Option' => array( 'Suboption 1' => '2.1' 'Suboption 2' => '2.2' ) ) ));
您也可以强制小部件方法的小部件类型,这将覆盖您之前定义的小部件类型
$a->widget('confirmed','RadioInput')->render(array( 'false', 'true' ));
操作视图
操作视图可能包含一个 formkit 布局构建器,但操作视图为您构建了一切。
要创建操作视图,您可以简单调用 createView
方法
$view = $action->createView('+AdminUI\Action\StackView'); $view->render(array( ... render options ... ));
通过内置 StackView 进行操作渲染
通过使用 WebAction StackView,您不需要编写 HTML,表单元素将自动生成。
以下是 StackView 概述
$action = new SomeWhatAction; $view = new WebAction\View\StackView($action, array( ... options ... )); $view->render();
用例
$action = new User\Action\ChangePassword; $view = new WebAction\View\StackView( $action ); echo $view->render();
您可以通过 Action 的 asView
方法渲染操作视图
echo $action->asView('WebAction\View\StackView')->render(); echo $action->asView()->render(); // implies view class WebAction\View\StackView
因此,如果您在 Twig 模板中,您可以这样做
{{ action.asView('WebAction\\View\\StackView').render()|raw}}
您还可以向 View 类传递额外的选项
echo $action->asView('WebAction\View\StackView', array( ... view options ... ))->render();
纯 HTML 元素渲染操作
您只需简单地渲染一个 HTML 表单来触发相应的操作类,在这个例子中,我们触发了通过动态操作生成器自动生成的 User\Action\UpdateUser
操作。
<form method="post"> <!-- action signature --> <input type="hidden" name="action" value="User::Action::UpdateUser"/> <!-- action fields --> <input type="text" name="account" value="c9s"/> <input type="submit"/> </form>
操作渲染和 Action.js 集成
<script>
$(function() {
Action.form( $('#profile')).setup({
validation: "msgbox",
status: true
});
});
</script>
{{ Web.render_result( update.signature ) |raw}}
{{ update.asView('WebAction\\View\\StackView',{
'form_id': 'profile'
}).render() |raw }}
</div>
逐字段渲染操作
在控制器中,您可以初始化一个操作对象
function updateAction() { $changePasswordAction = new User\Action\ChangePassword( array( ... values to override field values ... ) , $record ); return $this->render('some_path.html',array( 'changePasswordAction' => $changePasswordAction )); }
然后在模板中,您可以通过以下方法调用操作API来渲染这些字段,例如 renderSignatureWidget
,renderWidget
,renderLabel
,renderSubmitWidget
等。
<form method="post"> # This renders a field named "action" with action signature "User::Action::ChangePassword" {{ changePasswordAction.renderSignatureWidget |raw}} {% if CRUD.Record.id %} {{ forms.hidden('id', CRUD.Record.id) }} {% endif %} <h5>Change/Setup password</h5> <div class="v-field"> <div class="label">{% trans 'Password' %}</div> <div class="input"> {{ changePasswordAction.widget('password1').render() |raw }} </div> </div> <div class="v-field"> <div class="label">{% trans 'Password' %}</div> <div class="input"> {{ changePasswordAction.widget('password1').render() |raw }} </div> </div> <div class="v-field"> <div class="label">{% trans 'Password Confirm' %}</div> <div class="input"> {{ changePasswordAction.widget('password2').render() |raw }} </div> </div> <div class="button-group"> {% if CRUD.Record.id %} {{ changePasswordAction.renderSubmitWidget({ class: 'create button', value: _('Save') }) |raw }} {% else %} {{ changePasswordAction.renderSubmitWidget({ class: 'create button', value: _('Create') }) |raw }} {% endif %} {{ changePasswordAction.renderButtonWidget({ class: 'button', value: _('Close'), onclick: 'Region.of(this).fadeRemove();' }) |raw}} </div> </form>
前端操作API
您可以从前端执行操作,这更像是一个API。要发送操作以执行,您需要从操作资源中包含 action.js。
action.js提供了一个名为 runAction
的简短助手,它可以帮助您执行操作,您可以在以下形式中调用 runAction 函数
runAction( {Action Signature}, {Arguments});
runAction( {Action Signature}, {Arguments} , {Options});
runAction( {Action Signature}, {Arguments} , {Options}, {Callback} );
runAction( {Action Signature}, {Arguments} , {Callback} );
runAction( {Action Signature}, {Callback} );
runAction( {Action Signature} );
在下面的示例中,我们向后端发送 Stock::Action::DeleteTransaction
并带有一个记录ID来删除交易记录,如果操作成功,则从HTML中淡出删除元素。
<div class="txn"> <div class="txn-status txn-status-{{ txn.status }}">{{ txn.display('status') }}</div> <div class="txn-delete"> <input type="button" onclick=" runAction('Stock::Action::DeleteTransaction', { id: {{ txn.id }} }, { confirm: '確定刪除嗎? ', remove: $(this).parents('.txn') });" value="刪除"/> </div> </div>