CakePHP コントローラーのメソッドを『private』や『protected』にする方法

CakePHPでコントローラーのメソッドを『private』宣言や『protected』宣言したい場合は、メソッド名の前に『_(アンダーバー)』をつけることで実現できます。
(『private』や『protected』については、「PHP publicとかprivateとかの修飾子」参照。)

『private』なメソッドを作成したい場合は『__(アンダーバー2個)』
『protected』なメソッドの場合は、『_(アンダーバー1個)』をメソッド名の前につけます。

こうする事で、自動的に『private』や『protected』メソッドとなります。

▼『private』『protected』メソッドの作成例

class TestsController extends Appcontroller {
    // これはpublic宣言になる
    function publicTest() {
    }

    // これはprivate宣言になる
    function __privateTest() {
    }

    // これはprotected宣言になる
    function _protectedTest() {
    }
}

また、『private』や『protected』なメソッドには、アクションとしてアクセスできないようになります。

例えば、上記の例なら『http://ドメイン名/tests/__privateTest』としてアクセスしようとした場合、『Debug Level』が『1』以上なら『privateメソッドなのでアクセスできないよ!!』みたいなエラーページが表示。

『Debug Level』が『0』なら『そんなアクションはないよ!!』なエラーページが表示されます。

ちなみに、以下のようにしても『private』や『protected』なメソッドは作成できますが、『Debug Level』が『1』以上の場合『privateメソッドなのでアクセスできないよ!!』エラーページは表示されず、『アクションがみつからないよ!!』みたいなエラーページが表示されます。

(CakePHPは「設定よりも規約」なので、『private』メソッドは『__』。『protected』メソッドには『_』をつけるべきだと思います。)

▼『_』をつけない場合

class TestsController extends Appcontroller {
    // これはpublic宣言になる
    public function publicTest() {
    }

    // これはprivate宣言になる
    private function privateTest() {
    }

    // これはprotected宣言になる
    protected function protectedTest() {
    }
}

『private』も『__』も書いた場合は、『privateメソッドなのでアクセスできないよ!!』エラーだったので、両方書いても問題ないのかな??
情報おまちしております・・・

PHP publicとかprivateとかの修飾子

PHP5から使えるprivateやprotectedとかの修飾子について。


public

メンバ変数・メンバメソッドに指定できる。
どこからでもアクセスできる。


private

メンバ変数・メンバメソッドに指定できる。
そのクラス内からしかアクセスできない。


protected

メンバ変数・メンバメソッドに指定できる。
そのクラスかサブクラスからしかアクセスできない。


static

メンバ変数・メンバメソッドに指定できる。
インスタンス化していないクラスでも関係なくアクセスできる。
static宣言したメンバ変数はインスタンス化されたクラスオブジェクトからはアクセスできない。


final

クラスとメンバメソッドに指定できる。
クラスに指定した場合は、このクラスを拡張することができなくなる。
メソッドに指定した場合は、サブクラスからオーバーライドできなくなる。


abstract

クラスとメンバメソッドに指定できる。
抽象クラスや抽象メソッドとなる。
メソッドに指定した場合は、そのクラスではメソッドの実装を定義することはできない。必ずサブクラスで実装する。
長くなりそうなので、コチラの『PHP:クラスの抽象化』をどうぞ・・・


番外編


__construct

コンストラクタメソッドとして使用できる。


__destruct

デストラクタメソッドとして使用できる。


Category » PHP

『Google +1 ボタン』とか『いいね!』ボタンとかをつけてみた

せっかくブログを書いているので、気になったことは色々試したいと思います。

というわけで、『Google +1 』ボタンをつけてみます。
ついでに、『いいね!』ボタン(facebook)・『ツイートする』ボタン(twitter)・『B!』ボタン(はてなブックマーク)もつけちゃいます。

では、WordPressを例に説明していきます。


1・『Google +1』ボタン

まずは、『Google +1』ボタンから。

以下のリンクから簡単にソースコードを取得できます。

「Google +1 をウェブサイトに表示」

基本的には、ボタンのサイズや使用する言語を選択するとソースコードが変更されていきますので、指示にしたがって張り付けるだけです。
詳細オプションをクリックすると、コールバック関数や『+1』をつけるサイトのURLを指定したりすることができます。

▼『Google +1 』ボタンを記事一覧に設置

