こんばんは。牧野です。
最近、会社で使っているPCも自分用のノートPCもハードディスク容量が少ないせいか、重く感じるようになっていました。
そんな時に発表されたsonyの新しいVAIO Z。シミュレータで出てきた価格にびっくりしつつも、心ひかれています。。。
さて、今日はsymfonyと使うDoctrineのTIPSその2です。ここのところ、またsymfonyを触る機会が増えていました。
1.生SQLを使う
schema.ymlをちゃんと書けばDQLでたいていのことはできるのですが、そのままSQLを実行してデータを取りたい時にどうぞ。
Doctrine_Connectionオブジェクトがあれば、とりあえず何でもできます。
$con = Doctrine::getTable(適当なテーブル)->getConnection();
$con = Doctrine_Manager::getInstance()->getConnection(コネクション名);
$sql = "select * from hogehgoe";
$results = $con->fetchAll($sql);
複数のデータを取得するfetchAll(=fetchAssoc)メソッド、一つのデータを連想配列で取得するfetchRowメソッドあたりが便利だと思います。
2.データ更新時の注意
Doctrineは関連するデータを簡単に引っ張ってこられるところが便利なのですが、データ更新をする際には気をつけたいことがあります。
あるモデルオブジェクトのデータ変更内容を確定させる前に、同じモデルオブジェクトを取得するようなことをすると、確定前のデータ変更内容はリセットされます。
具体例な例を見てみましょう。
config/schema.yml
DvdSeries:
tableName: dvd_series
columns:
id:
type: integer(4)
primary: true
autoincrement: true
series_title:
type: string(255)
notnull: true
DvdPackage:
tableName: dvd_package
columns:
id:
type: integer(4)
primary: true
autoincrement: true
dvd_series_id:
type: integer(4)
number:
type: integer(4)
package_title:
type: string(255)
price:
type: integer(11)
release_date:
type: date
relations:
DvdSeries:
local: dvd_series_id
foreign: id
class: DvdSeries
foreignAlias: DvdPackages
DBの中身
mysql> select * from dvd_series;
+----+-----------------+
| id | series_title |
+----+-----------------+
| 1 | シーズン1 |
+----+-----------------+
1 row in set (0.00 sec)
mysql> select * from dvd_package\G
*************************** 1. row ***************************
id: 1
dvd_series_id: 1
number: 1
package_title: テストタイトルその1
price: 3000
release_date: 2010-03-01
*************************** 2. row ***************************
id: 2
dvd_series_id: 1
number: 2
package_title: テストタイトルその2
price: 4000
release_date: 2010-04-02
*************************** 3. row ***************************
id: 3
dvd_series_id: 1
number: 3
package_title: テストタイトルその3
price: 4000
release_date: 2010-05-03
*************************** 4. row ***************************
id: 4
dvd_series_id: NULL
number: NULL
package_title: テストタイトル
price: 3000
release_date: 2010-06-04
4 rows in set (0.00 sec)
lib/model/doctrine/DvdPackage.class.php
class DvdPackage extends BaseDvdPackage
{
/**
* 同一シリーズのDVD発売日をまとめてずらす。
*
*/
public function changeAllSeriesReleaseDate($date_string)
{
$old_timestamp = strtotime($this->release_date);
$new_timestamp = strtotime($date_string);
$today_timestamp = strtotime(date('Y-m-d'));
$this->release_date = $date_string;
//変更前、または変更後の発売日が以前の場合は、他のDVDの発売日は変更しない。
if ($today_timestamp > $old_timestamp || $today_timestamp > $new_timestamp) {
return $this->save();
}
$change_timestamp = $new_timestamp - $old_timestamp;
if ($this->DvdSeries) {
foreach ($this->DvdSeries->DvdPackages as $key => $dvd) {
if ($dvd->id == $this->id) {
continue;
}
$this->DvdSeries->DvdPackages[$key]->release_date = date('Y-m-d', strtotime($dvd->release_date) + $change_timestamp);
}
}
return $this->save();
}
}
テスト用のアクションクラス
class testActions extends sfActions{
public function executeIndex(sfWebRequest $request)
{
echo "<pre>";
$con = Doctrine::getTable('DvdPackage')->getConnection();
//dvd_seriesをもつデータ
$test_dvd = Doctrine::getTable('DvdPackage')->find(1);
var_dump($test_dvd->toArray());
$test_dvd->changeAllSeriesReleaseDate('2010-04-01');
var_dump($test_dvd->toArray());
echo "</pre>";
exit;
}
}
上のアクションでは、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回ほどこのようなケースに出会ったことがありましたので、どこかで役立つことがあるかもしれません。