CodeZine(コードジン)

特集ページ一覧

PDOでサクサクDB開発

データベースへのアクセスを抽象化するPHP Data Objectの利用

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/07/10 00:00
目次

PDOで設定できる属性(2/3)

PDO::FETCH_INTO

 PDO::FETCH_INTOは指定された任意のオブジェクトに結果セットをマッピングし取得します。

 また、プロパティへのマッピングは通常の参照と同じく行っているので、プロパティ値はpublicにしておくか、__setでprivateなプロパティに対して値を代入しなければなりません。

fetch_into
class CDEntity {
    private $ID;
    private $TITLE;
    private $CONTENT;
}

class CDEntity2 {

    public $ID;
    public $TITLE;
    public $CONTENT;

    public function __get($name){
        $prop = strtoupper($name);
        return $this->$prop;
    }

    public function __set($name, $param){
        $prop = strtoupper($name);
        $this->$prop = $param;
    }
}

try {
    $pdo = CZ_PDO::getConnection();

    // カラム名と同名のプロパティがprivateであるので
    // "Cannot access private property"となる
    //$entity = new CDEntity();
    // public のプロパティにするか_setで対応する
    $entity = new CDEntity2();

    $stmt = $pdo->query("SELECT * FROM CD");
    $stmt->setFetchMode(PDO::FETCH_INTO, $entity);
    $rows = $stmt->fetch();

    var_dump($rows);
} catch (PDOException $e) {
    var_dump($e->getMessage());
}

 上記の実行結果は以下になります。

result
object(CDEntity2)#2 (3) {
    ["ID"]=>
    string(1) "1"
    ["TITLE"]=>
    string(5) "HAPPY"
    ["CONTENT"]=>
    string(10) "HAPPY SONG"
}

PDO::FETCH_FUNC

 PDO::FETCH_FUNCは結果セットのコールバックを指定して取得できます。クラスのメソッド(static)と関数に対して割り当てることができます。

fetch_func
function func_cd($id, $title, $content){
    return array("ID" => array(
                            "integer" => (int)$id,
                            "sha1" => sha1($id),
                            "md5" => md5($id),
                        ),
                "TITLE" => $title,
                "CONTENT" => $content);
}

class CD {

    private $id;
    private $title;
    private $content;

    public function __construct($id, $title, $content){
        $this->id = $id;
        $this->title = $title;
        $this->content = $content;
    }

    public static function method($id, $title, $content){
        // コンストラクタを呼び出し、インスタンスを返す。
        return new self($id, $title, $content);
    }
}

try {
    $pdo = CZ_PDO::getConnection();

    $stmt = $pdo->query("SELECT * FROM CD");
    $rows1 = $stmt->fetchAll(PDO::FETCH_FUNC, "func_cd");
    var_dump($rows1[0]);

    $stmt = $pdo->query("SELECT * FROM CD");
    $rows2 = $stmt->fetchAll(PDO::FETCH_FUNC, array("CD", "method"));
    var_dump($rows2[1]);

    $stmt = $pdo->query("SELECT * FROM CD");
    $rows3 = $stmt->fetchAll(PDO::FETCH_FUNC,
                            array(new CD(null,null,null), "method"));
    var_dump($rows3[2]);

    $stmt = $pdo->query("SELECT * FROM CD");
    $rows4 = $stmt->fetchAll(PDO::FETCH_FUNC, array(CD, "method"));
    var_dump($rows4[3]);
} catch (PDOException $e) {
    var_dump($e->getMessage());
}

 上記の例では通常の関数に結果セットをコールバックし、結果セットを変更しているものと、CDクラスのスタティックなメソッドに対して結果セットをコールバックしているものの例です。

 クラスメソッドにコールバックするときはarray(ClassName, MethodName)のように指定します。

 また、上記の実行結果は以下です。

