cakephp チュートリアル　Blog

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

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

#つまずいた所
＊admin権限ではないユーザーがログインした場合のアクション権限

＝＞ユーザーが投稿した記事のみ編集・削除できる機能実装（ほんとに最後...）

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

##ログインしてるのか・してないかでアクセスを分ける
まずユーザーの種類にadmin(管理者)とauther(著者)の２種類あります。

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

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


```php

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

続きにAppContorollerに

```php5

//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の編集

```php
//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テーブルで定義する！

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


#モデルのアソシエーション
関係するモデルはArticlesテーブルとUsersテーブルが関係あります。
####ここが大事！
つまり、投稿した**記事のid**とログインした**user_id**とが紐付かなければなりません！

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

**ArticlesTable**

```php

 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を紐付ける

```php

// 記事追加機能
    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

https://qiita.com/ota-yuki/items/31b25ef1e73fd1466d42

http://bashalog.c-brains.jp/15/06/15-101855.php