記事ごとに『Google +1』ボタンを設置したい時は、『+1』をつけるURLを変更します。
私の場合、記事一覧で記事ごとに『+1』をつけれるようにしたかったので、『content.php』に以下のコードを追加しました。

<!--▼Google+1 ここから-->
<g:plusone href="<?php the_permalink(); ?>" size="medium"></g:plusone>
<!--▲Google+1 ここまで-->

『the_permalink』テンプレートタグで記事のURLを取得しているだけです。
私は、『content-single.php(記事詳細)』にも同じコードを追加しました。

あとは、『header.php』に以下のコードを追加するだけです。
『head』要素の最後に貼り付ければオッケーです。

<script type="text/javascript" src="https://apis.google.com/js/plusone.js">{lang: 'ja'}</script>

これで、『Google +1』ボタンの設置は終了です。


2・『ツイートする』ボタン

次は、有名な『ツイートする』ボタンです。

これも以下のリンクから簡単にソースコードを取得できます。

「Twitter/ツイートボタン」

指示にしたがって、色々選択するとソースコードが変更されますのでコピペするだけです。

▼『ツイートする』ボタンを記事一覧に設置

記事ごとに『ツイートする』ボタンを設置したい時は、『a』要素の『data-url』の値を変更します。

ツイート内テキストを変更したい場合は、『a』要素の『data-text』の値を変更します。

私は、以下のようにしました。

<!--▼twitter ここから-->
<a href="https://twitter.com/share" class="twitter-share-button" data-url="<?php the_permalink(); ?>" data-text="『あなたのブログ名に変更』 <?php the_title(); ?>" data-count="none" data-lang="ja">Tweet</a>
<!--▲twitter ここまで-->

上記のコードを、『Google +1』ボタンのソースコードと同じ場所に追加します。

あとは、以下のコードを『header.php』に追加します。
これまた『head』要素の最後に貼り付ければオッケーです。

<script type="text/javascript" src="//platform.twitter.com/widgets.js"></script>

これで、『ツイートする』ボタンの設置は終了です。


3・『B!』ボタン

次は、『B!』ボタンです。

『B!』ボタンのソースコードは、以下のリンクから取得できます。

「はてなブックマークボタンの作成・設置について」

しつこいようですが、指示にしたがって色々選択するとソースコードが変更されますのでコピペするだけです。

▼『B!』ボタンを記事一覧に設置

記事ごとに『B!』ボタンを設置したい時は、『a』要素の『href』の値の『http://b.hatena.ne.jp/entry/』の後を変更します。

タイトルを変更したい場合は、『a』要素の『data-hatena-bookmark-title』の値を変更します。

私は、以下のようにしました。

<!--▼はてブ ここから-->
<a href="http://b.hatena.ne.jp/entry/<?php the_permalink(); ?>" class="hatena-bookmark-button" data-hatena-bookmark-title="『あなたのブログ名に変更』 <?php the_title(); ?>" data-hatena-bookmark-layout="simple" title="このエントリーをはてなブックマークに追加"><img src="http://b.st-hatena.com/images/entry-button/button-only.gif" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;" /></a>
<!--▲はてブ ここまで-->

上記のコードを、『Google +1』ボタン、『ツイートする』ボタンのソースコードと同じ場所に追加します。

あとは、以下のコードを『header.php』に追加します。
これも『head』要素の最後に貼り付ければオッケーです。

<script type="text/javascript" src="http://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>

これで、『B!』ボタンの設置は終了です。


4・『いいね!』ボタン

最後は、『いいね!』ボタンです。

『いいね!』ボタンのソースコードは、以下のリンクから取得できます。

「Like Buttom」

英語ですが、指示にしたがって色々選択していき、『Get Code』ボタンをクリックするとソースコードが表示されます。
ソースコードが表示されているウインドウの『実装』を変更すると、選択した実装方法のソースに変更されます。
私は、『iFrame』を選択しました。

▼『いいね!』ボタンを記事一覧に設置

私は、『iframe』を選択したので『iframe』にあわせて説明します。
記事ごとに『いいね!』ボタンを設置したい時は、『iframe』要素の『src』の『?href=』の値を変更します。
私は、以下のようにしました。

