CakePHP ページネーション機能(Paginator)

基本

コントローラ

設定

public $paginate = [
 'limit' => 5,
 並び順設定はデータ取得時に実施する方が自由度が高いのでここでは行わない
 'order' => [
   'id' => 'DESC'
 ],
 アソシエーションデータ取得時に実施する方が自由度が高いのでここでは行わない
 'contain' => ['People']
];

Paginatorコンポーネント読み込み

public function initialize(){
 parent::initialize();
 ↓ は不要(デフォルトで使用できる)
 $this->loadComponent('Pagenator');
}

ページネーションを利用したデータ取得

public function list(){
 $lists = $this->paginate(
  $this->Products
   ->find()
   ->contain(['~'])
   ->where(['~ !=' => '0'])
   ->order(['~' => 'DESC', '~' => 'DESC'])
   );
  $this->set(compact('lists'));
}

ビュー

ページ移動

※~\src\Template\(コントローラ名)\(ビュー名).ctp
<div class="paginator">
 <ul class="pagination">
  <?= $this->Paginator->first('<< ' . '最初') ?>
  <?= $this->Paginator->prev('< ' . '前へ') ?>
  <?= $this->Paginator->numbers() ?>
  <?= $this->Paginator->next('次へ' . ' >') ?>
  <?= $this->Paginator->last('最後' . ' >>') ?>
 </ul>
</div>

ページ番号

【通常】
<?= $this->Paginator->numbers() ?>

【オプション】
<?=
  $this->Paginator->numbers([
    現ページより前のページがあれば'<<'を表示
    'before' => $this->Paginator->hasPrev() ?
     $this->Paginator->first('<<') : '',
    現ページより後のページがあれば'>>'を表示
    'after' => $this->Paginator->hasNext() ?
     $this->Paginator->last('>>')
    'modules' => 4
    'separator' => '|'
  ]);
?>

設定項目 機能
before 最初のリンクの前に表示する内容
after 最後のリンクの前に表示する内容
modules カレントページ番号を除いた表示リンク数
2の場合→1,2(カレント),3
separator リンク間区切り文字

カウンター

<?=
 $this->Paginator->counter([
  'format' => 'ページ {{page}} / {{pages}}, レコード {{current}} / {{count}} '])
?>

メソッド

メソッド 機能
next
prev
first 最初
last 最後
counterRange カウンター(format=rangeの場合)
counterPages カウンター(format=pagesの場合)
numbers ページ番号
current 現在ページ番号
sort ソートヘッダー(並び順未指定)
sortAsc ソートヘッダー(昇順)
sortDesc ソートヘッダー(降順)

組込変数

変数名 機能
{text} ページ番号
{url} 遷移先URL
{page} 現ページ番号
{pages} 総ページ数
{current} 現ページでのレコード数
{count} 総レコード数

独自リンク

テンプレート

※~\config\my-paginator-temp.php
<?php

$number  = '<span style="';
$number .= 'border: solid 1px black; ';
$number .= 'padding: 10px; ';
$number .= 'margin: 1px; ">';
$number .= '<a href="{{url}}">{{text}}</a>';
$number .= '</span>';

$current  = '<span style="';
$current .= 'background-color: black; ';
$current .= 'color: white; ';
$current .= 'border: solid 1px black; ';
$current .= 'padding: 10px; ';
$current .= 'margin: 1px; ">';
$current .= '{{text}}';
$current .= '</span>';

return [
 'number' => $number,
 'current' => $current
];

テンプレート読み込み

※~\src\View\AppView.php
class AppView extends View
{
 public function initialize()
 {
  $this->loadHelper(
   'Paginator', [
    'templates' => 'my-paginator-temp'
   ]);
 }
}

CakePHP CSV出力機能(friendsofcake/cakephp-csvview)

インストール

Composerからライブラリインストール
# composer require friendsofcake/cakephp-csvview:~3.0

設定

※~/config/bootstrap.php
Plugin::load('CsvView');

コントローラ

直接出力

