読者です 読者をやめる 読者になる 読者になる

PieceFramework Cheatseat

PHP

プラグイン設定ポイント

KernelConfigurator
キー 説明 デフォルト値
eventNameKey イベント判定用パラメータのキー _event
autoloadClasses ? array()
eventName 現在のリクエスト内でのイベント null(通常は_GET or _PORTから自動取得)
importPathInfo ? false
pluginDirectories カスタムプラグインの配置ディレクトリ array()
proxyPath プロクシのパス null
validationConfigDirectory バリデーション設定ファイルの配置ディレクトリ null
validationCacheDirectory バリデーション設定ファイルのキャッシュディレクトリ null
validationValidatorDirectories カスタムバリデータの配置ディレクトリ? array()
validationValidatorDirectories カスタムフィルターの配置ディレクトリ? array()
nonSSLableServers ? array()


 もし、Dispatcher_SimpleからPirce_Rightを使うとしたら、次のようになるのでしょうか?

<?php
class Some_EventAction {
	function invoke(&$context) {
        $config =& $context->getConfiguration();
        $right =& new Piece_Right($config->getConfiguration('KernelConfigurator',
                                                            'validationConfigDirectory'),
                                  $config->getConfiguration('KernelConfigurator',
                                                            'validationCacheDirectory'));

        if ($right->validate('SomeCondition')) {
             # OK
             $results =& $right->getResults();
             $value = $results->getFieldValue('some_field');
        }
    }
}
?>
Root
キー 説明 デフォルト値
configurator 必須設定・必須クラスの読み込みを行うクラス KernelConfigurator
outputFilter 登録された出力フィルタを実行するクラス OutputBufferStack
interceptor 登録されたインタセプタを実行するクラス InterceptorChain
controller ディスパッチャーの起動、描画を行うクラス Controller


 通常はデフォルト値で十分です。逆に言うならば、ここをいじりたくなったならば、Pieceを使うこと自体を見直した方がよいのかもしれません。
 ですが、Pieceの設定ファイルやインタセプタ、出力フィルタ、ビューの仕組みを利用したまま、内部動作をかなり変更できるので、便利なこともあります。

Interceptor_Authentication
- name: Interceptor_Authentication
  point:
      # 以下に認証対象を記載
    - name: services 
      type: configuration
      value:

          # 認証名称?
        - name: Example 

          guard:
            # 認証を行うクラス
            class: AuthenticationAction

            # 認証メソッド。認証済みならば真、未承認ならば偽を返す。
            # 偽の場合は、以下のURLにリダイレクトされる。
            method: isAuthenticated

          # ログインページのURL
          # ホスト名は置換されるのでなんでもよい。
          url: "http://localhost/login.php"

          resources:
              # 認証対象となるエントリポイントのリスト
              # 以下の例だと、
              # http://localhost/myresource.php, 
              # http://localhost/another/resource/index.php
              # にアクセスした場合に認証の対象になる。
            - /myresource.php
            - /another/resource/index.php

          # ログイン後のリダイレクト先を示すパラメタのキー名
          # 以下の設定ならば、未認証の場合に、
          # http://localhost/logi.php?next=http://localhost/myresource.php
          # にリダイレクトされる。
          useCallback: true
          callbackKey: next

      # 認証クラスを配置したディレクトリ
    - name: guardDirectory
      type: configuration
      value: ../webapp/actions
Viwe
キー 説明 デフォルト値
renderer 使用するレンダラ Renderer_PHP
forcedView ? null
Renderer_HTML
キー 説明 デフォルト値
useLayout レイアウト機能を使うか false
layoutView レイアウト・テンプレートの名称(拡張子なし) null
layoutDirectory レイアウト・テンプレートのディレクトリ null
layoutCompileDirectory レイアウト・テンプレートのコンパイル・ディレクトリ null
turnOffLayoutByHTTPAccept HTTP Acceptに基づいてレイアウト機能の有効を切り替えるか false
useFallback ビューが見つからない場合に、フォールバック用のビューを使うか false
fallbackView フォールバック・テンプレートの名称(拡張子なし) null
fallbackDirectory フォールバック・テンプレートのディレクトリ null
fallbackCompileDirectory フォールバック・テンプレートのコンパイル・ディレクトリ null
Renderer_JSON
キー 説明 デフォルト値
contentType HTTPレスポンスヘッダーのContetType text/javascript
useJSONP JSONPを使用するか false
callbackKey JSONP有効時のコールバック関数名 null
useHTMLAJAX json拡張ではなくHTML_AJAXを強制的に使用する false
include 出力に含めるViewElementのキー名 --
exclude 出力しないViewElementのキー名 array()
ハマリやすいポイント。

 XMLHttpRequest関連

 Renderer_JSON経由でJSON、あるいは、レイアウト機能を使ってx-piece-html-fragmentをHTTP POSTで取得する場合は、Content-Typeを"application/x-www-form-urlencoded", "multipart/form-data"としてXMLHttpRequestを送らないといけません。
 MochiKit 1.4の場合の例。

