Nette Documentation Preview

syntax
Nette Database
**************

.[perex]
Nette Database - мощный и изящный слой работы с базой данных для PHP, нацеленный на простоту и продуманные возможности. Он предлагает два способа работы с базой: [Explorer |explorer] для быстрой разработки приложений или [SQL-подход |SQL way] для прямой работы с запросами.

<div class="grid gap-3">
<div>


[SQL-подход|sql-way]
====================
- Безопасные параметризованные запросы
- Точный контроль над структурой SQL-запроса
- Когда нужно писать сложные запросы с продвинутыми функциями
- Оптимизация производительности через конкретные функции SQL

</div>

<div>


[Explorer |explorer]
====================
- Быстрая разработка без написания SQL
- Интуитивная работа со связями между таблицами
- Выигрыш от автоматической оптимизации запросов
- Подходит для быстрой и удобной работы с базой данных

</div>

</div>


Установка
=========

Скачайте и установите библиотеку с помощью [Composer|best-practices:composer]:

```shell
composer require nette/database
```


Поддерживаемые базы данных
==========================

Nette Database поддерживает следующие базы данных:

|* Сервер базы данных   |* Имя DSN     |* Поддержка Explorer
|-----------------------|--------------|-----------------------|
| MySQL (>= 5.1)        | mysql        | ДА                    |
| PostgreSQL (>= 9.0)   | pgsql        | ДА                    |
| SQLite 3 (>= 3.8)     | sqlite       | ДА                    |
| Oracle                | oci          | НЕТ                   |
| MS SQL (PDO_SQLSRV)   | sqlsrv       | ДА                    |
| MS SQL (PDO_DBLIB)    | mssql        | НЕТ                   |
| ODBC                  | odbc         | НЕТ                   |


Два подхода к работе с базой данных
===================================

Nette Database даёт вам выбор: вы можете либо писать SQL-запросы напрямую (SQL-подход), либо позволить порождать их автоматически (Explorer). Посмотрим, как оба подхода справляются с одними и теми же задачами:

[SQL-подход|sql-way] - SQL-запросы

```php
// Вставка записи
$database->query('INSERT INTO books', [
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// Получение записей: авторы книг
$result = $database->query('
	SELECT authors.*, COUNT(books.id) AS books_count
	FROM authors
	LEFT JOIN books ON authors.id = books.author_id
	WHERE authors.active = 1
	GROUP BY authors.id
');

// Вывод (не оптимально, порождает N дополнительных запросов)
foreach ($result as $author) {
	$books = $database->query('
		SELECT * FROM books
		WHERE author_id = ?
		ORDER BY published_at DESC
	', $author->id);

	echo "Author $author->name has written $author->books_count books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}
```

[Подход Explorer|explorer] - автоматическое порождение SQL

```php
// Вставка записи
$database->table('books')->insert([
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// Получение записей: авторы книг
$authors = $database->table('authors')
	->where('active', 1);

// Вывод (автоматически порождает всего 2 оптимизированных запроса)
foreach ($authors as $author) {
	$books = $author->related('books')
		->order('published_at DESC');

	echo "Author $author->name has written {$books->count()} books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}
```

Подход Explorer порождает и оптимизирует SQL-запросы автоматически. В примере выше SQL-подход порождает N+1 запросов (один для авторов и затем по одному для книг каждого автора), тогда как Explorer автоматически оптимизирует запросы и выполняет только два: один для авторов и один для всех их книг.

Оба подхода можно свободно сочетать в приложении по мере надобности.


Соединение и настройка
======================

Чтобы подключиться к базе данных, достаточно создать экземпляр класса [api:Nette\Database\Connection]:

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