result
array(3) {
    ["ID"]=>
    array(3) {
        ["integer"]=>
        int(1)
        ["sha1"]=>
        string(40) "356a192b7913b04c54574d18c28d46e6395428ab"
        ["md5"]=>
        string(32) "c4ca4238a0b923820dcc509a6f75849b"
    }
    ["TITLE"]=>
    string(5) "HAPPY"
    ["CONTENT"]=>
    string(10) "HAPPY SONG"
}
object(CD)#4 (3) {
    ["id:private"]=>
    string(1) "2"
    ["title:private"]=>
    string(3) "SAD"
    ["content:private"]=>
    string(8) "SAD SONG"
}
object(CD)#13 (3) {
    ["id:private"]=>
    string(1) "3"
    ["title:private"]=>
    string(5) "ANGRY"
    ["content:private"]=>
    string(10) "ANGRY SONG"
}
object(CD)#20 (3) {
    ["id:private"]=>
    string(1) "4"
    ["title:private"]=>
    string(4) "LOVE"
    ["content:private"]=>
    string(9) "LOVE SONG"
}

PDO::FETCH_GROUP/PDO::FETCH_UNIQUE

 PDO::FETCH_GROUPPDO::FETCH_UNIQUEは結果セットの値をグループ化したり、結果セットから一意な値を取得する場合に使用します。

 この2つは単体では使用できず、ビットORして利用します。

fetch_grouping
try {
    $pdo = CZ_PDO::getConnection();

    $stmt = $pdo->prepare("SELECT * FROM CD limit 3");
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP));
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_UNIQUE));
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_NUM|PDO::FETCH_UNIQUE));
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_ASSOC|PDO::FETCH_GROUP));
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE, 1));
    $stmt->execute();
    var_dump($stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP, 2));
} catch (PDOException $e) {
    var_dump($e->getMessage());
}

 PDO::FETCH_GROUPPDO::FETCH_UNIQUEは、その組合せによって結果セットの表現を広げることができます。

 以下に上記の実行結果を載せます。

result
array(3) {
    [1]=>
    array(1) {
        [0]=>
        string(5) "HAPPY"
    }
    [2]=>
    array(1) {
        [0]=>
        string(3) "SAD"
    }
    [3]=>
    array(1) {
        [0]=>
        string(5) "ANGRY"
    }
}
array(3) {
    [1]=>
    object(stdClass)#3 (2) {
        ["TITLE"]=>
        string(5) "HAPPY"
        ["CONTENT"]=>
        string(10) "HAPPY SONG"
    }
    [2]=>
    object(stdClass)#4 (2) {
        ["TITLE"]=>
        string(3) "SAD"
        ["CONTENT"]=>
        string(8) "SAD SONG"
    }
    [3]=>
    object(stdClass)#5 (2) {
        ["TITLE"]=>
        string(5) "ANGRY"
        ["CONTENT"]=>
        string(10) "ANGRY SONG"
    }
}
array(3) {
    [1]=>
    array(2) {
        [0]=>
        string(5) "HAPPY"
        [1]=>
        string(10) "HAPPY SONG"
    }
    [2]=>
    array(2) {
        [0]=>
        string(3) "SAD"
        [1]=>
        string(8) "SAD SONG"
    }
    [3]=>
    array(2) {
        [0]=>
        string(5) "ANGRY"
        [1]=>
        string(10) "ANGRY SONG"
    }
}
array(3) {
    [1]=>
    array(1) {
        [0]=>
        array(2) {
        ["TITLE"]=>
        string(5) "HAPPY"
        ["CONTENT"]=>
        string(10) "HAPPY SONG"
        }
    }
    [2]=>
    array(1) {
        [0]=>
        array(2) {
        ["TITLE"]=>
        string(3) "SAD"
        ["CONTENT"]=>
        string(8) "SAD SONG"
        }
    }
    [3]=>
    array(1) {
        [0]=>
        array(2) {
        ["TITLE"]=>
        string(5) "ANGRY"
        ["CONTENT"]=>
        string(10) "ANGRY SONG"
        }
    }
}
array(3) {
    [1]=>
    string(5) "HAPPY"
    [2]=>
    string(3) "SAD"
    [3]=>
    string(5) "ANGRY"
}
array(3) {
    [1]=>
    array(1) {
        [0]=>
        string(10) "HAPPY SONG"
    }
    [2]=>
    array(1) {
        [0]=>
        string(8) "SAD SONG"
    }
    [3]=>
    array(1) {
        [0]=>
        string(10) "ANGRY SONG"
    }
}

