Jedro zbirke podatkov
Nette Database Core je abstraktni sloj podatkovne zbirke in zagotavlja osnovne funkcionalnosti.
Namestitev
Prenesite in namestite paket s programom Composer:
composer require nette/database
Priključitev in konfiguracija
Če se želite povezati s podatkovno zbirko, preprosto ustvarite primerek razreda Nette\Database\Connection:
$database = new Nette\Database\Connection($dsn, $user, $password);
Parameter $dsn
(ime vira podatkov) je enak tistemu, ki ga uporablja PDO,
npr. host=127.0.0.1;dbname=test
. V primeru neuspeha se vrže Nette\Database\ConnectionException
.
Vendar pa bolj prefinjen način ponuja konfiguracija aplikacije. Dodamo razdelek
database
in ta ustvari zahtevane predmete in ploščo s podatkovno bazo v vrstici Tracy.
database:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: password
Objekt povezave prejmemo kot storitev iz vsebnika DI, na primer:
class Model
{
// predajte Nette\Database\Explorer za delo s slojem Raziskovalec podatkovne baze
public function __construct(
private Nette\Database\Connection $database,
) {
}
}
Za več informacij glejte Konfiguracija podatkovne zbirke.
Poizvedbe
Za poizvedovanje po zbirki podatkov uporabite metodo query()
, ki vrne nabor rezultatov (ResultSet).
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
}
echo $result->getRowCount(); // vrne število vrstic, če je znano
Nad ResultSet
je mogoče iterirati samo enkrat, če pa moramo iterirati večkrat, je treba rezultat
pretvoriti v polje prek metode fetchAll()
.
Poizvedbi lahko preprosto dodate parametre, pri čemer upoštevajte vprašalni znak:
$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 je polje
Opozorilo, nikoli ne združujte nizov, da se izognete ranljivosti vbrizgavanja SQL!
$db->query('SELECT * FROM users WHERE name = ' . $name); // WRONG!!!
V primeru neuspeha query()
vrže Nette\Database\DriverException
ali enega od njegovih potomcev:
- ConstraintViolationException – kršitev katere koli omejitve
- ForeignKeyConstraintViolationException – neveljaven tuj ključ
- NotNullConstraintViolationException – kršitev pogoja NOT NULL
- UniqueConstraintViolationException – konflikt edinstvenega indeksa
Poleg query()
so na voljo še druge uporabne metode:
// vrne asociativno polje id => ime
$pairs = $database->fetchPairs('SELECT id, name FROM users');
// vrne vse vrstice kot polje
$rows = $database->fetchAll('SELECT * FROM users');
// vrne posamezno vrstico
$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);
// vrne posamezno polje
$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);
V primeru neuspeha vse te metode zavržejo Nette\Database\DriverException.
Vstavljanje, posodabljanje in brisanje
Parameter, ki ga vstavimo v poizvedbo SQL, je lahko tudi polje (v tem primeru je mogoče preskočiti nadomestno izjavo
?
), which may be useful for the INSERT
:
$database->query('INSERT INTO users ?', [ // tukaj se lahko izpusti vprašalno znamenje
'name' => $name,
'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
$id = $database->getInsertId(); // vrne samodejno povečanje vstavljene vrstice
$id = $database->getInsertId($sequence); // ali vrednost zaporedja
Večkratno vstavljanje:
$database->query('INSERT INTO users', [
[
'name' => 'Jim',
'year' => 1978,
], [
'name' => 'Jack',
'year' => 1987,
],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
Vstavljamo lahko tudi datoteke, objekte DateTime ali naštevanja:
$database->query('INSERT INTO users', [
'name' => $name,
'created' => new DateTime, // ali $databaza::literal('NOW()')
'avatar' => fopen('image.gif', 'r'), // vstavi vsebino datoteke
'status' => State::New, // enum State
]);
Posodabljanje vrstic:
$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(); // vrne število prizadetih vrstic
Za UPDATE lahko uporabimo operatorja +=
in -=
:
$database->query('UPDATE users SET', [
'age+=' => 1, // Opomba +=
], 'WHERE id = ?', $id);
// UPDATE users SET `age` = `age` + 1
Brisanje:
$result = $database->query('DELETE FROM users WHERE id = ?', $id);
echo $result->getRowCount(); // vrne število prizadetih vrstic
Napredne poizvedbe
Vstavljanje ali posodabljanje, če že obstaja:
$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
Upoštevajte, da podatkovna baza Nette prepozna kontekst SQL, v katerem je vstavljen parameter polja, in v skladu s tem
sestavi kodo SQL. Tako iz prvega polja ustvari (id, name, year) VALUES (123, 'Jim', 1978)
, medtem ko drugo pretvori v
name = 'Jim', year = 1978
.
Sortiranje lahko opišemo tudi z uporabo polja, pri čemer so ključi imena stolpcev, vrednosti pa logične vrednosti, ki določajo, ali naj se sortira v naraščajočem vrstnem redu:
$database->query('SELECT id FROM author ORDER BY', [
'id' => true, // naraščajoče
'name' => false, // padajoče
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC
Če zaznavanje ni delovalo, lahko določite obliko sklopa z nadomestnim znakom ?
, ki mu sledi namig. Ti namigi so
podprti:
(key1, key2, …) VALUES (value1, value2, …) | |
?set | key1 = value1, key2 = value2, … |
?and | key1 = value1 AND key2 = value2 … |
?or | key1 = value1 OR key2 = value2 … |
?order | key1 ASC, key2 DESC |
V stavku WHERE je uporabljen operator ?and
, zato so pogoji povezani s AND
:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
To lahko z uporabo nadomestnega znaka ?or
preprosto spremenite v OR
:
$result = $database->query('SELECT * FROM users WHERE ?or', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978
V pogojih lahko uporabljamo operatorje:
$result = $database->query('SELECT * FROM users WHERE', [
'name <>' => $name,
'year >' => $year,
]);
// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978
in tudi naštevanja:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => ['Jim', 'Jack'],
'role NOT IN' => ['admin', 'owner'], // naštevanje + operator NOT IN
]);
// SELECT * FROM users WHERE
// `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')
Vključimo lahko tudi del kode SQL po meri z uporabo tako imenovanega literala SQL:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
Druga možnost:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
SQL literal ima lahko tudi svoje parametre:
$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)
Zaradi tega lahko ustvarimo zanimive kombinacije:
$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')
Ime spremenljivke
Obstaja nadomestni znak ?name
, ki ga uporabite, če je ime tabele ali stolpca spremenljivka. (Pazite, da
uporabniku ne dovolite, da bi manipuliral z vsebino take spremenljivke):
$table = 'blog.users';
$column = 'name';
$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
Transakcije
Obstajajo trije načini obravnavanja transakcij:
$database->beginTransaction();
$database->commit();
$database->rollback();
Eleganten način ponuja metoda transaction()
. Predate povratni klic, ki se izvede v transakciji. Če se med
izvajanjem vrže izjema, se transakcija opusti, če je vse v redu, se transakcija izvede.
$id = $database->transaction(function ($database) {
$database->query('DELETE FROM ...');
$database->query('INSERT INTO ...');
// ...
return $database->getInsertId();
});
Kot lahko vidite, metoda transaction()
vrne povratno vrednost povratnega klica.
Metoda transaction() je lahko tudi vgnezdena, kar poenostavi izvajanje neodvisnih skladišč.
Razmislek
Podatkovna baza Nette zagotavlja orodja za introspekcijo strukture podatkovne baze prek razreda Nette\Database\Reflection. Ta razred omogoča pridobivanje informacij o tabelah, stolpcih, indeksih in tujih ključih. Refleksijo lahko uporabite za izdelavo shem, ustvarjanje prilagodljivih aplikacij, ki delajo s podatkovnimi zbirkami, ali za izdelavo splošnih orodij za podatkovne zbirke.
Objekt refleksije lahko pridobite iz primerka povezave s podatkovno bazo:
$reflection = $database->getReflection();
Delo s tabelami
Z uporabo refleksije lahko iterirate po vseh tabelah v zbirki podatkov:
// Seznam imen vseh tabel
foreach ($reflection->tables as $tableName => $table) {
echo $tableName . "\n";
}
// Preverite, ali tabela obstaja
if ($reflection->hasTable('users')) {
echo "The 'users' table exists";
}
// iskanje določene tabele
$table = $reflection->getTable('users');
Informacije o stolpcih
Za vsako tabelo lahko dobite podrobne informacije o njenih stolpcih:
// Iterirajte po vseh stolpcih
foreach ($table->columns as $column) {
echo "Column: " . $column->name . "\n";
echo "Type: " . $column->nativeType . "\n";
echo "Nullable: " . ($column->nullable ? 'Yes': 'No') . "\n";
echo "Default value: " . ($column->default ?? 'None') . "\n";
echo "Primary key: " . ($column->primary ? 'Yes': 'No') . "\n";
echo "Auto-increment: " . ($column->autoIncrement ? 'Yes': 'No') . "\n";
}
// Pridobite določen stolpec
$idColumn = $table->getColumn('id');
Indeksi in primarni ključi
Odsev zagotavlja informacije o indeksih in primarnih ključih:
$listColumnNames = fn(array $columns) => implode(', ', array_map(fn($col) => $col->name, $columns));
// Seznam vseh indeksov
foreach ($table->indexes as $index) {
echo "Index: " . ($index->name ?? 'Unnamed') . "\n";
echo "Columns: " . $listColumnNames($index->columns) . "\n";
echo "Unique: " . ($index->unique ? 'Yes': 'No') . "\n";
echo "Primary key: " . ($index->primary ? 'Yes': 'No') . "\n";
}
// Pridobite primarni ključ
if ($table->primaryKey) {
echo "Primary key: " . $listColumnNames($table->primaryKey->columns) . "\n";
}
Tuji ključi
Pridobite lahko tudi informacije o tujih ključih:
foreach ($table->foreignKeys as $fk) {
echo "Foreign key: " . ($fk->name ?? 'Unnamed') . "\n";
echo "Local columns: " . $listColumnNames($fk->localColumns) . "\n";
echo "References table: {$fk->foreignTable->name}\n";
echo "References columns: " . $listColumnNames($fk->foreignColumns) . "\n";
}