<!--▼facebook ここから-->
<iframe src="http://www.facebook.com/plugins/like.php?href=<?php the_permalink(); ?>" scrolling="no" frameborder="0" style="border:none; overflow: hidden; width: 80px; height: 25px;" allowTransparency="true"></iframe>
<!--▲facebook ここまで-->

上記のコードを、『Google +1』ボタン、『ツイートする』ボタン、『B!』ボタンのソースコードと同じ場所に追加します。

『いいね!』ボタンはこれで終了です。


ほとんどコピペで、簡単に実装できるのがいいですよね><

私は、はてブの数やツイート数は表示しないアイコンを選択しましたが(数が増えなかったら寂しいから・・・)、色々なアイコンが用意されているので、ご自分にあったアイコンを選択して実装してください^^

CakePHP ACLに苦しむ・・・

ACLコンポーネントを使ってみた結果を結論から言うと、ACL(アクセス制御リスト)ってすごい便利だと思います。

ACLとは、マニュアルにも書いてあるように「優れた保守性と管理性を保ちつつ、アプリケーションのパーミッションをきめ細かく設定する」機能を提供してくれます。
例えば、管理者しかアクセスできないコントローラーやアクションを設定することができます。

「これは是非とも使いたい」と思い、マニュアルを見たのですが、説明がすごく分かりにくかった・・・

というわけで、自分のためにもブログに残しておきます。
(文章力がないのでマニュアルより分かりづらくなりそうですが・・・)

適当なコントローラーにアクションをいくつか作成し、管理者(administrators)、運営者(managers)、利用者(users)の3つのグループとユーザーを作成。グループごとにパーミッションを設定していく流れで説明していきたいと思います。


1・データベースの準備

まずはデータベースの準備を行います。
認証とACLはセットとなるのが一般的なので、まずはuserテーブルから作成することにします。
グループごとにパーミッションを設定していく予定なので、usersテーブルには以下のフィールドを用意し作成します。
(Bakeで作成しても問題ありません。)

▼usersテープルの作成

CREATE TABLE users (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(80) NOT NULL,
    group_id INT(11) NOT NULL,
    created DATETIME,
    modified DATETIME
);

次に、usersテーブルの外部キーとなるgroupsテーブルを作成します。

▼groupsテープルの作成

CREATE TABLE groups (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    created DATETIME,
    modified DATETIME
);

その他のテーブルを作成します。
今回はテストなので、適当なテーブルを1つ作成することにします。

▼その他のテープルの作成

CREATE TABLE posts (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    user_id INT(11) NOT NULL,
    title VARCHAR(255) NOT NULL,
    body TEXT,
    created DATETIME,
    modified DATETIME
);

ACL関連のテーブルを作成します。
Bakeコマンドで『cake schema create DbAcl』を実行するか、以下のSQLでテーブルを作成します。
(このSQL文は、CakePHP1.3.10では『/cake/console/templates/skel/config/schema/db_acl.php』にも書いてあります。)

▼ACL関連のテーブルを作成

CREATE TABLE acos (
    id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    parent_id INTEGER(10) DEFAULT NULL,
    model VARCHAR(255) DEFAULT '',
    foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
    alias VARCHAR(255) DEFAULT '',
    lft INTEGER(10) DEFAULT NULL,
    rght INTEGER(10) DEFAULT NULL,
    PRIMARY KEY  (id)
);

CREATE TABLE aros_acos (
    id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    aro_id INTEGER(10) UNSIGNED NOT NULL,
    aco_id INTEGER(10) UNSIGNED NOT NULL,
    _create CHAR(2) NOT NULL DEFAULT 0,
    _read CHAR(2) NOT NULL DEFAULT 0,
    _update CHAR(2) NOT NULL DEFAULT 0,
    _delete CHAR(2) NOT NULL DEFAULT 0,
    PRIMARY KEY(id)
);

CREATE TABLE aros (
    id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    parent_id INTEGER(10) DEFAULT NULL,
    model VARCHAR(255) DEFAULT '',
    foreign_key INTEGER(10) UNSIGNED DEFAULT NULL,
    alias VARCHAR(255) DEFAULT '',
    lft INTEGER(10) DEFAULT NULL,
    rght INTEGER(10) DEFAULT NULL,
    PRIMARY KEY  (id)
);

これで、データーベースの準備は終了です。


2・Authの準備