PDO::FETCH_SERIALIZE

 PDO::FETCH_SERIALIZEはシリアル化されたクラスオブジェクトを、設定したクラスのunserializeメソッドにマッピングします。

 一般的にSPLのSerializableインターフェイスを実装したクラスをシリアル化(serializeメソッド呼び出し)してDBに格納し、取り出すときはunserializeを呼びオブジェクトを復元します。

 PHPのオブジェクト全体をDBに格納している場合に重宝すると思います。

fetch_serialize
class CDEntity implements Serializable {

    private $ID;
    private $TITLE;
    private $CONTENT;

    public function __get($name){
        $prop = strtoupper($name);
        return $this->$prop;
    }

    public function __set($name, $param){
        $prop = strtoupper($name);
        $this->$prop = $param;
    }

    public function serialize(){
        $serial = array();
        foreach($this as $prop => $value){
            $serial[$prop] = $value;
        }
        return serialize($serial);
    }

    public function unserialize($serialized){
        foreach(unserialize($serialized) as $prop => $value){
            $this->$prop = $value;
        }
        return true;
    }
}

try {
    $pdo = CZ_PDO::getConnection();

    $obj[0] = new CDEntity();
    $obj[0]->id = 222;
    $obj[0]->title = "hoge";
    $obj[0]->content = "Hello! Hoge";
    $obj[1] = new CDEntity();
    $obj[1]->id = 333;
    $obj[1]->content = "Hello! Foo";
    $obj[1]->title = "foo";
    $obj[2] = new CDEntity();
    $obj[2]->id = 444;
    $obj[2]->title = "bar";
    $obj[2]->content = "Hello! Bar";

    $stmt = $pdo->prepare(
"INSERT INTO CD(ID, TITLE, CONTENT) VALUES(:ID, :TITLE, :CONTENT)");
    foreach($obj as $o){
        $stmt->bindValue(":ID", $o->id);
        $stmt->bindValue(":TITLE", $o->title);
        $stmt->bindValue(":CONTENT", $o->serialize());
        $stmt->execute();
    }

    $stmt = $pdo->query("SELECT CONTENT FROM CD where ID > 100");
    $rows = $stmt->fetchAll(
PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, "CDEntity");

    var_dump($rows);
} catch (PDOException $e) {
    var_dump($e->getMessage());
}

 上記の実行結果は以下になります。

result
array(3) {
    [0]=>
    object(CDEntity)#5 (3) {
        ["ID:private"]=>
        int(222)
        ["TITLE:private"]=>
        string(4) "hoge"
        ["CONTENT:private"]=>
        string(11) "Hello! Hoge"
    }
    [1]=>
    object(CDEntity)#7 (3) {
        ["ID:private"]=>
        int(333)
        ["TITLE:private"]=>
        string(3) "foo"
        ["CONTENT:private"]=>
        string(10) "Hello! Foo"
    }
    [2]=>
    object(CDEntity)#8 (3) {
        ["ID:private"]=>
        int(444)
        ["TITLE:private"]=>
        string(3) "bar"
        ["CONTENT:private"]=>
        string(10) "Hello! Bar"
    }
}

 シリアル化したオブジェクトをデータベースにそのまま格納することで、オブジェクトをまるごとデータベースに文字列として格納できます。

 PHPのオブジェクトや配列でマッピングをするよりもオーバヘッドが少ないので、利用されている方もいるのではないかと思います。

bind時に関する設定項目

PDO::PARAM_で始まる定数

  • PDO::PARAM_BOOL
  • PDO::PARAM_NULL
  • PDO::PARAM_INT
  • PDO::PARAM_STR
  • PDO::PARAM_LOB

 上記のようなPDO::PARAM_で始まるPDOの定数は、PDOStatementでデータ値のバインドをPHPとDBで一致させる場合に使います。

