初心者による初心者のためのdoctrine

初めまして。社内にいるほとんどのプログラマーがpropelの中いきなりdoctrineを学んだピチピチ平成生まれ(20歳)、新卒のitaniです。

symfonyを学んだときに、こんな記事があったらいいなぁと思ったので書くことにしました。 というわけで、symfonyを触ってまだ間もない僕が、今回はdoctrineを使ってDBの基本操作を紹介します。

※symfonyのversionは1.4を使用します。

DBの準備

まずはDBの準備をします。 symfonyを使ってDBを作るにはまずスキーマを作らなければいけません。 スキーマを作る方法は2つあります。

1、自分でschema.ymlを書く

config/doctrine/schema.ymlにテーブルの構造をYAMLフォーマットで書きます。

//今回使うDBのschema.yml
User:
    columns:
        name: { type: string(255), notnull: true }
        age: { type: integer, notnull: true}

2、DBにテーブルを作った後、schema.ymlを生成

まず、自分でテーブルを作り、その構造に応じてsymfonyが自動でschema.ymlを作ってくれます。

$ php symfony doctrine:build-schema 

1、2ともにschema.ymlを生成した後はmodelを生成します。 symfonyでDBを扱うにはこのmodelが必要不可欠です。

$ php symfony doctrine:build-model  

modelを生成すると、lib/model/doctrine以下にテーブルに関する3種類のphpファイルが生成されています。

  • User.class.php
  • UserTable.class.php
  • BaseUser.class.php

Baseのファイルはsymfonyがコマンドによって勝手に書き換えてくれたりするので触らなくていいです。 重要なのは他の2つです。 基本的には2つともUserテーブルに関連しているファイルなんですが、大きな違いが2つあります。

Tableがついていない方(User.class.php)のオブジェクトは、そのテーブルの1つのレコードを表します。 Tableがついている方(UserTable.class.php)は、userオブジェクトに対してのメソッドを定義します。

この違いは最初の内はよくわからないと思います。 正直僕もそんなに理解していません。 なので僕はこんな風に覚えました。

Tableがついていない方は、1つのレコードを扱ったりするときに使う。 Tableがついている方は、DB関連の処理をまとめてするときに使う。

感じ方としてはざっくりこんな感じでいいんではないでしょうか?

テスト用DB

今回のブログを書くにあたって使用するDBです。

user 
+-------+-------+-------+ 
| id    | name | age | 
+-------+-------+-------+ 
| 1  | hoge | 15    | 
| 2  | fuga | 37    | 
+-------+-------+-------+ 

検索

doctrineでselect文を書くときは2つの方法があります。

まず1つ目は、自分でDoctrine_Queryを書く方法です。

//userテーブルからid=1,name=hogeのレコードを抽出
$q = Doctrin_Query::create()
    ->select('u.*')
    ->from('User u')
    ->where('u.id = ?','1')
    ->andWhere('u.name = ?', 'hoge');

whereを複数指定するときはandWhere()を使います。

もう1つの方法が、finderメソッドです。 finderメソッドは、バックグラウンドでDoctrine_Queryオブジェクトを自動的に作成します。 これを使えばたった1行で、レコードを検索して取り出すことができます。 find()は主キーによる検索。findOneBy()とfindBy()は指定したカラムによる検索です。

//主キーが1のレコードを抽出
$user = Dctrine::getTable('User')->find(1);
//name=hogeのレコードを1件抽出
$user = Doctrine::getTable('User')->findOneByName('hoge');
//age=20のレコードをすべて抽出
$users = Doctrine::getTable('User')->findByAge(20);

追加

doctrineはレコードの追加のための、便利なsave()メソッドを用意してくれています。 まず追加したいテーブルのClassを宣言した後に、setメソッドで、カラムに与えるデータを示します。 最後にsave()メソッドにより、セットされた値でINSERTされます。

//userテーブルにname='test',age='23'で追加
$q = new User();
$q->setName('test');
$q->setAge('23');   
$q->save();

これでレコードが新しく追加されています。 しかしこのsave()メソッドはこれだけではありません。 さらに便利な機能を実装してくれています。 テーブルにレコードを追加する時は、まず最初に重複しないかどうか、SELECT文をかけるのがセオリーです。 save()メソッドはそれを判断して、重複していない新たなレコードならINSERT。 すでにレコードがあった場合はUPDATEと使い分けてくれるのです。

//id=1のレコードを検索。あればnameを更新。なければid=2で追加。
$q = Doctrine::getTable('User')->find(1);
if (!$q){
    $q = new Question();
    $q->setId(2);
}
$q->setName("hogehoge");    
$q->save();

更新

//userテーブルのid=1のレコードのnameをhogehogeに更新
Doctrine_Query::create()
    ->update('User u')
    ->set('u.name', '?', 'hogehoge')
    ->where('u.id = ?', 1)
    ->execute();

削除

//userテーブルからid=1のレコードを削除
Doctrine_Query::create()
    ->delete()
    ->from('User u')
    ->where('u.id = ?', 1)
    ->execute();

これでテーブルからレコードは削除されています。

クエリを実行してデータを読みとる

実際にクエリを実行してデータを読み取る時によく使うのはこの2つ。 とりあえず最初はこれだけおさえとけばいいと思います。

$q = Doctrine_Query::create()
    ->select('u.*')
    ->from('User u');

//検索結果を全て取得
$users = $q->execute();
//全てを取得した後最初の1件を取得
$user = $q->execute()->getFirst();  

とまぁ、ものすごくざっくりDBを使うときの基本的な動き、SELECT・INSERT・UPDATE・DELETEについて書いてきました。 doctrineはpropelと比べると、sqlを直感的にイメージできるからわかりやすいですね。

最後に

今回書いたdoctrineは、moduleのactionsに書けば普通に動きます。 しかしそれだとactionsのソースは得てして読みにくいものになってしまいます。 symfonyを使った基本的なDBの扱いの流れは、modelにメソッドを作り、actionsでメソッドを読み込み、結果をtemplateに渡してあげます。 具体的な例をあげてみます。

//lib/model/UserTable.class.php
class UsreTable extends Doctrine_Table
{
    //userのデータを取得するgetUser()メソッドを作成
    public function getUser() {
        $q = $this->createQuery()
            ->select('u.*')
            ->from('User u');

        return $q->execute();
    }   
}

//apps/frontend/module/top/actions/actions.class.php
class topActions extends sfActions
{
    public function executeIndex (sfWebRequest $request) {
        //getUser()メソッドを呼び出し、結果をuser_dataというオブジェクトにしてtemplateに渡す
        $this->user_data = Doctrine_Core::getTable('User')->getUser();
    }
}

//apps/frontend/module/top/template/indexSuccess.php
//渡された$user_dataからユーザの名前を取り出し表示
foreach($user_data as $u) {
    echo $u->getName().'

'; }

//実行結果
hoge
fuga

この流れがMVCパターンと呼ばれるものです。 sqlを実際に実行するのはactionですが、どんなsqlかを記入するのはmodelです。 templateはただその結果を表示するのみです。

カヤックではつくる人を増やすことのできるプログラマーも募集しています!