次のコード:
<?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
なぜこれが起こるのですか?
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
オブジェクトのすべてのプロパティとメソッドを提供するためです。
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に適切なエラー報告を設定してください
これは、配列ではなくカーソルを読み取っているためです。これは、結果を順番に読んでいるので、最後まで到達したら、カーソルを結果の先頭にリセットして、再度読み取る必要があることを意味します。
結果を複数回読み取りたい場合は、 fetchAll を使用して結果を真の配列に読み取れば、期待どおりに機能します。
$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
をループしようとするとエラーが発生します。
最後の例では、最初の結果行を単純にフェッチしてループしています。