LoginSignup
16
19

More than 5 years have passed since last update.

CakePHP 2.x系におけるSchemaについて

Last updated at Posted at 2016-01-05

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.phpschema_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;
16
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
19