・データベーステーブルが規約に準拠している場合、すぐにORMの利用を開始できる。
use Cake\ORM\TableRegistry;
// articlesテーブルからデータ取得
$articles = TableRegistry::get('Articles');
$query = $articles->find();
foreach ($query as $row) {
// 各$rowはArticleエンティティクラスのインスタンス
echo $row->title;
}
・テーブルクラスの作成
src/Model/Table に置く。
テーブルクラス名は、テーブル名にTableを加える。
namespace App\Model\Table;
use Cake\ORM\Table;
class ArticlesTable extends Table {
}
・主キーの指定
id 以外の主キーの場合、initialize()で設定する。
$this->setPrimaryKey('my_id');
・エンティティクラスの指定
命名規則に従わないエンティティクラスを使用する場合、initialize()で設定する。
$this->setEntityClass('App\Model\Entity\PO');
・ビヘイビアの追加
ビヘイビアは、テーブルクラスの共通ロジック。
initialize()で設定する。
$this->addBehavior('Timestamp');
・データ検証
// コントローラ側
$article = $this->Articles->newEntity($this->request->getData());
if ($article->errors()) {
// 検証失敗
}
・バリデーションの作成
バリデーションは、ユーザ入力を検証する。
// デフォルトで呼ばれるバリデーション
public function validationDefault(Validator $validator) {
$validator->requirePresence('title', 'create')
->notEmpty('title');
$validator->allowEmpty('link')
->add('link', 'valid-url', ['rule' => 'url']);
return $validator;
}
// バリデーションのカスタマイズ
public function validationUpdate($validator) {
...
}
// コントローラ側
$article = $this->Articles->newEntity($this->request->getData(), ['validate' => 'update']);
・requirePresence()
バリデーション対象の配列にフィールドが実在すること。
第2パラメータ
create : create実行時のみ適用
update : update実行時のみ適用
$validator->requirePresence('author_id', 'create');
$validator->requirePresence(['author_id', 'title'], 'create');
$validator->requirePresence([
'author_id' => [
'mode' => 'create',
'message' => 'An author is required.',
],
'published' => [
'mode' => 'update',
'message' => 'The published state is required.',
]
]);
・allowEmpty()
フィールドが空欄でもよいこと。
第2パラメータ
create : create実行時のみ適用
update : update実行時のみ適用
$validator->->allowEmpty('header_image', 'update');
・notEmpty()
フィールドが空欄でないこと。
第2パラメータ
create : create実行時のみ適用
update : update実行時のみ適用
$validator->notEmpty('body', 'Body cannot be empty', 'create')
・add() 組み込みのバリデーションルール
alphaNumeric:半角のアルファベットまたは数字であるか。
'rule' => 'alphaNumeric',
between: データ長が指定範囲内であるか。
'rule' => array('between', 5, 15),
blank: 空かスペースのみであるか。
スペースには、半角スペース・タブ・キャリッジリターン・改行文字を含む。
'rule' => 'blank',
boolean: true/false または 0/1 または '0'/'1'であるか。
'rule' => array('boolean'),
cc: クレジットカード番号として適切か。
'rule' => array('cc', array('visa', 'maestro'), false, null),
comparison: 数字が指定範囲内であるか。
is greater, is less, greater or equal, less or equal, equal to, not equal
'rule' => array('comparison', '>=', 18),
'rule' => array('comparison', 'greater or equal', 18),
date: 日付形式であるか。
'rule' => array('date', array('ymd')),
decimal: データが小数であるか。小数点以下の桁数を指定。
'rule' => array('decimal', 2)
email: email形式であるか。trueを指定すると、メールサーバのホストが存在するか確認する。
'rule' => array('email', true),
equalTo: 指定値と等しいか。
'rule' => array('equalTo', 'cake'),
extension: ファイル名の拡張子が指定値と等しいか。
'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg'),
ip: IPv4形式であるか。
'rule' => 'ip',
isUnique: 重複しない値であるか。
'rule' => 'isUnique',
minLength: データ長が指定値以上であるか。
'rule' => array('minLength', '8'),
maxLength: データ長が指定値以下であるか。
'rule' => array('maxLength', '15'),
money: 金額として有効であるか。通貨記号の位置を'left'/'right'で指定。
'rule' => array('money', 'left'),
Multiple: 複数選択で指定した値が含まれるか。指定数の最小値と最大値を指定。
'rule' => array('multiple', array('in' => array('foo', 'bar'), 'min' => 1, 'max' => 3)),
inList: 指定リストに含まれるか。
'rule' => array('inList', array('Foo', 'Bar')),
numeric: 数字または数値形式であるか。
'rule' => 'numeric',
notEmpty: 空でないか。
'rule' => 'notEmpty',
phone: アメリカの電話番号形式であるか。日本の場合は、電話番号形式の正規表現を第2パラメータに指定。
'rule' => array('phone', null, 'us'),
postal: 郵便番号形式であるか。日本の場合は、郵便番号形式の正規表現を第2パラメータに指定。
'rule' => array('postal', null, 'us')
range: 指定範囲内の値であるか。
'rule' => array('range', 0, 10),
ssn: 社会保障番号であるか。
'rule' => array('ssn', null, 'us')
url: URL形式であるか。
'rule' => 'url'
・ルールの作成
ルールは、コード中で変更されたデータを検証する。
ルールは save()及びdelete()メソッド実行時にチェックされる。
テーブルクラスのbuildRules()メソッドに定義する。
public function buildRules(RulesChecker $rules) {
// add, update時のルール
$rules->add(function ($entity, $options) {
...
}, 'ruleName');
// add時のルール
$rules->addCreate(function ($entity, $options) {
...
}, 'ruleName');
// update時のルール
$rules->addUpdate(function ($entity, $options) {
...
}, 'ruleName');
// delete時のルール
$rules->addDelete(function ($entity, $options) {
...
}, 'ruleName');
return $rules;
}
・エンティティクラスの作成
src/Model/Entity に置く。
エンティティクラス名は、テーブル名。
namespace App\Model\Entity;
use Cakd\ORM\Entity;
class Article extends Entity {
}
・エンティティのデータへのアクセス
$this->Articles->title;
・get
get()をカスタマイズする際に使用する。
protected function _getTitle($title) {
return ucwords($title);
}
// コントローラ側
echo $this->Articles->title;
・set
set()をカスタマイズする際に使用する。
protected function _setTitle($title) {
return Text::slug($title);
}
// コントローラ側
$this->Articles->title = 'foo';
・仮想プロパティの生成
存在しないフィールドを作成し、アクセスを提供する。
protected function _getFullName() {
return $this->_properties['first_name'] . ' ' . $this->_properties['last_name'];
}
// コントローラ側
echo $this->Users->full_name;
・エンティティの変更チェック
・フィールドの変更チェック
$this->Articles->dirty('title');
・変更を設定する
$this->Articles->comments[] = $newComment;
$this->Articles->dirty('comments', true);
・変更前の値を取得する
$org = $this->Articles->getOriginal('title');
・変更されたフィールド一覧を取得する
$dirtyFields = $this->Articles->getDirty();
・一括代入
リクエストからユーザデータをエンティティへ一括代入できるかを指定する。
protected $_accessible = [
'title' => true,
'body' => true,
'*' => false,]; // 指定されていないフィールド
コントローラ側でアクセス可・不可を設定する。
$this->Articles->accessible('user_id', true);
・エンティティがDB上に存在するデータかチェックする
if (!$this->Articles->isNew()) {
echo 'already registered.';
}
・エンティティから配列への変換
$array = $this->Users->toArray();
・エンティティからJSONへの変換
$json = json_encode($this->Users);
・接続設定
config/app.php に 'default' の接続設定をする。
・データベースアクセスAPIで出来ること
・データベース接続
use Cake\Datasource\ConnectionManager;
$dsn = 'mysql://root:password@localhost/my_database';
ConnectionManager::config('default', ['url' => $dsn]);
$con = ConnectionManager::get('default');
・select
$results = $con->execute('select * from articles where id = :id', ['id' => 1])
->fetchAll('assoc');
・insert
$con->insert('articles',['title' => 'A New Article',
'created' => new DateTime('now')],
['created' => 'datetime']);
・update
// id=10を更新
$con->update('articles',['title' => 'New Title'],
['id' => 10]);
・delete
$con->delete('articles', ['id' => 10]);
・query
パラメータは使用できない。
$con->query('update articles set published = 1 where id = 2');
・execute
パラメータ、型指定を使用できる。
$con->execute('update articles set published_date = ? where id = ?', [new DateTime('now'), 2], ['date', 'integer']);
・トランザクション
$con->begin();
$con->execute('update articles set published = ? where id = ?', [true, 2]);
$con->execute('update articles set published = ? where id = ?', [false, 4]);
$con->commit();
// bigen,commit,rollbackを自動でハンドリングする
$con->transactional(function ($con) {
$con->execute('update articles set published = ? where id = ?', [true, 2]);
$con->execute('update articles set published = ? where id = ?', [false, 4]);
});
・結果行の取得
$stmt->execute('select ...');
// 1行読み込む
$row = $stmt->fetch('assoc');
// 全行読み込む
$rows = $stmt->fetchAll('assoc');
foreach ($rows as $row) {
}
・行数の取得
$stmt->execute('select ...');
$rowCount = count($stmt);
$rowCount = $stmt->rowCount();
・クエリオブジェクト
・クエリオブジェクトの作成
$articles = TableRegistry::get('Articles');
$query = $articles->find();
// コントローラの場合
$query = $this->Articles->find();
・メソッドをチェーンする
$query = $this->Articles->find()
->select(['id', 'name'])
->where(['id !=' => 1])
->order(['created' => 'DESC']);
・主キーで単一のエンティティを取得する
$article = this->Articles->get($id);
・カラムから値リストを取得する
$allTitles = $this->Articles->extract('title');
foreach ($allTitles as $title) {
echo $title;
}
・クエリはCollectionオブジェクト
Collectionオブジェクトで使用できるメソッドは、クエリオブジェクトでも使用できる。
$keyValueList = $this->Articles->find()->combine('id', 'title');
・SQL関数
$query=$this->Articles->find();
$query->select(['count' => $query->func()->count('*']);
sum()
avg()
min()
max()
count()
concat() : 2つの値を結合する。
coalesce()
dateDiff() : 2つの日にち/時間の差を取得する。
now()
extract() : SQL式から特定の日付部分(年など)をかえす。
dateAdd()
dayOfWeek()
・Group と Having
$query = $this->Articles->find();
$query->select(['count' => $query->func()->count('view_count'),
'published_date' => 'DATE(created)'])
->group('published_date')
->having(['count >' => 3]);
・Case
select
count(case when published = 'Y' then 1 end) as number_published
from articles
$query = $this->Articles->find();
$publishedCase = $query->newExpr()
->addCase($query->newExpr()->add(['published' => 'Y']),
1, 'integer');
$query->select(['number_published' => $query->func()->count($publishedCase)]);
・エンティティの代わりに配列を取得
$query = $this->Articles->find();
$query->hydrate(false); // エンティティの代わりに配列を返す
$result = $query->toList() // クエリを実行し配列を返す
・高度な条件
$query = $this->Articles->find()
->where(['author_id' => 3,
'OR' => [['view_count' => 2],['view_count' => 3]]]);
$query = $this->Articles->find()
->where(['author_id' => 2])
->orWhere(['author_id' => 3])
->andWhere(['published' => true,
'view_count >' => 10])
->orWhere(['promoted' => true]);
・In
// カラムのデータ型を明示する
$query = $this->Articles->find()
->where(['id' => $ids], ['id' => 'integer[]']);
// もしくはINを含める
$query = $this->Articles->find()
->where(['id IN' => $ids]);
・結果を取得する
// イテレートする
foreach($query as $row) {
}
// 結果を取得する
$results = $query->all();
// コレクションのメソッドを使う
$ids = $query->map(function ($row) {
return $row->id;
});
// 最初の行だけ取得する
$row = $query->first();
・関連付くデータをロードする
article毎に、authorとcommentをロードする。
$query = $this->Articles->find()
->contain(['Authors', 'Comments]);
・inner join with
$query = $this->Articles->find();
$query->innerJoinWith('Tags', function ($q) {
return $q->where(['Tags.name' => 'CakePHP']);
});
・データをinsertする
$query = $this->Articles->query;
$query->insert(['title', 'body'])
->values(['title' => 'First post',
'body' => 'Some body text'])
->values(['title' => 'Second post',
'body' => 'Another body text'])
->execute();
// または
$article = $this->Articles->newEntity();
$article->title = 'this is a new article.';
$article->body = 'this is a body.';
if ($this->Articles->save($article)) {
// 登録されたデータのid
$id = $article->id;
}
・データをupdateする
$query = $this->Articles->query();
$query->update()
->set(['published' => true])
->where(['id' => $id])
->execute();
// または
$article = $this->Articles->get(12);
$article->title = 'changed title';
$this->Articles->save($article);
・データをdeleteする
$query = $this->Articles->query();
$query->delete()
->where(['id' => $id])
->execute();
・関連テーブルのデータ追加・更新
$tag1 = $this->Articles->Tags->findByName('cakephp')->first();
$tag2 = $this->Articles->Tags->newEntity();
$tag2->name = 'fantastic';
$article = $this->Articles->get(12);
$article->tags = [$tag1, $tag2];
// 外部キーは自動でセットされる。
$this->Articles->save($article);
・中間テーブルへのデータ保存
// 最初にレコードを紐づける。
$tag1 = $this->Articles->Tags->findByName('cakephp')->first();
$tag1->_joinData = $this->Articles->ArticlesTags->newEntity();
$tag1->_joinData->tagComment = 'this is a tag comment.';
$this->Articles->Tags->link($article, [$tag1]);
// 既存のアソシエーションを更新する。
$article = $this->Articles->get(1, ['contain' => ['Tags']]);
$article->tags[0]->_joinData->tagComment = 'this is a new comment';
// 必須
$article->dirty('tags', true);
$this->Articles->save($article, ['associated' => ['Tags']]);
・SQLインジェクション対策
バインディングを使用する。
$query->where(['MATCH (comment) AGAINST (:userData)',
'created < NOW() - :moreUserData'])
->bind(':userData', $userData, 'string')
->bind(':moreUserData', $moreUserData, 'datetime');
・UNION
$query1 = $this->Articles->find()
->where(['need_review' => true]);
$query2 = $this->Articles->find()
->where(['published' => false]);
$query2->union($query1);
・ステートメントのロック
$query->epilog('FOR UPDATE');
0コメント