2012-12-30 10 views
5

Zważywszy buduję formę i wysyłania danych do PHP, typowy zapytania wkładka dla mnie jest tak:Optymalny sposób na tworzenie długich zapytań SQL insert z PDO

$sql = "INSERT INTO news 
      (title, body) 
      VALUES (?, ?)"; 
$stmt = $db->prepare($sql); 
$stmt->execute(array($_POST["title"], $_POST["body"])); 
$stmt->closeCursor(); 

To wygląda dobrze na małym zapytanie, i to jest moje zrozumienie, że to chroni mnie przed podobnymi SQL injection i cokolwiek.

Ale co, jeśli muszę pracować z całkiem dużą formą? Coś jak ...

$sql = "INSERT INTO ficha_item 
    (titulo, tipo_produto, quantidade_peso, unidade_de_venda, 
    unidades_por_caixa, caixas_piso, pisos_palete, tipo_de_palete, 
    unidades_palete, caixas_palete, uni_diametro, uni_largura, 
    uni_profundidade, uni_altura, uni_peso_bruto_unidade, caixa_largura, 
    caixa_profundidade, caixa_altura, altura_palete, volume_unidade, 
    peso_caixa, peso_palete) 
    VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; 

Myślę, że staje się żmudny licząc wszystkie te znaki zapytania. Czy istnieje jakiś bardziej przejrzysty sposób na zrobienie tego z PDO?

+0

Rozważ [ORM] (http://stackoverflow.com/questions/1346457/some-orm-using-pdo) zamiast ręcznie budowania takich potworności . – DCoder

Odpowiedz

6

chciałbym zrobić coś takiego:

$fields = array(
    'titulo', 
    'tipo_produto', 
    'quantidade_peso', 
    'unidade_de_venda', 
    'unidades_por_caixa', 
    'caixas_piso', 
    'pisos_palete', 
    'tipo_de_palete', 
    'unidades_palete', 
    'caixas_palete', 
    'uni_diametro', 
    'uni_largura', 
    'uni_profundidade', 
    'uni_altura', 
    'uni_peso_bruto_unidade', 
    'caixa_largura', 
    'caixa_profundidade', 
    'caixa_altura', 
    'altura_palete', 
    'volume_unidade', 
    'peso_caixa', 
    'peso_palete' 
); 
$sql = 'INSERT INTO ficha_item (%s) VALUES (%s)'; 
// make a list of field names: titulo, tipo_produto /*, etc. */ 
$fieldsClause = implode(', ', $fields); 
// make a list of named parameters: :titulo, :tipo_produto /*, etc. */ 
$valuesClause = implode(', ', array_map(function($value) { return ':' . $value; }, $fields)); 
// or, with create_function 
$valuesClause = implode(', ', array_map(create_function('$value', 'return ":" . $value;'), $fields)); 
$sql = sprintf($sql, $fieldsClause, $valuesClause); 

// $sql is now something similar to (formatted for display): 
// INSERT INTO ficha_item 
//  (titulo, tipo_produto /*, etc. */) 
// VALUES 
//  (:titulo, :tipo_produto /*, etc. */) 

$stmt = $db->prepare($sql); 

// if the keys in $_POST match with $fields, you can now simply pass $_POST here 
$stmt->execute($_POST); 
// or, as per Bill Karwin's sound suggestion, with the intersection of $_POST 
$stmt->execute(array_intersect_key($_POST, array_flip($fields))) 

Innymi słowy, używać nazwanych parametrów, i mieć je być generowane dynamicznie na podstawie tablicy $fields. Chociaż nazwane parametry nie są bezwzględnie konieczne; one do ułatwiają pracę, ponieważ kolejność elementów przekazywanych do, na przykład, PDOStatement::execute() już nie ma znaczenia.

Zakłada się jednak, że liczba elementów i ich kluczy w $_POST dokładnie odpowiada liczbie pól i ich nazwom w $sql.

+3

+1 ale polecam ograniczyć wartości parametrów: '$ stmt-> execute (array_intersect_key ($ _ POST, array_flip ($ fields)))'. –

+0

W połączeniu z wkładem Billa to wygląda dobrze. Mój serwer testowy nie działa jednak w PHP 5.3, czy mogę uzyskać przykład używając create_function? (Wersje PHP poniżej 5.3 nie obsługują funkcji anonimowych), próbowałem implementować to na własną rękę, ale moja nowość PHP mnie upośledza! –

+0

@BillKarwin Dzięki Bill, dodałem twoją sugestię. –

1

Używając metody bindParam, jest to clea (n | r) est sposobem na zrobienie tego, czego się spodziewasz. Jest to bardziej zrozumiałe niż ze znakami zapytania. Kod będzie więc łatwiejszy w utrzymaniu.

Przykład z official php doc:

Przykład # 1 Wykonaj przygotowaną instrukcję z wymienionych zastępcze

<?php 
/* Execute a prepared statement by   binding PHP variables */ 
$calories = 150; 
$colour = 'red'; 
$sth = $dbh->prepare('SELECT name, colour, calories 
FROM fruit 
WHERE calories < :calories AND colour = :colour'); 
$sth->bindParam(':calories', $calories, PDO::PARAM_INT); 
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12); 
$sth->execute(); 
?> 

nadzieję, że to pomaga.

2

Można utworzyć SQL dynamicznie:

$allowed_fields = array(
    'titulo', 
    'tipo_produto', 
    'quantidade_peso', 
    'unidade_de_venda', 
    'unidades_por_caixa', 
    'caixas_piso', 
    'pisos_palete', 
    'tipo_de_palete', 
    'unidades_palete', 
    'caixas_palete', 
    'uni_diametro', 
    'uni_largura', 
    'uni_profundidade', 
    'uni_altura', 
    'uni_peso_bruto_unidade', 
    'caixa_largura', 
    'caixa_profundidade', 
    'caixa_altura', 
    'altura_palete', 
    'volume_unidade', 
    'peso_caixa', 
    'peso_palete' 
); 

$columns = ""; 
$values = ""; 
$values_arr = array(); 
foreach ($_POST as $key=>$value) 
{ 
    if (!in_array($key, $allowed_fields)) continue; 
    $columns .= "$key, "; 
    $values .= "?, "; 
    $values_arr[] = $value; 
} 
// now remove trailing ", " from $columns and $values 
$columns = rtrim($columns, ", "); 
$values = rtrim($values, ", "); 
// finish them 
$sql = "INSERT INTO ficha_item($columns) VALUES($values)"; 
$stmt = $db->prepare($sql); 
$stmt->execute($values_arr); 
$stmt->closeCursor(); 
Powiązane problemy