La respuesta simple es: No se puede. PDO utiliza una abstracción para declaraciones preparadas que tiene algunas limitaciones. Por desgracia, este es uno, usted tiene que trabajar alrededor de usar algo como
$query = "UPDATE users SET firstname = :name1 WHERE firstname = :name2";
$stmt = $dbh -> prepare($query);
$stmt -> execute(array(":name1" => "Jackie", ":name2" => "Jackie"));
En ciertos casos, tales como declaraciones preparadas emulados con algunas versiones del driver PDO/MySQL, son compatibles repetidas parámetros con nombre; sin embargo, esto no se debe confiar, ya que es frágil (puede hacer que las actualizaciones requieran más trabajo, por ejemplo).
Si desea soportar múltiples apariciones de un parámetro llamado, siempre extend PDO and PDOStatement (por herencia clásica o por la composición), o simplemente PDOStatement y puede configurar su clase como la clase comunicado estableciendo el atributo PDO::ATTR_STATEMENT_CLASS
. La PDOStatement extendida (o PDO::prepare
) podría extraer los parámetros nombrados, buscar repeticiones y generar automáticamente reemplazos. También registraría estos duplicados. Los métodos de vinculación y ejecución, al pasar un parámetro con nombre, probarían si el parámetro se repite y vincularán el valor a cada parámetro de reemplazo.
Nota: el siguiente ejemplo no se ha probado y es probable que tenga errores (algunos relacionados con el análisis de sentencias se anotan en los comentarios del código).
class PDO_multiNamed extends PDO {
function prepare($stmt) {
$params = array_count_values($this->_extractNamedParams());
# get just named parameters that are repeated
$repeated = array_filter($params, function ($count) { return $count > 1; });
# start suffixes at 0
$suffixes = array_map(function ($x) {return 0;}, $repeated);
/* Replace repeated named parameters. Doesn't properly parse statement,
* so may replacement portions of the string that it shouldn't. Proper
* implementation left as an exercise for the reader.
*
* $param only contains identifier characters, so no need to escape it
*/
$stmt = preg_replace_callback(
'/(?:' . implode('|', array_keys($repeated)) . ')(?=\W)/',
function ($matches) use (&$suffixes) {
return $matches[0] . '_' . $suffixes[$matches[0]]++;
}, $stmt);
$this->prepare($stmt,
array(
PDO::ATTR_STATEMENT_CLASS => array('PDOStatement_multiNamed', array($repeated)))
);
}
protected function _extractNamedParams() {
/* Not actually sufficient to parse named parameters, but it's a start.
* Proper implementation left as an exercise.
*/
preg_match_all('/:\w+/', $stmt, $params);
return $params[0];
}
}
class PDOStatement_multiNamed extends PDOStatement {
protected $_namedRepeats;
function __construct($repeated) {
# PDOStatement::__construct doesn't like to be called.
//parent::__construct();
$this->_namedRepeats = $repeated;
}
/* 0 may not be an appropriate default for $length, but an examination of
* ext/pdo/pdo_stmt.c suggests it should work. Alternatively, leave off the
* last two arguments and rely on PHP's implicit variadic function feature.
*/
function bindParam($param, &$var, $data_type=PDO::PARAM_STR, $length=0, $driver_options=array()) {
return $this->_bind(__FUNCTION__, $param, func_get_args());
}
function bindValue($param, $var, $data_type=PDO::PARAM_STR) {
return $this->_bind(__FUNCTION__, $param, func_get_args());
}
function execute($input_parameters=NULL) {
if ($input_parameters) {
$params = array();
# could be replaced by array_map_concat, if it existed
foreach ($input_parameters as $name => $val) {
if (isset($this->_namedRepeats[$param])) {
for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
$params["{$name}_{$i}"] = $val;
}
} else {
$params[$name] = $val;
}
}
return parent::execute($params);
} else {
return parent::execute();
}
}
protected function _bind($method, $param, $args) {
if (isset($this->_namedRepeats[$param])) {
$result = TRUE;
for ($i=0; $i < $this->_namedRepeats[$param], ++$i) {
$args[0] = "{$param}_{$i}";
# should this return early if the call fails?
$result &= call_user_func_array("parent::$method", $args);
}
return $result;
} else {
return call_user_func_array("parent::$method", $args);
}
}
}
nunca he tuvo problemas para repetir la misma lista en una declaración 'ON DUPLICATE KEY UPDATE' ... – jeroen
De hecho, depende un poco del controlador PDO, no debe confiar en que funcione. – johannes
Interesante, siempre me ha funcionado. ¿Conoces alguna documentación sobre ese tema? – jeroen