Я не большой поклонник ОРМ. Я чувствую себя очень комфортно, работая с необработанными SQL, и из-за этого я обычно использую DBAL (или PDO в старых проектах). У меня есть одна небольшая библиотека для ежедневных операций с базами данных, и сегодня я написал эту библиотеку
Прежде всего, представьте одно соединение DBAL. В этом примере я использую базу данных sqlite in-memomy, но мы можем использовать любую базу данных, поддерживаемую DBAL (она же «почти все»):
use Doctrine\DBAL\DriverManager;
$conn = DriverManager::getConnection([
'driver' => 'pdo_sqlite',
'memory' => true
]);
Мы также можем создать одно соединение DBAL из соединения PDO. Полезно использовать DBAL в унаследованных приложениях вместо создания нового соединения (помните, что DBAL работает поверх PDO)
use Doctrine\DBAL\DriverManager;
$conn = DriverManager::getConnection(['pdo' => new PDO('sqlite::memory:')]);
Теперь мы настроили базу данных для примера
$conn->exec("CREATE TABLE users (
userid VARCHAR PRIMARY KEY NOT NULL ,
password VARCHAR NOT NULL ,
name VARCHAR,
surname VARCHAR
);");
$conn->exec("INSERT INTO users VALUES('user','pass','Name','Surname');");
$conn->exec("INSERT INTO users VALUES('user2','pass2','Name2','Surname2');");
Наша таблица «пользователи» имеет две записи. Теперь мы можем начать использовать нашу библиотеку.
Сначала мы создаем новый экземпляр нашей библиотеки:
use G\Db; $db = new Db($conn);
Теперь простой запрос из строки:
$data = $db->select("select * from users");
Иногда мне лень и я не хочу писать всю строку SQL, и я хочу выполнить select * from table:
use G\Sql;
$data = $db->select(SQL::createFromTable("users"));
Возможно, нам нужно отфильтровать наш оператор Select с предложением WHERE:
$data = $db->select(SQL::createFromTable("users", ['userid' => 'user2']));
А теперь кое-что очень интересное (по крайней мере, для меня). Я хочу перебрать набор записей и, возможно, изменить его. Конечно, я могу использовать «foreach» над $ data и делать все, что мне нужно, но я предпочитаю использовать следующий синтаксис:
$data = $db->select(SQL::createFromTable("users"), function (&$row) {
$row['name'] = strtoupper($row['name']);
});
Для меня это более читабельно. Я перебираю набор записей и меняю имя строки на верхний регистр. Здесь вы можете увидеть, что делает моя функция «выбор»:
/**
* @param Sql|string $sql
* @param \Closure $callback
* @return array
*/
public function select($sql, \Closure $callback = null)
{
if ($sql instanceof Sql) {
$sqlString = $sql->getString();
$parameters = $sql->getParameters();
$types = $sql->getTypes();
} else {
$sqlString = $sql;
$parameters = [];
$types = [];
}
$statement = $this->conn->executeQuery($sqlString, $parameters, $types);
$data = $statement->fetchAll();
if (!is_null($callback) && count($data) > 0) {
$out = [];
foreach ($data as $row) {
if (call_user_func_array($callback, [&$row]) !== false) {
$out[] = $row;
}
}
$data = $out;
}
return $data;
}
И, наконец, транзакции (я обычно никогда не использую autocommit, и мне нравится обрабатывать транзакции самостоятельно)
$db->transactional(function (Db $db) {
$userId = 'temporal';
$db->insert('users', [
'USERID' => $userId,
'PASSWORD' => uniqid(),
'NAME' => 'name3',
'SURNAME' => 'name3'
]);
$db->update('users', ['NAME' => 'updatedName'], ['USERID' => $userId]);
$db->delete('users', ['USERID' => $userId]);
});
«Транзакционная» функция очень похожа на транзакционную функцию DBAL.
public function transactional(\Closure $callback)
{
$out = null;
$this->conn->beginTransaction();
try {
$out = $callback($this);
$this->conn->commit();
} catch (\Exception $e) {
$this->conn->rollback();
throw $e;
}
return $out;
}
Я немного изменяюсь, потому что мне нравится возвращать значение в замыкании и разрешать делать такие вещи:
$status = $db->transactional(function (Db $db) {
$userId = 'temporal';
$db->insert('users', [
'USERID' => $userId,
'PASSWORD' => uniqid(),
'NAME' => 'name3',
'SURNAME' => 'name3'
]);
$db->update('users', ['NAME' => 'updatedName'], ['USERID' => $userId]);
$db->delete('users', ['USERID' => $userId]);
return "OK"
});
Другие функции (вставка, обновление, удаление) только обходят вызовы функций DBAL:
private $conn;
public function __construct(Doctrine\DBAL\Connection $conn)
{
$this->conn = $conn;
}
public function insert($tableName, array $values = [], array $types = [])
{
$this->conn->insert($tableName, $values, $types);
}
public function delete($tableName, array $where = [], array $types = [])
{
$this->conn->delete($tableName, $where, $types);
}
public function update($tableName, array $data, array $where = [], array $types = [])
{
$this->conn->update($tableName, $data, $where, $types);
}
И это все. Вы можете использовать библиотеку с composer и скачать на github .
Кстати, я протестировал новый продукт Sensiolabs ( SensioLabs Insight ), чтобы проанализировать код и проверить хорошие практики, и я получил платиновую медаль #yeah!