CakePHP 2.x系におけるSchemaについて
スキーマファイルの使い方
スキーマファイルは、プロジェクトで使用するデータベースのテーブル定義(テーブルの構造)をphpで記述したものです。現在のデータベースのテーブル定義にもとづき、その内容をスキーマファイルとして保存することが出来ます。
この機能は、複数人でプロジェクトを進める際に有用です。開発者1と開発者2で、プロジェクトを開発しているとすると、開発者1が自分で作成したテーブルをスキーマファイル(schema.php)に変換して、gitリポジトリにコミットすることが出来ます。開発者2は、リポジトリをcloneして、スキーマファイル(schema.php)に基づいて、テーブルを作成することが出来ます。
同様のことは、テーブル定義をdumpすることでも可能です。ただし、dump -> restoreの場合、データベースを1から作り直すことになります。このとき、開発中に挿入したレコードも一度すべて消えてしまうため、開発中に気軽に更新することができません。
一方、スキーマファイルを利用すると、現在のデータベースと、スキーマファイルに記述されている内容を比較して、足りないテーブルや変更されたテーブルのみを生成・修正することが出来ます。よって、開発中に他の開発者が加えた変更を、自分の作業環境に反映することが気軽に行うことが出来ます。
また基本的に、スキーマファイルでは、レコードについては関与しませんが、スキーマ処理のbeforeスクリプト、afterスクリプトが設定出来るので、スクリプト内で初期データのようなレコードを挿入する記述を行うことが可能です。
スキーマファイルの仕組みは、マイグレーション(CakeDC Migration)と異なり、一ファイルでデータベース全体をカバーしているので、複数の開発者がそれぞれテーブル追加、修正の変更を行った場合、スキーマファイルのコンフリクトが発生します。これが発生した場合は、コンフリクト解決を手作業で解決しなくてはいけません。
DBテーブルからスキーマファイルの作成
Cakeのプロジェクトを作成したばかりの状態では、Config/Schema/schema.php
ファイルは存在しません。
スキーマファイルの作成
次のコマンドで、schema.php
ファイルを作成します。
$ Console/cake schema generate -f
このコマンドは、現在のDBのテーブル情報をもとに、schema.php
ファイルを生成します。
(-f
は、CakeのModelとひもづいていないテーブルも、スキーマの対象にする場合につけます。
通常は、いつも-f
をつけておけば良いと思ってください。)
空のスキーマファイルの作成
まだテーブルが何もない状態で
$ Console/cake schema generate -f
を実行すると、Config/Schema/schema.php
が生成されて、次のようになります。
<?php
class AppSchema extends CakeSchema {
public function before($event = array()) {
return true;
}
public function after($event = array()) {
}
}
テーブルがなにもない状態なので、空のschema.php
ファイルが出来ました。
テーブルが存在する場合のスキーマファイルの作成
次に、SQLコマンドを実行して、テーブルを一つ作成してみます。
CREATE TABLE person
( id int not null auto_increment,
name text,
primary key (id)
);
この状態で、再度
$ Console/cake schema generate -f
を実行してみます。
すでに、Config/Schema/schema.phpファイルが存在するので、このファイルを上書き(Overwrite)するか、それともこれとは別ファイルとして保存(Snapshot)するかを確認してきます。
Schema file exists.
[O]verwrite
[S]napshot
[Q]uit
Would you like to do? (o/s/q)
O(Overwrite)を選択すると、既存のschema.phpファイルが上書きされます。
S(Snapshot)を選択すると、新しくschema_1.phpファイルが作成されます。(すでにschema_1.phpファイルが存在する場合は、schema_2.phpファイルが作成されます)
いずれにしても、新しいスキーマファイルは次のようになります。
<?php
class AppSchema extends CakeSchema {
public $file = 'schema.php'; // Overwriteを選択したときは、ここが'schema_1.php'のようになります。
public function before($event = array()) {
return true;
}
public function after($event = array()) {
}
public $person = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'unsigned' => false, 'key' => 'primary'),
'name' => array('type' => 'text', 'null' => true, 'default' => null, 'collate' => 'utf8_general_ci', 'charset' => 'utf8'),
'indexes' => array(
'PRIMARY' => array('column' => 'id', 'unique' => 1)
),
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
}
パブリックフィールドの$person
が、personテーブルの定義になります。
今回、personテーブルは空でしたが、仮にpersonテーブルにレコードがあったとしても、レコードの内容はスキーマファイルには反映されません。
スキーマファイルからDBテーブルの反映
既存のテーブルを削除して作成
既存のテーブルを削除して作成する場合、
$ Console/cake schema create
を実行する。すると、次のように既存のテーブルを削除して良いかの確認が出る。(以下は、スキーマファイル内にanimal
テーブル、book
テーブル、person
テーブルがある場合)
The following table(s) will be dropped.
animal
book
person
Are you sure you want to drop the table(s)? (y/n)
テーブルをdropして良ければyを、ダメであればnを選択する。削除する場合、表示されたすべてのテーブルが全部削除されてしまい、個別にy/nを選べるわけではないので注意。
yを選択した場合、次のように削除処理が実行される
Dropping table(s).
animal updated.
book updated.
person updated.
なお、yを選択した場合であっても、スキーマファイルに記述されていないテーブルがDB側にあった場合、そのテーブルは削除されない。
次に、今度はテーブルを生成して良いのかの確認が出る
The following table(s) will be created.
animal
book
person
Are you sure you want to create the table(s)? (y/n)
テーブルを生成して良ければyを、ダメであればnを選択する。yを選択すると、テーブルの生成が実行される。
Creating table(s).
animal updated.
book: updated.
person updated.
仮に、drop処理でnを選択し、テーブル生成時点で同名の既存テーブルが存在している場合、そのテーブルの生成処理は実行されない。たとえば、bookテーブルがすでに存在している場合、
Creating table(s).
animal updated.
book: SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'book' already exists
person updated.
となる。
既存のテーブルとスキーマファイルを比較して、更新
テーブルの削除・新規作成ではなく、現在のテーブルとスキーマファイルを比較して、不足分を更新(修正)する場合は、
$ cake schema update
を実行する。
例えば、bookテーブルのみがない状態で上記を実行すると、
Comparing Database to Schema...
The following statements will run.
CREATE TABLE `cakeweb2`.`book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`price` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8,
COLLATE=utf8_general_ci,
ENGINE=InnoDB;
Are you sure you want to alter the tables? (y/n)
と実行されるSQLコマンドが表示されるので、問題なければyを、変更してはダメであればnを選択する。
yを選択した場合、次のように表示されて、更新処理が実行される。
Updating Database...
book updated.
End update.
スキーマファイルを指定してcreate/updateする。
これまでは、schema.php
ファイルが対象となり、createやupdateを行ってきた。
もしも、Snapshotで作成したschema_1.php
やschema_2.php
のような、バージョン付きのファイルを対象する場合、-s
オプションを使う。
例えば
$ Console/cake schema create -s 1
とすると、schema_1.php
が対象となり、テーブルの作成が実行される。
同様に
$ Console/cake schema update -s 2
とすると、schema_2.php
が対象となり、テーブルの更新が実行される。
フックスクリプトを使って、レコードを追加する
afterフックスクリプトを使って、レコードを追加することができます。この場合、手作業でschema.phpファイルを編集する必要が有ります。
また、モデルクラスを通してレコードを作成するので、モデルクラスも必要になります。
例えば、personテーブルにレコードを追加したい場合、Personクラスが必要です。例えば、person.php
ファイルは次のようになります。
<?php
class Person extends AppModel {
}
次に、スキーマファイルのafterスクリプト(afterメソッド)を手で編集して
public function after($event = array()) {
if (isset($event['create'])) {
switch ($event['create']) {
case 'person':
App::uses('ClassRegistry', 'Utility'); // 実際にレコードが追加される処理開始
$post = ClassRegistry::init('Person');
$post->create();
$post->save(
array('Person' =>
array('name' => 'Phper')
)
);
break;
}
}
}
のようにします。$event
変数は、スキーマファイルの呼び出し方(createやupdateなど)をキーとして、また対象となるテーブルをバリューとした
配列です。
上記では、createイベントで、personテーブルが対象となってときに、
App::uses('ClassRegistry', 'Utility'); // 実際にレコードが追加される処理開始
$post = ClassRegistry::init('Person');
$post->create();
$post->save(
array('Person' =>
array('name' => 'Phper')
)
);
break;
の部分が実行されます。
よって、例えば
$ Console/cake schema create
であれば上記コードは実行されますが、
$ Console/cake schema update
では実行されません。
スキーマファイルをsqlファイルに変換
スキーマファイルをdumpして、sqlファイルにすることができます。例えば、filename.sql
ファイルとして出力したいのであれば
$ Console/cake schema dump --write filename.sql
を実行します。Config/Schema
以下に、次のようなfilename.sql
ファイルが生成されます。
CREATE TABLE `cakeweb2`.`animal` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`weight` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8,
COLLATE=utf8_general_ci,
ENGINE=InnoDB;
CREATE TABLE `cakeweb2`.`book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`price` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8,
COLLATE=utf8_general_ci,
ENGINE=InnoDB;
CREATE TABLE `cakeweb2`.`person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8,
COLLATE=utf8_general_ci,
ENGINE=InnoDB;