public function receiptDownload(){

 出力対象
 $receipts = $this->Receipt
  ->find()
  ->all();

 $_serialize = ['receipts'];

 ヘッダー(※省略可)
 $_header = ['Column 1', 'Column 2', 'Column 3'];

 フッター(※省略可)
 $_footer = ['件数:' . $receipts->count()];

 ファイル名(※省略可)
 $this->response->download('売上一覧.csv');

 CsvViewを利用
 $this->viewBuilder()->className('CsvView.Csv');

 設定値をプラグインへ引き渡し
 $this->set(compact('receipts', '_serialize', '_header', '_footer'));
}

テンプレート使用

public function receiptDownload(){

 出力対象
 $receipts = $this->Receipt
  ->find()
  ->all();

 $_serialize = null;

 ファイル名(※省略可)
 $this->response->download('売上一覧.csv');

 CsvViewを利用
 $this->viewBuilder()->className('CsvView.Csv');

 設定値をプラグインへ引き渡し
 $this->set(compact('receipts', '_serialize'));
}

\src\Template\(コントローラ名)\csv\receipt_download.ctp
<?php foreach ($receipts as $receipt): ?>
  列名、デミリタ(,)を指定
  <?php echo $receipt->id ?><?php echo ',' ?>
  <?php echo $receipt->col1 ?><?php echo ',' ?>
  <?php echo $receipt->col2 ?><?php echo ',' ?>
  JOINした場合の複層モデルを指定
  <?php echo $receipt->Model1['col'] ?><?php echo ',' ?>
  最終列には改行を指定
  <?php echo $receipt->Model2['col'] ?><?php echo 'PHP_EOL' ?>
<?php endforeach; ?>

CakePHP 検索機能(friendsofcake/search)

インストール

composer require friendsofcake/search

設定

cd bin
cake plugin load Search

※~/config/bootstrap.php
Plugin::load('Search');

完全一致検索

ビュー

<form method="post" accept-charset="utf-8" action="~">
<div><?= $this->Form->text('name') ?></div>
<input type='submit' value='検索' />
<?= $this->Form->end() ?>

モデル

※~Table
public function initialize(array $config)
{
  parent::initialize($config);

  $this->addBehavior('Search.Search');
  $this->searchManager()
    ->value('name');
}

コントローラ

※~Controller
public function initialize()
{
  parent::initialize();

  $this->loadComponent('Search.Prg', [
    'actions' => ['(アクション名)']
  ]);
}

public function (アクション名)(){
  $query = $this->(モデル名)
    ->find('search', ['search'=>$this->request->query]);
  $this->set('(変数名)', $this->paginate($query));
}

あいまい検索

ビュー

<form method="post" accept-charset="utf-8" action="~">
<div><?= $this->Form->text('name') ?></div>
<input type='submit' value='検索' />
<?= $this->Form->end() ?>

モデル

※~Table
public function initialize(array $config)
{
  parent::initialize($config);

  $this->addBehavior('Search.Search');
  $this->searchManager()
    ->like('name',[
      'before' => true,
      'after' => true
    ]);
}

複数カラム検索

ビュー

<form method="post" accept-charset="utf-8" action="~">
<div><?= $this->Form->text('name1') ?></div>
<div><?= $this->Form->text('name2') ?></div>
<div><?= $this->Form->text('name3') ?></div>
<input type='submit' value='検索' />
<?= $this->Form->end() ?>

モデル

※~Table
public function initialize(array $config)
{
  parent::initialize($config);

  $this->addBehavior('Search.Search');
  $this->searchManager()
    ->add('name1',
      'Search.Like', [
      'before' => true,
    'after' => true,
    'mode' => 'or',
    'comparison' => 'LIKE',
    'wildcardAny' => '*',
    'wildcardOne' => '?',
    'field' => ['name1', 'name2', 'name3']
 ])
}

日付範囲検索

ビュー

<form method="post" accept-charset="utf-8" action="~">
<?= $this->Form->control('start_created', [
 'type'=>'text',
 'label'=>'開始',
 'id'=>'date_start']); ?>
<?= $this->Form->control('end_created', [
 'type'=>'text',
 'label'=>'終了',
 'id'=>'date_end']); ?>

