一覧画面に「アクション」を追加するプラグインの作成

一覧画面で選択したオブジェクトに対して一括で何らかの処理を行う機能のことを「アクション」といいます。

一覧画面で「アクション...」というドロップダウンメニューで選択して実行するものです(「モデル」「フィールド」など、アクションの存在しないモデルもあります)。この、アクションをプラグインによって追加することができます。

作成するプラグインの概要

一覧画面からURLマップに対して、個別のモデルごとに再構築トリガーを一括設定(追加・削除)できるようにします。

作成したプラグインを追加した一覧画面のスクリーンショット

  • トリガーの設定を行いたいURLマップをチェックして選択する
  • 選択すると入力欄が表示され、カンマ区切りでモデル名を入力する(例 : page,folder )
  • 「Go」ボタンをクリックすると、処理が実行され、一覧画面に戻る

PluginStarterでのひな形の作成

PluginStarterでプラグインひな形を作成する 以下の内容を入力してプラグインひな形を作成します。

プラグインひな形の作成画面のスクリーンショット

項目名 内容
名前 BatchTriggerControl
バージョン 1.0
作成者 Alfasado Inc.
作成者URL https://alfasado.net/
説明 Collectively set URL Map's rebuild triggers from the Listing Screen.
説明(日本語) 一覧画面からURLマップの再構築トリガーを一括設定します。

続いて「アクション」の左にある 追加 ボタンをクリックしてアクションを2つ追加します。

追加したアクションの入力欄

キー モデル ラベル ラベル (日本語) 入力 表示順
remove_trigger URLマップ Delete Model's Triggers モデルのトリガー削除 指定あり 100
add_trigger URLマップ Add Model's Triggers モデルのトリガー追加 指定あり 101

プラグインひな形の入力項目は以上です。保存したら、エクスポートして、プラグインの ZIP ファイルをダウンロードしてください。

plugins/
 └ BatchTriggerControl/ (root)
   ├ config.json (定義ファイル)
   ├ BatchTriggerControl.php (プラグイン・クラス)
   ├ locale/
   │  └ ja.json (翻訳ファイル)
   └ docs/
      └ README.ja.md (ドキュメント)

プラグインの設置と動作の確認

ファイルを解凍したらプラグインディレクトリに設置します。開発中のものについては別途環境変数で「plugin_paths」を指定して、別のディレクトリに設置することをお勧めします。

何もコードを書かない状態でも、URLマップの一覧画面にアクションが2つ追加され、選択すると入力欄が出て、実行すると一覧画面に戻ってくることが確認できると思います(この段階では、オブジェクトに対する何の処理も行いません)。

プラグインへのコードの追加

ドロップダウンでの項目選択時のヒントの表示

まずはじめに、ドロップダウン選択時に表示される入力フィールドに何を入れたら良いのかがわかりませんので、メッセージを表示してから入力欄を出すようにします。

JavaScriptによってアラートを表示

