The symfony and Doctrine book

6章 - データを扱う

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "The symfony and Doctrine book" which is licensed under the GFDL license.

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

データを検索する

DQL

finder

データを変更する

オブジェクトのプロパティ

アクセサとミューテータをオーバーライドする

リレーションを扱う

データを削除する

symfony training
Be trained by symfony experts
Feb 21: Köln (Getting Started with Symfony2 - English)
Feb 27: Köln (Mastering Symfony2 - English)
Mar 05: Köln (Web Development with Symfony2 - Deutsch)
Mar 05: Montreal (Web Development with Symfony2 - English)
Mar 05: Montreal (Getting Started with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "The symfony and Doctrine book" in Japanese for the 1.2 version - Switch to language:
This work is licensed under a GFDL license.
This version of symfony is not maintained anymore.
If some of your projects still use this version, consider upgrading as soon as possible.

データを検索する

DoctrineにおいてRDBMSから複雑な結果を読み取りそれらをリレーション構造を表す配列もしくはオブジェクトのデータ構造にハイドレイト(hydrate)できます。 これはDoctrine Query Languageによって行われます。 これは可能な限り最小のクエリですべてのデータを検索するための最良の方法です。 利便性のために単一のテーブルに取り組むとき、シンプルなfinderメソッドを提供します。 このメソッドはこれらのクエリを動的に構築して実行します。

DQL

データを読み取りそれらをビルドするための完全なDoctrine_Query APIを提供するためにDoctrineはDQLを使います。 活用例と同様にメソッドの完全な一覧は下記のとおりです。

クエリのAPI

共通のAPI

関数名 SQL append 説明
where('u.username = ?', 'jwage') u.username = ? No WHEREを設定して既存のWHERE条件をオーバーライドする
andWhere('u.username = ?', 'jwage') AND u.username = ? Yes ANDでappendされるWHERE条件を追加する
whereIn('u.id', array(1, 2, 3)) AND u.id IN (?, ?, ?) Yes appendされるAND IN WHERE 条件を追加する
andWhereIn('u.id', array(1, 2, 3)) ^ Yes whereIn()用のコンビニエンス/プロキシメソッド
orWhereIn('u.id', array(1, 2, 3)) OR u.id IN (?, ?, ?) Yes appendされるOR IN WHERE条件を追加する
whereNotIn('u.id', array(1, 2, 3)) AND u.id NOT IN (?, ?, ?) Yes appendされるAND NOT IN WHERE条件を追加する
andWhereNotIn('u.id', array(1, 2, 3)) ^ Yes whereNotIn()用のコンビニエンス/プロキシメソッド
orWhereNotIn('u.id', array(1, 2, 3)) OR u.id NOT IN (?, ?, ?) Yes appendされるOR NOT IN WHEREの条件を追加する
orWhere('u.username = ?', 'jwage') OR u.username = ? Yes ORWHERE条件を追加する
groupBy('u.id') GROUP BY u.id, u.username No GROUP BYを設定して既存のGROUP BYをオーバーライドする
addGroupBy('u.username') GROUP BY u.username Yes appendされるGROUP BYを追加する that is appended
having('num_phonenumbers > 0') HAVING num_phonenumbers > 0 No HAVINGを設定して既存のHAVINGをオーバーライドする
addHaving('u.username = ?', 'jwage') HAVING u.username = ? Yes 追加されるHAVINGを追加する

選択のAPI

関数名 説明
distinct($flag = true) フラグをdistinct selectに設定する
select('u.id, u.username, COUNT(p.id) as num_phonenumbers') SELECTを設定して既存のSELECTをオーバーライドする
addSelect('u.email_address') appendされるselectを追加する
from('User u, u.Phonenumber p') FROMを設定して既存のFROMとjoinをオーバーライドする
leftJoin('u.Phonenumber p') FROMにappendされるLEFT JOINを追加する
innerJoin('u.Profile p') FROMにappendされるINNER JOINを追加する
addFrom('u.Phonenumber p') FROMにappendされるFROM joinを追加する
orderBy('u.username') ORDER BYを設定して既存のORDER BYをオーバーライドする
addOrderBy('u.is_active = ?', 1) appendされるORDER BYを追加する
limit(20) 結果セットを制限するレコードの数を設定する
offset(5) レコードの制限をオフセットする番号を設定する

更新API

関数名 説明
forUpdate($flag = true) FOR UPDATEを使うためにクエリを変更する
update('User u') UPDATEするモデルの名前を指定する
set('u.username', '?', 'jwage') UPDATEクエリのために新しい値を設定する。最初の引数は修正するデータで、2番目はDQLの文字列に直接加える式(?もしくはDBMS関数)、で3番目は新しい値。

削除API

関数名 説明
delete() 削除するクエリを変更する

新しいクエリを作成する

Doctrine_Tableインスタンスから新しいクエリを作成します。

$q = Doctrine::getTable('User')->createQuery('u')
  ->where('u.username = ?', 'jwage');

新しいクエリを手動で作成する。

$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.username = ?', 'jwage');

上記の2つのクエリは理想的ですが、わかりやすくするために最初のコードは2番目のコードを内部で行っています。

クエリの例

Doctrineで結果セットを読み取る方法はいくつかのクエリの例を見ればわかります。

算出されるカラム

カラムを算出するためにDBMSの関数を使うとき、これらは式に最初に関わるコンポーネント/モデルにハイドレイトされます。 下記の例において、クエリで最初に遭遇するコンポーネントなので、モデルはPhonenumberリレーションにハイドレイトされます。

$q = Doctrine_Query::create()
  ->select('u.*, COUNT(DISTINCT p.id) AS num_phonenumbers')
  ->from('User u')
  ->leftJoin('u.Phonenumbers p')
  ->groupBy('u.id');
 
$users = $q->fetchArray();
 
echo $users[0]['num_phonenumbers'];

ユーザーとユーザーが所属するグループを読み取る

$q = Doctrine_Query::create()
  ->from('User u')
  ->leftJoin('u.Groups g');
 
$users = $q->fetchArray();
 
foreach ($users[0]['Groups'] as $group) {
    echo $group['name'];
}

1つのパラメーターの値を持つシンプルなWHERE

$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.username = ?', 'jwage');
 
$users = $q->fetchArray();

複数のパラメーターの値を持つ複数のWHERE

$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.is_active = ? AND u.is_online = ?', array(1, 1));
 
$users = $q->fetchArray();
 
// 既存のwhere部分に追加するためにオプションとしてandWhere()も使用できる
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.is_active = ?', 1)
  ->andWhere('u.is_online = ?', 1);
 
$users = $q->fetchArray();

whereIn()コンビニエンスメソッドを使う

$q = Doctrine_Query::create()
  ->from('User u')
  ->whereIn('u.id', array(1, 2, 3));
 
$users = $q->fetchArray();
 
// 上記と同じ
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.id IN (1, 2, 3)');
 
$users = $q->fetchArray();

WHEREでDBMS関数を使う

$userEncryptedKey = 'a157a558ac00449c92294c7fab684ae0';
$q = Doctrine_Query::create()
  ->from('User u')
  ->where("MD5(CONCAT(u.username, 'secret_user_key')) = ?", $userEncryptedKey);
 
$user = $q->fetchOne();
 
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('LOWER(u.username) = LOWER(?)', 'jwage');
 
$user = $q->fetchOne();

集約関数を使用して結果セットを制限する

// 電話番号を複数持つユーザー
$q = Doctrine_Query::create()
  ->select('u.*, COUNT(DISTINCT p.id) AS num_phonenumbers')
  ->from('User u')
  ->leftJoin('u.Phonenumbers p')
  ->having('num_phonenumbers > 1')
  ->groupBy('u.id');
 
$users = $q->fetchArray();

WITHを使用して最初の電話番号だけJOINする

$q = Doctrine_Query::create()
  ->from('User u')
  ->leftJoin('u.Phonenumbers p WITH p.primary_num = ?', true);
 
$users = $q->fetchArray();

ONを使用してJOIN条件をオーバーライドする

$q = Doctrine_Query::create()
  ->from('User u')
  ->leftJoin('u.Phonenumbers p ON u.id = p.user_id AND p.primary_num = ?', true);
 
$users = $q->fetchArray();

最適化用に特定のカラムを選択する

$q = Doctrine_Query::create()
  ->select('u.username, p.phone')
  ->from('User u')
  ->leftJoin('u.Phonenumbers p');
 
$users = $q->fetchArray();

すべてのカラムを選択するためにワイルドカードを使う

// すべてのUserカラムを選択するが電話番号のカラムのみ
$q = Doctrine_Query::create()
  ->select('u.*, p.phone')
  ->from('User u')
  ->leftJoin('u.Phonenumbers p');
 
$users = $q->fetchArray();

シンプルなWHEREでDQLのdeleteを実行する

// user id = 5に対して電話番号を削除する
$deleted = Doctrine_Query::create()
  ->delete()
  ->from('Phonenumber')
  ->andWhere('user_id = 5')
  ->execute();

カラム用にシンプルなDQLのupdateを実行する

// 有効にするためにuser id = 1をセットする
Doctrine_Query::create()
  ->update('User u')
  ->set('u.is_active', '?', true)
  ->where('u.id = ?', 1)
  ->execute();

DBMS関数のDQLのupdateを実行する

// すべてのユーザー名を小文字にする
Doctrine_Query::create()
  ->update('User u')
  ->set('u.username', 'LOWER(u.username)')
  ->execute();

レコードを検索するためにMySQLのLIKEを使う

$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.username LIKE ?', '%jwage%');
 
$users = $q->fetchArray();

レコードエントリのキーが割り当てるカラムの名前であるデータをハイドレイトするにはINDEXBYキーワードを使う

$q = Doctrine_Query::create()
  ->from('User u INDEXBY u.username');
 
$users = $q->fetchArray();
print_r($users['jwage']); // usernameがjwageであるユーザーを表示する

位置と名前つきパラメーターを使う

// 位置パラメーター
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.username = ?', array('Arnold'));
 
$users = $q->fetchArray();
 
// 名前つきパラメーター
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.username = :username', array(':username' => 'Arnold'));
 
$users = $q->fetchArray();

WHEREでサブクエリを使う

// 名前がGroup 2であるグループに存在しないユーザーを見つける
$q = Doctrine_Query::create()
  ->from('User u')
  ->where('u.id NOT IN (SELECT u.id FROM User u2 INNER JOIN u2.Groups g WHERE g.name = ?)', 'Group 2');
 
$users = $q->fetchArray();
 
// 下記の2つの例のようにこれをサブクエリ無しで実現できる
// これは上記と同じ
$q = Doctrine_Query::create()
  ->from('User u')
  ->innerJoin('u.Groups g WITH g.name != ?', 'Group 2')
 
$users = $q->fetchArray();
 
// もしくはこれ
$q = Doctrine_Query::create()
  ->from('User u')
  ->leftJoin('u.Groups g')
  ->where('g.name != ?', 'Group 2');
 
$users = $q->fetchArray();

Doctrineでクエリを実行してデータを読み取る方法はいろいろあります。下記はクエリを実行できるすべての異なる方法の一覧です。

$q = Doctrine_Query::create()
  ->from('User u');
 
// 配列のハイドレーション
$users = $q->fetchArray();                                         // ハイドレイトされた配列として結果を取得する
$users = $q->execute(array(), Doctrine::HYDRATE_ARRAY);            // 上記と同じ
$users = $q->setHydrationMode(Doctrine::HYDRATE_ARRAY)->execute(); // これも同じ
 
// ハイドレーション無し
$users = $q->execute(array(), Doctrine::HYDRATE_NONE);             // プレーンなPDOとハイドレーション無しでクエリを実行する
$users = $q->setHydrationMode(Doctrine::HYDRATE_NONE)->execute();  // これは上記と同じ
 
// 1つを取得する
$user = $q->fetchOne();
 
// すべてを取得してコレクションから最初のものを得る
$user = $q->execute()->getFirst();

finder

Doctrineはシンプルで魔法の力を持つfinderメソッドを提供します。 このメソッドはバックグラウンドでDoctrine_Queryオブジェクトを自動的に作成します。 下記のものはこれらのメソッドを活用するいくつかの例です。

finderマジックメソッド

1つのフィールドの値によってレコードを見つけるためにfindBy*()findOneBy*()マジックメソッドを活用できます。

$user = Doctrine::getTable('User')->findOneByUsername('jwage');
$users = Doctrine::getTable('User')->findByIsActive(1);

識別子によるfind

Doctrine_Table::find()メソッドは主キーでレコードを見つけるためにあります。サロゲートもしくは複合主キーを持つ両方のモデルに対して機能します。

$user = Doctrine::getTable('User')->find(1);
$userGroup = Doctrine::getTable('UserGroup')->find(array(1, 2));

データを変更する

DoctrineによってDQLのupdateとdeleteクエリを直接実行することでデータを変更する、もしくはオブジェクトを取得しプロパティを変更して保存することができます。 下記のものは両方の戦略の例です。

オブジェクトのプロパティ

Doctrineはオブジェクトのプロパティを変更する方法を3つ提供し、sfDoctrinePluginは4番目の方法を実装します。 これらのアクセス方法のスタイルにはオブジェクト、配列、と関数とPropelがあります。

$user = new User();
$user->username = 'jwage';        // オブジェクト
$user['username'] = 'jwage';      // 配列
$user->set('username', 'jwage');  // 関数
$user->setUsername('jwage');      // Propelスタイル
$user->save();

アクセサとミューテータをオーバーライドする

class User extends BaseUser
{
  public function setPassword($password)
  {
    return $this->_set('password', md5($password));
  }
 
  public function getUsername()
  {
    return 'PREFIX_' . $this->_get('username');
  }
}
 
$user = new User();
$user->username = 'jwage';
$user->password = 'changeme'; // setPassword()を起動する
echo $user->username; // getUsername()を起動してPREFIX_jwageを返す

リレーションを扱う

DoctrineでPHPオブジェクトを活用すればオブジェクトグラフでデータを操作することは簡単です。

User hasOne Profile

$user = new User();
$user->username = 'jwage';
$user->password = 'changeme';
$user->Profile->name = 'Jonathan H. Wage';
$user->Profile->about = 'His name is Jonathan';
$user->save();

User hasMany Phonenumber as Phonenumbers

$user = new User();
$user->username = 'jwage';
$user->password = 'changeme';
$user->Phonenumbers[]->phonenumber = '6155139185';
$user->Phonenumbers[]->phonenumber = '1234567890';
$phonenumber = $user->Phonenumbers[2];
$phonenumber->phonenumber = '0987654321';

BlogPost hasMany Tag as Tags

$blogPost = new BlogPost();
$blogPost->title = 'Test blog post';
$blogPost->body = 'This is the content of the test blog post';
$tag = Doctrine::getTable('Tag')->findOneByName('doctrine');
if ( ! $tag) {
  $tag = new Tag;
  $tag->name = 'doctrine';
}
$blogPost->Tags[] = $tag;

上記のコードは醜いので、lib/model/doctrine/TagTable.class.phpに設置されているTagTable子クラスにそのログインを抽出します.

class TagTable extends Doctrine_Table
{
  public function findOneByName($name)
  {
    $tag = $this->findOneBy('name', $name);
    if ( ! $tag) {
      $tag = new Tag();
      $tag->name = $name;
    }
    return $tag;
  }
}

最初の例は次のように簡略化できます。

$blogPost = new BlogPost();
$blogPost->title = 'Test blog post';
$blogPost->body = 'This is the content of the test blog post';
$tag = Doctrine::getTable('Tag')->findOneByName('doctrine');
$blogPost->Tags[] = $tag;

別の方法はlib/model/doctrine/Tag.class.phpに設置されている生成されたTagクラスのsetName()という名前の関数を作ることによるTagnameミューテーターです。

class Tag extends BaseTag
{
  public function setName($name)
  {
    $tag = Doctrine::getTable('Tag')->findOneByName($name);
    if ($tag) {
      $this->assignIdentifier($tag->identifier());
    } else {
      $this->_set('name', $name);
    }
  }
}

コードはよりシンプルになり、重複タグはデータベースに挿入されていないことが保証されます。

$blogPost = new BlogPost();
$blogPost->title = 'Test blog post';
$blogPost->body = 'This is the content of the test blog post';
$blogPost->Tags[]->name = 'doctrine';

データを削除する

データを削除する方法は2つあります。 最初にオブジェクトを取得してDoctrine_Record::delete()メソッドを呼び出すかDQLのdeleteクエリを単独で実行します。

$user = Doctrine::getTable('User')->find(1);
$user->delete();

単独のDQL deleteクエリが発行されます。 使うクエリは1つだけなので上述の方法よりも効率的です。 上述の例はオブジェクトを読み取り削除しなければなりません。

$deleted = Doctrine_Query::create()
  ->delete()
  ->from('User u')
  ->where('u.id = ?', 1)
  ->execute();

Doctrineでデータを扱う方法の詳細はマニュアルをご覧ください。

7章 - マイグレーション »
« 5章 - データのフィクスチャ

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.