繰り返しになりますが、ACLはあくまで「アプリケーションのパーミッションをきめ細かく設定する」ものなので、認証(ログイン処理など)とは異なります。
認証はAuthコンポーネントを使用して行います。
(Authコンポーネントについては、「CakePHP Authコンポーネントを使ってみた」を参照。)

今回は、すべてのコントローラーにAuthコンポーネントを使用したいので、『AppController』を作成したいと思います。

▼『AppController』の作成と認証処理

class AppController extends Controller {
    public $components = array('Auth', 'Acl', 'Session');

    public function beforeFilter() {
    // Authコンポーネントの設定

    /*
     * モードの指定
     * [actions] でアクション単位のパーミッションチェック(アクセス可能か確認)を行う
     * [crud]    でコントローラー単位のパーミッションチェック(CRUDによる確認)を行う
     */
    $this->Auth->authorize = 'actions';

    $this->Auth->loginRedirect  = array('controller' => 'posts', 'action' => 'index');
    $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');

    // ↓グループとユーザーとパーミッションを登録したら削除する
    $this->Auth->allow('*');
    }
}

ACLでもっとも苦しんだのが『$this->Auth->authorize』でした・・・
実は、『$this->Auth->authorize』に指定する値でパーミッションをチェックする方法が大きく変わります。

『actions』を指定すると、アクションに対するアクセス可能か確認し、『crud』を指定するとコントローラーに対してCRUDによるパーミッションの確認となります。

つまり、『actions』ではCRUDによる確認はできず、『crud』ではコントローラーだけにCRUDによる確認をすることができるということになります。

今回のケースでは、アクションごとにパーミッションを設定したいので『actions』を指定します。

ちなみに、マニュアルの5.1 アクセス制御リストは『crud』を使った場合の説明
11.2 ACL を制御するシンプルなアプリケーションは『actions』を使った場合の説明となっています。
マニュアルを読むときは、このことを意識しながら見ないと私みたいに苦労します・・・

zistaさんの『CakePHPのACLを理解する(Authコンポーネントから探る) その2』がとても参考になりました。詳しく知りたい方は、是非一度ご覧ください。


3・AROの作成

次は、AROを作成していきます。
グループとユーザーをACLに関連付け、自動的にarosテーブルにも登録する処理を作成します。
AclBehaviorを使用した以下のコードをGroupモデルとUserモデルに書くことによって、グループやユーザーを登録すると自動的にarosテーブルにも登録してくれるようになります。

▼Userモデル

class User extends AppModel {
    public $name   = 'User';
    public $actsAs = array('Acl' => 'requester');

    public $belongsTo = array(
        'Group' => array(
            'className' => 'Group',
                'foreignKey' => 'group_id',
                'conditions' => '',
                'fields'     => '',
                'order'      => ''
            )
        );

    public $hasMany = array(
        'Post' => array(
            'className'    => 'Post',
            'foreignKey'   => 'user_id',
            'dependent'    => false,
            'conditions'   => '',
            'fields'       => '',
            'order'        => '',
            'limit'        => '',
            'offset'       => '',
            'exclusive'    => '',
            'finderQuery'  => '',
            'counterQuery' => ''
        )
    );

    /* 以下を追加
    --------------------------------- */
    // モデルとACLテーブルを自動的に結びつける処理
    public function parentNode() {
        if (!$this->id && empty($this->data)) return null;

        $data = $this->data;

        if (empty($this->data)) $data = $this->read();

        if (!$data['User']['group_id']) {
            return null;
        } else {
            return array('Group' => array('id' => $data['User']['group_id']));
        }
    }

    // ユーザーの所属するグループが変更された時の処理
    public function afterSave($created) {
        if (!$created) {
            $parent = $this->parentNode();
            $parent = $this->node($parent);

            $node = $this->node();

            $aro = $node[0];
            $aro['Aro']['parent_id'] = $parent[0]['Aro']['id'];
            $this->Aro->save($aro);
        }
    }
}

▼Groupモデル

class Group extends AppModel {
    public $name   = 'Group';
    public $actsAs = array('Acl' => array('requester'));

    public $hasMany = array(
        'User' => array(
            'className'    => 'User',
            'foreignKey'   => 'group_id',
            'dependent'    => false,
            'conditions'   => '',
            'fields'       => '',
            'order'        => '',
            'limit'        => '',
            'offset'       => '',
            'exclusive'    => '',
            'finderQuery'  => '',
            'counterQuery' => ''
        )
    );

