Ядро на базата данни

Ядрото на базата данни на Nette е слой за абстракция на базата данни и осигурява основни функции.

Инсталация

Изтеглете и инсталирайте пакета с помощта на Composer:

composer require nette/database

Свързване и конфигуриране

За да се свържете с базата данни, просто създайте инстанция на класа Nette\Database\Connection:

$database = new Nette\Database\Connection($dsn, $user, $password);

Параметърът $dsn (име на източника на данни) е същият, който се използва в PDO, например host=127.0.0.1;dbname=test. Ако не успее, се изхвърля изключение Nette\Database\ConnectionException.

Конфигурацията на приложението обаче предлага по-сложен начин. Ще добавим раздел database, а той ще създаде необходимите обекти и панел Database в панела за отстраняване на грешки на Tracy.

database:
	dsn: 'mysql:host=127.0.0.1;dbname=test'
	user: root
	password: password

Обектът за връзка, който получаваме като услуга от DI-контейнера, напр:

class Model
{
	// подайте Nette\Database\Explorer, за да работите със слоя Database Explorer
	public function __construct(
		private Nette\Database\Connection $database,
	) {
	}
}

За повече информация вижте Конфигурация на базата данни.

Запитвания

За да направите заявка към базата данни, използвайте метода query(), който връща ResultSet.

$result = $database->query('SELECT * FROM users');

foreach ($result as $row) {
	echo $row->id;
	echo $row->name;
}

echo $result->getRowCount(); // връща броя на редовете, ако е известен

Можете да итерирате над ResultSet само веднъж, ако трябва да итерирате повече от веднъж, трябва да преобразувате резултата в масив, като използвате метода fetchAll().

Можете лесно да добавяте параметри към заявката, като обърнете внимание на въпросителния знак:

$database->query('SELECT * FROM users WHERE name = ?', $name);

$database->query('SELECT * FROM users WHERE name = ? AND active = ?', $name, $active);

$database->query('SELECT * FROM users WHERE id IN (?)', $ids); // $ids - массив

ПРЕДУПРЕЖДЕНИЕ Никога не конкатенирайте низове, за да избегнете уязвимост чрез SQL инжекция!

$db->query('SELECT * FROM users WHERE name = ' . $name); // НЕПРАВИЛЬНО!!!

Ако не успее, query() изхвърля изключението Nette\Database\DriverException или някое от неговите подчинени изключения:

Освен query(), има и други полезни методи:

// Връща асоциативен масив id => name
$pairs = $database->fetchPairs('SELECT id, name FROM users');

//връщане на всички редове като масив
$rows = $database->fetchAll('SELECT * FROM users');

//връща един ред
$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);

// връщане на едно поле
$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);

При неуспешен опит всички тези методи хвърлят изключение Nette\Database\DriverException.

Вмъкване, актуализиране и изтриване

Параметърът, който вмъкваме в SQL заявката, може да бъде и масив (в този случай можем да пропуснем заместващия символ ?), что может быть полезно для оператора INSERT:

