Ошибка с перезаписью BindParam в PHP
Вопрос
Это немного странно, и я вполне мог кодировать это совершенно неправильно - поэтому я столкнулся с одной и той же ошибкой дважды за два дня, в совершенно разных частях сценария.Код, который я использую, приведен ниже:
public function findAll( $constraints = array() ) {
// Select all records
$SQL = 'SELECT * FROM ' . $this->tableName;
// See if there's any constraints
if( count( $constraints ) > 0 ) {
$SQL .= ' WHERE ';
foreach( $constraints as $field => $value ) {
$SQL .= $field . ' = :' . $field . ' AND ';
}
}
// Remove the final AND and prepare the statement
$SQL = substr( $SQL, 0, -5 );
$PDOStatement = $this->PDO->prepare( $SQL );
// Loop through constraints and bind parameters
foreach( $constraints as $field => $value ) {
print 'Binding ' . $field . ' to ' . $value . '
';
$PDOStatement->bindParam( $field, $value );
}
$PDOStatement->execute();
var_dump($PDOStatement);
while ( $results = $PDOStatement->fetch( PDO::FETCH_ASSOC ) ) {
var_dump($results);
}
}
Я новичок в использовании PDO, но в основном я пытаюсь передать массив ограничений, например.
array( 'active' => 1, 'name' => 'James' )
и вернуть все строки из таблицы WHERE active = 1 AND name = 'James'
Если я использую этот массив, SQL будет выполнен с самого начала.
var_dump( )
является SELECT * FROM {table} WHERE active = :active AND name = 'James'
- именно так, как я ожидаю.Привязанные параметры печатают «Привязка активна к 1» и «Привязка имени к Джеймсу» — точно так, как и ожидалось.Строки существуют в базе данных, но вторая var_dump()
вызов $results ничего не выводит, т.е.никакие строки не возвращаются.
Если я передам массив одного ограничения, например.
array( 'active' => 1 )
, это прекрасно работает.Кажется, что всякий раз, когда передается несколько ограничений, он перестает работать.
Решение
Это потому что bindParam
работает путем привязки к переменной, и вы повторно используете переменную ($value
) для нескольких значений.Попробуйте с bindValue
вместо.
Или еще лучше;Передайте значения как массив в execute
вместо.Это делает оператор без сохранения состояния, что обычно хорошо в программировании.
Другие советы
Как уже упоминалось, использование bindValue
вместо bindParam
обязательно добьется этого.Однако, потратив немало времени на устранение этой проблемы в последнее время, я обнаружил альтернативное решение.Вот как можно выполнить привязку переменной PDO в цикле foreach с помощью bindParam:
Замените следующую строку из исходного сообщения:
$PDOStatement->bindParam( $field, $value );
...с этим:
$PDOStatement->bindParam( $field, $constraints[$field] );
Вместо привязки $value
, использовать $array_name[$array_key]
.Это работает, потому что теперь вы привязываетесь к уникальной переменной, а не к той, которая повторно используется на каждом проходе цикла.
Переменная $field
Однако, используемый в качестве заполнителя, очевидно, не обязательно должен быть уникальной переменной.Я еще не тщательно исследовал этот вопрос, но переменная, используемая в качестве заполнителя, похоже, анализируется немедленно (вместо того, чтобы быть назначена в качестве ссылки на переменную), даже когда используется bindParam.
Кроме того, поскольку вам больше не понадобится доступ $value
напрямую, вы также можете заменить это:
foreach( $constraints as $field => $value ) {
...с этим:
foreach (array_keys($constraints) as $field) {
Это необязательно, так как без этого изменения все будет работать нормально.На мой взгляд, это выглядит чище, так как позже может возникнуть путаница, почему $value
назначается, но никогда не используется.