    /* 以下を追加
    --------------------------------- */
    // モデルとACLテーブルを自動的に結びつける処理
    public function parentNode() {
        return null;
    }
}

これで、自動的にarosテーブルにも登録されるようになりましたので、グループとユーザーを登録するアクションを作成し登録していきます。
管理者(administrators)、運営者(managers)、利用者(users)の3つのグループとそれぞれのグループに所属するユーザーを登録したら次に進みます。


4・ACOの作成

ACOの作成ですが、マニュアルに便利なコードがあったので、それをそのまま使用したいと思います。
以下がコードです。

▼ACOの作成を自動化するコード

// ACOの作成
public function build_acl() {
    if (!Configure::read('debug')) return $this->_stop();

    $log  = array();
    $aco  =& $this->Acl->Aco;

    $root = $aco->node('controllers');

    if (!$root) {
        $aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
        $root = $aco->save();
        $root['Aco']['id'] = $aco->id;
        $log[] = 'Created Aco node for controllers';
    } else {
        $root = $root[0];
    }

    App::import('Core', 'File');
        $Controllers = Configure::listObjects('controller');
        $appIndex = array_search('App', $Controllers);

        if ($appIndex !== false ) unset($Controllers[$appIndex]);

        $baseMethods = get_class_methods('Controller');
        $baseMethods[] = 'buildAcl';
        $Plugins = $this->_getPluginControllerNames();
        $Controllers = array_merge($Controllers, $Plugins);

        // look at each controller in app/controllers
        foreach ($Controllers as $ctrlName) {
            $methods = $this->_getClassMethods($this->_getPluginControllerPath($ctrlName));

            // Do all Plugins First
            if ($this->_isPlugin($ctrlName)) {
                $pluginNode = $aco->node('controllers/'.$this->_getPluginName($ctrlName));

                if (!$pluginNode) {
                    $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginName($ctrlName)));
                    $pluginNode = $aco->save();
                    $pluginNode['Aco']['id'] = $aco->id;
                    $log[] = 'Acoを作成しました ' . $this->_getPluginName($ctrlName) . ' Plugin';
                }
            }

            // find / make controller node
            $controllerNode = $aco->node('controllers/'.$ctrlName);

            if (!$controllerNode) {
                if ($this->_isPlugin($ctrlName)) {
                    $pluginNode = $aco->node('controllers/' . $this->_getPluginName($ctrlName));
                    $aco->create(array('parent_id' => $pluginNode['0']['Aco']['id'], 'model' => null, 'alias' => $this->_getPluginControllerName($ctrlName)));
                    $controllerNode = $aco->save();
                    $controllerNode['Aco']['id'] = $aco->id;
                    $log[] = 'Created Aco node for ' . $this->_getPluginControllerName($ctrlName) . ' ' . $this->_getPluginName($ctrlName) . ' Plugin Controller';
                } else {
                    $aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
                    $controllerNode = $aco->save();
                    $controllerNode['Aco']['id'] = $aco->id;
                    $log[] = 'Created Aco node for ' . $ctrlName;
                }
            } else {
                $controllerNode = $controllerNode[0];
            }

            //clean the methods. to remove those in Controller and private actions.
            foreach ($methods as $k => $method) {
                if (strpos($method, '_', 0) === 0) {
                    unset($methods[$k]);
                    continue;
                }

                if (in_array($method, $baseMethods)) {
                    unset($methods[$k]);
                    continue;
                }

                $methodNode = $aco->node('controllers/'.$ctrlName.'/'.$method);

                if (!$methodNode) {
                    $aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
                    $methodNode = $aco->save();
                    $log[] = 'Created Aco node for '. $method;
                }
            }
        }

        if(count($log)>0) $this->set('logs', $log);
    }

    private function _getClassMethods($ctrlName = null) {
        App::import('Controller', $ctrlName);

        if (strlen(strstr($ctrlName, '.')) > 0) {
            // plugin's controller
            $num = strpos($ctrlName, '.');
            $ctrlName = substr($ctrlName, $num+1);
        }

        $ctrlclass = $ctrlName . 'Controller';

        return get_class_methods($ctrlclass);
    }

    private function _isPlugin($ctrlName = null) {
        $arr = String::tokenize($ctrlName, '/');

        if (count($arr) > 1) {
            return true;
        } else {
            return false;
        }
    }

    private function _getPluginControllerPath($ctrlName = null) {
        $arr = String::tokenize($ctrlName, '/');

        if (count($arr) == 2) {
            return $arr[0] . '.' . $arr[1];
        } else {
            return $arr[0];
        }
    }

    private function _getPluginName($ctrlName = null) {
        $arr = String::tokenize($ctrlName, '/');

        if (count($arr) == 2) {
            return $arr[0];
        } else {
            return false;
        }
    }

    private function _getPluginControllerName($ctrlName = null) {
        $arr = String::tokenize($ctrlName, '/');

        if (count($arr) == 2) {
            return $arr[1];
        } else {
            return false;
        }
    }

    /**
     * Get the names of the plugin controllers ...
     *
     * This function will get an array of the plugin controller names, and
     * also makes sure the controllers are available for us to get the
     * method names by doing an App::import for each plugin controller.
     *
     * @return array of plugin names.
     *
     */
    private function _getPluginControllerNames() {
        App::import('Core', 'File', 'Folder');

        $paths = Configure::getInstance();
        $folder =& new Folder();
        $folder->cd(APP . 'plugins');

        // Get the list of plugins
        $Plugins = $folder->read();
        $Plugins = $Plugins[0];
        $arr = array();

        // Loop through the plugins
        foreach($Plugins as $pluginName) {
            // Change directory to the plugin
            $didCD = $folder->cd(APP . 'plugins'. DS . $pluginName . DS . 'controllers');

            // Get a list of the files that have a file name that ends
            // with controller.php
            $files = $folder->findRecursive('.*_controller\.php');

            // Loop through the controllers we found in the plugins directory
            foreach($files as $fileName) {
                // Get the base file name
                $file = basename($fileName);

                // Get the controller name
                $file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));

                if (!preg_match('/^'. Inflector::humanize($pluginName). 'App/', $file)) {
                    if (!App::import('Controller', $pluginName.'.'.$file)) {
                        debug('Error importing '.$file.' for plugin '.$pluginName);
                    } else {
                        // Now prepend the Plugin name ...
                        // This is required to allow us to fetch the method names.
                        $arr[] = Inflector::humanize($pluginName) . "/" . $file;
                    }
                }
            }
        }

        return $arr;
    }

