Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
salve pessoal...
tenho uma função para inserir dados no banco, ela funciona muito bem...
a chamada dela é assim:
inserir('tabela', 'valor_para_inserir_vindo_do_array');
array('id' => NULL, 'descricao' => 'Programador');
a saída é essa: INSERT INTO produto (id, descricao) VALUES ('', 'Programador')
ta funcionando certinho...
mas queria adapta-lá para PDO, mas não to conseguindo, ja que em PDO tem o bindValue:
$qr->bindValue(':campo', $varialvel, PDO::PARAM_STR|INT|etc);
como fazer para dinamicamente ele fazer o bindValue corretamente?
minha função completa:
<?php
mysql_connect('localhost', 'root', '') or die (mysql_error());
mysql_select_db('teste') or die (mysql_error());
function inserir($tabela, $dados) {
// verifica se os dados são um array
if (is_array($dados)) {
$totalCampos = count($dados); // conta total de campos que serão inseridos
$posicao = array_keys($dados); // pega posição no array de acordo com o Ãndice
$sql = 'INSERT INTO ' .$tabela. ' (';
for ($i = 0; $i < $totalCampos; $i++) {
$sql .= $posicao[$i] . ', ';
}
$sql = substr($sql, 0, -2);
$sql .= ') VALUES (';
$indice = 0;
for ($i = 0; $i < $totalCampos; $i++) {
$sql .= "'" .$dados[$posicao[$i]]. "', ";
}
$sql = substr($sql, 0, -2);
echo $sql .= ')';
} else {
echo 'São permitido apenas entrada por array!';
die();
}
$res = mysql_query($sql) or die ('<br />ERRO SQL: ' .mysql_error());
}
$dados = array('id' => NULL, 'descricao' => 'Programador');
inserir('produto', $dados);
?>Andrey, vi isto no manual do PHP mas não entendi muito bem, nem consigo aplica-lo, tu não pode dar outra dica ou um exemplo mais explicativo?
Olha só que legal a saída do code abaixo:
$fields = array( 'id' => NULL, 'name' => 'Bruno', 'age' => 23 );
// Está separado pois a linha acima não existe. array_filter() vai receber o array que você passar
$fields = array_filter( $fields );
$table = 'users';
printf(
'INSERT INTO `%s` ( `%s` ) VALUES ( %s )',
$table,
implode( '`, `',
array_keys( $fields )
),
implode( ', ',
array_fill( 0, count( $fields ), '?' )
)
);
Vou explicar.
Primeiro você "limpa" seu array de dados, removendo todos os valores vazios dele através da função array_filter() sem segundo parâmetro.
Isso porque você não precisa das partes do SQL referentes a campos nulos pois se um campo pode ser nulo, omitir da query já o fará. Se ele não pode ser nulo, você deve validar antes de montar a query.
Depois você monta sua query. printf() vai ecoar em tela. No seu caso, como a query vai ser passada para PDO::prepare(), você vai usar sprintf().
Você pode não usar a função e ir concatenando tudo. Mas assim fica bem mais legível :thumbsup:
Meu printf() tem três placeholders: Um para a tabela, que virá através de parâmetro e outros dois para, respectivamente, os campos a serem inseridos e seus valores.
Tanto para os campos como para os valores, a estrutura são strings separadas por vírgulas. Como os nomes dos campos já estão nos índices do array, array_keys() aliado à implode() é a combinação perfeita.
Já os valores você não vai preenchê-los na query, mas sim criar novos placeholders para que PDOStatement::execute() saiba o que fazer.
Como você nem sempre vai saber quantos campos estarão sendo passados, para evitar loops e condicionais desnecessários, usamos array_fill() que vai criar um novo array ali, na hora, com aquilo que você vai passar.
E você vai justamente passar o símbolo de interrogação, chamado no âmbito da PDO "muito criativamente" de question mark placeholders tantas vezes quantos valores existirem no array. E para isso usamos count()
Com a query pronta, basta passá-la para PDO::prepare() e invocar PDOStatement::execute() passando como argumento o array que você limpou lá em cima.
O próprio método se encarregará de combinar os nomes dos campos com os placeholders e atribuir os devidos valores direitinho.
E você não faz mais nada :grin:
Value Bruno, deu quase tudo certo aqui no meu script...
ta dando um erro Warning: PDOStatement::execute() expects parameter 1 to be array, string given in C:\wamp\www\funcao_insert.php on line 49;
<?php
function inserir($tabela, $dados) {
// arquivo de conexão
require 'config.php';
// verifica se os dados são um array
if (is_array($dados)) {
$totalCampos = count($dados); // conta total de campos que serão inseridos
$posicao = array_keys($dados); // pega posição no array de acordo com o Ãndice
$sql = 'INSERT INTO ' .$tabela. ' (';
for ($i = 0; $i < $totalCampos; $i++) {
$sql .= $posicao[$i] . ', ';
}
$sql = substr($sql, 0, -2);
$sql .= ') VALUES (';
$indice = 0;
for ($i = 0; $i < $totalCampos; $i++) {
$sql .= "'" .$dados[$posicao[$i]]. "', ";
}
$sql = substr($sql, 0, -2);
echo $sql .= ')';
} else {
echo 'São permitido apenas entrada por array!';
die();
}
// filtra os dados
$dados = array_filter($dados);
$sql1 = sprintf (
'INSERT INTO %s (%s) VALUES (%s)',
$tabela,
implode(', ', array_keys($dados)),
implode(', ', array_fill(0, count($dados), '?'))
);
try {
$qr = $conecta->prepare($sql1);
$qr->execute($dados);
} catch (Exception $erroSQL) {
echo 'Erro:<br />', $erroSQL->getMessage();
}
echo 'valor da sql1: ', $sql1;
}
// passando os valores e chamado a função para inserir
$dados = array('descricao' => 'Programador');
inserir('produto', $dados);
?>A idéia do Bruno Augusto é perfeita, basta você adaptar o bindValue para não ter problemas com injection.
<?php
$PDO = new PDO( ... );
function createInsertStatement( Array $Data , $Table ) {
$Data = array_filter( $Data ) ;
$SQLStatement = sprintf( 'INSERT INTO `%s`( `%s` ) VALUES( %s )' , $Table ,
implode( '`, `' , array_keys( $Data ) ) , implode( ', ' , array_fill( 0 , count( $Data ) , '?' ) ) );
return $SQLStatement;
}
function attach( Array $Data , PDOStatement $Statement ) {
$i = 1 ;
foreach( $Data as $Value ) {
$DataType = !is_numeric( $Value ) ? PDO::PARAM_STR : PDO::PARAM_INT;
call_user_func( Array( $Statement , 'bindValue' ) , $i , $Value , $DataType );
++$i;
}
}
$Dados = Array( 'id' => 10 , 'nome' => 'aeiou' );
$Statement = $PDO->prepare( createInsertStatement( $Dados , 'teste' ) );
attach( $Dados , $Statement );
print_r( $Statement->debugDumpParams() );
$Statement->execute();
Saída:
SQL: [50] INSERT INTO teste( id, nome ) VALUES( ?, ? )
Params: 2
Key: Position #0:
paramno=0
name=[0] ""
is_param=1
param_type=1
Key: Position #1:
paramno=1
name=[0] ""
is_param=1
param_type=2
No banco de dados ..
C:\dev\mysql\bin>mysql -u root -p
Enter password: ************
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.1.41 Source distribution
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create schema teste;
Query OK, 1 row affected (0.03 sec)
mysql> use teste;
Database changed
mysql> create table teste(
-> id integer,
-> nome varchar( 10 )
-> );mysql> select * from teste;
+------+---------+
| id | nome |
+------+---------+
| 10 | aeiou |
+------+---------+
1 rows in set (0.02 sec)
mysql>
Mas Andrey, não precisa fazer o bind semi-manualmente.
No seu exemplo mesmo, basta:
$Dados = Array( 'id' => 10 , 'nome' => 'aeiou' );
$Statement = $PDO->prepare( createInsertStatement( $Dados , 'teste' ) );
$Statement->execute( array_values( $Dados ) );
PDOStatement::execute() se encarregará sozinho de combinar as interrogações com os respectivos valores.
Mas, se comparar tem apenas uma coisinha de diferente, que esqueci de mencionar antes.
Descobri "aos trancos e barrancos" que quando usando question mark placeholders, pelo menos para alguns drivers suportados pela PDO, você deve enviar um array indexado numericamente, então deve-se passar um array_value() para reindexar o array ali, na hora.
@Annyh, seu código ficou confuso. Você apenas pegou o que eu escrevi e jogou dentro da sua função antiga gerando códig desnecessário.
O problema é que o jeito que você fazia antes, considerava uma verificação com is_array() e agora o bloco novo ficou fora dele.
Eu imagino que alguma coisa está sobrescrevendo o $dados, alterando o tipo dele.
Dá uma olhadinha no código do Andrey, especificamente na declaração da função e veja o que tem de diferente do seu e você vai entender o porquê sua função não precisa desse is_array() e vai garantir que funcione.
galera, na boa...
vocês me fizeram ficar mais confusa do que ja estava...
o Andrey mostra um código, o Bruno mostra outro...
poxa eu não manjo tanto quanto vocês de PDO...
Bruno, .. você não entendeu o uso do call_user_func .. o execute vai tratar tudo como String, qualquer valor .. então se você tiver inteiro, ele vai entrar lá no banco com 'aspas' e ser tratado como string.
An array of values with as many elements as there are bound parameters in the SQL statement being executed.
All values are treated as PDO::PARAM_STR.
>
Descobri "aos trancos e barrancos" que quando usando question mark placeholders, pelo menos para alguns drivers suportados pela PDO, você deve enviar um array indexado numericamente, então deve-se passar um array_value() para reindexar o array ali, na hora.
Veja onde o $i da função attach começa, não precisava dos 'trancos e barrancos' para descobrir isso ..
Parameter identifier. For a prepared statement using named placeholders, this will be a parameter name of the form :name.
For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter.
Annyh, o código é o mesmo .. apenas passei pra uma função separada .. da mesma forma que você pode usar a função 'attach', você pode usar sem ela, da forma que o Bruno Augusto mostrou, enviando $Dados para o execute.
Andrey, então...
resumindo fica como, qual o certo?
meu script agora esta assim, mas acho ele meio confuso para mim...
<?php
define('HOST', 'localhost');
define('USER', 'root');
define('SENHA', '');
define('BD', 'teste');
$conex = 'mysql:host='.HOST.';dbname='.BD;
$PDO = new PDO($conex, USER, SENHA);
function Inserir(Array $dados, $tabela) {
$dados = array_filter($dados);
$SQLStatement = sprintf('INSERT INTO `%s` (`%s`) VALUES (%s)', $tabela,
implode(', ', array_keys($dados)),
implode(', ', array_fill(0, count($dados), '?')));
return $SQLStatement;
}
function anexa(Array $dados, PDOStatement $qr) {
$i = 1 ;
foreach($dados as $valor) {
$tipoDado = !is_numeric($valor) ? PDO::PARAM_STR : PDO::PARAM_INT;
call_user_func(Array($qr, 'bindValue'), $i, $valor, $tipoDado);
++$i;
}
}
$dados = Array('nome' => 'Junior', 'sobrenome' => 'Eberhardt');
$qr = $PDO->prepare(Inserir($dados, 'produto'));
anexa($dados, $qr);
//print_r($qr->debugDumpParams());
$qr->execute();Ao meu ver, está tudo certo.
>
Ao meu ver, está tudo certo.
então tambem esta protegido contra sqlinjection?
se sim eu vou colocar o script no banco de scripts, certo?
>
Bruno, .. você não entendeu o uso do call_user_func .. o execute vai tratar tudo como String, qualquer valor .. então se você tiver inteiro, ele vai entrar lá no banco com 'aspas' e ser tratado como string.
An array of values with as many elements as there are bound parameters in the SQL statement being executed.
All values are treated as PDO::PARAM_STR.
Bom a julgar que quando desenvolvi esta parte em particular do meu sistema e com os testes que eu fiz, mesmo que o campo na Tabela fosse um tipo numérico, se eu cadastrasse um inteiro ou uma string numérica, ao fazer o SELECT sempre me retornava como string numérica.
>
Veja onde o $i da função attach começa, não precisava dos 'trancos e barrancos' para descobrir isso ..
Parameter identifier. For a prepared statement using named placeholders, this will be a parameter name of the form :name.
For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter.
Quando eu criei pela primeira vez (mas a primeira meeeesmo), achava mais fácil os named placeholders, até mesmo para depurar.
Evoluindo, percebi que, nativamente, a depuração com qualquer dois dois tipos de placeholder era a mesma, sendo ambas limitadas, então fiz algo melhor.
E então, ampliei o sistema, permitindo suportar com maior precisão cada particularidade dos drivers da PDO, e percebi que haviam algumas inconsistências em alguns deles, fazendo com que uma query construída com parâmetros nomeados não funcionasse da mesma forma que com interrogações.
Daí padronizei tudo e foi aí que caracterizou os "trancos e barrancos" citados.
Annyh, o código é o mesmo .. apenas passei pra uma função separada .. da mesma forma que você pode usar a função 'attach', você pode usar sem ela, da forma que o Bruno Augusto mostrou, enviando $Dados para o execute.
;)
fiz uma outra função para facilitar a chamada da mesma..
<?php
function Inserir($dados, $tabela) {
require 'config.php';
function filtro(array $dados, $tabela) {
$dados = array_filter($dados) ;
$sql = sprintf('INSERT INTO `%s` (`%s`) VALUES (%s)',
$tabela,
implode('`, `', array_keys($dados)),
implode(', ', array_fill(0, count($dados), '?')));
return $sql;
}
function anexa(array $dados, PDOStatement $afirmacao) {
$i = 1;
foreach ($dados as $valor) {
$tipoDados = !is_numeric($valor) ? PDO::PARAM_STR : PDO::PARAM_INT;
call_user_func(array($afirmacao, 'bindValue'), $i, $valor, $tipoDados);
++$i;
}
}
//$dados = array('nome' => 'Isabelly', 'sobrenome' => 'LE');
$qr = $PDO->prepare(filtro($dados, $tabela));
anexa($dados, $qr);
//print_r($Statement->debugDumpParams());
$qr->execute();
}
$dados = array('nome' => 'Isabelly', 'sobrenome' => 'LE');
Inserir($dados, 'produto');
call_user_func_array, atrelando as variáveis na string do sql.