laravel5でサンプルアプリ作成
サンプルアプリを作成する
簡単なブログチュートリアルみたいなもので記事の一覧と作成を行う
参考
Laravel5でシンプルなCRUDアプリを開発する : アシアルブログ
※一気にやってもうまくついていけないので少しだけ
DBのマイグレーション
create
$ php artisan make:migration create_articles_table Created Migration: 2015_05_10_104527_create_articles_table $
※関連したマイグレーションファイルを新規に作成する
ファイルの確認
$ ll database/migrations/ total 12 2014_10_12_000000_create_users_table.php 2014_10_12_100000_create_password_resets_table.php 2015_05_10_104527_create_articles_table.php $
※3つあるけど作ったのは最後のものだけで他のはそのままあった感じ
編集
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateArticlesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { // 新規作成 Schema::create('articles', function(Blueprint $table) { $table->increments('id'); $table->string('title'); $table->string('body'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { // 削除 Schema::drop('articles'); } }
マイグレーション実行
php artisan migrate Migration table created successfully. Migrated: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_100000_create_password_resets_table Migrated: 2015_05_10_104527_create_articles_table $
↓
コントローラーやモデルの用意
雛形を作成する
$ php artisan make:model Article Model created successfully. Created Migration: 2015_05_10_105751_create_articles_table $ php artisan make:controller ArticlesController Controller created successfully. $
※マイグレーションファイルを作成したけどいらないので削除しておく
モデルを編集する
app/Article.php
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { // table protected $table = "articles"; // fields protected $fillable = ["title", "body"]; }
コントローラーを編集する
app/Http/Controllers/ArticlesController.php
<?php namespace App\Http\Controllers; use App\Article; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class ArticlesController extends Controller { protected $article; public function __construct(Article $article){ $this->article = $article; } /** * Display a listing of the resource. * 一覧 * @return Response */ public function index() { // } /** * Show the form for creating a new resource. * 新規作成 * @return Response */ public function create() { // } /** * Store a newly created resource in storage. * 新規登録 * @return Response */ public function store() { // } /** * Display the specified resource. * 詳細 * @param int $id * @return Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * 編集 * @param int $id * @return Response */ public function edit($id) { // } /** * Update the specified resource in storage. * 更新 * @param int $id * @return Response */ public function update($id) { // } /** * Remove the specified resource from storage. * 削除 * @param int $id * @return Response */ public function destroy($id) { // } }
※メソッド名にgetとかいれなイカンみたいです
ルーティングを編集
app/Http/routes.php
<?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the controller to call when that URI is requested. | */ Route::get('/', 'WelcomeController@index'); Route::get('home', 'HomeController@index'); Route::controllers([ 'auth' => 'Auth\AuthController', 'password' => 'Auth\PasswordController', ]); // 追加 Route::get('/', 'ArticlesController@index'); Route::controller('articles', 'ArticlesController');
↓
※まだ、なにも表示していないので空
各機能の作り込み
app/Http/routes.php
<?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It's a breeze. Simply tell Laravel the URIs it should respond to | and give it the controller to call when that URI is requested. | */ // get Route::get('/', 'ArticlesController@index'); Route::get('articles/index', 'ArticlesController@index'); Route::get('articles/show/{id}', 'ArticlesController@show'); Route::get('articles/edit/{id}', 'ArticlesController@edit'); Route::get('articles/destroy/{id}', 'ArticlesController@destroy'); // post Route::post('articles/store', 'ArticlesController@store'); Route::post('articles/update/{id}', 'ArticlesController@update'); // resource Route::resource('articles', 'ArticlesController');
app/Http/Controllers/ArticlesController.php
<?php namespace App\Http\Controllers; use App\Article; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class ArticlesController extends Controller { protected $article; public function __construct(Article $article) { $this->article = $article; } /** * Display a listing of the resource. * 一覧 * @return Response */ public function index() { // $articles = $this->article->all(); return view('articles.index')->with(compact('articles')); } /** * Show the form for creating a new resource. * 新規作成 * @return Response */ public function create() { // 新規登録画面を表示 return view('articles.create'); } /** * Store a newly created resource in storage. * 新規登録 * @return Response */ public function store(Request $request) { // パラメータを取得して保存 $data = $request->all(); $this->article->fill($data); $this->article->save(); // 一覧画面へ遷移 return redirect()->to('/'); } /** * Display the specified resource. * 詳細 * @param int $id * @return Response */ public function show($id) { // $article = $this->article->find($id); return view('articles.show', compact('article')); } /** * Show the form for editing the specified resource. * 編集 * @param int $id * @return Response */ public function edit($id) { // $article = $this->article->find($id); return view('articles.edit')->withArticle($article); } /** * Update the specified resource in storage. * 更新 * @param int $id * @return Response */ public function update(Request $request, $id) { // $article = $this->article->find($id); $data = $request->all(); $article->fill($data); $article->save(); return redirect()->to('/'); } /** * Remove the specified resource from storage. * 削除 * @param int $id * @return Response */ public function destroy($id) { // $article = $this->article->find($id); $article->delete(); return redirect()->to('/'); } }
resources/views/articles/index.blade.php
@extends('app') @section('content') <h2 class="page-header">記事一覧</h2> <div> <a href="/articles/create" class="btn btn-primary">投稿</a> </div> <table class="table table-striped table-hover"> <thead> <tr> <th>タイトル</th> <th>本文</th> <th>作成日時</th> <th>更新日時</th> <th></th> </tr> </thead> <tbody> @foreach($articles as $article) <tr> <td>{{{ $article->title }}}</td> <td>{{{ $article->body }}}</td> <td>{{{ $article->created_at }}}</td> <td>{{{ $article->updated_at }}}</td> <td> <a href="/articles/show/{{{ $article->id }}}" class="btn btn-default btn-xs">詳細</a> <a href="/articles/edit/{{{ $article->id }}}" class="btn btn-success btn-xs">編集</a> <a href="/articles/destroy/{{{ $article->id }}}" class="btn btn-danger btn-xs">削除</a> </td> </tr> @endforeach </tbody> </table> @endsection
resources/views/articles/create.blade.php
@extends('app') @section('content') <h2 class="page-header"><a href="/">記事投稿</a></h2> <form name="form1" action="/articles/store" method="post"> <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> <div class="form-group"> <label>タイトル</label> <input type="text" name="title" value="" required="required" class="form-control" /> </div> <div class="form-group"> <label>本文</label> <textarea name="body" required="required" class="form-control"></textarea> </div> <button type="submit" class="btn btn-default">投稿</button> </form> @endsection
resources/views/articles/show.blade.php
@extends('app') @section('content') <h2 class="page-header"><a href="/">記事詳細</a></h2> <ul class="list-inline"> <li> <a href="/articles/edit/{{{ $article->id }}}" class="btn btn-primary pull-left">編集</a> </li> <li> <a href="/articles/destroy/{{{ $article->id }}}" class="btn btn-danger pull-left">削除</a> </li> </ul> <table class="table table-striped"> <tbody> <tr> <th>タイトル</th> <td>{{{ $article->title }}}</td> </tr> <tr> <th>本文</th> <td>{{{ $article->body }}}</td> </tr> <tr> <th>作成日時</th> <td>{{{ $article->created_at }}}</td> </tr> <tr> <th>更新日時</th> <td>{{{ $article->updated_at }}}</td> </tr> </tbody> </table> @endsection
resources/views/articles/edit.blade.php
@extends('app') @section('content') <h2 class="page-header"><a href="/">記事編集</a></h2> <form name="form1" action="/articles/update/{{ $article->id }}" method="post"> <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>"> <div class="form-group"> <label>タイトル</label> <input type="text" name="title" value="{{ $article->title }}" required="required" class="form-control" /> </div> <div class="form-group"> <label>本文</label> <textarea name="body" required="required" class="form-control">{{ $article->body }}</textarea> </div> <button type="submit" class="btn btn-default">編集</button> </form> @endsection
完成イメージ
所感
サンプルの通りにやろうとしたけどうまく動かなかったのでちょっと変えてやってみました。
なんとか動くようになったのでおk
laravel5の初期環境を構築
laravel5があるのでちょっと触ってみる
なんか以前やったけど保留中だったし最近メジャーな感じが感じるので…
ただ、今回はヘルパーとかモデルとかで便利なものがあっても使いません。
まだ、よく知らないので…
参考にさせて頂いたもの
Laravel5でシンプルなCRUDアプリを開発する : アシアルブログ
Laravel5初期設定&環境設定まとめ - 魔法使いの卵
※基本、参考にしたのはインストールとDB,デバッガーあたりです
やったこと
composerを入れてappを用意
curl -sS https://getcomposer.org/installer | php php composer.phar create-project laravel/laravel sampleapp1 --prefer-dist
※もう、composerはデフォルトで別の場所に置いた方がいいかも…
データベースの作成
mysql> CREATE DATABASE `l5_sample1_db`; mysql> GRANT ALL PRIVILEGES ON *.* TO 'l5_user'@'localhost' IDENTIFIED BY 'password' WITH GRANT OPTION;
.envを編集
DB_HOST=localhost DB_DATABASE=l5_sample1_db DB_USERNAME=l5_user DB_PASSWORD=password
config/app.phpを編集
<?php return [ 'timezone' => 'Asia/Tokyo', 'locale' => 'ja', 'fallback_locale' => 'ja', ];
※基本的なものはこれだけ、他にもあるけど省略
デバッグ用のものを追加インストール
php ../composer.phar require barryvdh/laravel-debugbar
config/app.phpに追加
<?php 'providers' => [ // 追加 'Barryvdh\Debugbar\ServiceProvider', ], 'aliases' => [ // 追加 'Debugbar' => 'Barryvdh\Debugbar\Facade', ],
まとめ
新しいことに取り組む場合はつい前回まで使っていたことが足を引っ張って「前のままでよくね?」思考に陥ってしまいます。
それでも問題無い場合もありますが、環境を大幅に変えるような状況に陥ったときに柔軟に対応できるようにいくつか経験しておくことで…
とかいうのはいままで散々書いてきたので次回以降やりそうなことを書いておく
- laravelでブログチュートリアル的なこと
- laravelで認証
他にもあるけどやらんかも知れないのでとりあえずこんだけを頑張る
Laravelエキスパート養成読本[モダンな開発を実現するPHPフレームワーク!] (Software Design plus)
- 作者: 川瀬裕久,古川文生,松尾大,竹澤有貴,小山哲志,新原雅司
- 出版社/メーカー: 技術評論社
- 発売日: 2015/04/21
- メディア: 大型本
- この商品を含むブログを見る
PHPエンジニア養成読本 〔現場で役立つイマドキ開発ノウハウ満載! 〕 (Software Design plus)
- 作者: 新原雅司,原田康生,小山哲志,田中久輝,保科一成,大村創太郎,増永玲,PHPエンジニア養成読本編集部
- 出版社/メーカー: 技術評論社
- 発売日: 2013/09/13
- メディア: 大型本
- この商品を含むブログ (3件) を見る
cakephp3のチュートリアル(Bookmarker Tutorial)
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
※一部は動かせそうだけど、認証とかいろいろ加工がまだまだ必要
所感
英語難しい、ソースを見てもうーん???っていう場合がある。
ある程度なんとかなる感じがしてきたけど
うまくとりかかる要因がまだないのでなんか作るアイデアを捻出しよう
cakephp3【beta3】クエリービルダーでjoin
1つのテーブルより別のテーブルと紐付ける場合
joinを使用することが可能らしい
containsとかあったけどなんか関連付のイメージがしっくりきていないのでjoinをやってみる
準備
テーブル
CREATE TABLE `members` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `age` int(11) DEFAULT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `comments` ( `member_id` int(11) NOT NULL, `id` int(11) NOT NULL, `data` varchar(255) DEFAULT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`member_id`, `id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
雛形作成
sh app/bin/cake bake shell sample1 sh app/bin/cake bake model members sh app/bin/cake bake model comments
サンプル1
join
<?php namespace App\Shell; use Cake\Console\Shell; use Cake\ORM\TableRegistry; /** * Sample1 shell command. */ class Sample1Shell extends Shell { /** * main() method. * * @return bool|int Success or error code. */ public function main() { // table $members = TableRegistry::get('Members'); // find $query = $members->find() ->hydrate(false) ->join([ 'table' => 'comments', 'alias' => 'c', 'type' => 'LEFT', 'conditions' => 'c.member_id = Members.id', ])->select([ "id" => "Members.id" , "name" => "Members.name" , "comments_data" => "c.data" ]); // SQ var_dump($query->sql()); // 出力 foreach($query as $item){ var_dump($item); } } }
出力されたSQL(整形済み)
SELECT Members.id AS `id`, Members.name AS `name`, c.data AS `comments_data` FROM members Members LEFT JOIN comments c ON c.member_id = Members.id
抽出できるデータ
array(3) { 'id' => int(1) 'name' => string(3) "aaa" 'comments_data' => string(7) "aaa-111" } array(3) { 'id' => int(1) 'name' => string(3) "aaa" 'comments_data' => string(7) "aaa-222" } array(3) { 'id' => int(1) 'name' => string(3) "aaa" 'comments_data' => string(7) "aaa-333" } array(3) { 'id' => int(2) 'name' => string(3) "bbb" 'comments_data' => string(7) "bbb-111" } array(3) { 'id' => int(3) 'name' => string(3) "ccc" 'comments_data' => string(7) "ccc-111" } array(3) { 'id' => int(3) 'name' => string(3) "ccc" 'comments_data' => string(7) "ccc-222" }
まとめ
joinでleftやinnerを使用できる。
その場合は一緒に抽出したいテーブルのカラムはセットしておかないと取得できない
「hydrate」を「false」で指定した場合は配列で項目を取得、「true」の場合はプロパティみたいな感じになる
所感
cakephp3では日本語化が微妙な感じですすんでいる感じ。ただ、まだ英語の方が詳しく書かれているのでそちらしか参考にできない部分があると思います。
なんかだんだんとやっぱりSQLがいいなと思い始めて聞か感じがする。無駄に学習コストがかかると便利には感じないかも…
cakephp2でプラグインを作成
作ったことがない
ので作成する
参考資料
プラグイン — CakePHP Cookbook 2.x ドキュメント
※公式だけどなんかいまいちしっくりきていない
環境構築
mkdir app1 cd app1/ git clone https://github.com/cakephp/cakephp.git -b 2.5.3 . git submodule add https://github.com/cakephp/debug_kit.git app/Plugin/DebugKit
※あとはいろいろとデータベースとかcore.phpとかごにょごにょする
モデル用のtable
CREATE TABLE `contacts` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL, `data` varchar(256) DEFAULT NULL, `created` datetime DEFAULT NULL, `modified` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
サンプル
作成
sh app/Console/cake bake plugin ContactManager sh app/Console/cake bake controller Contacts --plugin ContactManager sh app/Console/cake bake model Contacts --plugin ContactManager sh app/Console/cake bake view Contacts --plugin ContactManager
※最初にテーブルを作成しておくこと
app/Config/bootstrap.php
CakePlugin::load('ContactManager', array('bootstrap' => false, 'routes' => false));
※末尾に追加されている
プラグイン構成
. ├── Config │ └── Schema │ └── empty ├── Console │ └── Command │ └── Task │ └── empty ├── Controller │ ├── Component │ │ └── empty │ ├── ContactManagerAppController.php │ └── ContactsController.php ├── Lib │ └── empty ├── Model │ ├── Behavior │ │ └── empty │ ├── ContactManagerAppModel.php │ ├── Contact.php │ └── Datasource │ └── empty ├── Test │ ├── Case │ │ ├── Controller │ │ │ ├── Component │ │ │ │ └── empty │ │ │ └── ContactsControllerTest.php │ │ ├── Model │ │ │ ├── Behavior │ │ │ │ └── empty │ │ │ └── ContactTest.php │ │ └── View │ │ └── Helper │ │ └── empty │ └── Fixture │ ├── ContactFixture.php │ └── empty ├── Vendor │ └── empty ├── View │ ├── Contacts │ │ ├── add.ctp │ │ ├── edit.ctp │ │ ├── index.ctp │ │ └── view.ctp │ └── Helper │ └── empty └── webroot └── empty 25 directories, 24 files $
アクセス
所感
プラグインを作成することでアプリケーションとは分離した機能を付与できるかも、
ビューだけやヘルパー、モデルだけのプラグインも作成できると思われる
毎回コントローラーなどを用意したものをプラグインとして用意することも考えておく必要がありそう。
気になっていること
debugkitってどうやって出力しているんだろう?
よくわからない…
cakephp3のmigrations3
テーブル追加などのいくつかの機能
なんか必要そうな記法を調査
テーブル
テーブル作成
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { $sample1 = $this->table("sample1"); $sample1 ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `id` int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※テーブルに何も指定しない場合はidが主キーで設定される
idを主キーにしない
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { $this->dropTable("sample1"); $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("shohin_cd", "string", array("limit" => 10)) ->addColumn("shohin_name", "string", array("limit" => 256)) ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `shohin_cd` varchar(10) NOT NULL, `shohin_name` varchar(256) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※idを無効にしたら主キーは任意で設定しないといけない
プライマリキーを設定
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { $this->dropTable("sample1"); $sample1 = $this->table("sample1" , array( "id" => false, "primary_key" => array("shohin_cd") ) ); $sample1 ->addColumn("shohin_cd", "string", array("limit" => 10)) ->addColumn("shohin_name", "string", array("limit" => 256)) ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `shohin_cd` varchar(10) NOT NULL, `shohin_name` varchar(256) NOT NULL, PRIMARY KEY (`shohin_cd`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※テーブル設定時にプライマリキーを指定する場合
テーブル名変更
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { $this->table("sample1") ->rename("hoge11111"); } /** * Migrate Down. */ public function down() { } }
※tableで取得したあとにrenameメソッドで変更
テーブル削除
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { $this->droptable("hoge11111"); } /** * Migrate Down. */ public function down() { } }
※dropTableメソッドで削除
項目のデータ型
string
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "string") ->addColumn("data2", "string", array("limit" => 100)) ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` varchar(255) NOT NULL, `data2` varchar(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※varcharで未指定の場合は255桁数となる
integer、biginteger
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "integer") ->addColumn("data2", "integer", array("limit" => 10)) ->addColumn("data3", "biginteger") ->addColumn("data4", "biginteger", array("limit" => 10)) ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` int(11) NOT NULL, `data2` int(10) NOT NULL, `data3` bigint(20) NOT NULL, `data4` bigint(10) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※integerやbigintegerの場合
decimal
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "decimal") ->addColumn("data2", "decimal", array("limit" => "5,2")) // ->addColumn("data2", "decimal", array("precision" => 5, "scale" => 2)) ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` decimal(10,0) NOT NULL, `data2` decimal(5,2) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
※データの桁数が小数部も指定する場合は”n,n”のように指定すればできるよう
text
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "text") ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` text NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
date、time、datetime
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "date") ->addColumn("data2", "time") ->addColumn("data3", "datetime") ->addColumn("data4", "timestamp") ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` date NOT NULL, `data2` time NOT NULL, `data3` datetime NOT NULL, `data4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
その他
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "binary") ->addColumn("data2", "boolean") ->create() ; } /** * Migrate Down. */ public function down() { } }
↓
CREATE TABLE `sample1` ( `data1` blob NOT NULL, `data2` tinyint(1) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
追加位置
予めテーブルを作成
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Migrate Up. */ public function up() { if($this->hasTable("sample1")){ $this->dropTable("sample1"); } $sample1 = $this->table("sample1" , array( "id" => false ) ); $sample1 ->addColumn("data1", "string") ->addColumn("data2", "string") ->create() ; } /** * Migrate Down. */ public function down() { } }
data1の後に追加
<?php use Phinx\Migration\AbstractMigration; class Sample1 extends AbstractMigration { /** * Change Method. * * More information on this method is available here: * http://docs.phinx.org/en/latest/migrations.html#the-change-method * * Uncomment this method if you would like to use it. * public function change() { } */ /** * Migrate Up. */ public function up() { $sample1 = $this->table("sample1"); $sample1 ->addColumn("hoge1", "integer", array("after" => "data1")) ->addColumn("hoge2", "char", array("after" => "hoge1")) ->save(); } /** * Migrate Down. */ public function down() { } }
※afterに設定
↓
CREATE TABLE `sample1` ( `data1` varchar(255) NOT NULL, `hoge1` int(11) NOT NULL, `hoge2` char(255) NOT NULL, `data2` varchar(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
所感
全部のデータ型を保証しているわけではない感じ。
一部は対応できるけど、できない部分はSQLで対応するしかないかも…
cakephp3のmigrations2
やったこと
- 初回作成
- テーブルに項目追加
- テーブルに項目削除、新規テーブル作成
- ロールバック
初回作成
マイグレーションファイルを作成
$ sh app/bin/cake migrations create Initial Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations created ./app/config/Migrations/20141016121505_initial.php $
※マイグレーション名はキャメルケースなんで小文字はだめらしい
20141016121505_initial.php
<?php use Phinx\Migration\AbstractMigration; class Initial extends AbstractMigration { /** * Change Method. * * More information on this method is available here: * http://docs.phinx.org/en/latest/migrations.html#the-change-method * * Uncomment this method if you would like to use it. * public function change() { } */ /** * Migrate Up. */ public function up() { $sample1 = $this->table("sample1"); $sample1 ->addColumn("name", "string", array("limit" => 128)) ->addColumn("deleted", "integer") ->addColumn("created", "datetime") ->addColumn("modified", "datetime") ->create() ; } /** * Migrate Down. */ public function down() { } }
※簡単にテーブルを作成する
実行
$ sh app/bin/cake migrations migrate Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations using environment default using adapter mysql using database my_app == 20141016121505 Initial: migrating == 20141016121505 Initial: migrated 0.0321s All Done. Took 0.0578s $
※「20141016121505」が実行タイムスタンプ
DB内容
2回目
マイグレーションを作成
$ sh app/bin/cake migrations create Sample1 Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations created ./app/config/Migrations/20141016124037_sample1.php $
20141016124037_sample1.php
<?php use Phinx\Migration\AbstractMigration; class Sample1 extends AbstractMigration { /** * Change Method. * * More information on this method is available here: * http://docs.phinx.org/en/latest/migrations.html#the-change-method * * Uncomment this method if you would like to use it. * public function change() { } */ /** * Migrate Up. */ public function up() { $sample1 = $this->table("sample1"); $sample1 ->addColumn("age", "integer") ->addColumn("sex", "integer") ->save() ; } /** * Migrate Down. */ public function down() { } }
マイグレーション
$ sh app/bin/cake migrations migrate Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations using environment default using adapter mysql using database my_app == 20141016124037 Sample1: migrating == 20141016124037 Sample1: migrated 0.0791s All Done. Took 0.0988s $
結果
3回目
マイグレーションファイルを作成
$ sh app/bin/cake migrations create Sample2 Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations created ./app/config/Migrations/20141016125733_sample2.php $
20141016125733_sample2.php
<?php use Phinx\Migration\AbstractMigration; class Sample2 extends AbstractMigration { /** * Change Method. * * More information on this method is available here: * http://docs.phinx.org/en/latest/migrations.html#the-change-method * * Uncomment this method if you would like to use it. * public function change() { } */ /** * Migrate Up. */ public function up() { // 変更 $sample1 = $this->table("sample1"); $sample1 ->removeColumn("age") ->renameColumn("sex", "seibetsu") ->addColumn("memo", "string") ->save() ; // テーブル作成 $sample2 = $this->table("sample2"); $sample2 ->addColumn("name", "string", array("limit" => 256)) ->addColumn("memo", "string") ->addColumn("memo2", "string") ->addColumn("deleted", "integer") ->addColumn("created", "datetime") ->addColumn("modified", "datetime") ->create() ; } /** * Migrate Down. */ public function down() { } }
実行
$ sh app/bin/cake migrations migrate Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations using environment default using adapter mysql using database my_app == 20141016125733 Sample2: migrating == 20141016125733 Sample2: migrated 0.0860s All Done. Took 0.1146s $
結果
※新しいテーブルも追加されている
ロールバック
したらどうなる
実行
$ sh app/bin/cake migrations rollback Welcome to CakePHP v3.0.0-beta2 Console --------------------------------------------------------------- App : src Path: /vagrant/projects/beta2/app/src/ --------------------------------------------------------------- using migration path /vagrant/projects/beta2/app/config/Migrations using environment default using adapter mysql using database my_app == 20141016125733 Sample2: reverting == 20141016125733 Sample2: reverted 0.0020s All Done. Took 0.0259s $
結果
※前回実行したテーブルの定義は変わらないけど前回実行した履歴がなくなるのでやり直すことができる