c9s/webaction

dev-master / 4.0.x-dev 2022-05-27 05:53 UTC

This package is auto-updated.

Last update: 2024-08-27 10:31:56 UTC


README

Coverage Status Build Status Latest Stable Version Total Downloads Latest Unstable Version License

这是什么?

WebAction 是一个库,允许您从数据模式自上而下地跨控制器、页面、Ajax 请求共享业务逻辑。

为什么存在这个?

可能您需要在不同的控制器、页面、Ajax 请求中重用 CRUD 逻辑或业务逻辑,因此您可能需要坐下来编写一个共享的控制器类来共享通用代码。

这种方法对于小型应用程序可能效果不错。然而,当您的应用程序变得越来越大时,共享通用代码将非常复杂,并且难以维护。

解决方案

WebAction 提供了一种封装您的通用代码的方法,并使这些通用代码在应用程序的各个地方可重用。

您只需定义带有名称、类型、验证器、规范化和过滤器等参数,以处理表单请求、渲染表单小部件并集成到您的 ORM。

这也有助于减少、简化您的应用程序代码。

工作流程

操作就像 API(应用程序编程接口),可以从 HTTP 请求、Ajax 请求或后端触发,以下是工作流程

WebAction - PHP

基本操作

最小操作骨架

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 方法依次触发 runPreinitrunInitbeforeRunrunafterRun

操作模式

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 操作

  1. 创建
  2. 更新
  3. 删除

映射的操作类是

  1. CreateRecordAction
  2. UpdateRecordAction
  3. 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来渲染这些字段,例如 renderSignatureWidgetrenderWidgetrenderLabelrenderSubmitWidget 等。

<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>