jQuery処理
<?= $this->Html->scriptStart(['block' => true]) ?>
 $( function() {
  $( "#date_start" ).datepicker({
   dateFormat: 'yy-mm-dd'
  });
  $( "#date_end" ).datepicker({
   dateFormat: 'yy-mm-dd'
  });
 } );
<?= $this->Html->scriptEnd() ?>

<input type='submit' value='検索' />
<?= $this->Form->end() ?>

モデル

※~Table
public function initialize(array $config)
{
  parent::initialize($config);

  $this->addBehavior('Search.Search');
  $this->searchManager()
    ->callback('start_created', [
   'callback' => function ($query, $args, $filter) {
    if(empty($args['end_created'])) {
     return;
    }
    return $query->where(function ($exp) use ($args) {
     時間の最小/最大を付与
     $args['start_created'] .= ' 00:00:00';
     $args['end_created'] .= ' 23:59:59';
     return $exp->between(
       'products.created',
       $args['start_created'],
       $args['end_created']
      );
     });
    }
   ])

->callback('end_created', [
   'callback' => function ($query, $args, $filter) {}
 ])
}

別モデル検索

モデル

※~Table
public function initialize(array $config)
{
  parent::initialize($config);
  $this->addBehavior('Search.Search');
  $this->searchManager()

    他の検索と組み合わせ可
    ->like('col',[
      'before' => true,
      'after' => true,
      'field' => $this->aliasField('others.col')
    ])
}

コントローラ

※~Controller
public function initialize()
{
  parent::initialize();
  $this->loadComponent('Search.Prg', [
    'actions' => ['(アクション名)']
  ]);

}

public function (アクション名)(){
  $query = $this->(モデル名)
    ->find('search', ['search'=>$this->request->query]);
    ->contain(['others'])
  $this->set('(変数名)', $this->paginate($query));
}

CakePHP ファイルアップロード機能(josegonzalez/Upload)

インストール

Composerからライブラリインストール
# composer require josegonzalez/cakephp-upload

設定

ライブラリロード設定

# bin/cake plugin load josegonzalez/Upload

↓ にロード設定が追加される
※/config/bootstrap.php
Plugin::load('Josegonzalez/Upload.Upload');

モデル

class ProductsTable extends Table
{
 public function initialize(array $config)
 {
  parent::initialize($config);

  $this->setTable('products');
  $this->setDisplayField('id');
  $this->setPrimaryKey('id');

  $this->addBehavior('Timestamp');

  // Upload Plugin
  $this->addBehavior('Josegonzalez/Upload.Upload', [
   最小機能(アップロードのみ)
   'image' => [],

   ファイル名自動作成
   'image' => [
    'nameCallback' => function ($data, $settings) {
     return uniqid().'-'.strtolower($data['name']);
    }
   ],

   レコード削除時にファイルを削除
   'image' => [
     'keepFilesOnDelete' => false
   ],
  ]);
 }
}

ビュー

<?= $this->Form->create($product, ['type' => 'file']) ?>
 <?= $this->Form->control('image', ["type"=>"file"]); ?>
<?= $this->Form->end() ?>

CakePHP 認証機能(CakeDC/Users)

インストール

composer require cakedc/users

設定

Security.salt設定

CakePHP インストール・設定/Security.salt設定」参照

ライブラリ読み込み

※~/config/bootstrap.php
Plugin::load('CakeDC/Users', ['routes' => true, 'bootstrap' => true]);

テーブル作成

cd bin
cake migrations migrate -p CakeDC/Users

管理者ユーザ作成

cake users add_superuser
※パスワードをメモ

cake users add_superuser -u (ログインID) -p (パスワード)

リダイレクト設定

外部ファイル指定

※~/config/bootstrap.php
Configure::write('Users.config', ['users']);

外部ファイル

※~/config/users.php
<?php
use Cake\Core\Configure;
use Cake\Routing\Router;

