Подтвердить что ты не робот

PHP PDO с foreach и fetch

Следующий код:

<?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" дважды не является моей целью, мне просто любопытно, почему два оператора "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);
    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

Почему это происходит?

4b9b3361

Ответ 1

A 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 как сущность. Кэширующий itertor, вероятно, более разумен, чем сохранение набора результатов в массив, поскольку он по-прежнему предлагает все свойства и методы объекта PDOStatement, который он завернул.

Ответ 2

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

Ответ 3

Это потому, что вы читаете курсор, а не массив. Это означает, что вы последовательно просматриваете результаты, и когда вы доберетесь до конца, вам потребуется сбросить курсор на начало результатов, чтобы прочитать их снова.

Если вы хотите прочитать результаты несколько раз, вы можете использовать fetchAll для чтения результатов в истинный массив, и тогда он будет работать так, как вы ожидаете.

Ответ 4

$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, над которым вы можете выполнять итерацию. Первая итерация выводит все результаты, вторая ничего не делает, поскольку вы можете только перебирать результат один раз. Это потому, что данные передаются из базы данных и итерация по результату с помощью foreach по существу сокращается:

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

Как только вы закончите этот цикл, вам нужно reset курсор на стороне базы данных, прежде чем вы сможете снова перебрать его.

$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.

В последнем примере вы просто извлекаете первую строку результатов и перебираете ее.