2011-07-18 9 views
15

Moja aplikacja internetowa ma obecnie wykonuj proste zapytania: proste operacje CRUD, liczenie, ...Proste PDO wrapper

Kilka miesięcy temu, ktoś polecił mi tutaj napisać prosty PDO otoki dla tego (aby uniknąć pisania try/catch, prepare(), execute(), itp. za każdym razem, gdy powinno zostać wykonane zapytanie). Przykład ten sposób wykazano (Zrobiłem kilka zmian, więc można go używać w moim projekcie):

public function execute() { 
    $args = func_get_args(); 
    $query = array_shift($args); 
    $result = false; 

    try { 
     $res = $this->pdo->prepare($query); 
     $result = $res->execute($args); 
    } catch (PDOException $e) { echo $e->getMessage(); } 

    return $result; 
    } 

Kiedy trzeba wykonać więcej operacji (wykonywanie zapytań, zdobycie 1 rekord, pobieranie wielu rekordów, licząc wyniki) Stworzyłem metodę dla wszystkich z nich:

public function getMultipleRecords() { 
    $args = func_get_args(); 
    $query = array_shift($args); 
    $records = array(); 

    try { 
     $res = $this->pdo->prepare($query); 
     $res->execute($args); 
     $records = $res->fetchAll(); 
    } catch (PDOException $e) { echo $e->getMessage(); } 

    return $records; 
    } 

    public function getSingleRecord() { 
    $args = func_get_args(); 
    $query = array_shift($args); 
    $record = array(); 

    try { 
     $res = $this->pdo->prepare($query); 
     $res->execute($args); 
     $record = $res->fetch(); 
    } catch (PDOException $e) { echo $e->getMessage(); } 

    return $record; 
    } 

    public function execute() { 
    $args = func_get_args(); 
    $query = array_shift($args); 
    $result = false; 

    try { 
     $res = $this->pdo->prepare($query); 
     $result = $res->execute($args); 
    } catch (PDOException $e) { echo $e->getMessage(); } 

    return $result; 
    } 

    public function count() { 
    $args = func_get_args(); 
    $query = array_shift($args); 
    $result = -1; 

    try { 
     $res = $this->pdo->prepare($query); 
     $res->execute($args); 
     $result = $res->fetchColumn(); 
    } catch(PDOException $e) { echo $e->getMessage(); } 

    return $result; 
    } 

Jak widać, większość kodu jest taka sama. Tylko 2 linie kodu są różne dla każdej metody: inicjalizacja $ result (zawsze chcę zwrócić wartość, nawet jeśli zapytanie się nie powiedzie) i pobieranie. Zamiast używać 4 metod, mógłbym napisać tylko jeden z nich i przekazać dodatkowy parametr z typem akcji. W ten sposób mógłbym użyć wielu instrukcji if/else instrukcji switch. Jednak myślę, że kod może stać się bałaganiarski. Czy to dobry sposób na rozwiązanie tego problemu? Jeśli nie, jakie byłoby dobre rozwiązanie?

Drugi problem, jaki mam (dlatego pracuję teraz nad tą klasą), polega na tym, że chcę używać przygotowanych instrukcji za pomocą instrukcji SQL LIMIT. Jednak to nie jest możliwe, aby to zrobić:

$res = $pdo->prepare("SELECT * FROM table LIMIT ?"); 
$res->execute(array($int)); 

variabele będą notowane z jakiegoś powodu (a więc kwerenda nie powiedzie się), jak wyjaśniono tutaj: https://bugs.php.net/bug.php?id=40740

Rozwiązaniem wydaje się używać bindValue() i użyj typu danych jako parametru: http://www.php.net/manual/de/pdostatement.bindvalue.php

Mogłabym przepisać metody, aby to wspierać, ale potrzebowałabym również użyć dodatkowego parametru. Nie mogę po prostu użyć $db->execute($sql, $variable1, $variable2);, ponieważ potrzebuję znać typ danych.

Jaki jest najlepszy sposób rozwiązania tego problemu?

Dzięki

Odpowiedz

33

Jak o tworzeniu klasy z metod, które można łańcuch (dla jasności, ja usunęliśmy sprawdzanie błędów):

class DB { 

    private $dbh; 
    private $stmt; 

    public function __construct($user, $pass, $dbname) { 
     $this->dbh = new PDO(
      "mysql:host=localhost;dbname=$dbname", 
      $user, 
      $pass, 
      array(PDO::ATTR_PERSISTENT => true) 
     ); 
    } 

    public function query($query) { 
     $this->stmt = $this->dbh->prepare($query); 
     return $this; 
    } 

    public function bind($pos, $value, $type = null) { 

     if(is_null($type)) { 
      switch(true) { 
       case is_int($value): 
        $type = PDO::PARAM_INT; 
        break; 
       case is_bool($value): 
        $type = PDO::PARAM_BOOL; 
        break; 
       case is_null($value): 
        $type = PDO::PARAM_NULL; 
        break; 
       default: 
        $type = PDO::PARAM_STR; 
      } 
     } 

     $this->stmt->bindValue($pos, $value, $type); 
     return $this; 
    } 

    public function execute() { 
     return $this->stmt->execute(); 
    } 

    public function resultset() { 
     $this->execute(); 
     return $this->stmt->fetchAll(); 
    } 

    public function single() { 
     $this->execute(); 
     return $this->stmt->fetch(); 
    } 
} 

Można następnie używać go tak:

// Establish a connection. 
$db = new DB('user', 'password', 'database'); 

// Create query, bind values and return a single row. 
$row = $db->query('SELECT col1, col2, col3 FROM mytable WHERE id > ? LIMIT ?') 
    ->bind(1, 2) 
    ->bind(2, 1) 
    ->single(); 

// Update the LIMIT and get a resultset. 
$db->bind(2,2); 
$rs = $db->resultset(); 

// Create a new query, bind values and return a resultset. 
$rs = $db->query('SELECT col1, col2, col3 FROM mytable WHERE col2 = ?') 
    ->bind(1, 'abc') 
    ->resultset(); 

// Update WHERE clause and return a resultset. 
$db->bind(1, 'def'); 
$rs = $db->resultset(); 

Można zmienić metodę bind, aby zaakceptować tablicę lub tablicę asocjacyjną, jeśli wolisz, ale ja znajduję tę składnię całkiem jasno - unika się konieczności budowania tablicy. Sprawdzanie typu parametru jest opcjonalne, ponieważ dla większości wartości działa PDO::PARAM_STR, ale należy pamiętać o potencjalnych problemach podczas przekazywania wartości null (patrz dokumentacja comment in PDOStatement->bindValue).

+1

To jest piękne, Mike. Uwielbiam ideę klasy łańcuchowej! – dotancohen

+2

Bardzo fajny pomysł, ale użyłbym '$ row = $ db-> query ('SELECT col1, col2, col3 FROM mytable WHERE id>: id LIMIT: limit') -> bind (": id ", 2) - > bind (": limit", 1) -> single(); 'bardzo łatwy do odczytania – Ergec

+1

@Ergec tylko wskazówka, nie możesz mieć tego samego symbolu': limit', nawet jeśli ma on taką samą wartość w obu miejsc –