$config = [
  'Auth' => [
    ~
    ログイン後の遷移先
    URLを指定したが未ログインの為、ログイン画面へ飛ばされ、
    その後ログインに成功した場合はこの遷移先には飛ばずに最初の指定URLへ遷移する

    'loginRedirect' => [
      'plugin' => null,
      'controller' => 'Tests',
      'action' => 'index',
    ],
    CakeDCのユーザー一覧画面へ遷移させる場合
    'loginRedirect' => [
      'plugin' => 'CakeDC/Users',
      'controller' => 'Users',
      'action' => 'index',
    ],
    'logoutRedirect' => [
      'plugin' => null,
      'controller' => 'Tests',
      'action' => 'display', 'home',
    ]
  ],
];

return $config;

アクセス権限設定

※~/config/permissions.php
<?php
return [
  'Users.SimpleRbac.permissions' => [
    [
      ユーザーはユーザ用のコントローラ、アクションにアクセス可能
      'role' => 'user',
      'controller' => 'Test',
      'action' => '*',
    ],
    [
      スーパーユーザーは全てのコントローラ、アクションにアクセス可能
      'role' => 'superuser',
      'controller' => '*',
      'action' => '*',
    ],
  ]
];

※~Contoroller.php
public function initialize()
{
  
  ログイン無しでアクセス可能なアクションの設定
  $this->Auth->allow([
   'index',
   'search',
   'searchResult',
  ]);
}

メール設定

設定ファイル

CakePHP インストール・設定/メール設定」参照

テンプレート

メールメッセージ
vendor\cakedc\users\src\Template\Email\html\~
vendor\cakedc\users\src\Template\Email\text\~
からコピーして配置する

src
└Template
 └Plugin
  └CakeDC
   └Users
    └Email
     └html
      └reset_password.ctp
      └social_account_validation.ctp
      └validation.ctp
     └text
      └reset_password.ctp
      └social_account_validation.ctp
      └validation.ctp

日本語化

日本語ファイル作成

※\vendor\cakedc\users\src\Locale\ja_JP\Users.po
msgid "Please enter your username and password"
msgstr "ユーザー名 / パスワードを入力して下さい"

日本語ファイルを読み込み

※config\bootstrap.php
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
↓ を追加
ini_set('intl.default_locale', 'ja_JP');

reCAPTCHA

reCAPTCHAライブラリインストール

composer require google/recaptcha "~1.1"

キー取得

reCAPTCHA認証」参照

users.php

