LoginSignup
4
5

More than 3 years have passed since last update.

cakephp チュートリアル Blog

Last updated at Posted at 2020-04-09

はじめに

会社に入社して業務のために、php(cakephp)を学ぶ研修で、cakePHPのチュートリアル(主にBlog)をしたときに、つまずいた点を記事として残そうかと思います。

*ブログチュートリアル
https://book.cakephp.org/3/ja/tutorials-and-examples/blog/blog.html

つまずいた所

*admin権限ではないユーザーがログインした場合のアクション権限

=>ユーザーが投稿した記事のみ編集・削除できる機能実装(ほんとに最後...)

認可(誰が何にアクセスするのを許可するか)

ログインしてるのか・してないかでアクセスを分ける

まずユーザーの種類にadmin(管理者)とauther(著者)の2種類あります。

そこの使い分けはまず、全体に認証をかけたいようなときはAppControllerappcontrollerでAuthコンポーネントの設定します。

*ちなみに、コントローラの拡張をコンポーネント!モデルはビヘイバー!Viewはヘルパーというものがあります。


public function initialize()
{
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [       //Authコンポーネントの読み込み
        'authorize' => ['Controller'], 
        'loginRedirect' => [              //ログイン後のリダイレクト先
            'controller' => 'Articles',
            'action' => 'index'
        ],
        'logoutRedirect' => [             //ログアウト後のリダイレクト先
            'controller' => 'Pages',
            'action' => 'display',
            'home'
        ]
    ]);
}

続きにAppContorollerに


//isAuthorizedはAuthコンポーネントで定義されている!
public function isAuthorized($user)
    {
      // roleというカラムで、adminを定義して管理者はすべての操作に権限を与える
        if (isset($user['role']) && $user['role'] ==='admin') {
            return true;
        }
   //その他のロールの場合はデフォルトで拒否とする(許可したい場合は各コントローラで個別に設定)
        return false;
    }

  // 共通メソッド beforeFilterでコントローラーのメソッドよりも先に実行する(ロールバック)
    public function beforeFilter(\Cake\Event\Event $event)
    {
       //beforeFilterでisAuthorizedメソッドよりも先に実行してくれるので
       //認証なしでアクセスできるアクションの指定
        $this->Auth->allow(['index', 'view', 'display']);
        $this->set('auth', $this->Auth->user());
    }

ユーザーの権限

AppConroller 内でやるかわりに、 各個別のコントローラーにさらなるルールを追加します。
追加しようとしているルールというのは、 ArticlesController によって、著者は記事を作成できるが、 自分のものではない記事を編集・削除できないようにする、というもの(本題)。

まずはArticlesControllerの編集

//Authコンポーネントで定義されているメソッド(認証済みであるという意味)
public function isAuthorized($user)
    {
        //addアクションのリクエストであればtrueを返す = つまり登録ユーザー全員が記事を追加できます
        if ($this->request->getParam('action') === 'add') {

       // LoginUserであれば実行可能なので、trueを返す
            return true;
        }

       //(in_array — 配列に値があるかチェックする)  編集や削除のリクエストである場合
        if (in_array($this->request->getParam('action'), ['edit', 'delete'])) {

//pass.0はURL内で渡されたパラメータを表します、つまりURLのアクションの下のパスセグメント( / で区切ったキーワード)を数字をインデックスとする配列で呼べるようにする。
//'pass.0' とインデックスを指定すると配列の先頭要素、つまりパスセグメントの先頭要素を取り出せる。
            $articleId = (int)$this->request->getParam('pass.0');

         // actionがedit,deleteの場合、request($articleId)の値とuserテーブルのuser_idの比較を行う。
            if ($this->Articles->isOwnedBy($articleId, $user['id'])) {
         //一致すればtrueを返して、編集削除を許可する
                return true;
            } 

        }
        //  比較がfalseの場合、AppControllerのisAuthorizedへ、
        //  権限が'admin'の場合はtrue、それ以外はfalse。
        return parent::isAuthorized($user);
    }
}

ちなみにisOwnedByはarticleテーブルで定義する!

 public function isOwnedBy($articleId, $userId)
    {
        return $this->exists(['id' => $articleId, 'user_id' => $userId]);
    }

モデルのアソシエーション

関係するモデルはArticlesテーブルとUsersテーブルが関係あります。

ここが大事!

つまり、投稿した記事のidとログインしたuser_idとが紐付かなければなりません!

アソシエーションを組みます!

ArticlesTable


 public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('articles');
        $this->displayField('title');
        $this->primaryKey('id');

        $this->addBehavior('Timestamp');

        // アソシエーション
        //*foreignKeyはリレーションする「先」のテーブルの、紐づけるカラム(外部キー)
        $this->belongsTo('Users', ['foreignKey' => 'user_id']);
        $this->belongsTo('Categories', ['foreignKey' => 'category_id']);
    }

*ここで迷ったのは、Userstable.phpもアソシエーション 定義しないでいいの???

今回は投稿した記事(=ArticlesControllerのaddアクションのみ)なのでいりません!!

これで、ArticlesControllerでusersテーブルでのデータを持ってくることができます!

最後にコントローラーのメソッド!!

投稿した記事(addアクション)にuser_idを紐付ける


// 記事追加機能
    public function add()
    {
   //newEntityとは 新規追加、保存処理をするときに使用新しく保存されるときにデータの検証が行われる
        $article = $this->Articles->newEntity();

    //コントローラの処理でHTTPリクエストメソッドが「Post」でない場合はエラーに飛ばす、
        if ($this->request->is('post')) {

     //ここでuserテーブルのuser_idと紐付けてくれる(ここ大事です!!)
            $article->user_id = $this->Auth->user('id'); 

     // patchEntity メソッドは、データがエンティティーにコピーされる前に 検証を行います
            $article = $this->Articles->patchEntity($article, $this->request->getData());
            if ($this->Articles->save($article)) {
                $this->Flash->success(__('Your article has been saved.'));
                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('Unable to add your article.'));
        }
        $this->set('article', $article);

        $categories = $this->Articles->Categories->find('treeList');
        $this->set(compact('categories'));
    }

これで無事に投稿した記事とuser_idが紐付きました!
先ほどArticlesControllerで定義したisAuthorizedメソッドが効いてアクセス制限を分けることができました!!

最後に

デバックしたりすることで、今回のエラーをがアソシエーション されてない事に気付き対処することができました!

参考
https://sites.google.com/site/kobashijiangyiyong/detabesu/e-detabesu-she-ji-yan-xi-3-4

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5