web-dev-qa-db-ja.com

PHP foreachおよびfetchを使用したPDO

次のコード:

<?php
try {
    $dbh = new PDO("mysql:Host=$hostname;dbname=$dbname", $username, $password);
    echo "Connection is successful!<br/>";
    $sql = "SELECT * FROM users";
    $users = $dbh->query($sql);
    foreach ($users as $row) {
        print $row["name"] . "-" . $row["sex"] ."<br/>";
    }
    foreach ($users as $row) {
        print $row["name"] . "-" . $row["sex"] ."<br/>";
    }
    $dbh = null;
}
catch (PDOexception $e) {
    echo "Error is: " . $e-> etmessage();
}

出力:

Connection is successful!

person A-male
person B-female

「foreach」を2回実行することは私の目的ではありません。2つの「foreach」ステートメントが1回だけ結果を出力するのはなぜですか?

同様のケースは次のとおりです。

<?php
try {
    $dbh = new PDO("mysql:Host=$hostname;dbname=$dbname", $username, $password);
    echo "Connection is successful!<br/>";
    $sql = "SELECT * FROM users";
    $users = $dbh->query($sql);
    foreach ($users as $row) {
        print $row["name"] . "-" . $row["sex"] ."<br/>";
    }
    echo "<br/>";
    $result = $users->fetch(PDO::FETCH_ASSOC);
    foreach($result as $key => $value) {
        echo $key . "-" . $value . "<br/>";
    }
    $dbh = null;
}
catch (PDOexception $e) {
    echo "Error is: " . $e-> etmessage();
}

出力:

Connection is successful!

person A-male
person B-female

SCREAM: Error suppression ignored for
Warning: Invalid argument supplied for foreach()

しかし、上記のコードから最初の「foreach」を削除すると、出力は正常になります。

<?php
try {
    $dbh = new PDO("mysql:Host=$hostname;dbname=$dbname", $username, $password);
    echo "Connection is successful!<br/>";
    $sql = "SELECT * FROM users";
    $users = $dbh->query($sql);

    echo "<br/>";
    $result = $users->fetch(PDO::FETCH_ASSOC);
    foreach($result as $key => $value) {
        echo $key . "-" . $value . "<br/>";
    }
    $dbh = null;
}
catch (PDOexception $e) {
    echo "Error is: " . $e-> etmessage();
}

出力:

Connection is successful!

user_id-0000000001
name-person A
sex-male

なぜこれが起こるのですか?

15
nut

PDOStatement$users)は前方カーソルです。つまり、一度消費すると(最初のforeach反復)、結果セットの先頭まで巻き戻されません。

foreachの後にカーソルを閉じて、ステートメントを再度実行できます。

$users       = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

$users->execute();

foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

または、フルキャッシュで調整されたCachingIteratorを使用してキャッシュできます。

$users       = $dbh->query($sql);

$usersCached = new CachedPDOStatement($users);

foreach ($usersCached as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
foreach ($usersCached as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

要点としてCachedPDOStatementクラスを見つける 。キャッシングイテレーターは、結果セットを配列に格納するよりもおそらく健全です。これは、ラップされたPDOStatementオブジェクトのすべてのプロパティとメソッドを提供するためです。

22
hakre

foreachステートメントは、通常の一方向fetch()ループの単なる構文糖です。データを複数回ループする場合は、最初に通常の配列として選択してください

$sql = "SELECT * FROM users";
$stm = $dbh->query($sql);
// here you go:
$users = $stm->fetchAll();

foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
echo "<br/>";
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

また、そのtry..catchを終了します。使用しないでください。ただし、PHPおよびPDOに適切なエラー報告を設定してください

11

これは、配列ではなくカーソルを読み取っているためです。これは、結果を順番に読んでいるので、最後まで到達したら、カーソルを結果の先頭にリセットして、再度読み取る必要があることを意味します。

結果を複数回読み取りたい場合は、 fetchAll を使用して結果を真の配列に読み取れば、期待どおりに機能します。

8
Kevin Garman
$users = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}

ここに $usersは、反復可能なPDOStatementオブジェクトです。最初の反復ではすべての結果が出力され、2番目の反復では結果を1回しか反復できないため、何も実行されません。これは、データがデータベースからストリーミングされており、foreachを使用して結果を反復処理することが、本質的に次の短縮形だからです。

while ($row = $users->fetch()) ...

そのループが完了したら、再度ループする前に、データベース側のカーソルをリセットする必要があります。

$users = $dbh->query($sql);
foreach ($users as $row) {
    print $row["name"] . "-" . $row["sex"] ."<br/>";
}
echo "<br/>";
$result = $users->fetch(PDO::FETCH_ASSOC);
foreach($result as $key => $value) {
    echo $key . "-" . $value . "<br/>";
}

ここでは、すべての結果が最初のループによって出力されています。 fetchの呼び出しはfalseを返します。既に結果セットを使い果たしているため(上​​記参照)、falseをループしようとするとエラーが発生します。

最後の例では、最初の結果行を単純にフェッチしてループしています。

0
deceze