※~\config\users.php
$config = [
  'Users' => [
    ~
    'reCaptcha' => [
      // reCaptcha key goes here
      'key' => null,
      // reCaptcha secret
      'secret' => null,
      // use reCaptcha in registration
      'registration' => false,
      // use reCaptcha in login, valid values are false, true
      'login' => false,

    ↓

    'reCaptcha' => [
      // reCaptcha key goes here
      'key' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      // reCaptcha secret
      'secret' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      // use reCaptcha in registration
      'registration' => true,
      // use reCaptcha in login, valid values are false, true
      'login' => true,

コントローラ

コンポーネントロード

※~Contoroller.php
public function initialize()
{
  parent::initialize();
  ~
  $this->loadComponent('CakeDC/Users.UsersAuth');
}

ビュー

ヘルパー読込

※~\src\View\AppView.php
public function initialize()
{
 $this->loadHelper('CakeDC/Users.User');
}

ログアウト
$this->User->logout();
ログアウト画面へのHTMLリンクが生成される

組込画面

ログイン画面
http://~/login

ログアウト画面
http://~/logout

ユーザー:プロフィール画面
http://~/profile

管理者:ユーザー一覧
http://~/users/users/

管理者:ユーザー登録
http://~/users/users/add

ユーザー:登録画面
http://~/users/users/register

ユーザー:パスワードリセット画面
http://~/users/users/request-reset-password

CakePHP 認証機能(Auth)

テーブル作成

usersテーブル
id
username
password
role
created
modified

Bake実行結果

Controller
 └UsersController.php
Model
 └Entity
  └User.php
 └Table
  └UsersTable.php
Template
 └Users
  └add.ctp
  └edit.ctp
  └index.ctp
  └view.ctp

User.php

↓ を追加
use Cake\Auth\DefaultPasswordHasher;

protected function _setPassword($password){
 return (new DefaultPasswordHasher)->hash($password);
}

AppController.php

~\src\Controller\AppController.php
※基底クラスAppContorollerで処理する事により、継承先の全コントローラで認証処理がなされる

【Authコンポーネント追加】
CakePHP Component操作/Authコンポーネントコントローラ」参照

UsersController.php

login処理

function login(){
  if($this->request->isPost()){
    $user = $this->Auth->identify();
    if(!empty($user)){
      $this->Auth->setUser($user);
      return $this->redirect($this->Auth->redirectUrl());
    }
    $this->Flash->error('ユーザー名、またはパスワードが違います。');
  }
}

logout処理

function logout(){
  $this->request->session()->destroy();
  return $this->redirect($this->Auth->logout());
}

認証例外ページ設定処理

function beforeFilter(Event $event){
  parent::beforeFilter($event);
  $this->Auth->allow([
    'index',
    'login',
  ]);
}

ログイン後処理

public function isAuthorized($user = null){
  $action = $this->request->params['action'];

  if (in_array($action, ['index','view'])){
    return true;
  }

  if ($user['role'] === 'admin'){
    return true;
  }

  if ($user['role'] === 'guest'){
    return true;
  }

  return false;
}

login.ctp

~\src\Template\Users\login.ctp

CakePHP Component操作/Authコンポーネントログインページ」参照

POSTデータ

$this->request["params"]=> array(7) {
    ["controller"]=> string(5) "Users"
    ["action"]=> string(5) "login"
    ["pass"]=> array(0) {}
    ["plugin"]=> NULL
    ["_matchedRoute"]=> string(22) "/:controller/:action/*"
    ["_ext"]=> NULL
    ["isAjax"]=> bool(false)
  }
$this->request["data"]=> array(2) {
    ["username"]=> string(5) "user1"
    ["password"]=> string(5) "user1"
  }

CakePHP アソシエーション機能

モデル

※src\Model\Table\ProductsTable.php
class ProductsTable extends Table
{
  public function initialize(array $config)
  {
    ~
    $this->hasOne('Receipt', [
      'foreignKey' => 'product_id',
      'bindingKey' => 'id'
    ]);
    
    $this->hasMany(~, [
      'foreignKey' => ~,
    ]);
  }
}

※src\Model\Table\ReceiptTable.php
class ReceiptTable extends Table
{
  public function initialize(array $config)
  {
    ~
    $this->belongsTo('Users', [
      'foreignKey' => 'user_id',
      'joinType' => 'INNER',
    ]);

    $this->belongsTo('Products', [
      'foreignKey' => 'product_id',
      'joinType' => 'RIGHT',
    ]);
  }
}

コントローラ

※src\Controller\WatchesController.php
class WatchesController extends AppController
{
  public function productList(){

   $products = $this->Products
    ->find()
    ->contain(['receipt'])
    ->where(['Products.col1!=' => '0'])
    ;

    $this->set(compact('products'));
  }
}

※src\Controller\AdminsController.php
class AdminsController extends AppController
{
  public function receipt(){
    $receipts = $this->paginate(
      $this->Receipt
       ->find()
       ->contain(['products', 'users'])
       );

    $this->log($receipts);

    $this->set(compact('receipts'));
  }
}

ビュー

※src\Template\Watches\product_list.ctp
<table>
  ~
  <?php foreach ($products as $product): ?>
  <tr>
   <td><?= h($product->col1) ?></td>
    <td><?= h($product->Receipt['col1']) ?></td>
  </tr>
  <?php endforeach; ?>
</table>

CakePHP Helper操作

ヘルパーとは

ビューから利用する共通処理
特定のビューに限定しない処理を記述し、全てのビューから利用できる

ヘルパーの作成

ヘルパー

※~\src\View\Helper\UtilHelper.php
<?php
namespace App\View\Helper;

use Cake\View\Helper;

class UtilHelper extends Helper {
 public function initialize(array $config){
  parent::initialize($config);
 }

 public function getText(){
  return 'aaa';
 }
}

ヘルパーのロード

※~\src\View\AppView.php
class AppView extends View{
 public function initialize()
 {
  $this->loadHelper('Util');
 }
}

ヘルパーの利用

View

※~\src\Template\(コントローラ名)\~.ctp
<?php
 echo $this->Util->getText();
?>

CakePHP Behavior操作

ビヘイビアとは

モデルから利用する共通処理
特定のモデルに限定しない処理を記述して、特定のモデルから利用する

ビヘイビアの作成

Behavior

※src\Model\Behavior\UtilBehavior.php
namespace App\Model\Behavior;

use Cake\ORM\Behavior;
use Cake\ORM\Table;

class UtilBehavior extends Behavior {

 public function initialize(array $config){
  parent::initialize($config);
 }

 public function getAll() {
  特定のモデルでないモデルを操作
  return $this->_table->find()->all();
 }
}

Model

class ~Table extends Table
{
 public function initialize(array $config){
  parent::initialize($config);
  ~
  ビヘイビアのロード
  $this->addBehavior('Util');
 }
}
 ~
}

ビヘイビアの利用

Controller

namespace App\Controller;
class ~Controller extends AppController
{
 public function initialize(){
  parent::initialize();
  $this->~= TableRegistry::get('~');
 }

 public function index(){
  $ret = $this->~->getAll();
 }
}

CakePHP Component操作

コンポーネントとは

コントローラの機能を拡張するプログラム

独自コンポーネント

コントローラ

use App\Controller\Component;

ロード
$this->loadComponent('Util');

コンポーネントの使用
$point = $this->Util->getPoint();

コンポーネント

※src\Controller\Component\UtilComponent.php
<?php
namespace App\Controller\Component;
use Cake\Controller\Component;
use Cake\ORM\TableRegistry;

class UtilComponent extends Component
{
  public function startup(){
   $this->Users = TableRegistry::get('users');
  }

  public function getPoint()
  {
   $point = $this->Users
    ->find()
    ->select(['point'])
    ->where(['id' => $_SESSION['Auth']['User']['id']])
    ->hydrate(false)
    ->toList()[0]['point'];
    
   return $point;
  }
}

フラッシュコンポーネント

※コントローラ
$this->loadComponent('Flash');
$this->Flash->set('Test');

※ビュー
<?= $this->Flash->render() ?>

Authコンポーネント

コントローラ

Authコンポーネント追加処理
$this->loadComponent(
 'Auth', [
  'authorize' => ['Controller'],
  'authenticate' => 認証に関する設定
  'loginRedirect' => ログイン後のリダイレクト先
  'logoutRedirect' => ログアウト後のリダイレクト先
  'authError' => 認証エラー時メッセージ
 ]
);

※コントローラ
public function initialize()
{
  parent::initialize();

  $this->loadComponent('RequestHandler');
  $this->loadComponent('Flash');
  $this->loadComponent(
   'Auth', [
    'authorize' => ['Controller'],
    'authenticate' => [
     'Form' => [
      'fields' => [
       'username' => 'username',
       'password' => 'password',
      ]
     ]
    ],
    'loginRedirect' => [
     'controller' => 'Users',
     'action' => 'index',
    ],
    'logoutRedirect' => [
     'controller' => 'Users',
     'action' => 'login',
    ],
    'authError' => 'ログインしてください。',
   ]
  );
}

ログインページ

<div>
 <?= $this->Flash->render('auth') ?>
 <?= $this->Form->create() ?>
  <fieldset>
   <legend>アカウント名 / パスワード</legend>
   <?= $this->Form->input('username') ?>
   <?= $this->Form->input('password') ?>
  </fieldset>
  <?= $this->Form->button(__('送信')); ?>
 <?= $this->Form->end() ?>
</div>

callbackメソッドの実行順序

Controller Component
initialize  
  initialize initialize(array $config)
  beforeFilter beforeFilter(Event $event)
beforeFilter  
  startup startup(Event $event)
action  
  beforeRender beforeRender(Event $event)
beforeRender
  shutdown shutdown(Event $event)
afterFilter