これをアクションとして実行するとACOが自動的に作成されます。

実行するアクションは『build_acl』なので、GroupsControllerのアクションにした場合は『http://ドメイン/groups/build_acl』といった感じで実行します。

ACOの作成が終了したら、パーミッションの設定を行っていきます。


5・パーミッションを設定する

ここまででACLを使用する準備が完了したので、実際にパーミッションを設定していきます。
今回のケースでは、『$this->Auth->authorize』に『actions』を指定したので、アクションごとにパーミッションを設定することになります。

設定するパーミッションは、

管理者(administrators)グループには、GroupsController・UsersController・PostsControllerの全てのアクションに対してアクセスを許可。

運営者(managers)グループには、UsersControllerのlogin・logoutアクションとPostsControllerの全てのアクションに対してアクセスを許可。

利用者(users)グループには、UsersControllerのlogin・logoutアクションとPostsControllerのindex・show・addアクションに対してのアクセスを許可

と、します。

また、各コントローラーのアクションは、

GroupsControllerにはindex・show・add・delete・build_aclアクション。

UsersControllerにはlogin・logout・index・show・add・deleteアクション。

PostsControllerにはindex・show・add・deleteアクション。

があるものとします。

パーミッションの設定は、『$this->Acl->allow($aroAlias, $acoAlias)』『$this->Acl->deny($aroAlias, $acoAlias)』で行っていきます。

▼パーミッションを設定する

