Symfony2入門(1) でとりあえずインストールを完了させました。 なので次はCakePHPでもおなじみのCRUDを作ろうと思います。
モデルを作るその前に
Symfony2にはMVCよりも上位の概念となる 「Bundle(バンドル)」 が存在します。 MVCや、その他必要なコンポーネントやアセット(画像やCSS, Javascript等)をひとまとめにした物です。 このBundleの存在によって、一連の処理をプラグインのようにして使う事が可能になります。
アプリケーションバンドルを作る
とりあえずBundleを作らなければ何も始まらないので、アプリケーション用のバンドルを作ります。
$ cd /path/to/symfony-project/
$ php app/console generate:bundle
補足: Symfonyでの開発中は
php app/console
をCakePHPと比べるとかなり頻繁に使います。 面倒に感じたらphp app/console --shell
コマンドを1度実行すると、Symfony専用のShellを起動でき、以降はphp app/console
を省略できますのでオススメです。
# {ベンダー名}/Bundle/{バンドル名}Bundle
Bundle namespace: Quartet/Bundle/BlogBundle [ENTER]
Bundle name [QuartetBlogBundle]: [ENTER]
Target directory [/usr/local/Cellar/httpd/2.2.25/share/apache2/htdocs/symfony-blog-tuts/src]: [ENTER]
# 設定ファイルは基本的にYaml, XML, PHP, Annotation の4種類の中から好きに選べます。
# 今回はファイル間の移動を減らす為にAnnotationを使いますが、基本的に何でもいいです。
Configuration format (yml, xml, php, or annotation): annotation [ENTER]
# 以降エンター連打でOK
To help you get started faster, the command can generate some
code snippets for you.
Do you want to generate the whole directory structure [no]? [ENTER]
Do you confirm generation [yes]? [ENTER]
Generating the bundle code: OK
Checking that the bundle is autoloaded: OK
Confirm automatic update of your Kernel [yes]? [ENTER]
Enabling the bundle inside the Kernel: OK
Confirm automatic update of the Routing [yes]? [ENTER]
Importing the bundle routing resource: OK
symfony-blog-tuts|master ⇒
これでバンドルのひな形作成は完了です。
モデルを作る
CakePHPではデータベース上に物理テーブルを作ってから、自動的にモデルのソースを生成しましたが、Symfony2(というかDoctrine2)は 真逆 です。
モデルのソースコード上にテーブルスキーマを定義し、物理テーブルのスキーマを自動更新します。
- CakePHP: テーブルを作る => モデルを生成
- Symfony: モデルを作る => テーブルを生成
モデルのソースコードもコンソールから生成できるので、やってみます。
$ php app/console doctrine:generate:entity
The Entity shortcut name: QuartetBlogBundle:Post [Enter]
Configuration format (yml, xml, php, or annotation) [annotation]: [Enter]
Available types: array, simple_array, json_array, object,
boolean, integer, smallint, bigint, string, text, datetime, datetimetz,
date, time, decimal, float, blob, guid.
# id は勝手に追加されるのでID以外の項目を定義します。
# 今回は Post モデルに、title, content, created を設定します。
New field name (press <return> to stop adding fields): title [Enter]
Field type [string]: [Enter]
Field length [255]: [Enter]
New field name (press <return> to stop adding fields): content [Enter]
Field type [string]: text [Enter]
New field name (press <return> to stop adding fields): created [Enter]
Field type [string]: datetime [Enter]
New field name (press <return> to stop adding fields): [Enter]
# リポジトリはDAOのような物です。マスターテーブルは大体必要になってくるので、作っておきましょう。
Do you want to generate an empty repository class [no]? yes [Enter]
Do you confirm generation [yes]? [Enter]
これでPostモデルが作成されました。
CRUD を生成する。
$ php app/console doctrine:generate:crud
# {バンドル名}:{モデル名}
You must use the shortcut notation like AcmeBlogBundle:Post [ENTER]
Do you want to generate the "write" actions [no]? yes [ENTER]
Determine the format to use for the generated CRUD.
Configuration format (yml, xml, php, or annotation) [annotation]: [ENTER]
Determine the routes prefix (all the routes will be "mounted" under this
prefix: /prefix/, /prefix/new, ...).
# URLプレフィックスの指定
Routes prefix [/post]: [ENTER]
Do you confirm generation [yes]? [ENTER]
表示してみましょう
ブラウザで http://localhost/symfony-blog-tuts/web/app_dev.php/post
を開きます。
例外が発生してしまいました。
物理テーブルはまだ作ってないので当たり前ですね。 少し話がそれますが、Symfonyのスタックトレースはとてつもなく親切なので、わざわざソースの行数を追っかけなくてもView上で確認できます。
物理テーブルのスキーマを更新します。
# スキーマ作成時
php app/console doctrine:schema:create
# スキーマ更新時
php app/console doctrine:schema:update
まさかのこれだけです。
実際にどんなSQLが発行されるか確認する際は --dump-sql
オプションを付けるとSQLが表示されます。
表示してみましょう(再)
CRUD全ての処理が実行できる事が確認できます。
フォームのカスタマイズ
時刻の入力が面倒なので、CRUD生成時のフォームの created
を自動入力にします。
Postフォームオブジェクト(PostType)の確認
フォームとエンティティの関係
<?php
// PostController.php
/**
* 新規投稿
*/
public function newAction()
{
$entity = new Post();
// 空のエンティティからフォームを作る
$form = $this->createCreateForm($entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* @フォームを作る
*/
private function createCreateForm(Post $entity)
{
// フォームとモデルをバインドする
$form = $this->createForm(new PostType(), $entity, array(
'action' => $this->generateUrl('post_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
フォームの定義
<?php
// PostType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content')
// ->add('created') # => 削除
;
}
フォームから削除した項目の初期値を設定する処理
<?php
// Post.php
class Post
{
public function __construct()
{
// 初期値として現在時刻を設定
$this->created = new \DateTime();
}
}
入力画面を表示する(再)
作成日時が自動入力されました。
Doctrine LifeCycleCallbacksを使う方法
更新日時はどうすんの? もちろんこれも定義しておけば自動で実行させる事ができます。 @PreUpdate
<?php
/**
* @PreUpdate
*/
public function setUpdateDate
{
$this->updateDate = new \DateTime();
}
Symfony EventDispatcherを使う
SymfonyのServiceContainerはDoctrineのコールバックにサービスをバインドさせる事ができます。