add to hatena hatena.comment (8) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 8

CakePHP コントローラーのアクションをコンポーネントにまとめる

本業のほうが忙しく久々の更新になってしまいましたが小ネタです。

Cakeを使っていると最初はbakeやscaffoldを利用する機会がありますが、序々に利用頻度が減ってきました。(現在では殆ど使っていません)

理由としては、社内での共通のルールであったり、プロジェクト毎の特性を反映した雛型から複製して作成したほうが効率的になってきたということが大きいと思います。

ただ、bakeでの自動生成や複製方式だと初回は良いのですが、大規模なものになってくると、同様のソースが点在し一括での改修などが面倒なことになってきます。

ということで、明らかに同様の処理はscaffold風にコンポーネント化することにしました。
※コンポーネントからコントローラーを制御する場合は、ロジックが分散すると見通しが悪くなりますので、ロジック全体を委譲できる場合に利用することをお勧めします。

実装例

今回の例として、

コントローラーA が モデルA
コントローラーB が モデルB

にそれぞれ対応しており、同様の編集ロジック(edit)を必要としているという前提です。
編集ロジックはbakeで生成したものを共通化することとします。
※実際にはプロジェクト毎などにカスタマイズする想定です。

共通の編集ロジックをActionEditコンポーネントとし、
以下のソースのように記述します。
/app/controllers/components/action_edit.php

PHP:
  1. <?php
  2. class ActionEditComponent extends Object {
  3.    
  4.     var $C; // コントローラーインスタンス
  5.     var $M; // モデルのインスタンス
  6.    
  7.     //--------------------------------------------
  8.     // コントローラーで制御する変数を設定
  9.     //--------------------------------------------
  10.     // ページタイトル
  11.     var $pageTitle;
  12.     // ビュー
  13.     var $view = 'edit';
  14.     var $redirectAction = 'index';
  15.    
  16.     //--------------------------------------------
  17.     // 初期化時にインスタンスを設定
  18.     //--------------------------------------------
  19.     function startup(&$controller) {
  20.         $this->C =& $controller;
  21.         $model = $controller->modelClass;
  22.         $this->M =& $controller->{$model};
  23.     }
  24.    
  25.     //--------------------------------------------
  26.     // コントローラーの動作記述
  27.     //--------------------------------------------
  28.     function run($id){
  29.         $C =& $this->C;
  30.         $M =& $this->M;
  31.        
  32.         $C->pageTitle = $this->pageTitle;
  33.         if (!$id && empty($C->data)) {
  34.             $C->Session->setFlash('データが不正です');
  35.             $C->redirect(array('action'=>$this->redirectAction));
  36.         }
  37.         if (!empty($C->data)) {
  38.             if ($M->save($C->data)) {
  39.                 $C->Session->setFlash('情報を保存しました');
  40.                 $C->redirect(array('action'=>$this->redirectAction));
  41.             } else {
  42.                 $C->Session->setFlash('[エラー]情報が保存できませんでした');
  43.             }
  44.         }
  45.         if (empty($C->data)) {
  46.             $C->data = $M->read(null, $id);
  47.         }
  48.         $C->render($this->view);
  49.     }
  50. }

コントローラーAでは以下のように記述します。

PHP:
  1. class ModelAController extends AppController {
  2.     var $components = array('ActionEdit');
  3.     :
  4.     function edit($id = null) {
  5.         $this->ActionEdit->pageTitle = 'モデルAの編集';
  6.         $this->ActionEdit->run($id);
  7.     }

コントローラーBでも同様にpageTitleだけ変更すれば同じ挙動になります。

ポイント

  

・コントローラー、モデルのインスタンスをコンポーネントに保持する
・コントローラー毎に制御する必要があるものはプロパティとし
 コントローラー側でセット後に実行メソッド(run)内で処理を行う

今回の例ではページタイトル以外に

・ビュー
・保存後のリダイレクト先アクション

を可変にしていますので、例えばコントローラーBで

・ビュー → edit_c
・リダイレクト先アクション → 自身の編集ページ

に変更したい場合などは、

PHP:
  1. function edit($id = null) {
  2.         $this->ActionEdit->pageTitle = 'モデルCの編集';
  3.         $this->ActionEdit->view = 'edit_c';
  4.         $this->ActionEdit->redirectAction = 'edit'.DS.$id;
  5.         $this->ActionEdit->run($id);
  6.     }

とすれば、期待する動作が可能です。

動的にモデルを切り替えたい場合は

PHP:
  1. $this->ActionEdit->M =& $this->OtherModel;

とすれば参照モデルを変更することも可能です。

今回の例では、bakeソースを共通化していますのでそれほど効果はありませんが、
各自・プロジェクトのルールやCRUD以外の汎用アクション(データ並び替えや論理削除など)を
アクションコンポーネントとして用意していれば、中・大規模の管理画面の作成時などに役に立つと思います。

関連するその他の記事

Comments

Leave a Reply