// パーミッションの設定
public function build_parmition() {
    // 管理者のパーミッションを設定
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/groups/index');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/groups/show');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/groups/add');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/groups/delete');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/groups/build_acl');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/login');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/logout');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/index');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/show');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/add');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/users/delete');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/posts/index');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/posts/show');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/posts/add');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 1), 'controllers/posts/delete');

    // 運営者のパーミッションを設定
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/groups/index');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/groups/show');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/groups/add');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/groups/delete');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/groups/build_acl');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/login');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/logout');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/index');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/show');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/add');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 2), 'controllers/users/delete');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/posts/index');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/posts/show');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/posts/add');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 2), 'controllers/posts/delete');

    // 利用者のパーミッションを設定
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/groups/index');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/groups/show');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/groups/add');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/groups/delete');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/groups/build_acl');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/login');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/logout');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/index');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/show');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/add');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/users/delete');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers/posts/index');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers/posts/show');
    $this->Acl->allow(array('model' => 'Group', 'foreign_key' => 3), 'controllers/posts/add');
    $this->Acl->deny(array('model' => 'Group', 'foreign_key' => 3), 'controllers/posts/delete');
}

グループごとにパーミッションを設定するので、aroAliasにはGroupモデルを使用しています。
上記のアクションを適当なコントローラーに配置し、実行します。
これでパーミッションの設定は完了です。

『build_parmition』アクションは、パーミッション設定後は削除してしまってオッケーです。


6・確認

AppControllerの『$this->Auth->allow(‘*’);』をコメントアウトか削除し、動作確認を行います。

ちゃんと動作していれば、リンクからの推移なら推移元のページにリダイレクト、直接URLを入力ならトップページにリダイレクトされると思います。


7・エラーメッセージを出力する場合

エラーメッセージを出力したい場合は、『$this->Auth->authError』にエラーメッセージを指定します。

▼エラーメッセージの例

$this->Auth->authError  = 'アクセス権限がありません。';

あとは、『$session->flash』でエラーメッセージをビューに表示します。

▼エラーメッセージの例

echo $session->flash('auth');

基本的なACLの使い方は以上です。
あとは、パーミッションを簡単に変更するアクションを作成したりして、使いやすいようにしていくだけです。

ACLコンポーネントを使用する上で、私が思う注意点は『$this->Auth->authorize』に指定した値でパーミッションチェックの方法が変わるという事だと思います。
私のように、マニュアルや他の方のブログなど読んで混乱する人は、この事を強く意識した方が理解への近道になるかもしれませんね。

さすがに長文になりました・・・
最後まで読んでくださった方。ありがとうございました。

CakePHP ブラウザを閉じたらSESSIONを破棄

CakePHPで『ブラウザを閉じるとSESSIONを破棄』をしたかったので、メモも兼ねて紹介します。

手っ取り早い方法としては、『/app/config/core.php』の200行目付近の『Security.level』を『high』に変更します。

『Security.level』に『high』を指定していると、『/cake/libs/cake_session.php』の480行目付近で

if ($this->security == 'high') {
    $this->cookieLifeTime = 0;
} else {
    $this->cookieLifeTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
}

となり、『cookie_lifetime』に0が指定されます。
さらに、『Security.level』が『high』だと、リクエストごとにSESSION ID が作られるようになります。


しかし、『Security.level』が『high』だと色々と不都合があったりしますよね・・・
『Security.level』が『medium』のまま『ブラウザを閉じるとSESSIONを破棄』する方法は以下のようにして行います。

まずは、『/app/config/core.php』の127行目付近の『Session.save』を好きな名前に変更します。

▼『Session.save』を変更

// ▼変更前
// Configure::write('Session.save', 'php');

// ▼変更後
Configure::write('Session.save', 'new_session');

次に、『Session.save』で指定した名前のPHPファイルを『/app/config/』に作成します。
上記の例だと、『new_session.php』となります。
『new_session.php』の内容は、『/cake/libs/cake_session.php』の487行目付近のswitch文で色々書かれている『ini_set』関数たちをコピーしてきます。

▼『new_session.php』の内容

<?php
    ini_set('session.use_trans_sid', 0);
    ini_set('session.name', Configure::read('Session.cookie'));

    // ↓cookie_lifetimeに0を指定する
    ini_set('session.cookie_lifetime', 0);

    ini_set('session.cookie_path', $this->path);
?>

これで、『Security.level』が『medium』のまま『ブラウザを閉じるとSESSIONを破棄』することができます。

注意点は『Session.save』に[cake][database][php][cache]などを指定しないことです。
[cake][database][php][cache]のいずれかを指定してしまうと、
『/cake/libs/cake_session.php』の487行目付近のswitch文のcaseに該当してしまうので、自分で作成したファイルを読み込みません。