Asial Blog

Recruit! Asialで一緒に働きませんか?

symfony DoctrineのTIPS その2

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
Doctrine
こんばんは。牧野です。

最近、会社で使っているPCも自分用のノートPCもハードディスク容量が少ないせいか、重く感じるようになっていました。
そんな時に発表されたsonyの新しいVAIO Z。シミュレータで出てきた価格にびっくりしつつも、心ひかれています。。。

さて、今日はsymfonyと使うDoctrineのTIPSその2です。ここのところ、またsymfonyを触る機会が増えていました。

1.生SQLを使う
schema.ymlをちゃんと書けばDQLでたいていのことはできるのですが、そのままSQLを実行してデータを取りたい時にどうぞ。
Doctrine_Connectionオブジェクトがあれば、とりあえず何でもできます。
  1. $con = Doctrine::getTable(適当なテーブル)->getConnection();
  2. $con = Doctrine_Manager::getInstance()->getConnection(コネクション名);
  3. $sql = "select * from hogehgoe";
  4. $results = $con->fetchAll($sql);
複数のデータを取得するfetchAll(=fetchAssoc)メソッド、一つのデータを連想配列で取得するfetchRowメソッドあたりが便利だと思います。


2.データ更新時の注意
Doctrineは関連するデータを簡単に引っ張ってこられるところが便利なのですが、データ更新をする際には気をつけたいことがあります。
あるモデルオブジェクトのデータ変更内容を確定させる前に、同じモデルオブジェクトを取得するようなことをすると、確定前のデータ変更内容はリセットされます。

具体例な例を見てみましょう。

config/schema.yml

  1. DvdSeries:
  2.   tableName: dvd_series
  3.   columns:
  4.     id:
  5.       type: integer(4)
  6.       primary: true
  7.       autoincrement: true
  8.     series_title:
  9.       type: string(255)
  10.       notnull: true
  11. DvdPackage:
  12.   tableName: dvd_package
  13.   columns:
  14.     id:
  15.       type: integer(4)
  16.       primary: true
  17.       autoincrement: true
  18.     dvd_series_id:
  19.       type: integer(4)
  20.     number:
  21.       type: integer(4)
  22.     package_title:
  23.       type: string(255)
  24.     price:
  25.       type: integer(11)
  26.     release_date:
  27.       type: date
  28.   relations:
  29.     DvdSeries:
  30.       local: dvd_series_id
  31.       foreign: id
  32.       class: DvdSeries
  33.       foreignAlias: DvdPackages


DBの中身
  1. mysql> select * from dvd_series;
  2. +----+-----------------+
  3. | id | series_title    |
  4. +----+-----------------+
  5. |  1 | シーズン1      | 
  6. +----+-----------------+
  7. 1 row in set (0.00 sec)
  8. mysql> select * from dvd_package\G
  9. *************************** 1. row ***************************
  10.            id: 1
  11. dvd_series_id: 1
  12.        number: 1
  13. package_title: テストタイトルその1
  14.         price: 3000
  15.  release_date: 2010-03-01
  16. *************************** 2. row ***************************
  17.            id: 2
  18. dvd_series_id: 1
  19.        number: 2
  20. package_title: テストタイトルその2
  21.         price: 4000
  22.  release_date: 2010-04-02
  23. *************************** 3. row ***************************
  24.            id: 3
  25. dvd_series_id: 1
  26.        number: 3
  27. package_title: テストタイトルその3
  28.         price: 4000
  29.  release_date: 2010-05-03
  30. *************************** 4. row ***************************
  31.            id: 4
  32. dvd_series_id: NULL
  33.        number: NULL
  34. package_title: テストタイトル
  35.         price: 3000
  36.  release_date: 2010-06-04
  37. 4 rows in set (0.00 sec)

lib/model/doctrine/DvdPackage.class.php
  1. class DvdPackage extends BaseDvdPackage
  2. {
  3.   /**
  4.    * 同一シリーズのDVD発売日をまとめてずらす。
  5.    *
  6.    */
  7.   public function changeAllSeriesReleaseDate($date_string)
  8.   {
  9.     $old_timestamp = strtotime($this->release_date);
  10.     $new_timestamp = strtotime($date_string);
  11.     $today_timestamp = strtotime(date('Y-m-d'));
  12.  
  13.     $this->release_date = $date_string;
  14.  
  15.   //変更前、または変更後の発売日が以前の場合は、他のDVDの発売日は変更しない。
  16.     if ($today_timestamp > $old_timestamp || $today_timestamp > $new_timestamp) {
  17.       return $this->save();
  18.     }
  19.  
  20.     $change_timestamp = $new_timestamp - $old_timestamp;
  21.  
  22.     if ($this->DvdSeries) {
  23.       foreach ($this->DvdSeries->DvdPackages as $key => $dvd) {
  24.         if ($dvd->id == $this->id) {
  25.           continue;
  26.         }
  27.         $this->DvdSeries->DvdPackages[$key]->release_date = date('Y-m-d', strtotime($dvd->release_date) + $change_timestamp);
  28.       }
  29.     }
  30.     return $this->save();
  31.   }
  32. }

テスト用のアクションクラス
  1. class testActions extends sfActions{
  2.   public function executeIndex(sfWebRequest $request)
  3.   {
  4.     echo "<pre>";
  5.     $con = Doctrine::getTable('DvdPackage')->getConnection();
  6.  
  7.     //dvd_seriesをもつデータ
  8.     $test_dvd = Doctrine::getTable('DvdPackage')->find(1);
  9.  
  10.     var_dump($test_dvd->toArray());
  11.  
  12.     $test_dvd->changeAllSeriesReleaseDate('2010-04-01');
  13.  
  14.     var_dump($test_dvd->toArray());
  15.  
  16.     echo "</pre>";
  17.     exit;
  18.   }
  19. }
上のアクションでは、idが1のdvd_packageデータの発売日(release_date)を1ヶ月遅らせて、
関連するdvd_packageデータの発売日も1ヶ月遅らせようとしています。
結果をみると、、


肝心のidが1のdvd_packageデータが2010-03-03のままで、更新できていません。
これは、changeAllSeriesReleaseDateメソッドのforeachの部分が原因です。
$dvdとして自分自身を取得した際に、保存前のrelease_dateが消えてしまったのです。

保存前、というのが問題なので、
$this->release_date = $date_string;
の後に
$this->save();
を入れれば大丈夫です。

今まで2回ほどこのようなケースに出会ったことがありましたので、どこかで役立つことがあるかもしれません。