var data = MochiKit.DOM.formContents('myform');
MochiKit.Async.doXHR(form.action, {
    method      : 'POST',
    sendContent : MochiKit.Base.queryString(data),
    headers     : [['Content-Type','application/x-www-form-urlencoded'],
                   ['Accept', 'application/x-piece-html-fragment']]
});


 Interceptor関連。

 独自InterceptorはPiece_Unity_Plugin_Commonクラスを継承し、invokeメソッドを実装するだけで簡単に追加できますが、後に続くInterceptorを実行させるには、invokeメソッドがTrueを返さなくてはなりません


バットノウハウ

 「フォーム-確認-完了」のフローに必要なアクションクラス、設定ファイル、テンプレートを生成するためのPythonスクリプト。
 そろそろscaffoldみたいな仕組みか、初期化、保存のロジックの部分だけオーバーライドすればすむ、汎用フロー・クラスみたいなのが欲しい・・・。

# -*- coding: utf-8 -*-
import os
from os.path import join as pathjoin
from os.path import isdir, isfile

current = os.getcwd()
PIECE_DIR = [
    ['actions' ,  pathjoin(current, 'webapp', 'actions')],
    ['config_flows' , pathjoin(current, 'webapp', 'config', 'flows')],
    ['config_validations' , pathjoin(current, 'webapp', 'config', 'validations')],
    ['cache' , pathjoin(current, 'webapp', 'cache')],
    ['cache_flow' , pathjoin(current, 'webapp', 'cache', 'flows')],
    ['cache_validations' , pathjoin(current, 'webapp', 'cache', 'validations')],
    ['template' , pathjoin(current, 'webapp', 'template')],
    ['template_c' , pathjoin(current, 'webapp', 'template_c')]
]
PIECE_DIR_MAP = dict(PIECE_DIR)

ACTION = """<?php
require_once 'Piece/Flow/Action.php';

class %(flow)sAction extends Piece_Flow_Action {
    function %(flow)sAction() {
        $this->object = new stdClass();
        $this->object->id = (isset($_GET['id']) && $_GET['id'] != '') ? intval($_GET['id']) : null;
    }

    function setupForm() {
        $el =& $this->_payload->getViewElement();
        $el->setElement('object', $this->object);
    }

    function setupConfirmation() {
        $el =& $this->_payload->getViewElement();
        $el->setElement('object', $this->object);
    }

    function validate() {
        $validation =& $this->_payload->getValidation();
        if ($validation->validate('%(flow)s', $this->object)) {
            return 'ok';
        } else {
            return 'error';
        }
    }

    function save() {
        if ($this->object->id) {
            // update the object.
        } else {
            // create a new object.
        }
        return 'ok';
    }
}
?>"""

FLOW = """
firstState: DisplayForm

lastState:
  name: DisplayFinish
  view: %(flow)s_Finish
  activity:
    method: setupFinish

viewState:
  - name: DisplayForm
    view: %(flow)s_Form
    activity:
      method: setupForm
    transition:
      - event: confirm
        nextState: ProcessConfirmation
        action:
          method: validate

  - name: DisplayConfirmation
    view: %(flow)s_Confirm
    activity:
      method: setupConfirmation
    transition:
      - event: save
        nextState: ProcessSave
        action:
          method: save
      - event: back
        nextState: DisplayForm

  - name: DisplayFinish
    view: %(flow)s_Finish

actionState:
  - name: ProcessConfirmation
    transition:
      - event: ok
        nextState: DisplayConfirmation
      - event: error
        nextState: DisplayForm

  - name: ProcessSave
    transition:
      - event: ok
        nextState: DisplayFinish
      - event: error
        nextState: DisplayError
"""

FORM = """
<h3>Form</h3>
<form method="post">
<input type="hidden" value="{$__flowExecutionTicket}" name="{$__flowExecutionTicketKey}" id="flowExecutionTicket" />
<p><input type="submit" name="{$__eventNameKey}_confirm" value="Confirm" /></p>
</form>
"""

CONFIRM = """
<h3>Confirm</h3>
<form method="post">
<input type="hidden" value="{$__flowExecutionTicket}" name="{$__flowExecutionTicketKey}" id="flowExecutionTicket" />

<p>
<input type="submit" name="{$__eventNameKey}_back" value="Back" />
<input type="submit" name="{$__eventNameKey}_save" value="Save" />
</p>
</form>
"""

FINISH = """
<h3>Finish</h3>
<p>Done</p>
"""

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 2:
        sys.exit()

    flow = sys.argv[1]

    for name, d in PIECE_DIR:
        if not isdir(d):
            os.makedirs(d)

    actionfile = pathjoin(PIECE_DIR_MAP['actions'], '%sAction.php' % flow)
    if not isfile(actionfile):
        file(actionfile, 'w').write(ACTION % dict(flow=flow))

    for tpl, config in ((FLOW, 'config_flows'), ('', 'config_validations')):
        configfile = pathjoin(PIECE_DIR_MAP[config], '%s.yaml' % flow)
        if not isfile(configfile):
            yaml = file(configfile, 'w')
            yaml.write(tpl % dict(flow=flow))
            yaml.close()

    for state in ('Form', 'Confirm', 'Finish'):
        path = pathjoin(PIECE_DIR_MAP['template'], flow)
        if not isdir(path):
            os.makedirs(path)
        
        filename = pathjoin(path, '%s.tpl' % state)
        if not isfile(filename):
            result = file(filename, 'w')
            result.write(globals()[state.upper()])
            result.close()