「CakePHP 超入門」まとめ
はじめに
この記事は、CakePHP初学者である私が教材として使用した「CakePHP 超入門」の内容をまとめたものです。
出版社サポートサイト:https://www.shuwasystem.co.jp/support/7980html/5409.html
私はプログラミング学習歴4ヶ月目に突入した初学者です。
この教材を約3週間かけて3周しました。
私の学習歴としては、以下のとおりです。
- HTML&CSS
- PHP
- SQL
まとめの内容は、当書を3周する中で、頻出箇所を中心としたメモ書きに基づいています。
メモは、メモアプリNotionを利用して小まめに書きました。
必須の内容・よく使う内容を中心にまとめています。ロジックや概念の理解というよりは備忘録としての要素が強いです。ですので当書(CakePHP)で紹介されるCRUDを作成する上での基礎的流れは理解した上で、参照するような位置づけになるかと思います。
※特に重要な箇所は今後色分け予定。
本記事の構成は、以下の見出しに分け、基本的に教材に登場する順で記述しています。
- 便利な基本コマンド
- Controller
- View
- Model
- Webアプリ作成時の手順(一例)
- 参考にしたサイト
(内容の括りの都合で、一部ページ順と前後している内容もあります。)
※この教材はCakePHPver.3.5に基づき解説されていますが、私はver.3.8(PHPverは7.4.8)で学習しました。マシンはWindows機です。
便利な基本コマンド
★プロジェクト作成
php composer.phar create-project --prefer-dist cakephp/app:3.8 mycakeapp
★bake (bin\cake bake オプション パラメータ…)
bin\cake bake all 〇〇s
※アソシエーションはチェックすること(ModelのTable内)
★マイグレーション(DBテーブルを作成し使用できる)
※プラグインの追加が必要
/config/boostrap.phpを開き末尾に「$this->ddPlugin('Migrations');」を追記
【マイグレーションファイルを生成(例)】
bin\cake bake migration Create〇〇s title:string[50] content:text stars:integer created:datetime
※「created」「updated」はデフォルトカラム名でdatetimeが指定される
⇒configフォルダ内に「20201124140130_Create〇〇s.php」の様な名前のマイグレーションファイルが作成される
【マイグレーションファイルの読み込み&実行】
bin\cake migrations migrate
Controller
★名前空間はフォルダ階層のような認識(空間を分けることで同名のクラスや関数を使用可)
★URLパラメーター取得
$this->request->query['key'];
★フォームリクエスト(post送信したか)の状態確認(ture or false)
$this->request->is(['post', 'put']);
$this->request->isPost() or **is('post')
★送信されたFormの取得
$this->request->data['People'];
★リクエストを取得
$this->request->getData();
★View(テンプレート)への受け渡し
$this->set('変数名',値);
$this->set(compact('auction')); ⇒(= $this->set('auction',$auction); )
★Model(テーブル)への受け渡し
$this->find('変数名',値); (カスタムファインダーの設定時等)
★独自レイアウトの使用を設定
$this->ViewBuilder()->setLayout('auction');
★モデルの読み込み(※コントローラーと同じ名前のモデルは自動でロードされる)
$this->loadModel('Users');
$this->loadModel('Bidmessages');
※public $useTable=false; ⇐デフォルトテーブルを使わない(initializeより前の冒頭で記述)
★データベースからエンティティを取り出す(検索) conditions
$this->モデル名(〇〇Table)->find('all',['conditions=>[
'and'=>[
'カラム名 like','値',
'カラム名 >=','値']
]]);
※連動するテーブル名と同じ名前のプロパティが組み込まれる
(例)
$find=$this->request->data['People']['find']; フォームの値を取得
$arr=explode(',',$find);
※explode関数は第一引数に指定した記号「,」で区切って配列で取り出す
$condition=['conditions'=>[
'and'=>[
'age ≤'=>$arr[0],
'age ≤'=>$arr[1]
]];
$data=$this->People->find('all',$condition);
★検索の並び替えや一部取り出し ⇐findメソッドの第2引数以降に連想配列を指定
$data=$this->People->find('all',
['order'=>['People.age'=>'asc']]
);
$condition=[
'limit'=>3,'page'=>$find
];
★動的ファインダーのメソッド findBy〇〇And〇〇(値1,値2)
$data=$this->People->findByNameOrMail($find,$find);
※複雑な検索はカスタムファインダーでクエリビルダーと組み合わせる
★クエリービルダー
メソッドチェーン(自身を返してメソッドを連続して呼び出す仕組み)
※「find」メソッドでDBテーブルから取り出したQueryインスタンスのメソッドを呼び出
※メソッドの順不同
->find() ※⇐Queryオブジェクトを返す
->contain(['テーブル名']) ※アソシエーション(デフォルトでは取り出されない)
->where(['カラム名 like'=>'%'.値.'%'])->orWhere(['カラム名 <='=>値])
※1句に1条件のみ
->order('テーブル名.カラム名'=>'asc')->limit(INT)->page(INT);
(例)
$data=$this->People->find()
->where(['name like'=>$find])
->andWhere(['カラム名'=>**値**])
->order(['テーブル名.カラム名'=>'asc'])
->limit(3)->page(1);
★DBテーブルからパラメータに指定したid(PK)のエンティティを取り出す(get)
$entity=$this->People->get($data['id']);
★テーブルに新規挿入したいエンティティのインスタンスを作成
$this->「テーブル」->newEntity(保存したいデータの連想配列);
★作成日を登録(冒頭「use CakeI18n\Time;」記述)
$entity->created_at=new Time(date('Y-m-d H:i:s'));
★エンティティの内容を更新
$this->People->patchEntity($entity,$data);
★エンティティインスタンスの保存(saveメソッドはtrue,falseで返り値)
$this->「テーブル」->save($entity);
◯バリデーションを行い、保存が成功したらリダイレクト、エラーメッセージを表示
if($this->People->save($entity)){
return $this->redirect(['action'=>'index']);
}
$msg='Error was occured...';
★エンティティを削除
$this->People->delete($entity);
★リダイレクト
return $this->**redirect**(['action'=>'index']);
★ページネーション
Paginatorコンポネントは標準搭載でないため、initializeメソッド内でロードする
public function initialize(){
parent::initialize();
$this->loadComponent('Paginator') ;
}
public $paginate=[
※'finder'=>'byAge', ※Model(Table)のカスタムファインダーを利用可能
'limit'=>5,
'sort'=>'id', ⇐URLのクエリパラメータのキーと値になる
'direction'=>'asc', ⇐同じ
'contain'=>['Messages'],
];
$this->paginate('$this->People') ; ※テーブルクラスのインスタンスを引数に指定
(Queryオブジェクト)
★ユーザー認証(ログイン&ログアウトの初期設定)
まずは/config/boostrap.phpを開き、Security設定のenv関数の第2引数をランダムに書き換え
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'=>'login'],
'logoutRedirect'=>[
'controller'=>'Users',
'action'=>'logout'],
'authError'=>'ログインしてください。']);
}
★ログインしてきてたユーザーのエンティティを取り出し、ログインユーザーを設定
$this->set('authuser',$this->Auth->user());
※initialieで記述しsetUserされたエンティティを取得
$user=$this->Auth->identify(); ⇐!empty($user)であればユーザー登録されている
$this->**Auth->setUser**($user); ⇐ログイン中と判断されるように処理
成功 $this->redirect($this->Auth->redirectUrl()); ⇒元ページにリダイレクト
$this-**>Flash->success(__('保存しました。'));
失敗 $this->**flash->error**(__('ログインに失敗しました。')); ⇒エラーメッセージ
★ログイン前の状態で認証不要でアクセスできるアクションを設定(フィルター処理)
beforeFilterは常に最初に処理が実行されるため、各アクションを実行する前に共通処理や初期処理を記述するのに適している。
public function beforeFilter(Event $event){
parent::beforeFilter($event);
$this->Auth->allow(['login','index','add']);
}
★roleによる利用許可設定(管理者のみUsersControllerを利用できる)
public function isAuthorized**($user=null){
// 管理者はtrue
if($user['role']==='admin'){
return true;
}else{
return false;
}// 他は全てfalse
return false;
}
★ログアウト(=セッションを破棄してリダイレクト)
$this->request->session()->destroy();
return $this->redirect($this->Auth->logout());
View
★レイアウト
<?=$this->fetch('title') ?></h1>
<?=$this->fetch('content') ?>
★エレメント
<?=$this->element('header',$header) ?>
「Controller⇒Layout⇒Element」に値を受け渡し
※第一引数に「Element」フォルダ内の相対パスを記述(直下の場合はファイル名)
■ヘルパーはPHPのクラスとして組み込まれている(PHPをHTMLに変換する)
メソッドを呼び出す文を記述してHTMLタグ等に変換されて出力される。
★URLヘルパー
◯階層の指定
$this->Url->build(['controller'=>'■■','action'=>'△△',〇〇] ⇒「/■■/△△/〇〇」
(例)$this->Url->build(['action'=>'bid',$biditem->id])?>
◯クエリパラメータの指定 ⇒「/■■/△△?id=2&password=xxx」
$this->Url->build(['controller'=>'■■','action'=>'△△',
'?'=>['id'=>2,'password'=>'xxx']]);
( ?id= <?=h($obj)->id ?> )
★Textヘルパー
<?=$this->Text->autoLinkUrls('http://google.com') ?> ⇔$this->Html->link
<?=$this->Text->autoLinkEmails('google@gmail.com') ?> (mailto:google@gamil.com)
<?=$this->Text->autoParagraph('one\ntwo\nthree') ?>
★Numberヘルパー
<?=$this->Number->currency('1234567','JPY') ?> 通貨表示 「円」
<?=$this->Number->precision('1234.567',2) ?> 少数切り捨て 「1,234.57」
<?=$this->**Number->toPercentage**('0.12345',2,['multiply'=>true]) ?> 「12.34%」
★Formヘルパー
<?=$this->Form->create(Model, ※$〇〇=$this->Biditems->newEntity();等
['type'=>'post',
'url'=>['controller'=>'〇〇',
'action'=>'〇〇']]) ?>
$this->Form->control('Form.username'); ⇐万能に使える
<?=$this->Form->text('Form◯.name') ?>
<?=$this->Form->checkbox('Form◯.check' , [id=>△△]) ?> ※返る値は0か1
<?=$this->Form->label('△△','〇〇') ?>
<?=$this->Form>checkbox('Form◯.name' , [■■=>△△]) ?>
<?=$this->Form->select('Form◯.select',
['one'=>'最初','two'=>'真ん中','three'=>'最後'],
['multiple'=>true ,'size'=>5]) ?> ⇒foreach文を使って取り出し
<?=$this->Form->submit('送信') ?>
(フォームの入力エラー(バリデーションエラー)の表示)
<?=$this->Form->error('テーブル名.カラム名') ?>
<?=$this->Form->end() ?>
★他、主なHTMLヘルパー・メソッド
$this->Html->doctype('html5')
$this->Html->charset('utf-8')
$this->Html->css('style')
$this->Html->style(['color'=>'red','background'=>'blue',…])
$this->Html->link('リンク',リンク先URL,[])
$this->Html->image('cakeicon.png',['width'=>'100px,'height'=>'100px'])
$this->Html->tableHeaders(['title','name','mail'],['styles'=>['background:#066;color:#fff']])
$this->Html->tableCells([[’Hello!’,'taro','taro@yamada'],['…','…','…'],['…','…','…'],],
['styles'=>['background:#ccf']],['styles'=>['background:#dff']])
$this->Html->nestedList(['first line','second line','third line'=>['one','two','three']])
['_ext'=>'png','sample'] ⇒sample.png
★HTMLエスケープ
<?=h($■■) ?>
★「find」メソッドでDBテーブルから取り出したQueryインスタンス(=$data)からエンティティの配列を取り出す
$data->**toArray() as $〇〇 ⇐コントローラーで処理しておいた方が◎
※toArray()で取り出したレコードは、エンティティの配列になっている
⇒エンティティにはカラム名がそのままプロパティに組み込まれる
★containでアソシエーションしたレコードの取り出し
containで組み込まれるプロパティが単数形(belongsTo)か複数形(hasMany)か
取り出されるオブジェクトが1個(belongsTo)か配列(hasMany)か
【belongsTo】
<?=h(**$obj->person(単数形)->name) ?> **※取り出される値が1つ
【hasMany】
<?php foreach($obj->messages(複数形) as $item): ?> ※取り出される$itemが配列
<?=$item->message ?>
★プロパティが定義されていて値が存在するかの確認
<?=$biditem->has('user')
has() メソッドは、プロパティが定義されていてNull以外の値を持つ場合、
true を返します。 isEmpty() と hasValue() を使って、プロパティに '空でない'
値が含まれているかどうかを 調べることができます。
★ページネーション
Paginatorヘルパー
(ナビゲーション)
<div class="paginator">
<ul class="pagination">
<?=$this->Paginator->first('|<<'.'最初へ') ?>
<?=$this->Paginator->prev('<<'.'前へ') ?>
<?=$this->Paginator->next('次へ'.'>>') ?>
<?=$this->Paginator->last('最後へ'.'>>|') ?>
</ul>
</div>
(ソート)
<th><?=$this->Paginator->sort('項目名') ?></th>
★ログイン画面へのリダイレクト時にエラーメッセージを表示
<?=$this->Flash->render('auth') ?>
Model (TableとEntityで構成)
★Table
★DBアクセス設定
/config/app.php の以下の箇所を書き換え
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'username' => 'root',
'password' => '',
'database' => 'mydata',
'timezone' => '+09:00',
…
]
]
★カスタムファインダー
public function find〇〇(Query $query,array $options){
$me=$options['me']; ⇐コントロラー側で「->find('me',['me'=>$find]);」の処理
return $query->**~具体的な処理~;
}
※$query(Queryオブジェクト)=$this->People->find()
★アソシエーション(リレーションシップ)
※関連する全てのテーブルクラスに記載する
class 〇〇Table extends Table{
public function initialize(array $config){
~~
$this->hasOne('モデル名');※どちらが無くても大丈夫かでhasOneか判断
$this->hasMany('モデル名');
$this->belongsTo('モデル名');
$this->belongsToMany('モデル名');
}
}
hasMany/hasOne ⇔ belongsTo
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER',
]);
★データをINSERT、UPDATE時の時間を自動的に設定
$this->addBehavior('Timestamp');
★バリデーション(一部)
public function validationDefault(Validator $validator){
~カラム毎にバリデーションの設定~
$validator
->integer('カラム名','「エラーメッセージ」')
->numeric('カラム名','「エラーメッセージ」') ※数字全般を許可
->scalar('カラム名')
->requirePresence('カラム名','create') ※null & 空文字("")を拒否する
->notEmpty('カラム名','「エラーメッセージ」') ※null
->allowEmpty('カラム名','update')
->email('mail',(真偽値),'「エラーメッセージ」')
->equals('カラム名',1,'「エラーメッセージ」') ※1の場合
->greaterThan('カラム名',-1,'「エラーメッセージ」') ※0より大きいの値の場合
->lessThanOrEquals('カラム名',-1,'「エラーメッセージ」') ※0以上の値の場合
->ascii('カラム名','「エラーメッセージ」') ※半角文字のみ許可
->alphaNumeric('カラム名','「エラーメッセージ」') ※文字と数字のみ許可
->containsNonAlphaNumeric('カラム名','「エラーメッセージ」')※半角英数字含む
->boolean('カラム名','「エラーメッセージ」') ※真偽値のみ許可(checkbox用)
->isArray('カラム名','「エラーメッセージ」') ※配列のみ許可
->range('カラム名',['min'=>■■,'max'=>△△]) ※指定した範囲のみ許可
->minLength('カラム名',5] ※文字数チェック(5文字以上)
->lengthBetween('カラム名',['min'=>■■,'max'=>△△])※指定文字数の範囲を許可
->dateTime('カラム名',[,'ymd')]) ※指定した範囲のみ許可
->inList('カラム名',配列) ※指定の配列に含まれるかチェック
}
return $validator;
★特別なルールの設定
public function buildRules(RulesChecker $rules){
◯ユニーク設定(重複した値不可)
$rules->add($rules->isUnique(['username']));
◯外部テーブルにその値を持つレコードがあるか確認
$rules->add($rules->existsIn(['user_id'], 'Users')); ※Usersは外部テーブル
return $rules;
}
(カスタマイズ用)※デフォルトで設定あり
■データベーステーブルへのアクセス指定
$this->setTable('people');
■->find('list')で特定のカラムのエンティティをまとめて取り出す
$this->setDisplayField('name');
★Entity
★一括代入の設定(trueにするとリクエストデータ(Formの値等)をで一括代入できる)
protected $_accessible=[
'name'=>true,
'mail'=>true,
'age'=>true,
'biditems'=>true ※アソシエーションも設定しておく
];
★パスワードの非表示&暗号化のオブジェクト
use Cake\Auth\DefaultPasswordHasher;
protected $_hidden = [
'password'
];
protected function _setPassword($password){
return (new DefaultPasswordHasher)->hash($password);
}
Webアプリ作成時の流れ(一例)
機能設計・DB設計
⇓
DB作成(migration)
⇓
CRUD作成(bake all)
⇓
Model(Entity)の確認 $_accessible
⇓
Model(Table)の確認 アソシエーション、バリデーション、buildRules
⇓
認証機能の設定 Authコンポネント
⇓
Controller(usersの認証等)
⇓
Controller(その他アクション)
⇓
View(Layout、Element)
⇓
View(Template)
デバッグ方法
CakePHP3系のデバッグに役立つ関数の紹介 | レコチョクのエンジニアブログ
参考サイト
【PHP超入門】名前空間(namespace・use)について - Qiita
CakePHP3のMVC入門[Model]モデル[Table/Entity]を用いデータベースからデータ取得を行い表示させる
Discussion