config.json の各アクションのところに「"hint": "Enter the model names separated by commas.",」を追加して保存します。

    "list_actions": {
        "action_urlmapping_remove_trigger": {
            "urlmapping": {
                "action_urlmapping_remove_trigger": {
                    "name": "action_urlmapping_remove_trigger",
                    "label": "Delete Model's Triggers",
                    "hint": "Enter the model names separated by commas.",

これで、ドロップダウンを選択した際に JavaScriptによる alert でメッセージが表示され、その後入力が表示されてフォーカスがあたるようになりました。

ヒントのローカライズCSVの追加

このままでは表示されるメッセージが英語のままです。ユーザーの選択言語が日本語のときは日本語で表示させたいというときは、localeディレクトリに ja.csv を追加します。CSVを作成して保存したら、このフレーズをシステムに認識させるために、BatchTriggerControlプラグインを一旦無効化して、再度有効化してください。システムの「システムオブジェクト」「フレーズ」にこのレコードが追加され、メッセージが日本語化されます。

Enter the model names separated by commas.,モデル名をカンマ区切りで入力してください。

処理部分のコード解説

続いて、BatchTriggerControl.php 内に実行させたい処理を追加します。「action_urlmapping_remove_trigger」「action_urlmapping_add_trigger」の2つのメソッドが追加されています。概要をコメントに追加しました。

    function action_urlmapping_remove_trigger ( $app, $objects, $action ) {
        // $app は クラスPrototype
        // $objects は選択されたオブジェクト
        // $actionは config.jsonに指定したアクション設定の配列

        $class = new PTListActions(); // class.PTActions.php クラス
        $input = $app->param( 'itemset_action_input' ); // 入力された値 ( 例 : page,folder )
        $model = $app->param( '_model' ); // モデル(この場合は urlmapping )
        $counter = 0;
        foreach ( $objects as $obj ) {
            // Some action to $obj. // ここにオブジェクトに対する処理を書く
            $counter++;
        }
        if ( $counter ) {
            $action = $action['label'];
            $class->log( $action, $model, $counter ); // ログを保存する
        }
        $return_args = "does_act=1&__mode=view&_type=list&_model={$model}"
                     . "&apply_actions={$counter}" . $app->workspace_param;
        if ( $add_params = $class->add_return_params( $app ) ) {
            $return_args .= "&{$add_params}";
        }
        $app->redirect( $app->admin_url . '?' . $return_args );  // 元の画面にパラメタを追加してリダイレクト
    }
  1. 第一引数 $app は クラスPrototypeです。
  2. $objects は選択されたオブジェクトの配列です。操作可能な権限はあらかじめチェックされていて、操作可能なもののみが渡されます。
  3. $actionは config.jsonに指定したアクション設定の配列です。

実際のオブジェクトに対する処理は、foreach ( $objects as $obj ) { のループの中に記述します。

メソッド action_urlmapping_remove_trigger

    function action_urlmapping_remove_trigger ( $app, $objects, $action ) {
        $class = new PTListActions();
        $input = $app->param( 'itemset_action_input' );
        $triggers = preg_split( '/\s*,\s*/', $input ); // カンマ区切りで渡されたモデルを配列化
        $model = $app->param( '_model' );
        $counter = 0;
        foreach ( $objects as $obj ) {
            $done = false;
            foreach ( $triggers as $trigger ) {
                $table = $app->get_table( $trigger ); // page
                if (! $table ) { // 渡されたモデルがない
                    continue;
                }
                // 再構築トリガーは、mt_relation テーブルに relation_name='triggers' で保存されている
                // mt_relationは、 from_obj, from_id, to_obj, to_id で何から何に対するリレーションかを idで関連付けているレコード
                // SELECT * FROM mt_relation WHERE relation_name='triggers' AND relation_from_obj='urlmapping' AND 
                // relation_from_id=URLマップのID AND relation_to_obj='table' AND relation_to_id=モデル(mt_table)のID
                $relation = $app->db->model( 'relation' )->get_by_key(
                    ['name' => 'triggers',
                     'from_obj' => 'urlmapping',
                     'from_id' => $obj->id,
                     'to_obj' => 'table',
                     'to_id' => $table->id ]
                );
                if ( $relation->id ) {
                    // get_by_key は存在しない場合は新規オブジェクトを生成するので
                    // idに値があるかで存在チェック
                    $relation->remove();
                    $done = true;
                }
            }
            if ( $done ) {
                $counter++;
            }
        }
        if ( $counter ) {
            $action = $action['label'];
            $class->log( $action, $model, $counter );
        }
        $return_args = "does_act=1&__mode=view&_type=list&_model={$model}"
                     . "&apply_actions={$counter}" . $app->workspace_param;
        if ( $add_params = $class->add_return_params( $app ) ) {
            $return_args .= "&{$add_params}";
        }
        $app->redirect( $app->admin_url . '?' . $return_args );
    }

メソッド action_urlmapping_add_trigger

    function action_urlmapping_add_trigger ( $app, $objects, $action ) {
        $class = new PTListActions();
        $input = $app->param( 'itemset_action_input' );
        $triggers = preg_split( '/\s*,\s*/', $input ); // カンマ区切りで渡されたモデルを配列化
        $model = $app->param( '_model' );
        $counter = 0;
        foreach ( $objects as $obj ) {
            $done = false;
            foreach ( $triggers as $trigger ) {
                $table = $app->get_table( $trigger ); // page
                if (! $table ) { // 渡されたモデルがない
                    continue;
                }
                // 再構築トリガーは、mt_relation テーブルに relation_name='triggers' で保存されている
                // mt_relationは、 from_obj, from_id, to_obj, to_id で何から何に対するリレーションかを idで関連付けているレコード
                // SELECT * FROM mt_relation WHERE relation_name='triggers' AND relation_from_obj='urlmapping' AND 
                // relation_from_id=URLマップのID AND relation_to_obj='table' AND relation_to_id=モデル(mt_table)のID
                $relation = $app->db->model( 'relation' )->get_by_key(
                    ['name' => 'triggers',
                     'from_obj' => 'urlmapping',
                     'from_id' => $obj->id,
                     'to_obj' => 'table',
                     'to_id' => $table->id ]
                );
                // $relation->idに値があれば既にトリガーの設定があるので、何もしない
                // 存在しないときは指定したキー・バリューで新規オブジェクトを生成(この段階では未保存)
                if (! $relation->id ) {
                    // relation_order に最大値+1をしていして保存するためにorderの一番値の大きなオブジェクトを取得
                    // SELECT * FROM mt_relation WHERE relation_name='triggers' AND relation_from_obj='urlmapping' AND 
                    // relation_from_id=URLマップのID AND relation_to_obj='table' ORDER BY relation_order DESC LIMIT 1;
                    $last = $app->db->model( 'relation' )->get_by_key(
                        ['name' => 'triggers',
                         'from_obj' => 'urlmapping',
                         'from_id' => $obj->id,
                         'to_obj' => 'table'],
                        ['sort_by' => 'order',
                         'direction' => 'descend'] );
                    $order = 0;
                    if ( $last->id ) {
                        $order = $last->order;
                    }
                    $order++;
                    $relation->order( $order );
                    $relation->save(); // 新規保存
                    $done = true;
                }
            }
            if ( $done ) {
                $counter++;
            }
        }
        if ( $counter ) {
            $action = $action['label'];
            $class->log( $action, $model, $counter );
        }
        $return_args = "does_act=1&__mode=view&_type=list&_model={$model}"
                     . "&apply_actions={$counter}" . $app->workspace_param;
        if ( $add_params = $class->add_return_params( $app ) ) {
            $return_args .= "&{$add_params}";
        }
        $app->redirect( $app->admin_url . '?' . $return_args );
    }

完成版プラグインのダウンロード