bind
try {
    $pdo = CZ_PDO::getConnection();

    $stmt = $pdo->prepare(
"INSERT INTO CD(ID, TITLE, CONTENT) VALUES(?, ?, ?)");
    $stmt->bindValue(1, 15, PDO::PARAM_INT);
    $stmt->bindValue(2, null, PDO::PARAM_NULL);
    $stmt->bindValue(3, "", PDO::PARAM_STR);
    $stmt->execute();

    $fp = fopen("hoge.jpg", "r");
    $stmt->bindValue(1, 16);
    $stmt->bindValue(2, true, PDO::PARAM_BOOL);
    $stmt->bindValue(3, $fp, PDO::PARAM_LOB);
    $stmt->execute();
    fclose($fp);

    var_dump($pdo->query(
"SELECT * FROM CD WHERE ID >= 15")->fetchAll(PDO::FETCH_ASSOC));
} catch (PDOException $e){
    var_dump($e);
}

 よく使う例として、PHP変数に""(空文字)やnull、0の値が代入された場合に、その変数の値はDB上でどのように扱うかを定義します。

 また、空文字列とnullの扱いをあらかじめ設定するPDO::ATTR_ORACLE_NULLSというパラメータもあります。

 上記の実行結果は以下です。

result
array(2) {
    [0]=>
    array(3) {
        ["ID"]=>
        string(2) "15"
        ["TITLE"]=>
        NULL
        ["CONTENT"]=>
        string(0) ""
    }
    [1]=>
    array(3) {
        ["ID"]=>
        string(2) "16"
        ["TITLE"]=>
        string(1) "1"
        ["CONTENT"]=> <<<ここにはバイナリデータが表示されて
しまうので注意が必要です。>>>
    }
}

PDO::PARAM_INPUT_OUTPUT

 PDO::PARAM_INPUT_OUTPUTはストアドプロシージャをコールする際のINOUTパラメータを使用する場合に設定します。

 ストアドプロシージャはDBMS自体がその機能を持っていないと、使えないので注意が必要です。

 また、SQLレベルでのプロシージャコールとなるので、DBMSごとに発行するSQLを用意する必要があります。

 次のサンプルはPostgreSQL 8.1.2を使用するサンプルとなっています。

 また、作成したストアドプロシージャは次のとおりです。

pg_stored_procedure
CREATE OR REPLACE FUNCTION HOGE(IN arg INTEGER) RETURNS INTEGER AS $$
begin
    return arg * 1234;
end;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION FOO(OUT arg INTEGER) AS $$
begin
    arg := 123;
end;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION BAR(INOUT arg INTEGER) AS $$
begin
    arg := arg * 1023;
end;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION BAZ(IN arg1 INTEGER, OUT arg2 INTEGER) AS $$
begin
    arg2 := arg1 * 200;
end;
$$ LANGUAGE plpgsql;

 以下、PDOでプロシージャを呼び出す場合のサンプルになります。

pdo_procedure
try {
    $pdo = CZ_PDO::getConnection();

    $stmt = $pdo->prepare("SELECT HOGE(?)");
    $stmt->bindValue(1, 200, PDO::PARAM_INT);
    $stmt->execute();
    var_dump($stmt->fetch(PDO::FETCH_ASSOC));

    $stmt = $pdo->prepare("SELECT FOO()");
    $stmt->execute();
    var_dump($stmt->fetch(PDO::FETCH_ASSOC));

    $bar = 700;
    $stmt = $pdo->prepare("SELECT BAR(?)");
    $stmt->bindParam(1, $bar,
        PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT, 100);
    $stmt->execute();
    var_dump($stmt->fetch(PDO::FETCH_ASSOC));

    $baz = 123;
    $stmt = $pdo->prepare("SELECT BAZ(?)");
    $stmt->bindParam(1, $baz, PDO::PARAM_INT);
    $stmt->execute();

    var_dump($stmt->fetch(PDO::FETCH_ASSOC));
} catch (PDOException $e){
    var_dump($e);
}

 上記の実行結果です。

result
array(1) {
    ["hoge"]=>
    int(246800)
}
array(1) {
    ["foo"]=>
    int(123)
}
array(1) {
    ["bar"]=>
    int(716100)
}
array(1) {
    ["baz"]=>
    int(24600)
}

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • ハタ(ハタ)

    PHPの魅力に取り付かれた一人。 現在はSeasar.PHPとしてSeasar(Java)をPHP5に移植する活動をしている。 http://blog.xole.net/(ブログ)

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5