Como posso colocar os resultados de uma declaração preparada do MySQLI em uma matriz associativa?
-
13-09-2019 - |
Pergunta
Eu tenho uma consulta SQL e uma declaração preparada para mysqli:
$sql = 'SELECT photographers.photographer_id, photographers.photographer_name
FROM photographers';
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
$stmt->bind_result($photographer_id, $photographer_name);
$OK = $stmt->execute();
$stmt->fetch();
}
Como posso armazenar os resultados em uma matriz associativa para que eu possa fazer isso mais tarde e chegar a todos os dados retornados pela sequência SQL?
Solução
Experimente o seguinte:
$meta = $statement->result_metadata();
while ($field = $meta->fetch_field()) {
$params[] = &$row[$field->name];
}
call_user_func_array(array($statement, 'bind_result'), $params);
while ($statement->fetch()) {
foreach($row as $key => $val) {
$c[$key] = $val;
}
$hits[] = $c;
}
$statement->close();
Primeiro, você obtém os metadados da consulta e, a partir disso, obtém todos os campos que buscou (você pode fazer isso manualmente, mas esse código funciona para todas as consultas em vez de construir à mão). o call_user_func_array()
função chama o mysqli_stmt::bind_result()
função para você em cada um desses parâmetros.
Depois disso, é apenas uma questão de percorrer cada linha e criar uma matriz associativa para cada linha e adicioná -la a uma matriz, resultando em todos os resultados.
Outras dicas
Atualizar: Como o PHP 5.3.0, você pode obter um objeto MySQLI_RESULT que fornece um método Fetch_array.
$sql = 'SELECT photographers.photographer_id, photographers.photographer_name
FROM photographers';
$data = null;
$stmt = $conn->stmt_init();
if ($stmt->prepare($sql)) {
$stmt->bind_result($photographer_id, $photographer_name);
$OK = $stmt->execute();
$result = $stmt->get_result();
$data = $result->fetch_array();
}
Documentação: http://php.net/manual/en/mysqli-stmt.get-sult.php
Me deparei com essa discussão para encontrar uma solução para obter dados de declarações preparadas para MySQLI sem o MySqlnd. Eu tenho desenvolvido uma aula para lidar com declarações preparadas com o MySQLI de maneira útil. Por favor, dê uma olhada no código ou simplesmente use -o (veja um exemplo de uso no final do pedaço de código) para escrever instruções preparadas rapidamente e obter seus resultados.
class DbUtils {
private $host;
private $user;
private $pass;
private $database;
private $connection;
public function __construct($host, $user, $pass, $database) {
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->database = $database;
$this->connection = new mysqli($host, $user, $pass, $database);
}
public function query(Array $params) {
$args = array();
// 0. Correct the input function parameters
if (array_key_exists("query", $params)) {
$args["query"] = $params["query"];
} else {
throw new Exception("Parameter not found: 'query'.");
}
if (array_key_exists("types", $params)) {
$args["types"] = $params["types"];
} else {
$args["types"] = '';
}
if (array_key_exists("input", $params)) {
$args["input"] = $params["input"];
} else {
$args["input"] = array();
}
// 1. Check the connection:
if ($this->connection->connect_errno) {
echo "Connection to MySQL failed: [" . $this->connection->connect_errno . "]: " . $this->connection->connect_error . "<br/>";
}
// 2. Prepare the sentence:
if (!($stmt = $this->connection->prepare($args["query"]))) {
echo "Prepared statement failed: [" . $stmt->errno . "]: " . $stmt->error . "<br/>";
}
// 3. Bind the input parameters:
if ( ( 0 != sizeof( $args["input"] ) ) && !(call_user_method_array("bind_param", $stmt, array_merge(array($args["types"]), $args["input"])))) {
echo "Binding parameters failed: [" . $stmt->errno . "]: " . $stmt->error . "<br/>";
}
// 4. Execute the sentence
if (!($stmt->execute())) {
echo "Sentence execution failed: [" . $stmt->errno . "]: " . $stmt->error . "<br/>";
}
// 5. Bind the results:
$data = array();
$meta = $stmt->result_metadata();
$row = array();
while( $field = $meta->fetch_field() ) {
$argos[] = &$row[$field->name];
}
call_user_method_array('bind_result', $stmt, $argos);
// 6. Collect the results:
while ($stmt->fetch()) {
foreach($argos as $key => $val) {
$dataItem[$key] = $val;
}
$data[] = $dataItem;
}
// 7. Close the sentence:
$stmt->close();
// 8. Return interesting data properly ordered:
return $data;
}
}
// 1. Instantiate it:
$dbUtils = new DbUtils(
"127.0.0.1",
"user",
"password",
"database"
);
// 2. Query prepared statements like this:
$users = $dbUtils->query(array(
"query" => "SELECT * FROM user WHERE name LIKE ? AND pass LIKE ?;",
"input" => array('%', '%'),
"types" => 'ss'
));
// 3. Enjoy securely CRUD Ops!
Curiosamente, você não pode. Simplesmente não há como obter um objeto mysqli_result de uma instância mysqli_stmt. Eu sempre considerei isso uma falha importante e acho que essa é uma das principais razões pelas quais Mysqli nunca alcançou nenhuma popularidade real. Hoje em dia, foi praticamente substituído pelo PDO, que faz o que você deseja sem esforço.
Editar: Minha resposta significa apenas que você não pode fazer isso por padrão. É claro que você pode implementá -lo, como Chris sugeriu. Ainda assim, acho que você deve usar o PDO, se for possível.
Se você não pode usar a extensão da PDO. Ou você está tendo problemas para criar sua classe de banco de dados com declarações preparadas. Como usar para inserir atualização, excluir e inserir:
$db = new database();
$db->query = "INSERT INTO blabla (name,date,number) VALUES(?,?,?)";
$db->params = array($name,$date,$number);
$db->type = 'ssi'; //s=string,i=integer
if($db->insert())
echo 'success';
Fetch funciona um pouco diferente
$array = array();
$db = new database();
$db->query = "SELECT * FROM blabla WHERE id=? and someother=?";
$db->params = array($id,$other);
$db->type = 'is';
$r = $db->fetch();
//$r[0]['id'] for row 1
//$r[0]['name'] for row 1
//$r[1] .... For row 2
//$r[2] .... For row 3
//etc...
Agora para a classe de banco de dados
class database {
private $stmt;
private $mysqli;
private $query;
private $params = array();
private $type;
public function __set($name, $value) {
switch ($name) {
case 'params':
$this->params = $value;
break;
case 'query':
$this->query = $value;
break;
case 'type':
$this->type = $value;
break;
default:
break;
}
}
public function __get($name) {
if ($name !== "mysqli" && $name !== "stmt")
return $this->$name;
}
public function __construct() {
$this->mysqli = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT);
$this->stmt = $this->mysqli->stmt_init();
}
private function close_con($bool) {
if ($bool) {
$this->stmt->free_result();
}
$this->stmt->close();
$this->mysqli->close();
}
private function nofetch() {
$this->stmt->prepare($this->query);
$bind_names[] = $this->type;
for ($i = 0; $i < count($this->params); $i++) {
$bind_name = 'bind' . $i;
$$bind_name = $this->params[$i];
$bind_names[] = &$$bind_name;
}
call_user_func_array(array($this->stmt, "bind_param"), $bind_names);
if ($this->stmt->execute()) {
$this->close_con(false);
return true;
}
$this->close_con(false);
return false;
}
public function insert() {
if ($this->nofetch()) {
return true;
}
return false;
}
public function update() {
if ($this->nofetch()) {
return true;
}
return false;
}
public function delete() {
if ($this->nofetch()) {
return true;
}
return false;
}
public function fetch() {
$result_out = array();
$this->stmt->prepare($this->query);
$bind_names[] = $this->type;
if (count($this->params) > 0) {
for ($i = 0; $i < count($this->params); $i++) {
$bind_name = 'bind' . $i;
$$bind_name = $this->params[$i];
$bind_names[] = &$$bind_name;
}
call_user_func_array(array($this->stmt, "bind_param"), $bind_names);
}
if ($this->stmt->execute()) {
$result = $this->stmt->result_metadata();
$cols = $result->fetch_fields();
foreach ($cols as $col) {
$name = str_replace("-", "_", $col->name);
$$name = null;
if ($name == null)
$name = 'name';
$bindarray[$name] = &$$name;
}
call_user_func_array(array($this->stmt, 'bind_result'), $bindarray);
$this->stmt->store_result();
$copy = create_function('$a', 'return $a;');
while ($this->stmt->fetch()) {
$result_out[] = array_map($copy, $bindarray);
}
}
$this->close_con(true);
return $result_out;
}
}
Espero que isto seja útil
Um simples que realmente funciona surpreendentemente. Eu sei que é processual, mas ainda assim:
$query = "SELECT * FROM foo WHERE bar = ?;";
$stmt = mysqli_prepare($dbc, $query);
mysqli_stmt_bind_param($stmt, "s", $bar);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
return mysqli_fetch_assoc($result);
https://stackoverflow.com/users/5849505/carl-gentleman
Sua resposta é uma maneira de versões anteriores do PHP como "call_user_method_array" foi depreciada no Php 4.1.0 e removida no Php 7.0.0.
Por isso, acho relevante postar uma resposta atualizada, pelo menos PHP7, já que recentemente me encontrei sem o driver nativo do MySqlnd para a extensão do MySQLI em um novo host que transferi. Yay!...
Nota: Existem 2 funções aqui. O último é necessário. É a única maneira de saber tudo para funcionar. (Editar a resposta não produz uma matriz associativa ... fixada)
public function arr($query, $data, $format) { // Some parts have been used from others. I don't know who.
$d = array();
$row = array();
// 1. Connect to the database // This is how I do it
$db = $this->con;
// 2. Prepare the sentence:
if( !($stmt = $db->prepare($query)) ) {
echo "Prepared statement failed: [" . $stmt->errno . "]: " . $stmt->error . "<br>";
$d[0] = false;// I return an object array so [0] I can check later. It is true or false however I define it.
return $d;
}
// cast to array
$data = (array) $data;
$format = (array) $format;
//Normalize format
$format = implode('', $format);
$format = str_replace('%', '', $format);
// Prepend $format onto $values
array_unshift($data, $format);
// 3. Bind the input parameters: (note "call_user_func_array" is not depriciated)
if ( !(call_user_func_array( array( $stmt, 'bind_param'), $this->ref_values($data) )) ) {
echo "Binding parameters failed: [" . $stmt->errno . "]: " . $stmt->error . ";<br>";
}
// 4. Execute the sentence
if ( !($stmt->execute()) ) {
echo "Sentence execution failed: [" . $stmt->errno . "]: " . $stmt->error . ";<br>";
}
// 5. Prepare to Bind the results:
$meta = $stmt->result_metadata();
while( $field = $meta->fetch_field() ) {
$argos[] = &$row[$field->name];
$fld_nms[] = $field->name;
}
// 6. Bind the results to the argos array:
call_user_func_array( array( $stmt, 'bind_result'), $argos);
/* // I left some debuging tools that are helpful
echo "<br>argos<br>";
print_r($argos);
echo "<br><br>";
$class_methods = get_class_methods($stmt);
foreach ($class_methods as $method_name) {
echo "$method_name<br>";
}
*/
// 7. Collect the results:
while ($ftch = $stmt->fetch()) {
$dataItem = array();
/*
echo "<br>ftch<br>";
print_r($ftch);
echo "<br><br>";
*/
foreach($argos as $key => $val) {
echo "Args: k:" . $key . "; v:" . $val . ";<br>";
$nme = $fld_nms[$key];
$dataItem[$nme] = $val;
//$dataItem[$key] = $val;
}
$d[] = $dataItem; // I am not interested in returning the multi level array yet but I left it
}
// 8. Close the sentence:
$stmt->close();
// 9. Return interesting data properly ordered:
return $d;
}
private function ref_values($array) {
$refs = array();
foreach ($array as $key => $value) {
$refs[$key] = &$array[$key];
}
return $refs;
}