cakephp3はもう使えるかもしれないので
チュートリアルからやってみる
Bookmarker Tutorial Part 1
composer.pharを取得して初期化
curl -s https://getcomposer.org/installer | php php composer.phar create-project --prefer-dist -s dev cakephp/app bookmarker
ディレクトリ確認
[vagrant@localhost app2]$ ll bookmarker/ total 48 drwxrwxrwx 1 vagrant vagrant 170 Feb 7 10:22 bin -rwxrwxrwx 1 vagrant vagrant 1226 Feb 7 10:22 composer.json -rwxrwxrwx 1 vagrant vagrant 31003 Feb 8 08:53 composer.lock drwxrwxrwx 1 vagrant vagrant 306 Feb 8 08:53 config -rwxrwxrwx 1 vagrant vagrant 648 Feb 7 10:22 index.php drwxrwxrwx 1 vagrant vagrant 102 Feb 7 10:22 logs -rwxrwxrwx 1 vagrant vagrant 819 Feb 7 10:22 phpunit.xml.dist drwxrwxrwx 1 vagrant vagrant 102 Feb 7 10:22 plugins -rwxrwxrwx 1 vagrant vagrant 944 Feb 7 10:22 README.md drwxrwxrwx 1 vagrant vagrant 272 Feb 7 10:22 src drwxrwxrwx 1 vagrant vagrant 170 Feb 7 10:22 tests drwxrwxrwx 1 vagrant vagrant 170 Feb 7 10:22 tmp drwxrwxrwx 1 vagrant vagrant 544 Feb 8 08:55 vendor drwxrwxrwx 1 vagrant vagrant 272 Feb 7 10:22 webroot [vagrant@localhost app2]$
データベースを作成
create database cake_bookmarks default charset utf8; GRANT ALL PRIVILEGES ON *.* TO 'cake_bookmarks'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
テーブルを作成
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, created DATETIME, updated DATETIME ); CREATE TABLE bookmarks ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, title VARCHAR(50), description TEXT, url TEXT, created DATETIME, updated DATETIME, FOREIGN KEY user_key (user_id) REFERENCES users(id) ); CREATE TABLE tags ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), created DATETIME, updated DATETIME, UNIQUE KEY (title) ); CREATE TABLE bookmarks_tags ( bookmark_id INT NOT NULL, tag_id INT NOT NULL, PRIMARY KEY (bookmark_id, tag_id), INDEX tag_idx (tag_id, bookmark_id), FOREIGN KEY tag_key(tag_id) REFERENCES tags(id), FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id) );
作成したテーブルを確認
データベースの設定を変更
config/app.php
'Datasources' => [ 'default' => [ 'className' => 'Cake\Database\Connection', 'driver' => 'Cake\Database\Driver\Mysql', 'persistent' => false, 'host' => 'localhost', /* * CakePHP will use the default DB port based on the driver selected * MySQL on MAMP uses port 8889, MAMP users will want to uncomment * the following line and set the port accordingly */ //'port' => 'nonstandard_port_number', 'username' => 'cake_bookmarks', 'password' => 'password', 'database' => 'cake_bookmarks', 'encoding' => 'utf8', 'timezone' => 'UTC', 'cacheMetadata' => true,
Scaffoldで雛形を作成する
bin/cake bake all users bin/cake bake all bookmarks bin/cake bake all tags
usersのエンティティにパスワード設定処理を追加
src/Model/Entity/User.php
<?php namespace App\Model\Entity; use Cake\ORM\Entity; use Cake\Auth\DefaultPasswordHasher; /** * User Entity. */ class User extends Entity { /** * Fields that can be mass assigned using newEntity() or patchEntity(). * * @var array */ protected $_accessible = [ 'email' => true, 'password' => true, 'bookmarks' => true, ]; protected function _setPassword($value) { $hasher = new DefaultPasswordHasher(); return $hasher->hash($value); } }
Specific Tag???
config/routes.php
<?php /** * Routes configuration * * In this file, you set up routes to your controllers and their actions. * Routes are very important mechanism that allows you to freely connect * different URLs to chosen controllers and their actions (functions). * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @license http://www.opensource.org/licenses/mit-license.php MIT License */ use Cake\Core\Plugin; use Cake\Routing\Router; /** * The default class to use for all routes * * The following route classes are supplied with CakePHP and are appropriate * to set as the default: * * - Route * - InflectedRoute * - DashedRoute * * If no call is made to `Router::defaultRouteClass`, the class used is * `Route` (`Cake\Routing\Route\Route`) * * Note that `Route` does not do any inflections on URLs which will result in * inconsistently cased URLs when used with `:plugin`, `:controller` and * `:action` markers. * */ Router::defaultRouteClass('Route'); Router::scope('/', function ($routes) { /** * Here, we are connecting '/' (base path) to a controller called 'Pages', * its action called 'display', and we pass a param to select the view file * to use (in this case, src/Template/Pages/home.ctp)... */ $routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); /** * ...and connect the rest of 'Pages' controller's URLs. */ $routes->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); /** * Connect catchall routes for all controllers. * * Using the argument `InflectedRoute`, the `fallbacks` method is a shortcut for * `$routes->connect('/:controller', ['action' => 'index'], ['routeClass' => 'InflectedRoute']);` * `$routes->connect('/:controller/:action/*', [], ['routeClass' => 'InflectedRoute']);` * * Any route class can be used with this method, such as: * - DashedRoute * - InflectedRoute * - Route * - Or your own route class * * You can remove these routes once you've connected the * routes you want in your application. */ $routes->fallbacks('InflectedRoute'); }); // 追加 Router::scope( '/bookmarks', ['controller' => 'Bookmarks'], function ($routes) { $routes->connect('/tagged/*', ['action' => 'tags']); } ); /** * Load all plugin routes. See the Plugin documentation on * how to customize the loading of plugin routes. */ Plugin::routes();
タグ検索用のアクションを追加
src/Controller/BookmarksController.php
<?php namespace App\Controller; use App\Controller\AppController; /** * Bookmarks Controller * * @property \App\Model\Table\BookmarksTable $Bookmarks */ class BookmarksController extends AppController { /** * Index method * * @return void */ public function index() { $this->paginate = [ 'contain' => ['Users'] ]; $this->set('bookmarks', $this->paginate($this->Bookmarks)); $this->set('_serialize', ['bookmarks']); } /** * View method * * @param string|null $id Bookmark id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $bookmark = $this->Bookmarks->get($id, [ 'contain' => ['Users', 'Tags', 'BookmarksTags'] ]); $this->set('bookmark', $bookmark); $this->set('_serialize', ['bookmark']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $bookmark = $this->Bookmarks->newEntity(); if ($this->request->is('post')) { $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->data); 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.'); } } $users = $this->Bookmarks->Users->find('list', ['limit' => 200]); $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } /** * Edit method * * @param string|null $id Bookmark id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ 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); 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.'); } } $users = $this->Bookmarks->Users->find('list', ['limit' => 200]); $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } /** * Delete method * * @param string|null $id Bookmark id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $bookmark = $this->Bookmarks->get($id); if ($this->Bookmarks->delete($bookmark)) { $this->Flash->success('The bookmark has been deleted.'); } else { $this->Flash->error('The bookmark could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } // 追加 public function tags() { $tags = $this->request->params['pass']; $bookmarks = $this->Bookmarks->find('tagged', [ 'tags' => $tags ]); $this->set(compact('bookmarks', 'tags')); } }
タグの検索用メソッドを付与
src/Model/Table/BookmarksTable.php
<?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 */ class BookmarksTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { $this->table('bookmarks'); $this->displayField('title'); $this->primaryKey('id'); $this->addBehavior('Timestamp'); $this->belongsTo('Users', [ 'foreignKey' => 'user_id' ]); $this->belongsToMany('Tags', [ 'foreignKey' => 'bookmark_id', 'targetForeignKey' => 'tag_id', 'joinTable' => 'bookmarks_tags' ]); } /** * Default validation rules. * * @param \Cake\Validation\Validator $validator Validator instance. * @return \Cake\Validation\Validator */ public function validationDefault(Validator $validator) { $validator ->add('id', 'valid', ['rule' => 'numeric']) ->allowEmpty('id', 'create') ->add('user_id', 'valid', ['rule' => 'numeric']) ->requirePresence('user_id', 'create') ->notEmpty('user_id') ->allowEmpty('title') ->allowEmpty('description') ->allowEmpty('url'); return $validator; } /** * Returns a rules checker object that will be used for validating * application integrity. * * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. * @return \Cake\ORM\RulesChecker */ public function buildRules(RulesChecker $rules) { $rules->add($rules->existsIn(['user_id'], 'Users')); return $rules; } // 追加 public function findTagged(Query $query, array $options) { $fields = [ 'Bookmarks.id', 'Bookmarks.title', 'Bookmarks.url', ]; return $this->find() ->distinct($fields) ->matching('Tags', function ($q) use ($options) { return $q->where(['Tags.title IN' => $options['tags']]); }); } }
タグ用のビューを作成
src/Template/Bookmarks/tags.ctp
<h1> Bookmarks tagged with <?= $this->Text->toList($tags) ?> </h1> <section> <?php foreach ($bookmarks as $bookmark): ?> <article> <h4><?= $this->Html->link($bookmark->title, $bookmark->url) ?></h4> <small><?= h($bookmark->url) ?></small> <?= $this->Text->autoParagraph($bookmark->description) ?> </article> <?php endforeach; ?> </section>
Bookmarker Tutorial Part 2
認証設定を追加
src/Controller/AppController.php
<?php /** * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @since 0.2.9 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ namespace App\Controller; use Cake\Controller\Controller; /** * Application Controller * * Add your application-wide methods in the class below, your controllers * will inherit them. * * @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller */ class AppController extends Controller { /** * Initialization hook method. * * Use this method to add common initialization code like loading components. * * @return void */ public function initialize() { $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']); } }
※認証設定用の処理を記載
ログイン処理を追加
src/Controller/UsersController.php
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users */ class UsersController extends AppController { /** * Index method * * @return void */ public function index() { $this->set('users', $this->paginate($this->Users)); $this->set('_serialize', ['users']); } /** * View method * * @param string|null $id User id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $user = $this->Users->get($id, [ 'contain' => ['Bookmarks'] ]); $this->set('user', $user); $this->set('_serialize', ['user']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $user = $this->Users->newEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Edit method * * @param string|null $id User id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Delete method * * @param string|null $id User id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $user = $this->Users->get($id); if ($this->Users->delete($user)) { $this->Flash->success('The user has been deleted.'); } else { $this->Flash->error('The user could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } // ログイン処理を追加 public function login() { if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('Your username or password is incorrect.'); } } }
ログインページを追加
src/Template/Users/login.ctp
<h1>Login</h1> <?= $this->Form->create() ?> <?= $this->Form->input('email') ?> <?= $this->Form->input('password') ?> <?= $this->Form->button('Login') ?> <?= $this->Form->end() ?>
ログアウト処理を追加
src/Controller/UsersController.php
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users */ class UsersController extends AppController { /** * Index method * * @return void */ public function index() { $this->set('users', $this->paginate($this->Users)); $this->set('_serialize', ['users']); } /** * View method * * @param string|null $id User id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $user = $this->Users->get($id, [ 'contain' => ['Bookmarks'] ]); $this->set('user', $user); $this->set('_serialize', ['user']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $user = $this->Users->newEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Edit method * * @param string|null $id User id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Delete method * * @param string|null $id User id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $user = $this->Users->get($id); if ($this->Users->delete($user)) { $this->Flash->success('The user has been deleted.'); } else { $this->Flash->error('The user could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } public function login() { if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('Your username or password is incorrect.'); } } // ログアウト処理を追加 public function logout() { $this->Flash->success('You are now logged out.'); return $this->redirect($this->Auth->logout()); } }
ユーザーの新規追加処理のアクセス許可を設定
src/Controller/UsersController.php
<?php namespace App\Controller; use App\Controller\AppController; /** * Users Controller * * @property \App\Model\Table\UsersTable $Users */ class UsersController extends AppController { // addアクションは認証不要 public function beforeFilter(\Cake\Event\Event $event) { $this->Auth->allow(['add']); } /** * Index method * * @return void */ public function index() { $this->set('users', $this->paginate($this->Users)); $this->set('_serialize', ['users']); } /** * View method * * @param string|null $id User id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $user = $this->Users->get($id, [ 'contain' => ['Bookmarks'] ]); $this->set('user', $user); $this->set('_serialize', ['user']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $user = $this->Users->newEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Edit method * * @param string|null $id User id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function edit($id = null) { $user = $this->Users->get($id, [ 'contain' => [] ]); if ($this->request->is(['patch', 'post', 'put'])) { $user = $this->Users->patchEntity($user, $this->request->data); if ($this->Users->save($user)) { $this->Flash->success('The user has been saved.'); return $this->redirect(['action' => 'index']); } else { $this->Flash->error('The user could not be saved. Please, try again.'); } } $this->set(compact('user')); $this->set('_serialize', ['user']); } /** * Delete method * * @param string|null $id User id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $user = $this->Users->get($id); if ($this->Users->delete($user)) { $this->Flash->success('The user has been deleted.'); } else { $this->Flash->error('The user could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } public function login() { if ($this->request->is('post')) { $user = $this->Auth->identify(); if ($user) { $this->Auth->setUser($user); return $this->redirect($this->Auth->redirectUrl()); } $this->Flash->error('Your username or password is incorrect.'); } } public function logout() { $this->Flash->success('You are now logged out.'); return $this->redirect($this->Auth->logout()); } }
AppControllerに認証有無を設定する
src/Controller/AppController.php
<?php /** * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @since 0.2.9 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ namespace App\Controller; use Cake\Controller\Controller; /** * Application Controller * * Add your application-wide methods in the class below, your controllers * will inherit them. * * @link http://book.cakephp.org/3.0/en/controllers.html#the-app-controller */ class AppController extends Controller { /** * Initialization hook method. * * Use this method to add common initialization code like loading components. * * @return void */ public function initialize() { $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']); } // メソッドを追加 public function isAuthorized($user) { return false; } }
BookmarksControllerの認証設定を確認
src/Controller/BookmarksController.php
<?php namespace App\Controller; use App\Controller\AppController; /** * Bookmarks Controller * * @property \App\Model\Table\BookmarksTable $Bookmarks */ class BookmarksController extends AppController { /** * Index method * * @return void */ public function index() { $this->paginate = [ 'contain' => ['Users'] ]; $this->set('bookmarks', $this->paginate($this->Bookmarks)); $this->set('_serialize', ['bookmarks']); } /** * View method * * @param string|null $id Bookmark id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $bookmark = $this->Bookmarks->get($id, [ 'contain' => ['Users', 'Tags', 'BookmarksTags'] ]); $this->set('bookmark', $bookmark); $this->set('_serialize', ['bookmark']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $bookmark = $this->Bookmarks->newEntity(); if ($this->request->is('post')) { $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->data); 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.'); } } $users = $this->Bookmarks->Users->find('list', ['limit' => 200]); $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } /** * Edit method * * @param string|null $id Bookmark id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ 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); 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.'); } } $users = $this->Bookmarks->Users->find('list', ['limit' => 200]); $tags = $this->Bookmarks->Tags->find('list', ['limit' => 200]); $this->set(compact('bookmark', 'users', 'tags')); $this->set('_serialize', ['bookmark']); } /** * Delete method * * @param string|null $id Bookmark id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $bookmark = $this->Bookmarks->get($id); if ($this->Bookmarks->delete($bookmark)) { $this->Flash->success('The bookmark has been deleted.'); } else { $this->Flash->error('The bookmark could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } // 追加 public function tags() { $tags = $this->request->params['pass']; $bookmarks = $this->Bookmarks->find('tagged', [ 'tags' => $tags ]); $this->set(compact('bookmarks', 'tags')); } // 認証有無確認メソッド追加 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); } }
認証メッセージを追加
src/Template/Layout/default.ctp
<?php /** * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @since 0.10.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ $cakeDescription = 'CakePHP: the rapid development php framework'; ?> <!DOCTYPE html> <html> <head> <?= $this->Html->charset() ?> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> <?= $cakeDescription ?>: <?= $this->fetch('title') ?> </title> <?= $this->Html->meta('icon') ?> <?= $this->Html->css('base.css') ?> <?= $this->Html->css('cake.css') ?> <?= $this->fetch('meta') ?> <?= $this->fetch('css') ?> <?= $this->fetch('script') ?> </head> <body> <header> <div class="header-title"> <span><?= $this->fetch('title') ?></span> </div> <div class="header-help"> <span><a target="_blank" href="http://book.cakephp.org/3.0/">Documentation</a></span> <span><a target="_blank" href="http://api.cakephp.org/3.0/">API</a></span> </div> </header> <div id="container"> <div id="content"> <?= $this->Flash->render() ?> <?= $this->Flash->render('auth') ?> <div class="row"> <?= $this->fetch('content') ?> </div> </div> <footer> </footer> </div> </body> </html>
BookmarksControllerを編集して追加、編集、リスト参照
<?php namespace App\Controller; use App\Controller\AppController; /** * Bookmarks Controller * * @property \App\Model\Table\BookmarksTable $Bookmarks */ class BookmarksController extends AppController { /** * Index method * * @return void */ public function index() { $this->paginate = [ 'conditions' => [ 'Bookmarks.user_id' => $this->Auth->user('id'), ] ]; $this->set('bookmarks', $this->paginate($this->Bookmarks)); } /** * View method * * @param string|null $id Bookmark id. * @return void * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function view($id = null) { $bookmark = $this->Bookmarks->get($id, [ 'contain' => ['Users', 'Tags', 'BookmarksTags'] ]); $this->set('bookmark', $bookmark); $this->set('_serialize', ['bookmark']); } /** * Add method * * @return void Redirects on successful add, renders view otherwise. */ public function add() { $bookmark = $this->Bookmarks->newEntity($this->request->data); $bookmark->user_id = $this->Auth->user('id'); if ($this->request->is('post')) { if ($this->Bookmarks->save($bookmark)) { $this->Flash->success('The bookmark has been saved.'); return $this->redirect(['action' => 'index']); } $this->Flash->error('The bookmark could not be saved. Please, try again.'); } $tags = $this->Bookmarks->Tags->find('list'); $this->set(compact('bookmark', 'tags')); } /** * Edit method * * @param string|null $id Bookmark id. * @return void Redirects on successful edit, renders view otherwise. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ 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']); } $this->Flash->error('The bookmark could not be saved. Please, try again.'); } $tags = $this->Bookmarks->Tags->find('list'); $this->set(compact('bookmark', 'tags')); } /** * Delete method * * @param string|null $id Bookmark id. * @return void Redirects to index. * @throws \Cake\Network\Exception\NotFoundException When record not found. */ public function delete($id = null) { $this->request->allowMethod(['post', 'delete']); $bookmark = $this->Bookmarks->get($id); if ($this->Bookmarks->delete($bookmark)) { $this->Flash->success('The bookmark has been deleted.'); } else { $this->Flash->error('The bookmark could not be deleted. Please, try again.'); } return $this->redirect(['action' => 'index']); } // 追加 public function tags() { $tags = $this->request->params['pass']; $bookmarks = $this->Bookmarks->find('tagged', [ 'tags' => $tags ]); $this->set(compact('bookmarks', 'tags')); } // 認証有無確認メソッド追加 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); } }
Bookmarkのエンティティを編集
src/Model/Entity/Bookmark.php
<?php namespace App\Model\Entity; use Cake\ORM\Entity; use Cake\Collection\Collection; /** * Bookmark Entity. */ class Bookmark extends Entity { /** * Fields that can be mass assigned using newEntity() or patchEntity(). * * @var array */ protected $_accessible = [ 'user_id' => true, 'title' => true, 'description' => true, 'url' => true, 'user' => true, 'tags' => true, ]; 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, ', '); } }
$_accessibleを追加
protected $_accessible = [ 'user_id' => true, 'title' => true, 'description' => true, 'url' => true, 'user' => true, 'tags' => true, 'tag_string' => true, // 追加 ];
Viewを変更
src/Template/Bookmarks/add.ctp
<div class="actions columns large-2 medium-3"> <h3><?= __('Actions') ?></h3> <ul class="side-nav"> <li><?= $this->Html->link(__('List Bookmarks'), ['action' => 'index']) ?></li> <li><?= $this->Html->link(__('List Users'), ['controller' => 'Users', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New User'), ['controller' => 'Users', 'action' => 'add']) ?> </li> <li><?= $this->Html->link(__('List Bookmarks Tags'), ['controller' => 'BookmarksTags', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New Bookmarks Tag'), ['controller' => 'BookmarksTags', 'action' => 'add']) ?> </li> <li><?= $this->Html->link(__('List Tags'), ['controller' => 'Tags', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New Tag'), ['controller' => 'Tags', 'action' => 'add']) ?> </li> </ul> </div> <div class="bookmarks form large-10 medium-9 columns"> <?= $this->Form->create($bookmark); ?> <fieldset> <legend><?= __('Add Bookmark') ?></legend> <?php echo $this->Form->input('user_id', ['options' => $users]); echo $this->Form->input('title'); echo $this->Form->input('description'); echo $this->Form->input('url'); echo $this->Form->input('tag_string', ['type' => 'text']); ?> </fieldset> <?= $this->Form->button(__('Submit')) ?> <?= $this->Form->end() ?> </div>
src/Template/Bookmarks/edit.ctp
<div class="actions columns large-2 medium-3"> <h3><?= __('Actions') ?></h3> <ul class="side-nav"> <li><?= $this->Form->postLink( __('Delete'), ['action' => 'delete', $bookmark->id], ['confirm' => __('Are you sure you want to delete # {0}?', $bookmark->id)] ) ?></li> <li><?= $this->Html->link(__('List Bookmarks'), ['action' => 'index']) ?></li> <li><?= $this->Html->link(__('List Users'), ['controller' => 'Users', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New User'), ['controller' => 'Users', 'action' => 'add']) ?> </li> <li><?= $this->Html->link(__('List Bookmarks Tags'), ['controller' => 'BookmarksTags', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New Bookmarks Tag'), ['controller' => 'BookmarksTags', 'action' => 'add']) ?> </li> <li><?= $this->Html->link(__('List Tags'), ['controller' => 'Tags', 'action' => 'index']) ?> </li> <li><?= $this->Html->link(__('New Tag'), ['controller' => 'Tags', 'action' => 'add']) ?> </li> </ul> </div> <div class="bookmarks form large-10 medium-9 columns"> <?= $this->Form->create($bookmark); ?> <fieldset> <legend><?= __('Edit Bookmark') ?></legend> <?php echo $this->Form->input('user_id', ['options' => $users]); echo $this->Form->input('title'); echo $this->Form->input('description'); echo $this->Form->input('url'); echo $this->Form->input('tag_string', ['type' => 'text']); ?> </fieldset> <?= $this->Form->button(__('Submit')) ?> <?= $this->Form->end() ?> </div>
Tag String設定
src/Model/Table/BookmarksTable.php
<?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 */ class BookmarksTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { $this->table('bookmarks'); $this->displayField('title'); $this->primaryKey('id'); $this->addBehavior('Timestamp'); $this->belongsTo('Users', [ 'foreignKey' => 'user_id' ]); $this->belongsToMany('Tags', [ 'foreignKey' => 'bookmark_id', 'targetForeignKey' => 'tag_id', 'joinTable' => 'bookmarks_tags' ]); } /** * Default validation rules. * * @param \Cake\Validation\Validator $validator Validator instance. * @return \Cake\Validation\Validator */ public function validationDefault(Validator $validator) { $validator ->add('id', 'valid', ['rule' => 'numeric']) ->allowEmpty('id', 'create') ->add('user_id', 'valid', ['rule' => 'numeric']) ->requirePresence('user_id', 'create') ->notEmpty('user_id') ->allowEmpty('title') ->allowEmpty('description') ->allowEmpty('url'); return $validator; } /** * Returns a rules checker object that will be used for validating * application integrity. * * @param \Cake\ORM\RulesChecker $rules The rules object to be modified. * @return \Cake\ORM\RulesChecker */ public function buildRules(RulesChecker $rules) { $rules->add($rules->existsIn(['user_id'], 'Users')); return $rules; } // 追加 public function findTagged(Query $query, array $options) { $fields = [ 'Bookmarks.id', 'Bookmarks.title', 'Bookmarks.url', ]; return $this->find() ->distinct($fields) ->matching('Tags', function ($q) use ($options) { return $q->where(['Tags.title IN' => $options['tags']]); }); } 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; } }
結果
※やっつけだからなんかうまく動かないかも…
github
https://github.com/mshige1979/cakephp3_bookmark1
※一部は動かせそうだけど、認証とかいろいろ加工がまだまだ必要
所感
英語難しい、ソースを見てもうーん???っていう場合がある。
ある程度なんとかなる感じがしてきたけど
うまくとりかかる要因がまだないのでなんか作るアイデアを捻出しよう