「食わず嫌い」とはアレなものでして、長いことPDOでfetchといえばFETCH_ASSOCと思ってたんですが、なんだよ、FETCH_CLASS超便利じゃん、と言うことに気付いたのでいくつか試行錯誤してみるなど。

まぁ便利さ言うならちゃんとO/Rマッパー使えこの野郎という話ですけど、設定が面倒なときもあってついあれなので...FETCH_CLASSとマジックメソッド使ってみたかった的なアレでひとつ。

PDO::FETCH_CLASS

詳細はこの辺に。

PHP: 定義済み定数 - Manual
PDO::FETCH_STYLE - 酔いどれコード

クラス名を別途指定してやることで、オブジェクトに値を格納して返す。

サンプル

class SampleObject
{
  protected $foo;
}

$pdo = new PDO('mysql:host=host;dbname=test', 'user', 'pass')
$sth = $pdo->prepare('SELECT foo, bar FROM hoge;');
$sth->setFetchMode(PDO::FETCH_CLASS, 'SampleObject');
$sth->execute();
$result = $sth->fetch();
$sth->closeCursor();

var_dump($result);

実行結果

object(SampleObject)[1]
  protected 'foo' => string '696' (length=3)
  public 'bar' => string 'bazz' (length=4)


特徴は以下の通り。
  • 対応するプロパティがあればそれに格納
  • 格納時には文字列型として格納される
  • 対応するプロパティがなければ__set()をコールし、publicでプロパティを作成し値を格納

これで、データベースから引っ張ってきてすぐに使えるようになりました。

大体は問題ないのですが、個人的には次の点が気になりました。

  • 任意の値は整数型で格納したい
  • クラスに何も設定せずに $obj->getFoo() とかで値を取得したい

悩みつつSymfonyの実装を覗いてみたらああそうか__call()でやればいいのね。



setter/getterにマジックメソッドを使う

そんなわけでこんなんにしてみました。

class SampleObject
{
  public function __set($key, $value)
  {
    if($key == 'foo'):
      $this->$key = (int) $value;
    else:
      $this->$key = $value;
    endif;
  }
  public function __call($method, $arguments)
  {
    if(!preg_match('/^(get|set)(\w)(\w+)?/', $method, $matches))
      return null;
  //getter
    if($matches[1] === 'get'):
      return $this->{strtolower($matches[2]) . $matches[3]};
  //setter
    elseif($matches[1] === 'set'):
      $this->{strtolower($matches[2]) . $matches[3]} = $arguments[0];
    endif;
  }
}

これで、こうなります。

echo $result->getFoo();  //696
echo $result->getBar();  //bazz


全プロパティがpublicになってしまうのは仕方がないとしても、どの値を整数型として格納するかをハードコーディングってのはいかにも筋が悪い。ですが...PDOはデータベースで型指定があってもそれは見ずに全部文字列型で持って来ちゃう仕様みたいなので、set時に型を考えるにはこれくらいしかないかと。そうかー。残念。

あ、もしくは、「int_foo」ってエイリアス名で取得すると、整数型でfooというプロパティに格納するっていう手もあるか...ハードコーディングよりかはマシかも知れないけど似たようなもんかな...


車輪の再発明としてはこんなもんかなー。
もうちょっと色んなコード見てみよう。