Параметр `$dsn` (Data Source Name) такой же, [какой использует PDO |https://www.php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters], например `host=127.0.0.1;dbname=test`. В случае неудачи он выбрасывает `Nette\Database\ConnectionException`.

Однако более удобный способ предлагает [конфигурация приложения |configuration], где достаточно добавить секцию `database`. Так создаются нужные объекты, а также панель базы данных в панели [Tracy |tracy:].

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

Затем объект соединения можно [получить как сервис из DI-контейнера |dependency-injection:passing-dependencies], например:

```php
class Model
{
	public function __construct(
		// или Nette\Database\Explorer
		private Nette\Database\Connection $database,
	) {
	}
}
```

Подробнее о [конфигурации базы данных|configuration].


Создание Explorer вручную
-------------------------

Если вы не используете DI-контейнер Nette, вы можете создать экземпляр `Nette\Database\Explorer` вручную:

```php
// соединение с базой данных
$connection = new Nette\Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// хранилище кеша, реализующее Nette\Caching\Storage, например:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// занимается рефлексией структуры базы данных
$structure = new Nette\Database\Structure($connection, $storage);
// задаёт правила отображения имён таблиц, столбцов и внешних ключей
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);
```


Управление соединением
======================

При создании объекта `Connection` соединение устанавливается автоматически. Если вы хотите отложить соединение, используйте ленивый режим: включите его в [конфигурации|configuration] параметром `lazy` или так:

```php
$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);
```

Для управления соединением служат методы `connect()`, `disconnect()` и `reconnect()`.
- `connect()` создаёт соединение, если его ещё нет, и может выбросить `Nette\Database\ConnectionException`.
- `disconnect()` разрывает текущее соединение с базой данных.
- `reconnect()` выполняет разрыв и последующее повторное подключение к базе данных. Этот метод тоже может выбросить `Nette\Database\ConnectionException`.

Кроме того, вы можете следить за событиями, связанными с соединением, через событие `onConnect`, которое представляет собой массив callback-функций, вызываемых после установки соединения с базой данных.

```php
// выполняется после подключения к базе данных
$database->onConnect[] = function($database) {
	echo "Connected to the database";
};
```

Похожим образом работает событие `onQuery`: это массив callback-функций, вызываемых после каждого выполненного запроса (и когда запрос завершается ошибкой), что удобно для логирования или профилирования.


Панель отладки Tracy
====================

Если вы используете [Tracy |tracy:], панель Database в Debug Bar включается автоматически. Она показывает все выполненные запросы, их параметры, время выполнения и место в коде, откуда они были вызваны.

[* db-panel.webp *]

Nette Database

Nette Database – мощный и изящный слой работы с базой данных для PHP, нацеленный на простоту и продуманные возможности. Он предлагает два способа работы с базой: Explorer для быстрой разработки приложений или SQL-подход для прямой работы с запросами.

SQL-подход

  • Безопасные параметризованные запросы
  • Точный контроль над структурой SQL-запроса
  • Когда нужно писать сложные запросы с продвинутыми функциями
  • Оптимизация производительности через конкретные функции SQL

Explorer

  • Быстрая разработка без написания SQL
  • Интуитивная работа со связями между таблицами
  • Выигрыш от автоматической оптимизации запросов
  • Подходит для быстрой и удобной работы с базой данных

Установка

Скачайте и установите библиотеку с помощью Composer:

composer require nette/database

Поддерживаемые базы данных

Nette Database поддерживает следующие базы данных:

Сервер базы данных Имя DSN Поддержка Explorer
MySQL (>= 5.1) mysql ДА
PostgreSQL (>= 9.0) pgsql ДА
SQLite 3 (>= 3.8) sqlite ДА
Oracle oci НЕТ
MS SQL (PDO_SQLSRV) sqlsrv ДА
MS SQL (PDO_DBLIB) mssql НЕТ
ODBC odbc НЕТ

Два подхода к работе с базой данных

Nette Database даёт вам выбор: вы можете либо писать SQL-запросы напрямую (SQL-подход), либо позволить порождать их автоматически (Explorer). Посмотрим, как оба подхода справляются с одними и теми же задачами:

SQL-подход – SQL-запросы

// Вставка записи
$database->query('INSERT INTO books', [
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// Получение записей: авторы книг
$result = $database->query('
	SELECT authors.*, COUNT(books.id) AS books_count
	FROM authors
	LEFT JOIN books ON authors.id = books.author_id
	WHERE authors.active = 1
	GROUP BY authors.id
');

// Вывод (не оптимально, порождает N дополнительных запросов)
foreach ($result as $author) {
	$books = $database->query('
		SELECT * FROM books
		WHERE author_id = ?
		ORDER BY published_at DESC
	', $author->id);

	echo "Author $author->name has written $author->books_count books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}

Подход Explorer – автоматическое порождение SQL

// Вставка записи
$database->table('books')->insert([
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// Получение записей: авторы книг
$authors = $database->table('authors')
	->where('active', 1);

// Вывод (автоматически порождает всего 2 оптимизированных запроса)
foreach ($authors as $author) {
	$books = $author->related('books')
		->order('published_at DESC');

	echo "Author $author->name has written {$books->count()} books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}

Подход Explorer порождает и оптимизирует SQL-запросы автоматически. В примере выше SQL-подход порождает N+1 запросов (один для авторов и затем по одному для книг каждого автора), тогда как Explorer автоматически оптимизирует запросы и выполняет только два: один для авторов и один для всех их книг.

Оба подхода можно свободно сочетать в приложении по мере надобности.

Соединение и настройка

Чтобы подключиться к базе данных, достаточно создать экземпляр класса Nette\Database\Connection:

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

Параметр $dsn (Data Source Name) такой же, какой использует PDO, например host=127.0.0.1;dbname=test. В случае неудачи он выбрасывает Nette\Database\ConnectionException.

Однако более удобный способ предлагает конфигурация приложения, где достаточно добавить секцию database. Так создаются нужные объекты, а также панель базы данных в панели Tracy.

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

Затем объект соединения можно получить как сервис из DI-контейнера, например:

class Model
{
	public function __construct(
		// или Nette\Database\Explorer
		private Nette\Database\Connection $database,
	) {
	}
}

Подробнее о конфигурации базы данных.

Создание Explorer вручную

Если вы не используете DI-контейнер Nette, вы можете создать экземпляр Nette\Database\Explorer вручную:

// соединение с базой данных
$connection = new Nette\Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// хранилище кеша, реализующее Nette\Caching\Storage, например:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// занимается рефлексией структуры базы данных
$structure = new Nette\Database\Structure($connection, $storage);
// задаёт правила отображения имён таблиц, столбцов и внешних ключей
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);

Управление соединением

При создании объекта Connection соединение устанавливается автоматически. Если вы хотите отложить соединение, используйте ленивый режим: включите его в конфигурации параметром lazy или так:

$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);

Для управления соединением служат методы connect(), disconnect() и reconnect().

  • connect() создаёт соединение, если его ещё нет, и может выбросить Nette\Database\ConnectionException.
  • disconnect() разрывает текущее соединение с базой данных.
  • reconnect() выполняет разрыв и последующее повторное подключение к базе данных. Этот метод тоже может выбросить Nette\Database\ConnectionException.

Кроме того, вы можете следить за событиями, связанными с соединением, через событие onConnect, которое представляет собой массив callback-функций, вызываемых после установки соединения с базой данных.

// выполняется после подключения к базе данных
$database->onConnect[] = function($database) {
	echo "Connected to the database";
};

Похожим образом работает событие onQuery: это массив callback-функций, вызываемых после каждого выполненного запроса (и когда запрос завершается ошибкой), что удобно для логирования или профилирования.

Панель отладки Tracy

Если вы используете Tracy, панель Database в Debug Bar включается автоматически. Она показывает все выполненные запросы, их параметры, время выполнения и место в коде, откуда они были вызваны.