$database->query('INSERT INTO users ?', [ // тук може да се пропусне въпросителен знак
	'name' => $name,
	'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)

$id = $database->getInsertId(); // връща автоматичното увеличение на вмъкнатия низ

$id = $database->getInsertId($последователност); // или стойност на последователността

Вмъкване на няколко стойности:

$database->query('INSERT INTO users', [
	[
		'name' => 'Jim',
		'year' => 1978,
	], [
		'name' => 'Jack',
		'year' => 1987,
	],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)

Можем също така да предаваме файлове, обекти DateTime или изброявания:

$database->query('INSERT INTO users', [
	'name' => $name,
	'created' => new DateTime, // или $database::literal('NOW()')
	'avatar' => fopen('image.gif', 'r'), // вмъква съдържанието на файла
	'status' => State::New, // enum State
]);

Струни за актуализация:

$result = $database->query('UPDATE users SET', [
	'name' => $name,
	'year' => $year,
], 'WHERE id = ?', $id);
// UPDATE users SET `name` = 'Jim', `year` = 1978 WHERE id = 123

echo $result->getRowCount(); // връща броя на засегнатите редове

За UPDATE можем да използваме операторите += и -=:

$database->query('UPDATE users SET', [
	'age+=' => 1, // note +=
], 'WHERE id = ?', $id);
// UPDATE users SET `age` = `age` + 1

Изтриване:

$result = $database->query('DELETE FROM users WHERE id = ?', $id);
echo $result->getRowCount(); // връща броя на засегнатите редове

Разширени заявки

Вмъкнете или актуализирайте, ако даден запис вече съществува:

$database->query('INSERT INTO users', [
	'id' => $id,
	'name' => $name,
	'year' => $year,
], 'ON DUPLICATE KEY UPDATE', [
	'name' => $name,
	'year' => $year,
]);
// INSERT INTO users (`id`, `name`, `year`) VALUES (123, 'Jim', 1978)
//   ON DUPLICATE KEY UPDATE `name` = 'Jim', `year` = 1978

Обърнете внимание, че Nette Database разпознава SQL контекста, в който е вмъкнат параметърът на масива, и изгражда SQL кода по съответния начин. Така той генерира (id, name, year) VALUES (123, 'Jim', 1978) от първия масив и преобразува втория масив в name = 'Jim', year = 1978.

Можем също така да опишем сортирането с помощта на масив, в който ключовете са имена на колони, а стойностите са булеви стойности, които определят дали да се сортират във възходящ ред:

$database->query('SELECT id FROM author ORDER BY', [
	'id' => true, // възходящо
	'name' => false, // низходящо
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC

Ако откриването е неуспешно, можете да посочите формата на асемблито, като използвате заместителя ?, последван от подсказка. Поддържат се следните команди:

?values (key1, key2, …) VALUES (value1, value2, …)
?set key1 = value1, key2 = value2, …
?and key1 = value1 AND key2 = value2 …
?or ключ1 = стойност1 ИЛИ ключ2 = стойност2 …
?order key1 ASC, key2 DESC

В декларацията WHERE се използва операторът ?and, така че условията са свързани AND:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978

Което може лесно да се промени на OR, като се използва заместителният символ ?or:

$result = $database->query('SELECT * FROM users WHERE ?or', [
	'name' => $name,
	'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978

Можем да използваме оператори в условията:

$result = $database->query('SELECT * FROM users WHERE', [
	'name <>' => $name,
	'year >' => $year,
]);
// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978

както и трансфери:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => ['Jim', 'Jack'],
	'role NOT IN' => ['admin', 'owner'], // изброяване + оператор NOT IN
]);
// SELECT * FROM users WHERE
//   `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')

Можем също така да включим част от персонализирания SQL код, като използваме така наречения SQL литерал:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())

Алтернативно:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())

Литералът на SQL може да има и свои собствени параметри:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	$database::literal('year > ? AND year < ?', $min, $max),
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND (year > 1978 AND year < 2017)

Благодарение на това можем да създаваме интересни комбинации:

$result = $database->query('SELECT * FROM users WHERE', [
	'name' => $name,
	$database::literal('?or', [
		'active' => true,
		'role' => $role,
	]),
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND (`active` = 1 OR `role` = 'admin')

Име на променливата

Има заместващ символ `?name', който се използва, ако името на таблица или колона е променлива. (Внимавайте да не позволите на потребителя да манипулира съдържанието на такава променлива):

$table = 'blog.users';
$column = 'name';
$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'

Транзакции

Съществуват три метода за обработка на транзакции:

$database->beginTransaction();

$database->commit();

$database->rollback();

Един елегантен метод предлага методът transaction(). Предавате обратно извикване, което се изпълнява в транзакция. Ако по време на изпълнението възникне изключение, транзакцията се прекратява, а ако всичко е наред, транзакцията се предава.

$id = $database->transaction(function ($database) {
	$database->query('DELETE FROM ...');
	$database->query('INSERT INTO ...');
	// ...
	return $database->getInsertId();
});

Както можете да видите, методът transaction() връща стойността на обратната връзка.

Функцията Transaction() също може да бъде вложена, което опростява прилагането на независими хранилища.

версия: 4.0