- 更新日: 2015年11月1日
- PHP & CakePHP
CakePHP3入門・開発チュートリアル
CakePHP3 公式ページに掲載されているブックマーク・アプリケーションのチュートリアルをやってみました。CakePHP3 はモデルのデータを配列じゃなくてオブジェクトで取得できたり、Composer が標準で対応されたりとさらに便利になっています。
ブックマークチュートリアル – CakePHP Cookbook 3.x ドキュメント
Bookmarker Tutorial – CakePHP Cookbook 3.x documentation
公式のチュートリアルと違う方法で進めた箇所もありまして、例えばデータベースのテーブル作成にマイグレーション機能を利用している点などです。本格的な開発時にはマイグレーションを使うのは必須になりますので。
CakePHP 3 を使うためのシステム要件は以下をチェック。
インストール・システム要件 – CakePHP Cookbook 3.x ドキュメント
CakePHP は Rails の概念をほぼそっくりそのまま取り込んでいるので、Rails 大好きな自分の場合は PHP のフレームワークとしては CakePHP が一番好きです。というか、Rails に本格的に取り組む前は CakePHP 1.2〜2.x を使っていましたので。CakePHP をやってたおかげで Rails にも割とすんなり慣れることができました。CoC(設定より規約)のルールとか両者ともほぼ同じです。
— 環境 —
Mac OS X Yosemite 10.10.5
PHP 5.5.19
CakePHP 3.1.1
— 目次 —
・CakePHP をインストール&プロジェクト作成
・Git リポジトリを初期化して Git 管理にする
・MySQL デーベース・ユーザー作成
・Datasources 設定
・マイグレーション利用のための準備
・マイグレーションファイルの作成
・生成したマイグレーション用ファイルの修正
・マイグレーションの実行とDB確認
・マイグレーションのステータス確認
・マイグレーションのロールバック
・スキャフォールドでMVCファイルを作成
・スキャフォールドを使わずに MVC ファイルを生成
・パスワードをハッシュ化
・ブックマークをタグで探せるようにする
・タグ・ブックマークを追加してブラウザで動作確認
・ログイン認証の機能を追加
・ログアウトの機能を追加
・ログイン・ログアウトは認証なしでアクセス許可する
・ユーザー登録の機能を有効にする
・ブックマークへのアクセスをログインユーザー本人のみに制限する
・ブックマークのビューとアクションを編集
・ブックマーク一覧ページを改善
・タグ用の入力フィールドを作成
・フィールドに入力された Tag String を保存する
それでは、チュートリアル開始です!
CakePHP をインストール&プロジェクト作成
まずは、CakePHP をインストールしてプロジェクトを作成します。Composer で vendor ライブラリとして CakePHP をインストールするのが今時のやり方らしい。
1 2 3 4 5 6 7 8 9 10 |
$ composer create-project --prefer-dist cakephp/app bookmarker Installing cakephp/app (3.1.1) - Installing cakephp/app (3.1.1) ... Set Folder Permissions ? (Default to Y) [Y,n]? Y Permissions set on /path/to/cakephp_projects/bookmarker/tmp/cache ... |
フォルダのパーミッションを設定するかどうか聞かれたので Y(Yes)として設定しました。これで CakePHP のセットアップ完了。簡単!
動作確認のためにサーバー起動。
1 2 3 4 |
$ cd bookmarker $ bin/cake server |
http://localhost:8765/ にアクセスして、「Get the Ovens Ready」のページが正しく表示されれば動作OKです。
Git リポジトリを初期化して Git 管理にする
CakePHP インストールで、デフォルトで以下の .gitignore が作成されていました。
.gitignore
1 2 3 4 5 6 |
/vendor/* /config/app.php /tmp/* /logs/* |
とりあえず .gitignore をこのまま使って、プロジェクトのディレクトリを Git 管理下にします。
1 2 3 4 5 |
$ git init $ git add . $ git commit -m "initial commit" |
以降は git 操作についての説明は省略しますが、適宜ブランチを切ったりコミットなどを行うようにする。
MySQL デーベース・ユーザー作成
CakePHP プロジェクトで利用する DB を作成します。
1 2 3 4 |
$ mysql -u root -p Enter password: |
データベース作成。公式チュートリアルに従って、cake_bookmarks という名前の DB を作成。DB の文字コードは utf8 を指定します。
1 2 3 |
mysql> CREATE DATABASE cake_bookmarks CHARACTER SET utf8; |
DBユーザーが作成済みでない場合は、ユーザーも作成しておきます。
1 2 3 |
mysql> GRANT ALL PRIVILEGES ON *.* TO ユーザー名@localhost IDENTIFIED BY 'パスワード'; |
作成したユーザーの確認。
1 2 3 4 |
mysql> select host,user from mysql.user; mysql> quit |
DB名(cake_bookmarks)、ユーザー名、パスワードは、次項目で config/app.php の Datasources に設定する。
Datasources 設定
config/app.php の Datasources に、DB名(cake_bookmarks)、ユーザー名、パスワードを設定する。
config/app.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php return [ // ... 'Datasources' => [ 'default' => [ 'className' => 'Cake\Database\Connection', 'driver' => 'Cake\Database\Driver\Mysql', 'persistent' => false, 'host' => 'localhost', //'port' => 'nonstandard_port_number', 'username' => 'username', 'password' => 'password', 'database' => 'cake_bookmarks', 'encoding' => 'utf8', 'timezone' => 'UTC', 'cacheMetadata' => true, 'log' => false, 'unix_socket' => '/var/lib/mysql/mysql.sock', 'quoteIdentifiers' => false, ], // ... |
‘unix_socket’ => ‘/var/lib/mysql/mysql.sock’ 追加に注意。MySQL ソケットのパスを指定する。
マイグレーション利用のための準備
デフォルトのスケルトンで、vendor/cakephp/migrations が入っており CakePHP 3 ではデフォルトでマイグレーションが利用可能になっています。
config/bootstrap.php
1 |
Plugin::load('Migrations'); |
config/bootstrap.php で上記が記載されていることを確認。なければ追記する。
マイグレーションファイルの作成
公式チュートリアルを参考に以下のDBテーブルを作成する。
users
bookmarks
tags
bookmarks_tags
以下コマンドを実行して、必要なマイグレーションファイルの一式を作成します。
1 2 3 4 5 6 |
$ bin/cake bake migration CreateUsers email:string password:string created modified $ bin/cake bake migration CreateBookmarks user_id:integer title:string description:text url:text created modified $ bin/cake bake migration CreateTags title:string created modified $ bin/cake bake migration CreateBookmarksTags bookmark_id:integer tag_id:integer created modified |
config/Migrations/ 内にマイグレーションファイルが生成されます。
1 2 3 4 5 6 7 8 9 |
bookmarker | config/ | | Migrations/ | | | ***_create_users.php | | | ***_create_bookmarks.php | | | ***_create_tags.php | | | ***_create_bookmarks_tags.php |
CakePHP のマイグレーションについては以下ページ等を参考。
Migrations – CakePHP Cookbook 3.x ドキュメント
cakePHP3でbake migrate – Qiita
Schema System – CakePHP Cookbook 3.x documentation
CakePHP のマイグレーションは Phinx というライブラリが担っており、CakePHP 3 ではデフォルトで vendor 以下に入っています。
Phinx
https://github.com/robmorgan/phinx
生成したマイグレーション用ファイルの修正
コマンドで作成したマイグレーションファイルの修正を行う。必要に応じて addIndex や addForeignKey 追加の修正を行う。Phinx のドキュメントが参考になります。
Writing Migrations – Phinx 0.4.6 documentation
config/Migrations/***_create_users.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php use Migrations\AbstractMigration; class CreateUsers extends AbstractMigration { public function change() { $table = $this->table('users'); $table->addColumn('email', 'string', [ 'default' => null, 'limit' => 255, 'null' => false ]); $table->addColumn('password', 'string', [ 'default' => null, 'limit' => 255, 'null' => false ]); $table->addColumn('created', 'datetime'); $table->addColumn('modified', 'datetime'); $table->create(); } } |
config/Migrations/***_create_bookmarks.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php use Migrations\AbstractMigration; class CreateBookmarks extends AbstractMigration { public function change() { $table = $this->table('bookmarks'); $table->addColumn('user_id', 'integer', [ 'default' => null, 'limit' => 11, 'null' => false ]); $table->addColumn('title', 'string', [ 'default' => null, 'limit' => 50, 'null' => true ]); $table->addColumn('description', 'text', [ 'default' => null, 'null' => true ]); $table->addColumn('url', 'text', [ 'default' => null, 'null' => true ]); $table->addColumn('created', 'datetime'); $table->addColumn('modified', 'datetime'); $table->addForeignKey('user_id', 'users', 'id'); $table->create(); } } |
config/Migrations/***_create_tags.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php use Migrations\AbstractMigration; class CreateTags extends AbstractMigration { public function change() { $table = $this->table('tags'); $table->addColumn('title', 'string', [ 'default' => null, 'limit' => 255, 'null' => true ]); $table->addColumn('created', 'datetime'); $table->addColumn('modified', 'datetime'); $table->addIndex(['title'], ['unique' => true]); $table->create(); } } |
config/Migrations/***_create_bookmarks_tags.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php use Migrations\AbstractMigration; class CreateBookmarksTags extends AbstractMigration { public function change() { $table = $this->table('bookmarks_tags', [ 'id' => false, 'primary_key' => ['bookmark_id', 'tag_id'] ]); $table->addColumn('bookmark_id', 'integer', [ 'default' => null, 'limit' => 11, 'null' => false ]); $table->addColumn('tag_id', 'integer', [ 'default' => null, 'limit' => 11, 'null' => false ]); $table->addForeignKey('tag_id', 'tags', 'id'); $table->addForeignKey('bookmark_id', 'bookmarks', 'id'); $table->create(); } } |
マイグレーションの実行とDB確認
マイグレーション用のファイルを修正し終えたら、マイグレート実行です。以下のコマンドを実行します。
1 2 3 |
$ bin/cake migrations migrate |
これで、必要なテーブルが一気に作成されました。
DB 確認。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
mysql> show databases; +---------------------+ | Database | +---------------------+ | information_schema | | cake_bookmarks | | ... mysql> use cake_bookmarks; mysql> show tables; +--------------------------+ | Tables_in_cake_bookmarks | +--------------------------+ | bookmarks | | bookmarks_tags | | phinxlog | | tags | | users | +--------------------------+ 5 rows in set (0.00 sec) |
DB テーブル確認。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> desc users; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | email | varchar(255) | NO | | NULL | | | password | varchar(255) | NO | | NULL | | | created | datetime | NO | | NULL | | | modified | datetime | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+ 5 rows in set (0.00 sec) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> desc bookmarks; +-------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | user_id | int(11) | NO | MUL | NULL | | | title | varchar(50) | YES | | NULL | | | description | text | YES | | NULL | | | url | text | YES | | NULL | | | created | datetime | NO | | NULL | | | modified | datetime | NO | | NULL | | +-------------+-------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec) |
1 2 3 4 5 6 7 8 9 10 11 12 |
mysql> desc tags; +----------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | title | varchar(255) | YES | UNI | NULL | | | created | datetime | NO | | NULL | | | modified | datetime | NO | | NULL | | +----------+--------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec) |
1 2 3 4 5 6 7 8 9 10 |
mysql> desc bookmarks_tags; +-------------+---------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+---------+------+-----+---------+-------+ | bookmark_id | int(11) | NO | PRI | NULL | | | tag_id | int(11) | NO | PRI | NULL | | +-------------+---------+------+-----+---------+-------+ 2 rows in set (0.00 sec) |
Foreign Key の確認。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
mysql> show create table bookmarks; +----------- | bookmarks | CREATE TABLE `bookmarks` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL, `title` varchar(50) DEFAULT NULL, `description` text, `url` text, `created` datetime NOT NULL, `modified` datetime NOT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), CONSTRAINT `bookmarks_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | +----------- 1 row in set (0.00 sec) |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> show create table bookmarks_tags; +---------------- | bookmarks_tags | CREATE TABLE `bookmarks_tags` ( `bookmark_id` int(11) NOT NULL, `tag_id` int(11) NOT NULL, PRIMARY KEY (`bookmark_id`,`tag_id`), KEY `tag_id` (`tag_id`), CONSTRAINT `bookmarks_tags_ibfk_1` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`), CONSTRAINT `bookmarks_tags_ibfk_2` FOREIGN KEY (`bookmark_id`) REFERENCES `bookmarks` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 | 1 row in set (0.00 sec) |
マイグレーションのステータス確認
マイグレーションのステータスを確認するには以下のコマンドを実行する。Rails でいう rake db:migrate:status です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ bin/cake migrations status Welcome to CakePHP v3.1.0 Console --------------------------------------------------------------- App : src Path: /Path/to/CakePHP/tutorials/bookmarker/src/ PHP : 5.5.19 --------------------------------------------------------------- using migration path /Path/to/CakePHP/tutorials/bookmarker/config/Migrations using environment default Status Migration ID Migration Name ----------------------------------------- up 20151007022746 CreateUsers up 20151007023000 CreateBookmarks up 20151007023018 CreateTags up 20151007023029 CreateBookmarksTags |
Status が up となっているのが適用済みのマイグレーション。まだ実行されていないマイグレーションは Status が down と表示される。Rails といっしょ。
マイグレーションのロールバック
適用せたマイグレーションを元に戻して、マイグレーション実行前の DB 状態に戻したい場合はロールバックを行えます。これも Rails と同じ感じで使えて便利。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ bin/cake migrations rollback Welcome to CakePHP v3.1.0 Console --------------------------------------------------------------- App : src Path: /Path/to/CakePHP/tutorials/bookmarker/src/ PHP : 5.5.19 --------------------------------------------------------------- using migration path /Path/to/CakePHP/tutorials/bookmarker/config/Migrations using environment default using adapter mysql using database cake_bookmarks == 20151007023029 CreateBookmarksTags: reverting == 20151007023029 CreateBookmarksTags: reverted 0.0607s All Done. Took 0.0775s |
ロールバック後にマイグレーションのステータスを確認。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ bin/cake migrations status Welcome to CakePHP v3.1.0 Console --------------------------------------------------------------- App : src Path: /Path/to/CakePHP/tutorials/bookmarker/src/ PHP : 5.5.19 --------------------------------------------------------------- using migration path /Path/to/CakePHP/tutorials/bookmarker/config/Migrations using environment default Status Migration ID Migration Name ----------------------------------------- up 20151007022746 CreateUsers up 20151007023000 CreateBookmarks up 20151007023018 CreateTags down 20151007023029 CreateBookmarksTags |
最後のぶんのマイグレーションがロールバックされて、Status が down となっています。
スキャフォールドでMVCファイルを作成
続いて、公式チュートリアル通りにスキャフォールドを使って、MVC のファイルを自動生成してみます。これまでの作業で CakePHP の規約に従っているので、スキャフォールディングによる自動生成が可能です。
1 2 3 4 5 |
$ bin/cake bake all users ... Bake All complete. |
bake コマンドによるスキャフォールドで、以下のファイル群が生成されました。
モデル
src/Model/Table/UsersTable.php
src/Model/Entity/User.php
モデル・テスト
tests/Fixture/UsersFixture.php
tests/TestCase/Model/Table/UsersTableTest.php
コントローラー
src/Controller/UsersController.php
コントローラー・テスト
tests/TestCase/Controller/UsersControllerTest.php
ビュー(Template)
src/Template/Users/index.ctp
src/Template/Users/view.ctp
src/Template/Users/add.ctp
src/Template/Users/edit.ctp
Rails とほぼ同様なのでとても分かりやすい。
他のモデルについてもスキャフォールドを実行する。
1 2 3 4 |
$ bin/cake bake all bookmarks $ bin/cake bake all tags |
bookmarks_tags はブックマークとタグを多対多で関連付けるためだけの中間テーブルなので、スキャフォールドしない。
ここまでできたら、サーバーを起動してブラウザで動作確認してみます。
1 2 3 |
$ bin/cake server |
http://localhost:8765/bookmarks/ にアクセス。スキャフォールドで自動生成されたブックマーク・アプリケーションが動作するのを確認できるかと思います。
スキャフォールドを使わずに MVC ファイルを生成
スキャフォールドを使わずにモデル、コントローラー、ビューのファイルを個別に生成するには、以下のように bin/cake bake コマンドを使う。
1 2 3 4 5 |
$ bin/cake bake model Users $ bin/cake bake controller Users $ bin/cake bake template Users |
CakePHP、MySQLのビューテーブルでbakeしてみる – Qiita
パスワードをハッシュ化
試しに http://localhost:8765/users/add からユーザーを追加してみます。
Email: email@example.com
Password: password
でユーザーを追加。
http://localhost:8765/users のページで、パスワードがそのまま平文で表示されており、これはちょっとまずいですので、パスワードを暗号化(ハッシュ化)して保存するようにします。
モデル・エンティティのファイルにパスワードをハッシュ化するコードを追加して、以下のように編集する。
src/Model/Entity/User.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?php namespace App\Model\Entity; use Cake\Auth\DefaultPasswordHasher; use Cake\ORM\Entity; /** * User Entity. * * @property int $id * @property string $email * @property string $password * @property \Cake\I18n\Time $created * @property \Cake\I18n\Time $modified * @property \App\Model\Entity\Bookmark[] $bookmarks */ class User extends Entity { // hash user's password protected function _setPassword($value) { $hasher = new DefaultPasswordHasher(); return $hasher->hash($value); } /** * Fields that can be mass assigned using newEntity() or patchEntity(). * * Note that when '*' is set to true, this allows all unspecified fields to * be mass assigned. For security purposes, it is advised to set '*' to false * (or remove it), and explicitly make individual fields accessible as needed. * * @var array */ protected $_accessible = [ '*' => true, 'id' => false, ]; } |
もう一度 http://localhost:8765/users/add からユーザーを追加してみます。
Email: email2@example.com
Password: password
でユーザーを追加。http://localhost:8765/users で確認すると、ハッシュ化して保存されたパスワードが表示されています。
ブックマークをタグで探せるようにする
Bookmark にオブジェクトを保存した後に、タグで検索できるようにする。具体的には…
http://localhost:8765/bookmarks/tagged/funny/cat/gifs
の URL で funny, cat, gifs のいずれかのタグが付けられたブックマークを検索して一覧表示できるようにします。
config/routes.php(ルーティング)
まずはルーティングの設定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php use Cake\Core\Plugin; use Cake\Routing\Router; Router::defaultRouteClass('Route'); // bookmarks/tagged/**/**/** Router::scope('/bookmarks', ['controller' => 'Bookmarks'], function ($routes) { $routes->connect('/tagged/*', ['action' => 'tags']); } ); // load default routes Router::scope('/', function ($routes) { $routes->connect('/', ['controller' => 'Bookmarks', 'action' => 'index']); $routes->fallbacks('InflectedRoute'); }); // load all plugin routes Plugin::routes(); |
続いて、BookmarksController に tags アクションを追加。
src/Controller/BookmarksController.php(コントローラー)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\Controller; use App\Controller\AppController; /** * Bookmarks Controller * * @property \App\Model\Table\BookmarksTable $Bookmarks */ class BookmarksController extends AppController { // ... // search bookmarks by tags public function tags() { $tags = $this->request->params['pass']; // Use the BookmarksTable to find tagged bookmarks. $bookmarks = $this->Bookmarks->find('tagged', [ 'tags' => $tags ]); // Pass variables into the view template context. $this->set([ 'bookmarks' => $bookmarks, 'tags' => $tags ]); } } |
BookmarksController の tags アクション内で使っている find(‘tagged’) を実行するための find メソッドを、Bookmarks のモデル(BookmarksTable)に実装します。
src/Model/Table/BookmarksTable.php(モデル)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php namespace App\Model\Table; use App\Model\Entity\Bookmark; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; /** * Bookmarks Model * * @property \Cake\ORM\Association\BelongsTo $Users * @property \Cake\ORM\Association\BelongsToMany $Tags */ class BookmarksTable extends Table { // ... // to find('tagged') in our controller action. public function findTagged(Query $query, array $options) { return $this->find() ->distinct(['Bookmarks.id']) ->matching('Tags', function ($q) use ($options) { return $q->where(['Tags.title IN' => $options['tags']]); }); } } |
findTagged() メソッドを BookmarksTable.php に実装することで、BookmarksController で find(‘tagged’) メソッドが動作するようになるという仕組みらしい。
捕捉… Model/Entity/Bookmark.php と Model/Table/BookmarksTable.php の区別についてですが、モデルのインスタンスメソッドは Model/Entity に実装、find メソッドなどのクラスメソッドは Model/Table に実装という使い分けっぽい。この辺りの CakePHP3 でのモデルの区分は Rails とちょっと違っています。
src/Template/Bookmarks/tags.ctp(ビュー)
最後に tags.ctp を作成して以下の内容のビューを追記。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<h1> Bookmarks tagged with <?= $this->Text->toList($tags) ?> </h1> <section> <?php foreach ($bookmarks as $bookmark): ?> <article> <!-- Use the HtmlHelper to create a link --> <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4> <small><?= h($bookmark->url) ?></small> <!-- Use the TextHelper to format text --> <?= $this->Text->autoParagraph($bookmark->description) ?> </article> <?php endforeach; ?> </section> |
タグ・ブックマークを追加してブラウザで動作確認
まず、http://localhost:8765/tags/add からタグを追加します。
Title: funny
Title: cat
Title: gifs
Title: poem
以上を追加。いずれも Bookmarks は空欄にしとく。
続いて、http://localhost:8765/bookmarks/add からブックマークをいくつか追加します。
User: 1
Title: Cute cat
Description: This is a cute cat.
Url: http://example.com/cat
Tags: cat
User: 1
Title: Funny gifs
Description: These are funny gifs.
Url: http://example.com/gifs
Tags: funny, gifs(複数選択)
User: 2
Title: Good poem
Description: This is a good poem.
Url: http://example.com/poem
Tags: poem
ブラウザで、以下のURL等にアクセスする。
http://localhost:8765/bookmarks/tagged/funny
http://localhost:8765/bookmarks/tagged/cat
http://localhost:8765/bookmarks/tagged/poem
http://localhost:8765/bookmarks/tagged/funny/gifs
http://localhost:8765/bookmarks/tagged/funny/cat
http://localhost:8765/bookmarks/tagged/funny/cat/gifs
などにアクセスして、http://localhost:8765/bookmarks/tagged/**/**/** の URL に含まれるいずれかのタグで、ブックマークが検索されて一覧表示できることを確認します。
これでブックマークチュートリアルの Part 1 は終了です。一応 CakePHP 製のアプリケーションが動作するようになりました。ブックマークチュートリアルの Part 2 のドキュメントは、本日時点(2015/10/31)ではまだ日本語翻訳されておらず英語のみですが、以下英語のチュートリアルを参考にして引き続き進めます。
Bookmarker Tutorial Part 2 – CakePHP Cookbook 3.x documentation
Bookmarker Tutorial Part 2 では、認証・認可、ビュー周りの改善などを行います。
ログイン認証の機能を追加
ログイン認証を実装するために、AppController で Auth コンポーネントを読み込みます。
src/Controller/AppController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<?php namespace App\Controller; use Cake\Controller\Controller; use Cake\Event\Event; class AppController extends Controller { /** * Initialization hook method. * * Use this method to add common initialization code like loading components. * * e.g. `$this->loadComponent('Security');` * * @return void */ public function initialize() { parent::initialize(); $this->loadComponent('RequestHandler'); $this->loadComponent('Flash'); $this->loadComponent('Auth', [ 'authenticate' => [ 'Form' => [ 'fields' => [ 'username' => 'email', 'password' => 'password' ] ] ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login' ] ]); // Allow the display action so our pages controller // continues to work. $this->Auth->allow(['display']); } // ... } |
UsersController に login メソッドを追加。
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace App\Controller; use App\Controller\AppController; class UsersController extends AppController { // ... public function login() { if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); $this->Flash->success('You are now logged in.'); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('Your username or password is incorrect.'); } } } |
ログインページのビュー作成。
src/Template/Users/login.ctp
1 2 3 4 5 6 7 |
<h1>Login</h1> <?= $this->Form->create() ?> <?= $this->Form->input('email') ?> <?= $this->Form->input('password') ?> <?= $this->Form->button('Login') ?> <?= $this->Form->end() ?> |
ログアウトの機能を追加
UsersController に logout メソッドを追加。
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace App\Controller; use App\Controller\AppController; class UsersController extends AppController { // ... public function logout() { $this->Flash->success('You are now logged out.'); return $this->redirect($this->Auth->logout()); } } |
ログイン・ログアウトは認証なしでアクセス許可する
ログイン、ログアウトのアクションは認証なしでアクセスできるようにします。
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users */ class UsersController extends AppController { public function beforeFilter(\Cake\Event\Event $event) { $this->Auth->allow(['login']); $this->Auth->allow(['logout']); } // ... } |
これで、/users/logout にアクセスするとログアウトできる。
CakePHP Authコンポーネントを使ってみた | Yasigani-ni Blog
php – CakePHP 3: users not allowed to logout? – Stack Overflow
ユーザー登録の機能を有効にする
ログインしていない状態で、サインアップのために /users/add にアクセスすると、ログインページ(/users/login)にリダイレクトされる。なので、UsersController で /users/add へのアクセスは、ログインにしていない状態でも許可する。
src/Controller/UsersController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users */ class UsersController extends AppController { public function beforeFilter(\Cake\Event\Event $event) { $this->Auth->allow(['login']); $this->Auth->allow(['logout']); $this->Auth->allow(['add']); } // ... } |
ブックマークへのアクセスをログインユーザー本人のみに制限する
ユーザーがログイン時に、ユーザー自身が追加したブックマークのみを操作できるように制限します。
AppController で一旦全てのアクションへのアクセスを拒否する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace App\Controller; use Cake\Controller\Controller; use Cake\Event\Event; class AppController extends Controller { // ... public function isAuthorized($user) { return false; } } |
isAuthorized(アクションを認可するかどうか)で false を返して、ログインしていないユーザーには、アクションを実行させないようにする。これがデフォルトの設定となり、AppController を継承する BookmarksController などで、isAuthorized() メソッドをオーバーライドして、認可するアクションを設定します。
AppController に「’authorize’ => ‘Controller’,」の行を追加。
src/Controller/AppController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<?php namespace App\Controller; use Cake\Controller\Controller; use Cake\Event\Event; class AppController extends Controller { public function initialize() { parent::initialize(); $this->loadComponent('RequestHandler'); $this->loadComponent('Flash'); $this->loadComponent('Auth', [ 'authorize' => 'Controller', 'authenticate' => [ 'Form' => [ 'fields' => [ 'username' => 'email', 'password' => 'password' ] ] ], 'loginAction' => [ 'controller' => 'Users', 'action' => 'login' ] ]); // Allow the display action so our pages controller // continues to work. $this->Auth->allow(['display']); } // ... } |
BookmarksController で認可するアクションの設定。
src/Controller/BookmarksController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php namespace App\Controller; use App\Controller\AppController; /** * Bookmarks Controller * * @property \App\Model\Table\BookmarksTable $Bookmarks */ class BookmarksController extends AppController { public function isAuthorized($user) { $action = $this->request->params['action']; // The add and index actions are always allowed. if (in_array($action, ['index', 'add', 'tags'])) { return true; } // All other actions require an id. if (empty($this->request->params['pass'][0])) { return false; } // Check that the bookmark belongs to the current user. $id = $this->request->params['pass'][0]; $bookmark = $this->Bookmarks->get($id); if ($bookmark->user_id == $user['id']) { return true; } return parent::isAuthorized($user); } // ... } |
これでログイン中のユーザーのみ、ブックマークの閲覧(index)、タグ検索(tags)、追加(add)ができるようになる。また、ブックマークの削除(delete)、編集(edit)などの操作は、ログイン中のユーザー自身が作成したもののみ可能になります。
ブックマークのビューとアクションを編集
user_id はコントローラー側で設定するために、ビューのフォーム中の user_id のフィールドを削除します。
src/Template/Bookmarks/add.ctp
src/Template/Bookmarks/edit.ctp
1 |
echo $this->Form->input('user_id', ['options' => $users]); |
上記の行を削除する。
続いて、BookmarksController#add(), BookmarksController#edit() を編集。
src/Controller/BookmarksController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace App\Controller; use App\Controller\AppController; class BookmarksController extends AppController { // ... public function add() { $bookmark = $this->Bookmarks->newEntity(); if ($this->request->is('post')) { $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->data); $bookmark->user_id = $this->Auth->user('id'); if ($this->Bookmarks->save($bookmark)) { $this->Flash->success(__('The bookmark has been saved.')); return $this->redirect(['action' => 'index']); } else { $this->Flash->error(__('The bookmark could not be saved. Please, try again.')); } } $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } public function edit($id = null) { $bookmark = $this->Bookmarks->get($id, [ 'contain' => ['Tags'] ]); if ($this->request->is(['patch', 'post', 'put'])) { $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->data); $bookmark->user_id = $this->Auth->user('id'); if ($this->Bookmarks->save($bookmark)) { $this->Flash->success(__('The bookmark has been saved.')); return $this->redirect(['action' => 'index']); } else { $this->Flash->error(__('The bookmark could not be saved. Please, try again.')); } } $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } // ... } |
ブックマーク一覧ページを改善
/bookmarks/index のブックマーク一覧ページで、ユーザー自身が追加したブックマークのみの一覧を表示するようにする。
src/Controller/BookmarksController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace App\Controller; use App\Controller\AppController; class BookmarksController extends AppController { // ... public function index() { $this->paginate = [ 'conditions' => [ 'Bookmarks.user_id' => $this->Auth->user('id'), ] ]; $this->set('bookmarks', $this->paginate($this->Bookmarks)); $this->set('_serialize', ['bookmarks']); } // ... } |
タグ用の入力フィールドを作成
ビューに tag_string という名前のフィールドを追加して、タグをカンマ区切りで入力できるようにします。add.ctp, edit.ctp を編集。
src/Template/Bookmarks/add.ctp
src/Template/Bookmarks/edit.ctp
1 |
echo $this->Form->input('tags._ids', ['options' => $tags]); |
を
1 |
echo $this->Form->input('tag_string', ['type' => 'text']); |
に置き換える。add.ctp, edit.ctp 両方とも編集します。
tag_string フィールドから値を取得する _getTagString() メソッドを Bookmark モデルに追加。$_accessible の設定も行います。
src/Model/Entity/Bookmark.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php namespace App\Model\Entity; use Cake\ORM\Entity; use Cake\Collection\Collection; class Bookmark extends Entity { // get tag string from tag_string input field protected function _getTagString() { if (isset($this->_properties['tag_string'])) { return $this->_properties['tag_string']; } if (empty($this->tags)) { return ''; } $tags = new Collection($this->tags); $str = $tags->reduce(function ($string, $tag) { return $string . $tag->title . ', '; }, ''); return trim($str, ', '); } protected $_accessible = [ 'user_id' => true, 'title' => true, 'description' => true, 'url' => true, 'user' => true, 'tags' => true, 'tag_string' => true, 'id' => false, ]; } |
フィールドに入力された Tag String を保存する
src/Model/Table/BookmarksTable.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<?php namespace App\Model\Table; use App\Model\Entity\Bookmark; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; class BookmarksTable extends Table { // build tags from tag_string input field before save it public function beforeSave($event, $entity, $options) { if ($entity->tag_string) { $entity->tags = $this->_buildTags($entity->tag_string); } } protected function _buildTags($tagString) { $new = array_unique(array_map('trim', explode(',', $tagString))); $out = []; $query = $this->Tags->find() ->where(['Tags.title IN' => $new]); // Remove existing tags from the list of new tags. foreach ($query->extract('title') as $existing) { $index = array_search($existing, $new); if ($index !== false) { unset($new[$index]); } } // Add existing tags. foreach ($query as $tag) { $out[] = $tag; } // Add new tags. foreach ($new as $tag) { $out[] = $this->Tags->newEntity(['title' => $tag]); } return $out; } // ... |
以上で、公式のチュートリアルの内容はおしまい。残りの作業としては、BookmarksController, UsersController, TagsController で適宜アクセス制御を設定するなど。
ログインしていないユーザーにもアクセスを許可するアクションを追加するには…
1 |
$this->Auth->allow(['some_action']); |
を追加する。
また、アクション認可の細かな設定については…
1 |
public function isAuthorized($user) |
をオーバーライドして行います。UsersController の edit, delete などについては、BookmarksController 同様に、ログイン中ユーザー自身のみに許可するように設定すべきでしょう。
これで CakePHP のブックマークチュートリアルは終了です。お疲れ様でした!記載内容に間違いなどありましたら、ご連絡頂けますと助かります。m(_ _)m
- PHP & CakePHP の関連記事
- PHP+MySQLでNo such file or directoryエラー
- bin/cakeコマンドでintlエラーが出る場合の対処(CakePHP)
- CakePHPアプリケーションをCapistranoでデプロイ
- Integrity constraint violation:Column ‘created’ in order clause is ambiguousエラー/CakePHP
- CakePHPでDB関連テーブルのレコード・データを取得
- CakePHPでカラム属性に別名/エイリアスを付ける仮想フィールド
- CakePHPで日付選択フォームのカスタマイズ
- CakePHP3で現在のコントローラー名・アクション名を取得
- PHPインストールでconfigure: error: freetype.h not foundエラー
- CakePHPでログイン後に元のページにリダイレクトさせる
- 初回公開日: 2015年10月31日
Leave Your Message!