作成中...。
http://www.symfony-project.org/jobeet/1_2/Doctrine/ja/03
この節では、symfonyの2番目のORMであるDoctrineにおける内容を記載しております。
ちなみに次期バージョンである1.3のデフォルトのORMは、Doctrineになるそうです。
第3日目のチュートリアルが完了するまでの目安
3時間~10時間
DB周りの設定やデータモデルの検討をします。
第3日目は、第1日目と同様に結構重めのタスクになります。
さらっと流すと3時間、じっくり試すと10時間かかると思われます。
symfonyのデフォルトのORMは、Propelというフレームワークです。
Propelは、Apache Torqueにインスパイヤ(影響)されているフレームワークです。
ただし、Apache Torgueは、Javaの世界では、それほど採用例が多くないフレームワークなため、情報量が少ないです。
一方、Doctorineは、Javaの世界におけるORMフレームワークであるHibarnateにインスパイヤ(影響)されているフレームワークです。
また、symfony1.3(次期バージョン)のデフォルトのORMになるそうです。
Hibarnateは、Javaの世界ではよく使われているフレームワークであるため、いろいろと解説記事があり、情報量が多いです。
ただし、HibarnateのPHP版であるDoctorineの情報量は少ないため、Hibarnateの記事からDoctrineに変換して考えなければなりません。
いずれにせよ、どちらも一長一短があり、甲乙つけがたいのですが、自分はDoctorineを選択します。
symfonyのデフォルトのORMは、Propelなので、Doctorineを使うためには若干変更を加えなければなりません。
まず、最初にデフォルトのORMをPropelからDoctorineを使用するように変更します。
ProjectConfigurationクラスのsetup()メソッドを以下のように書き換えます。
config/ProjectConfiguration.class.php
public function setup()
{
$this->enablePlugins(array('sfDoctrinePlugin'));
$this->disablePlugins(array('sfPropelPlugin'));
}
また、Propelでしか使用しない以下のファイルを削除します。
また、web/sfDoctrinePluginのサブディレクトリも削除します。
エンティティ関係図(Entiry-Relation図)を作成します。
ER図を描くツールは、いくつかありますが、MySQLをRDBMSに選択した場合、[MySQL Workbench]を選択すべきでしょう。
MySQL Workbenchを使用したいがために、MySQLを選択するというのも有りだと思います。
MySQL Workbenchのインストール方法は、以下の記事を参照してください。
論理DBのER図を描くためには、日本語が表示できなければなりません。
このため、[Tools]-[Options...]を選択します。
フォントタブを選択し、フォントフェイスを[メイリオ]のような日本語フォントを選択します。
特に[メイリオ]で無くとも構わず、[MS ゴシック]でも構いません。日本語が表示できるフォントであることが必要になります。
ちなみに全部、修正する必要があります。
MySQL Workbenchを使用して論理DBを描いてみました。

作成した論理モデルです。
これにより、論理DBでお話ができるようになります。
MySQL Workbenchを使用して、物理DBを描いてみました。

