Nette Database
**************
.[perex]
Nette Database to potężna i elegancka warstwa bazodanowa dla PHP, skupiona na prostocie i sprytnych funkcjach. Oferuje dwa sposoby pracy z bazą danych: [Explorer |explorer] do szybkiego tworzenia aplikacji albo [podejście SQL |SQL way] do bezpośredniej pracy z zapytaniami.
<div class="grid gap-3">
<div>
[Podejście SQL|sql-way]
=======================
- Bezpieczne, parametryzowane zapytania
- Precyzyjna kontrola nad strukturą zapytania SQL
- Gdy piszesz złożone zapytania z zaawansowanymi funkcjami
- Optymalizacja wydajności za pomocą konkretnych funkcji SQL
</div>
<div>
[Explorer |explorer]
====================
- Szybkie tworzenie bez pisania SQL
- Intuicyjna praca z relacjami między tabelami
- Korzyść z automatycznej optymalizacji zapytań
- Odpowiedni do szybkiej i wygodnej pracy z bazą danych
</div>
</div>
Instalacja
==========
Pobierz i zainstaluj bibliotekę za pomocą [Composera|best-practices:composer]:
```shell
composer require nette/database
```
Obsługiwane bazy danych
=======================
Nette Database obsługuje następujące bazy danych:
|* Serwer bazy danych |* Nazwa DSN |* Wsparcie Explorer
|-----------------------|--------------|-----------------------|
| MySQL (>= 5.1) | mysql | TAK |
| PostgreSQL (>= 9.0) | pgsql | TAK |
| SQLite 3 (>= 3.8) | sqlite | TAK |
| Oracle | oci | NIE |
| MS SQL (PDO_SQLSRV) | sqlsrv | TAK |
| MS SQL (PDO_DBLIB) | mssql | NIE |
| ODBC | odbc | NIE |
Dwa podejścia do pracy z bazą danych
====================================
Nette Database daje Ci wybór: możesz albo pisać zapytania SQL bezpośrednio (podejście SQL), albo pozwolić je generować automatycznie (Explorer). Zobaczmy, jak oba podejścia radzą sobie z tymi samymi zadaniami:
[Podejście SQL|sql-way] - zapytania SQL
```php
// Wstawienie rekordu
$database->query('INSERT INTO books', [
'author_id' => $authorId,
'title' => $bookData->title,
'published_at' => new DateTime,
]);
// Pobranie rekordów: autorzy książek
$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
');
// Wypisanie (nieoptymalne, generuje N dodatkowych zapytań)
foreach ($result as $author) {
$books = $database->query('
SELECT * FROM books
WHERE author_id = ?
ORDER BY published_at DESC
', $author->id);
echo "Autor $author->name napisał $author->books_count książek:\n";
foreach ($books as $book) {
echo "- $book->title\n";
}
}
```
[Podejście Explorer|explorer] - automatyczne generowanie SQL
```php
// Wstawienie rekordu
$database->table('books')->insert([
'author_id' => $authorId,
'title' => $bookData->title,
'published_at' => new DateTime,
]);
// Pobranie rekordów: autorzy książek
$authors = $database->table('authors')
->where('active', 1);
// Wypisanie (automatycznie generuje tylko 2 zoptymalizowane zapytania)
foreach ($authors as $author) {
$books = $author->related('books')
->order('published_at DESC');
echo "Autor $author->name napisał {$books->count()} książek:\n";
foreach ($books as $book) {
echo "- $book->title\n";
}
}
```
Podejście Explorer generuje i optymalizuje zapytania SQL automatycznie. W powyższym przykładzie podejście SQL generuje N+1 zapytań (jedno na autorów, a potem po jednym na książki każdego autora), podczas gdy Explorer automatycznie optymalizuje zapytania i wykonuje tylko dwa: jedno na autorów i jedno na wszystkie ich książki.
Oba podejścia można w aplikacji swobodnie łączyć według potrzeb.
Połączenie i konfiguracja
=========================
Żeby połączyć się z bazą danych, wystarczy utworzyć instancję klasy [api:Nette\Database\Connection]:
```php
$database = new Nette\Database\Connection($dsn, $user, $password);
```
Parametr `$dsn` (Data Source Name) jest taki sam, jak [używany przez PDO |https://www.php.net/manual/en/pdo.construct.php#refsect1-pdo.construct-parameters], np. `host=127.0.0.1;dbname=test`. W razie niepowodzenia rzuca `Nette\Database\ConnectionException`.
Wygodniejszą metodę oferuje jednak [konfiguracja aplikacji |configuration], gdzie wystarczy dodać sekcję `database`. Utworzy to potrzebne obiekty, a także panel bazy danych w pasku [Tracy |tracy:].
```neon
database:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: password
```
Obiekt połączenia można potem [uzyskać jako usługę z kontenera DI |dependency-injection:passing-dependencies], np.:
```php
class Model
{
public function __construct(
// albo Nette\Database\Explorer
private Nette\Database\Connection $database,
) {
}
}
```
Więcej informacji o [konfiguracji bazy danych|configuration].
Ręczne utworzenie Explorera
---------------------------
Jeśli nie używasz kontenera DI Nette, możesz utworzyć instancję `Nette\Database\Explorer` ręcznie:
```php
// połączenie z bazą danych
$connection = new Nette\Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// magazyn cache, implementuje Nette\Caching\Storage, np.:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// zajmuje się refleksją struktury bazy danych
$structure = new Nette\Database\Structure($connection, $storage);
// definiuje reguły mapowania nazw tabel, kolumn i kluczy obcych
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);
```
Zarządzanie połączeniem
=======================
Przy utworzeniu obiektu `Connection` połączenie nawiązywane jest automatycznie. Jeśli chcesz połączenie opóźnić, użyj trybu lazy: włączysz go w [konfiguracji|configuration] ustawieniem `lazy` albo tak:
```php
$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);
```
Do zarządzania połączeniem służą metody `connect()`, `disconnect()` i `reconnect()`.
- `connect()` tworzy połączenie, jeśli jeszcze nie istnieje, i może rzucić `Nette\Database\ConnectionException`.
- `disconnect()` rozłącza bieżące połączenie z bazą danych.
- `reconnect()` wykonuje rozłączenie i ponowne połączenie z bazą danych. Ta metoda również może rzucić `Nette\Database\ConnectionException`.
Poza tym możesz monitorować zdarzenia związane z połączeniem za pomocą zdarzenia `onConnect`, które jest tablicą callbacków wywoływanych po nawiązaniu połączenia z bazą danych.
```php
// wykonuje się po połączeniu z bazą danych
$database->onConnect[] = function($database) {
echo "Połączono z bazą danych";
};
```
Podobnie działa zdarzenie `onQuery`: jest to tablica callbacków wywoływanych po każdym wykonanym zapytaniu (a także wtedy, gdy zapytanie się nie powiedzie), przydatna do logowania albo profilowania.
Tracy Debug Bar
===============
Jeśli używasz [Tracy |tracy:], panel Database w Debug Barze aktywuje się automatycznie. Wyświetla wszystkie wykonane zapytania, ich parametry, czas wykonania i miejsce w kodzie, z którego zostały wywołane.
[* db-panel.webp *]
Nette Database
Nette Database to potężna i elegancka warstwa bazodanowa dla PHP, skupiona na prostocie i sprytnych
funkcjach. Oferuje dwa sposoby pracy z bazą danych: Explorer do
szybkiego tworzenia aplikacji albo podejście SQL do bezpośredniej pracy
z zapytaniami.
Odpowiedni do szybkiej i wygodnej pracy z bazą danych
Instalacja
Pobierz i zainstaluj bibliotekę za pomocą Composera:
composer require nette/database
Obsługiwane bazy danych
Nette Database obsługuje następujące bazy danych:
Serwer bazy danych
Nazwa DSN
Wsparcie Explorer
MySQL (>= 5.1)
mysql
TAK
PostgreSQL (>= 9.0)
pgsql
TAK
SQLite 3 (>= 3.8)
sqlite
TAK
Oracle
oci
NIE
MS SQL (PDO_SQLSRV)
sqlsrv
TAK
MS SQL (PDO_DBLIB)
mssql
NIE
ODBC
odbc
NIE
Dwa podejścia do pracy z bazą danych
Nette Database daje Ci wybór: możesz albo pisać zapytania SQL bezpośrednio (podejście SQL), albo pozwolić je generować
automatycznie (Explorer). Zobaczmy, jak oba podejścia radzą sobie z tymi samymi zadaniami:
// Wstawienie rekordu
$database->query('INSERT INTO books', [
'author_id' => $authorId,
'title' => $bookData->title,
'published_at' => new DateTime,
]);
// Pobranie rekordów: autorzy książek
$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
');
// Wypisanie (nieoptymalne, generuje N dodatkowych zapytań)
foreach ($result as $author) {
$books = $database->query('
SELECT * FROM books
WHERE author_id = ?
ORDER BY published_at DESC
', $author->id);
echo "Autor $author->name napisał $author->books_count książek:\n";
foreach ($books as $book) {
echo "- $book->title\n";
}
}
// Wstawienie rekordu
$database->table('books')->insert([
'author_id' => $authorId,
'title' => $bookData->title,
'published_at' => new DateTime,
]);
// Pobranie rekordów: autorzy książek
$authors = $database->table('authors')
->where('active', 1);
// Wypisanie (automatycznie generuje tylko 2 zoptymalizowane zapytania)
foreach ($authors as $author) {
$books = $author->related('books')
->order('published_at DESC');
echo "Autor $author->name napisał {$books->count()} książek:\n";
foreach ($books as $book) {
echo "- $book->title\n";
}
}
Podejście Explorer generuje i optymalizuje zapytania SQL automatycznie. W powyższym przykładzie podejście SQL generuje
N+1 zapytań (jedno na autorów, a potem po jednym na książki każdego autora), podczas gdy Explorer automatycznie optymalizuje
zapytania i wykonuje tylko dwa: jedno na autorów i jedno na wszystkie ich książki.
Oba podejścia można w aplikacji swobodnie łączyć według potrzeb.
$database = new Nette\Database\Connection($dsn, $user, $password);
Parametr $dsn (Data Source Name) jest taki sam, jak używany przez PDO, np.
host=127.0.0.1;dbname=test. W razie niepowodzenia rzuca Nette\Database\ConnectionException.
Wygodniejszą metodę oferuje jednak konfiguracja aplikacji,
gdzie wystarczy dodać sekcję database. Utworzy to potrzebne obiekty, a także panel bazy danych w pasku Tracy.
Jeśli nie używasz kontenera DI Nette, możesz utworzyć instancję Nette\Database\Explorer ręcznie:
// połączenie z bazą danych
$connection = new Nette\Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// magazyn cache, implementuje Nette\Caching\Storage, np.:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// zajmuje się refleksją struktury bazy danych
$structure = new Nette\Database\Structure($connection, $storage);
// definiuje reguły mapowania nazw tabel, kolumn i kluczy obcych
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);
Zarządzanie połączeniem
Przy utworzeniu obiektu Connection połączenie nawiązywane jest automatycznie. Jeśli chcesz połączenie
opóźnić, użyj trybu lazy: włączysz go w konfiguracji
ustawieniem lazy albo tak:
$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);
Do zarządzania połączeniem służą metody connect(), disconnect() i reconnect().
connect() tworzy połączenie, jeśli jeszcze nie istnieje, i może rzucić
Nette\Database\ConnectionException.
disconnect() rozłącza bieżące połączenie z bazą danych.
reconnect() wykonuje rozłączenie i ponowne połączenie z bazą danych. Ta metoda również może rzucić
Nette\Database\ConnectionException.
Poza tym możesz monitorować zdarzenia związane z połączeniem za pomocą zdarzenia onConnect, które jest
tablicą callbacków wywoływanych po nawiązaniu połączenia z bazą danych.
// wykonuje się po połączeniu z bazą danych
$database->onConnect[] = function($database) {
echo "Połączono z bazą danych";
};
Podobnie działa zdarzenie onQuery: jest to tablica callbacków wywoływanych po każdym wykonanym zapytaniu (a
także wtedy, gdy zapytanie się nie powiedzie), przydatna do logowania albo profilowania.
Tracy Debug Bar
Jeśli używasz Tracy, panel Database w Debug Barze aktywuje się automatycznie.
Wyświetla wszystkie wykonane zapytania, ich parametry, czas wykonania i miejsce w kodzie, z którego zostały wywołane.