cakephp3でfinderでログイン条件を追加
cakephp3になってからというか少しずつ変化しているものがあります
認証処理とかも…
環境
CensOS7.x
php 7.0.3
cakephp3.2.3
認証処理で条件を付ける際にfinderを使う
以前は"scope"がありましたがそれとは別に"finder"というものがあります。
Usersテーブルとは別にテーブルのチェックなどが必要な場合はこちらがあると便利な感じ
実装
AppController
src/Controller/AppController.php
<?php namespace App\Controller; use Cake\Controller\Controller; use Cake\Event\Event; class AppController extends Controller { public function initialize() { parent::initialize(); $this->loadComponent('RequestHandler'); $this->loadComponent('Flash'); // 認証コンポーネント $this->loadComponent('Auth', [ // ログイン後のリダイレクト 'loginRedirect' => [ 'controller' => 'Posts', 'action' => 'index' ], // ログアウト後のリダイレクト 'logoutRedirect' => [ 'controller' => 'Users', 'action' => 'login', ], // 認証 'authenticate' => [ // フォーム認証 'Form' => [ // 暗号化 'passwordHasher' => [ 'className' => 'Default', ], // 認証フィールド 'fields' => [ 'username' => 'username', 'password' => 'password' ], // モデル 'userModel' => 'Users', // 抽出メソッド 'finder' => 'login' ] ], // データ保存 'storage' => 'Session', // 権限 // isAuthorizedメソッドを実装する必要がある 'authorize' => ['Controller'], ]); } /** * アクション前処理 * @param Event $event */ public function beforeFilter(Event $event) { parent::beforeFilter($event); } /** * Before render callback. * * @param \Cake\Event\Event $event The beforeRender event. * @return void */ public function beforeRender(Event $event) { if (!array_key_exists('_serialize', $this->viewVars) && in_array($this->response->type(), ['application/json', 'application/xml']) ) { $this->set('_serialize', true); } } /** * 認証権限判定処理 * * @param $user * @return bool */ public function isAuthorized($user) { // ユーザーの権限が"admin"の場合は全てを許可する if (isset($user['role']) && $user['role'] === 'admin') { return true; } // Default deny return false; } }
※finderという項目に"login"を指定します。省略時は"all"
UsersTable.php
src/Model/Table/UsersTable.php
<?php namespace App\Model\Table; use App\Model\Entity\User; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; /** * Users Model * */ class UsersTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { parent::initialize($config); $this->table('users'); $this->displayField('id'); $this->primaryKey('id'); $this->addBehavior('Timestamp'); } /** * ログイン用メソッド * * 独自のfindメソッド * @param Query $query * @param array $options * @return Query */ public function findLogin(Query $query, array $options){ // 条件を付与 $query->where([ 'Users.status' => 0, 'Users.deleted' => 0 ]); return $query; } }
SQLのログ
SELECT Users.id AS `Users__id`, Users.username AS `Users__username`, Users.password AS `Users__password`, Users.role AS `Users__role`, Users.status AS `Users__status`, Users.deleted AS `Users__deleted`, Users.created AS `Users__created`, Users.modified AS `Users__modified` FROM users Users WHERE ( Users.username = 'test01' AND Users.status = 0 AND Users.deleted = 0 ) LIMIT 1
所感
HTMLのヘルパーって使いたくない…
生成されるまでどんな形かわからないんですよ…
多少手間がかかってもHTMLで書くべきかと思います…
なんでHTMLヘルパーってあるんだろう…
cakephp3でカスタムバリデーションプロバイダを作成
バリデーションのルールはチェックはいろいろありますが
多少細かいチェックなどがあった場合は対応できません。
各テーブルだけの機能ならともかくいろいろなテーブルクラスで使用する場合はバリデーションを使いまわしたい
対応
ディレクトリを作成
mkdir -p src/Model/Validation
バリデーションインターフェースを実装
src/Model/Validation/CustomValidationInterface.php
<?php namespace App\Model\Validation; interface CustomValidationInterface { // チェックメソッドを定義する public static function sampleCheck1($string); // チェックメソッドを定義する public static function sampleCheck2($string, $len); }
チェック処理を実装
src/Model/Validation/CustomValidation.php
<?php namespace App\Model\Validation; use Cake\Validation; class CustomValidation implements CustomValidationInterface { // チェックメソッドを実装する public static function sampleCheck1($check) { $result = null; if ($check == "check1") { $result = true; } else { $result = false; } return $result; } // チェックメソッドを実装する public static function sampleCheck2($check, $len) { $result = null; if ($check == "1234567890" && strlen($check) == $len) { $result = true; } else { $result = false; } return $result; } }
※サンプルって手抜きの方がわかりやすいと思います
テーブルのバリデーションに実装
<?php namespace App\Model\Table; use App\Model\Entity\Post; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Validation\Validator; use App\Model\Validation; /** * Posts Model * */ class PostsTable extends Table { /** * Initialize method * * @param array $config The configuration for the Table. * @return void */ public function initialize(array $config) { parent::initialize($config); $this->table('posts'); $this->displayField('title'); $this->primaryKey('id'); $this->addBehavior('Timestamp'); } /** * Default validation rules. * * @param \Cake\Validation\Validator $validator Validator instance. * @return \Cake\Validation\Validator */ public function validationDefault(Validator $validator) { // プロバイダを追加 $validator->provider('custom', Validation\CustomValidation::class); $validator ->integer('id') ->allowEmpty('id', 'create'); $validator ->requirePresence('title', 'create') ->notEmpty('title') ->add("title", [ "check1" => [ "rule" => ["sampleCheck2", 10], "message" => "カスタムプロバイダーエラー1", "provider" => "custom", // 使用するプロバイダ名を設定 ] ]); $validator ->requirePresence('body', 'create') ->notEmpty('body'); return $validator; } }
こんな感じ
参考
localized/src/Validation at master · cakephp/localized · GitHub
バリデーション — CakePHP Cookbook 3.x ドキュメント
所感
cakephp2の場合はビヘイビアに組み込むという方法で共通化していたので別の場所に配置できるようになるとメンテナンスもやりやすくなるかも…
データだけのチェックの場合とデータベースが絡む場合で難易度が変わるかもしれないので注意する必要があるかもしれないけど…
CentOS6.xにphp7のインストールテスト
せっかくなのでvmを作成してインストールしてみる(準備)
git clone https://github.com/mshige1979/vagrant-centos-dev-001.git -b test00 test02 cd test02 vagrant up vagrant ssh
yum
sudo yum -y install gcc gcc-c++ git wget tar m4 autoconf httpd vim sudo yum -y install httpd-devel gd-devel libxml2-devel mysql-devel t1lib-devel sudo yum -y install bzip2-devel curl-devel gmp-devel aspell-devel recode-devel sudo yum -y install icu libicu-devel sudo yum -y install php-intl
phpをダウンロードしてからビルド
wget https://github.com/php/php-src/archive/php-7.0.0RC1.tar.gz tar zxf php-7.0.0RC1.tar.gz cd php-src-php-7.0.0RC1/ ./buildconf --force ./configure \ --prefix=/usr/local/php \ --with-config-file-path=/usr/local/php/etc \ --enable-mbstring \ --enable-zip \ --enable-bcmath \ --enable-pcntl \ --enable-ftp \ --enable-exif \ --enable-intl \ --enable-calendar \ --enable-sysvmsg \ --enable-sysvsem \ --enable-sysvshm \ --enable-wddx \ --with-curl \ --with-mcrypt \ --with-iconv \ --with-gmp \ --with-pspell \ --with-gd \ --with-jpeg-dir=/usr \ --with-png-dir=/usr \ --with-zlib-dir=/usr \ --with-xpm-dir=/usr \ --with-freetype-dir=/usr \ --enable-gd-native-ttf \ --enable-gd-jis-conv \ --with-openssl \ --with-pdo-mysql=/usr \ --with-gettext=/usr \ --with-zlib=/usr \ --with-bz2=/usr \ --with-recode=/usr \ --with-mysqli=/usr/bin/mysql_config \ --with-apxs2=/usr/sbin/apxs make && make test sudo make insall
↓
$ sudo make install Installing PHP SAPI module: apache2handler /usr/lib64/httpd/build/instdso.sh SH_LIBTOOL='/usr/lib64/apr-1/build/libtool' libphp7.la /usr/lib64/httpd/modules /usr/lib64/apr-1/build/libtool --mode=install cp libphp7.la /usr/lib64/httpd/modules/ libtool: install: cp .libs/libphp7.so /usr/lib64/httpd/modules/libphp7.so libtool: install: cp .libs/libphp7.lai /usr/lib64/httpd/modules/libphp7.la libtool: install: warning: remember to run `libtool --finish /home/vagrant/php-src-php-7.0.0RC1/libs' chmod 755 /usr/lib64/httpd/modules/libphp7.so [activating module `php7' in /etc/httpd/conf/httpd.conf] Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20141001/ Installing PHP CLI binary: /usr/local/php/bin/ Installing PHP CLI man page: /usr/local/php/php/man/man1/ Installing phpdbg binary: /usr/local/php/bin/ Installing phpdbg man page: /usr/local/php/php/man/man1/ Installing PHP CGI binary: /usr/local/php/bin/ Installing PHP CGI man page: /usr/local/php/php/man/man1/ Installing build environment: /usr/local/php/lib/php/build/ Installing header files: /usr/local/php/include/php/ Installing helper programs: /usr/local/php/bin/ program: phpize program: php-config Installing man pages: /usr/local/php/php/man/man1/ page: phpize.1 page: php-config.1 Installing PEAR environment: /usr/local/php/lib/php/ --2015-08-23 14:13:15-- https://pear.php.net/~cweiske/1.10.0dev2/install-pear-nozlib.phar pear.php.net をDNSに問いあわせています... 5.77.39.20 pear.php.net|5.77.39.20|:443 に接続しています... 接続しました。 HTTP による接続要求を送信しました、応答を待っています... 200 OK 長さ: 3572266 (3.4M) [text/plain] `pear/install-pear-nozlib.phar' に保存中 100%[==================================================================================================================>] 3,572,266 177K/s 時間 39s 2015-08-23 14:14:01 (89.2 KB/s) - `pear/install-pear-nozlib.phar' へ保存完了 [3572266/3572266] [PEAR] Archive_Tar - installed: 1.4.0 [PEAR] Console_Getopt - installed: 1.4.1 [PEAR] Structures_Graph- installed: 1.1.1 [PEAR] XML_Util - installed: 1.3.0 [PEAR] PEAR - installed: 1.10.0dev2 Wrote PEAR system config file at: /usr/local/php/etc/pear.conf You may want to add: /usr/local/php/lib/php to your php.ini include_path /home/vagrant/php-src-php-7.0.0RC1/build/shtool install -c ext/phar/phar.phar /usr/local/php/bin ln -s -f phar.phar /usr/local/php/bin/phar Installing PDO headers: /usr/local/php/include/php/ext/pdo/ $
設定ファイルをコピーして、環境変数に追加
sudo cp -p php.ini-development /usr/local/php/etc/php.ini echo 'export PATH=$PATH:/usr/local/php/bin' >> ~/.bash_profile source ~/.bash_profile
実行
$ php -v PHP 7.0.0RC1 (cli) (built: Aug 23 2015 13:55:47) Copyright (c) 1997-2015 The PHP Group Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies $
cakephp3
mysql
mysql -u root -e "create database my_app default charset utf8" mysql -u root -e "create database test_my_app default charset utf8" mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO 'my_app'@'localhost' IDENTIFIED BY 'secret' WITH GRANT OPTION;"
install
curl -s https://getcomposer.org/installer | php php composer.phar create-project --prefer-dist cakephp/app app1 app1/ bin/cake server -H 192.168.33.10 -p 1234
↓
所感
なんとかcakephp3も初期ページまでは動くようです。拡張モジュールなどを利用している場合はうまく有効にしているかを
確認しないと動かせないことがあるのでインストール時などに注意を行うことが必要と思われる。
php7の新機能はまた今度確認する
cakephp3の学習(ルーティング)
BookMakerのチュートリアルの一部
きちんと理解しないとソースコピーじゃ動かん
これを見てから学習していく
今回はこれ
config/routes.php
Router::scope( '/bookmarks', ['controller' => 'Bookmarks'], function ($routes) { $routes->connect('/tagged/*', ['action' => 'tags']); } );
いままでの知識より考えると
/bookmarks
以下のURLにタグ文字を設定していくことでブックマーク用のタグをURLで設定できる
その処理をactionに設定できる
コントローラーに設定
src/Controller/BookmarksController.php
public function tags(){ $tags = $this->request->params['pass']; /* $bookmarks = $this->Bookmarks->find('tagged', [ 'tags' => $tags ]); */ var_dump($tags); }
※モデルの部分は無視するよ(^^)
※ここで「$this->request->params['pass'];」これでpassパラメータを配列で取得できるよ
で実験
この方法をとることでタグを保有するブックマークリストを取得できるはず
cakephp3でモデル(find+xxxx)任意のfindメソッドを準備する
モデルでデータを取得する場合はfindメソッドを使用することが多いと思う
<?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 $bookmarks = TableRegistry::get('bookmarks'); // find $query = $bookmarks ->find() ->contain(['users']); // SQ print_r($query->sql()); print_r("\n"); } }
※findメソッドにいろいろな条件を付与して様々な結果を取得するけど
ある程度まとめたい
毎回findに条件を付与するより、可変パラメータだけを渡したいな〜
find+任意の名前でメソッドを作成
サンプル
<?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){ return $this ->find() ->matching('Tags', function ($q) use ($options) { return $q->where(['Tags.title IN' => $options['tags']]); }); } }
※Tableのクラスに追加
それで、Tableのクラスを使用するところでfind以降のメソッド名を指定
<?php namespace App\Shell; use Cake\Console\Shell; use Cake\ORM\TableRegistry; /** * Sample2 shell command. */ class Sample2Shell extends Shell { /** * main() method. * * @return bool|int Success or error code. */ public function main() { // table $bookmarks = TableRegistry::get('bookmarks'); // find $query = $bookmarks->find('tagged', [ 'tags' => ['111', '222'] ]); // SQ print_r($query->sql()); print_r("\n"); } }
※第1引数のパラメータで"find"以降のメソッド名を指定する
こうすることで取得できる
戻り値はまあ好きに対応することが可能と思います
結果
SELECT bookmarks.id AS `bookmarks__id` , bookmarks.user_id AS `bookmarks__user_id` , bookmarks.title AS `bookmarks__title` , bookmarks.description AS `bookmarks__description` , bookmarks.url AS `bookmarks__url` , bookmarks.created AS `bookmarks__created` , bookmarks.updated AS `bookmarks__updated` , Tags.id AS `Tags__id` , Tags.title AS `Tags__title` , Tags.created AS `Tags__created` , Tags.updated AS `Tags__updated` , BookmarksTags.bookmark_id AS `BookmarksTags__bookmark_id` , BookmarksTags.tag_id AS `BookmarksTags__tag_id` FROM bookmarks bookmarks INNER JOIN tags Tags ON Tags.title in (:c0,:c1) INNER JOIN bookmarks_tags BookmarksTags ON ( bookmarks.id = (BookmarksTags.bookmark_id) AND Tags.id = (BookmarksTags.tag_id) )
所感
チュートリアルをやっていきながら不明な場所を解決して理解を進めていくようにします
cakephp3でjsonとかxmlとかを使う
jsonを使ったデータを使ってみたいけどなんかうまくいかない
英語のマニュアルを見て頑張った
バージョン
cakephp3.0.8
実装
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::extensions(['json', 'xml']); 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'); }); /** * Load all plugin routes. See the Plugin documentation on * how to customize the loading of plugin routes. */ Plugin::routes();
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() { parent::initialize(); $this->loadComponent('Flash'); $this->loadComponent('RequestHandler'); } }
※「$this->loadComponent('RequestHandler');」を追加
※AppControllerでなくても各コントローラーのinitializeに実装すればよい
独自のコントローラー
<?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\Core\Configure; use Cake\Network\Exception\NotFoundException; use Cake\View\Exception\MissingTemplateException; /** * Static content controller * * This controller will render views from Template/Pages/ * * @link http://book.cakephp.org/3.0/en/controllers/pages-controller.html */ class SamplesController extends AppController { public function test(){ $data = [ "aaa" => [111, 222, 333], "bbb" => [ "ccc1" => 10, "ccc2" => [ "aaa", "bbb", "ccc", ], ], ]; $this->set([ 'data' => $data, '_serialize' => ['data'] ]); } }
※jsonを意識しているのでこんな感じのショボイデータ
このへんは重要
laravel5でコントローラーを確認
単純にテキストを出力する場合やjson形式などを確認
viewを使用する場合とそうでない場合の確認
基本
サンプルアプリなので単純にコントローラーの雛形を作成して検証する
準備
php composer.phar create-project laravel/laravel sampleapp2 --prefer-dist cd sampleapp2/ php artisan make:controller Test1sController
ルーティング
<?php Route::get('/test1', 'Test1sController@index');
テキストをそのまま出す場合
app/Http/Controllers/Test1sController.php
<?php namespace App\Http\Controllers; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class Test1sController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { // return "aaaaaaaaaaaa"; } }
↓
json形式?
app/Http/Controllers/Test1sController.php
<?php namespace App\Http\Controllers; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class Test1sController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { // return [ "res" => 200, "body" => [ "aaa" => 10, "bbb" => "sssss", "ccc" => "sssssd" ], ]; } }
↓
viewを使用する場合
viewを作成
resources/views/test1s/index.blade.php
@extends('app') @section('content') <div>aaa={{$data["aaa"]}}</div> <div>bbb={{$data["bbb"]}}</div> <div>ccc= @foreach($data["ccc"] as $key => $item) <p>{{$key}} = {{$item}}</p> @endforeach </div> @endsection
※viewを使用する場合はlaravelの場合はblade.phpの拡張子でlaravel特有の記載方法があるので意識しておく
app/Http/Controllers/Test1sController.php
<?php namespace App\Http\Controllers; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class Test1sController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { $data = [ "aaa" => 100, "bbb" => 200, "ccc" => [ "xxx" => "111", "yyy" => "222", ], ]; // return view('test1s.index')->with(compact('data')); } }
※変数は1つにまとめた方が便利になる感じ?
↓
※基本的にphpが有効な記法も使用できる