作成した物理モデルです。
これにより、このようにDDLスクリプトが作成できます。
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
CREATE SCHEMA IF NOT EXISTS `jobeet` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `jobeet`;
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_category`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_category` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`name` VARCHAR(255) NOT NULL COMMENT 'カテゴリ名' ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `uk_name` USING BTREE (`name` ASC) )
ENGINE = InnoDB
COMMENT = 'カテゴリ';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_job`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_job` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`category_id` INT NOT NULL COMMENT 'カテゴリID' ,
`type` VARCHAR(255) NULL COMMENT '契約条件' ,
`company` VARCHAR(255) NOT NULL COMMENT '企業名' ,
`logo` BLOB NULL COMMENT 'ロゴ' ,
`url` VARCHAR(255) NULL COMMENT 'URL' ,
`position` VARCHAR(255) NOT NULL COMMENT '業務' ,
`location` VARCHAR(255) NOT NULL COMMENT '就業場所' ,
`description` LONGTEXT NOT NULL COMMENT '仕事の説明' ,
`how_to_apply` LONGTEXT NOT NULL COMMENT '応募方法' ,
`token` VARCHAR(255) NOT NULL COMMENT 'トークンキー' ,
`is_public` BOOLEAN NOT NULL DEFAULT TRUE COMMENT '公開' ,
`is_activated` BOOLEAN NOT NULL DEFAULT FALSE COMMENT '有効' ,
`email` VARCHAR(255) NOT NULL COMMENT 'E-Mail' ,
`expires_at` TIMESTAMP NOT NULL COMMENT '有効期限' ,
`created_at` TIMESTAMP NOT NULL COMMENT '作成日時' ,
`updated_at` TIMESTAMP NOT NULL COMMENT '更新日時' ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `uk_token` USING BTREE (`token` ASC) ,
INDEX `fk_category` (`category_id` ASC) ,
CONSTRAINT `fk_category`
FOREIGN KEY (`category_id` )
REFERENCES `jobeet`.`jobeet_category` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = '求人';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_affiliate`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_affiliate` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`url` VARCHAR(255) NOT NULL COMMENT 'URL' ,
`email` VARCHAR(255) NOT NULL COMMENT 'E-Mail' ,
`token` VARCHAR(255) NOT NULL COMMENT 'トークンキー' ,
`is_active` BOOLEAN NOT NULL DEFAULT FALSE COMMENT '有効' ,
`created_at` TIMESTAMP NOT NULL COMMENT '作成日時' ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `uk_affiliate` USING BTREE (`email` ASC) )
ENGINE = InnoDB
COMMENT = 'アフィリエイト';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_category_affiliate`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_category_affiliate` (
`category_id` INT NOT NULL COMMENT 'カテゴリID' ,
`affiliate_id` INT NOT NULL COMMENT 'アフィリエイトID' ,
PRIMARY KEY (`category_id`, `affiliate_id`) ,
INDEX `fk_category` (`category_id` ASC) ,
INDEX `fk_affiliate` (`affiliate_id` ASC) ,
CONSTRAINT `fk_category`
FOREIGN KEY (`category_id` )
REFERENCES `jobeet`.`jobeet_category` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION,
CONSTRAINT `fk_affiliate`
FOREIGN KEY (`affiliate_id` )
REFERENCES `jobeet`.`jobeet_affiliate` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'カテゴリ-アフィリエイト';
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
かなり道を外れてしまったので、もとに戻ります。
チュートリアルでは、データモデルをYAML形式で作成した後、PHPモデル、DBのDDLを作るという順序で
DB周りの設定をしようとしています。
チュートリアルの[config/doctrine/schema.yml]は、以下のようになっています。
(configディレクトリに[doctrine]という名のサブディレクトリを掘って、そこに[schema.yml]というファイルを作成します。)
# config/doctrine/schema.yml
JobeetCategory:
actAs: { Timestampable: ~ }
columns:
name: { type: string(255), notnull: true, unique: true }
JobeetJob:
actAs: { Timestampable: ~ }
columns:
category_id: { type: integer, notnull: true }
type: { type: string(255) }
company: { type: string(255), notnull: true }
logo: { type: string(255) }
url: { type: string(255) }
position: { type: string(255), notnull: true }
location: { type: string(255), notnull: true }
description: { type: string(4000), notnull: true }
how_to_apply: { type: string(4000), notnull: true }
token: { type: string(255), notnull: true, unique: true }
is_public: { type: boolean, notnull: true, default: 1 }
is_activated: { type: boolean, notnull: true, default: 0 }
email: { type: string(255), notnull: true }
expires_at: { type: timestamp, notnull: true }
relations:
JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id, foreignAlias: JobeetJobs }
JobeetAffiliate:
actAs: { Timestampable: ~ }
columns:
url: { type: string(255), notnull: true }
email: { type: string(255), notnull: true, unique: true }
token: { type: string(255), notnull: true }
is_active: { type: boolean, notnull: true, default: 0 }
relations:
JobeetCategories:
class: JobeetCategory
refClass: JobeetCategoryAffiliate
local: affiliate_id
foreign: category_id
foreignAlias: JobeetAffiliates
JobeetCategoryAffiliate:
columns:
category_id: { type: integer, primary: true }
affiliate_id: { type: integer, primary: true }
relations:
JobeetCategory: { onDelete: CASCADE, local: category_id, foreign: id }
JobeetAffiliate: { onDelete: CASCADE, local: affiliate_id, foreign: id }
schema.yml から DDLスクリプトを作成するタスクを実行するには、以下のようなコマンドを打ちます。
symfony doctrine:build-sql
出来上がったDDLスクリプトは、以下のファイルになります。
実際には、改行が含まれず、非常に見にくいDDLが作成されますが、整形しました。
CREATE TABLE jobeet_affiliate ( id BIGINT AUTO_INCREMENT, url VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, token VARCHAR(255) NOT NULL, is_active TINYINT(1) DEFAULT '0' NOT NULL, created_at DATETIME, updated_at DATETIME, PRIMARY KEY(id) ) ENGINE = INNODB; CREATE TABLE jobeet_category ( id BIGINT AUTO_INCREMENT, name VARCHAR(255) NOT NULL UNIQUE, created_at DATETIME, updated_at DATETIME, PRIMARY KEY(id) ) ENGINE = INNODB; CREATE TABLE jobeet_category_affiliate ( category_id BIGINT, affiliate_id BIGINT, PRIMARY KEY(category_id, affiliate_id) ) ENGINE = INNODB; CREATE TABLE jobeet_job ( id BIGINT AUTO_INCREMENT, category_id BIGINT NOT NULL, type VARCHAR(255), company VARCHAR(255) NOT NULL, logo VARCHAR(255), url VARCHAR(255), position VARCHAR(255) NOT NULL, location VARCHAR(255) NOT NULL, description TEXT NOT NULL, how_to_apply TEXT NOT NULL, token VARCHAR(255) NOT NULL UNIQUE, is_public TINYINT(1) DEFAULT '1' NOT NULL, is_activated TINYINT(1) DEFAULT '0' NOT NULL, email VARCHAR(255) NOT NULL, expires_at DATETIME NOT NULL, created_at DATETIME, updated_at DATETIME, INDEX category_id_idx (category_id), PRIMARY KEY(id) ) ENGINE = INNODB; ALTER TABLE jobeet_category_affiliate ADD FOREIGN KEY (category_id) REFERENCES jobeet_category(id) ON DELETE CASCADE; ALTER TABLE jobeet_category_affiliate ADD FOREIGN KEY (affiliate_id) REFERENCES jobeet_affiliate(id) ON DELETE CASCADE; ALTER TABLE jobeet_job ADD FOREIGN KEY (category_id) REFERENCES jobeet_category(id) ON DELETE CASCADE;
Propelが作ったDDLとDoctrineが作ったDDLについて幾つか相違があったので、列挙しておきます。
MySQL Workbenchで作成した[jobeet.sql]と[data/sql/schema.sql]を比較して判ったこと
doctrine:insert-sqlタスクを実行すると、[data/sql/schema.sql]で定義されたDDLでスキーマーが上書き、保存されてしまいます。
もちろん、スキーマの情報が消えるという警告は出ます。
とはいえ、doctrine:insert-sqlは実行する可能性が高く、[data/sql/schema.sql]と[jobeet.sql]が不一致である点はいただけません。
このため、RAD開発を維持するため、外部定義のDDLは、symfonyが想定するDDL定義を同一にする必要があります。
ということで、物理モデルを修正してみました。
#ref(): Usage:([pagename/]attached-file-name[,parameters, ... ][,title])
作成した物理モデルです。
新しいDDLスクリプトです。
これにより、ほとんど一緒になります。
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
CREATE SCHEMA IF NOT EXISTS `jobeet` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `jobeet`;
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_category`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jobeet`.`jobeet_category` ;
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_category` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`name` VARCHAR(255) NOT NULL COMMENT 'カテゴリ名' ,
`create_at` DATETIME NULL ,
`update_at` DATETIME NULL ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `uk_name` USING BTREE (`name` ASC) )
ENGINE = InnoDB
COMMENT = 'カテゴリ';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_job`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jobeet`.`jobeet_job` ;
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_job` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`category_id` BIGINT NOT NULL COMMENT 'カテゴリID' ,
`type` VARCHAR(255) NULL COMMENT '契約条件' ,
`company` VARCHAR(255) NOT NULL COMMENT '企業名' ,
`logo` VARCHAR(255) NULL COMMENT 'ロゴ' ,
`url` VARCHAR(255) NULL COMMENT 'URL' ,
`position` VARCHAR(255) NOT NULL COMMENT '業務' ,
`location` VARCHAR(255) NOT NULL COMMENT '就業場所' ,
`description` TEXT NOT NULL COMMENT '仕事の説明' ,
`how_to_apply` TEXT NOT NULL COMMENT '応募方法' ,
`token` VARCHAR(255) NOT NULL COMMENT 'トークンキー' ,
`is_public` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '公開' ,
`is_activated` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '有効' ,
`email` VARCHAR(255) NOT NULL COMMENT 'E-Mail' ,
`expires_at` DATETIME NOT NULL COMMENT '有効期限' ,
`created_at` DATETIME NULL COMMENT '作成日時' ,
`updated_at` DATETIME NULL COMMENT '更新日時' ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `uk_token` USING BTREE (`token` ASC) ,
INDEX `fk_category` (`category_id` ASC) ,
CONSTRAINT `fk_category`
FOREIGN KEY (`category_id` )
REFERENCES `jobeet`.`jobeet_category` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = '求人';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_affiliate`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jobeet`.`jobeet_affiliate` ;
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_affiliate` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`url` VARCHAR(255) NOT NULL COMMENT 'URL' ,
`email` VARCHAR(255) NOT NULL COMMENT 'E-Mail' ,
`token` VARCHAR(255) NOT NULL COMMENT 'トークンキー' ,
`is_active` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '有効' ,
`created_at` DATETIME NULL COMMENT '作成日時' ,
`update_at` DATETIME NULL ,
PRIMARY KEY (`id`) ,
UNIQUE INDEX `email` USING BTREE (`email` ASC) )
ENGINE = InnoDB
COMMENT = 'アフィリエイト';
-- -----------------------------------------------------
-- Table `jobeet`.`jobeet_category_affiliate`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jobeet`.`jobeet_category_affiliate` ;
CREATE TABLE IF NOT EXISTS `jobeet`.`jobeet_category_affiliate` (
`category_id` BIGINT NOT NULL COMMENT 'カテゴリID' ,
`affiliate_id` BIGINT NOT NULL COMMENT 'アフィリエイトID' ,
PRIMARY KEY (`category_id`, `affiliate_id`) ,
INDEX `fk_category` (`category_id` ASC) ,
INDEX `fk_affiliate` (`affiliate_id` ASC) ,
CONSTRAINT `fk_category`
FOREIGN KEY (`category_id` )
REFERENCES `jobeet`.`jobeet_category` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION,
CONSTRAINT `fk_affiliate`
FOREIGN KEY (`affiliate_id` )
REFERENCES `jobeet`.`jobeet_affiliate` (`id` )
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = 'カテゴリ-アフィリエイト';
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
データベースに、ユーザーとスキーマを作成します。
チュートリアルでは、rootでログインしていますが、アプリケーション用のユーザーを作成するのが一般的です。
MySQL Administratorで、[jobeet]ユーザーを作ってみました。
ユーザー名とパスワードは同一です。(公開していないので、パスワードを公開しても大丈夫です。)

symfonyにJobeetプロジェクト用にこのデータベースを使うことを伝えます。
(この例では改行を入れていますが、実際には1行で入力します。)
symfony configure:database --name=doctrine --class=sfDoctrineDatabase "mysql:host=localhost;dbname=jobeet" jobeet jobeet
これにより、config/databases.yml ファイルが以下のように更新されます。
all:
doctrine:
class: sfDoctrineDatabase
param:
dsn: 'mysql:host=localhost;dbname=jobeet'
username: jobeet
password: jobeet
データベースへの接続情報が設定されます。
[data/sql/schema.sql]を使って、MySQLの[jobeet]スキーマに、DDLを流してスキーマを構成します。
以下のコマンドを投入します。
symfony doctrine:insert-sql
まだまだ続く...。