Простая PDO-обертка
В настоящее время мое веб-приложение выполняет простые запросы: простые операции CRUD, подсчет,...
Несколько месяцев назад кто-то рекомендовал меня здесь написать простую оболочку PDO для этого (чтобы избежать написания try/catch, prepare(), execute() и т.д. каждый раз, когда запрос должен быть выполнен). Этот примерный метод был показан (я внес некоторые изменения, чтобы использовать его в своем собственном проекте):
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;
}
Поскольку мне нужно выполнить больше операций (выполнение запросов, извлечение 1 записи, извлечение нескольких записей, подсчет результатов), я создал метод для всех из них:
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;
}
Как вы видите, большая часть кода одинаков. Для каждого метода различаются только 2 строки кода: инициализация $result (я всегда хочу вернуть значение, даже если запрос завершается с ошибкой) и выборки. Вместо того, чтобы использовать 4 метода, я мог бы написать только один из них и передать дополнительный параметр с типом действия. Таким образом, я мог бы использовать кучу операторов if/else оператора switch. Однако, я думаю, код может стать беспорядочным. Это хороший способ решить эту проблему? Если нет, то что было бы хорошим решением?
Вторая проблема, с которой я сталкиваюсь (именно поэтому я сейчас работаю над этим классом), заключается в том, что я хочу использовать подготовленные операторы с помощью инструкции LIMIT SQL. Однако это невозможно сделать:
$res = $pdo->prepare("SELECT * FROM table LIMIT ?");
$res->execute(array($int));
По какой-то причине переменная будет указываться (и, следовательно, запрос не будет выполнен), как описано здесь:
https://bugs.php.net/bug.php?id=40740
Решение похоже использует bindValue() и использует тип данных int в качестве параметра:
http://www.php.net/manual/de/pdostatement.bindvalue.php
Я мог бы переписать метод для поддержки этого, но мне также нужно будет использовать дополнительный параметр. Я не могу просто использовать $db->execute($sql, $variable1, $variable2);
больше, поскольку мне нужно знать тип данных.
Какой лучший способ решить эту проблему?
Спасибо
Ответы
Ответ 1
Как создать класс с помощью методов, которые вы можете связать (для ясности я удалил проверку ошибок):
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();
}
}
Затем вы можете использовать его следующим образом:
// 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();
Вы можете изменить метод bind
, чтобы принять массив или ассоциативный массив, если хотите, но я нахожу этот синтаксис совершенно ясным - он избегает необходимости создавать массив. Проверка типа параметра является необязательной, поскольку PDO::PARAM_STR
работает для большинства значений, но помните о потенциальных проблемах при передаче нулевых значений (см. комментарий в документации PDOStatement->bindValue
).