Adatbázis mag
A Nette Database Core az adatbázis absztrakciós rétege, és alapvető funkciókat biztosít.
Telepítés
Töltse le és telepítse a csomagot a Composer segítségével:
composer require nette/database
Csatlakozás és konfiguráció
Az adatbázishoz való csatlakozáshoz egyszerűen hozzon létre egy példányt a Nette\Database\Connection osztályból:
$database = new Nette\Database\Connection($dsn, $user, $password);
A $dsn
(adatforrás neve) paraméter ugyanaz, mint amit a PDO használ,
pl. host=127.0.0.1;dbname=test
. Sikertelenség esetén a Nette\Database\ConnectionException
dobja.
Azonban egy kifinomultabb módot kínál az alkalmazás
konfigurálása. Hozzáadunk egy database
szakaszt, és ez létrehozza a szükséges objektumokat és egy
adatbázis panelt a Tracy sávban.
database:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: password
A kapcsolati objektumot például egy DI konténertől kapjuk szolgáltatásként:
class Model
{
// Nette\Database\Explorer átadása az adatbázis-kutató réteggel való együttműködéshez.
public function __construct(
private Nette\Database\Connection $database,
) {
}
}
További információért lásd: adatbázis-konfiguráció.
Lekérdezések
Az adatbázis lekérdezéséhez használja a query()
módszert, amely ResultSet-et ad vissza.
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
}
echo $result->getRowCount(); // visszaadja a sorok számát, ha ismert.
A ResultSet
felett csak egyszer lehet iterálni, ha többször kell iterálni, akkor az eredményt a
fetchAll()
metódussal kell tömbre konvertálni.
A lekérdezéshez könnyen adhatunk paramétereket, figyeljük meg a kérdőjelet:
$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 egy tömb
FIGYELMEZTETÉS, soha ne fűzzön össze karakterláncokat az SQL injekciós sebezhetőség elkerülése érdekében!
$db->query('SELECT * FROM users WHERE name = ' . $name); // WRONG!!!
Sikertelenség esetén a query()
a Nette\Database\DriverException
vagy valamelyik
leszármazottját dobja:
- ConstraintViolationException – bármelyik megkötés megsértése.
- ForeignKeyConstraintViolationException – érvénytelen idegen kulcs.
- NotNullConstraintViolationException – a NOT NULL feltétel megsértése.
- UniqueConstraintViolationException – az egyedi index konfliktusa.
A query()
mellett vannak más hasznos módszerek is:
// visszaadja az id => name asszociatív tömböt
$pairs = $database->fetchPairs('SELECT id, name FROM users');
// visszaadja az összes sort tömbként
$rows = $database->fetchAll('SELECT * FROM users');
// egyetlen sort ad vissza
$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);
// egyetlen mezőt ad vissza
$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);
Sikertelenség esetén ezek a módszerek mindegyike dobja a Nette\Database\DriverException.
Beszúrás, frissítés és törlés
Az SQL-lekérdezésbe beszúrandó paraméter lehet a tömb is (ebben az esetben kihagyható a vadkártyás ?
),
which may be useful for the INSERT
utasítás:
$database->query('INSERT INTO users ?', [ // itt kihagyható a kérdőjel
'name' => $name,
'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
$id = $database->getInsertId(); // visszaadja a beillesztett sor automatikus inkrementálását.
$id = $database->getInsertId($sequence); // vagy a sor értéke
Többszörös beszúrás:
$database->query('INSERT INTO users', [
[
'name' => 'Jim',
'year' => 1978,
], [
'name' => 'Jack',
'year' => 1987,
],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
DateTime objektumokat vagy felsorolásokat is átadhatunk:
$database->query('INSERT INTO users', [
'name' => $name,
'created' => new DateTime, // vagy $database::literal('NOW()')
'avatar' => fopen('image.gif', 'r'), // a fájl tartalmának beszúrása.
'status' => State::New, // enum állapot
]);
Sorok frissítése:
$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(); // visszaadja az érintett sorok számát
UPDATE esetén a +=
és a -=
operátorokat használhatjuk:
$database->query('UPDATE users SET', [
'age+=' => 1, // note +=
], 'WHERE id = ?', $id);
// UPDATE users SET `age` = `age` + 1
Törlés:
$result = $database->query('DELETE FROM users WHERE id = ?', $id);
echo $result->getRowCount(); // visszaadja az érintett sorok számát.
Speciális lekérdezések
Beszúrás vagy frissítés, ha már létezik:
$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
Vegye figyelembe, hogy a Nette Database felismeri az SQL-kontextust, amelybe a tömbparamétert beillesztik, és ennek
megfelelően építi fel az SQL-kódot. Így az első tömbből a (id, name, year) VALUES (123, 'Jim', 1978)
, míg a
másodikból a name = 'Jim', year = 1978
címet alakítja át.
A rendezést is leírhatjuk tömb használatával, a kulcsok az oszlopok nevei, az értékek pedig boolean értékek, amelyek meghatározzák, hogy növekvő sorrendbe rendezzük-e:
$database->query('SELECT id FROM author ORDER BY', [
'id' => true, // felfelé növekvő
'name' => false, // csökkenő
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC
Ha a felismerés nem működött, akkor az összeállítás formáját megadhatjuk a ?
jokerjelzéssel, amelyet
egy utalás követ. Ezek a tippek támogatottak:
?values | (kulcs1, kulcs2, …) VALUES (érték1, érték2, …) |
?set | kulcs1 = érték1, kulcs2 = érték2, … |
?and | kulcs1 = érték1 ÉS kulcs2 = érték2 … |
?or | kulcs1 = érték1 VAGY kulcs2 = érték2 … |
?order | kulcs1 ASC, kulcs2 DESC |
A WHERE záradék a ?and
operátort használja, így a feltételeket a AND
kapcsolja össze:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
Ami könnyen megváltoztatható OR
-ra a ?or
joker használatával:
$result = $database->query('SELECT * FROM users WHERE ?or', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978
Használhatunk operátorokat a feltételekben:
$result = $database->query('SELECT * FROM users WHERE', [
'name <>' => $name,
'year >' => $year,
]);
// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978
És felsorolásokat is:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => ['Jim', 'Jack'],
'role NOT IN' => ['admin', 'owner'], // enumeration + operator NOT IN
]);
// SELECT * FROM users WHERE
// `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')
Az úgynevezett SQL literál segítségével egy darab egyéni SQL-kódot is beilleszthetünk:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
Alternatívaként:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
SQL literál is lehet a paraméterei:
$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)
Ennek köszönhetően érdekes kombinációkat hozhatunk létre:
$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')
Változó neve
Van egy ?name
joker, amelyet akkor használhat, ha a táblázat neve vagy az oszlop neve egy változó.
(Vigyázat, ne engedje, hogy a felhasználó manipulálja egy ilyen változó tartalmát):
$table = 'blog.users';
$column = 'name';
$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
Tranzakciók
A tranzakciók kezelésére három módszer áll rendelkezésre:
$database->beginTransaction();
$database->commit();
$database->rollback();
A transaction()
módszer elegáns megoldást kínál. Átadjuk a tranzakcióban végrehajtott visszahívást. Ha a
végrehajtás során kivételt dobunk, a tranzakciót eldobjuk, ha minden rendben megy, a tranzakciót lekötjük.
$id = $database->transaction(function ($database) {
$database->query('DELETE FROM ...');
$database->query('INSERT INTO ...');
// ...
return $database->getInsertId();
});
Mint látható, a transaction()
metódus a callback visszatérési értékét adja vissza.
A tranzakció() is beágyazható, ami egyszerűsíti a független tárolók megvalósítását.
Reflection
A Nette Database a Nette\Database\Reflection osztályon keresztül eszközöket biztosít az adatbázis szerkezetének áttekintéséhez. Ez az osztály lehetővé teszi a táblákról, oszlopokról, indexekről és idegen kulcsokról szóló információk lekérdezését. A reflexiót használhatja sémák létrehozására, adatbázisokkal dolgozó rugalmas alkalmazások létrehozására vagy általános adatbázis-eszközök készítésére.
A reflection objektumot egy adatbázis-kapcsolati példányból szerezheti meg:
$reflection = $database->getReflection();
Munka táblázatokkal
A tükrözés segítségével az adatbázis összes tábláján végig tudsz menni:
// Az összes táblázat nevének felsorolása
foreach ($reflection->tables as $tableName => $table) {
echo $tableName . "\n";
}
// Ellenőrizze, hogy létezik-e táblázat
if ($reflection->hasTable('users')) {
echo "The 'users' table exists";
}
// Egy adott táblázat lekérdezése
$table = $reflection->getTable('users');
Oszlop információk
Az egyes táblák oszlopairól részletes információkat kaphat:
// Ismétlés az összes oszlopon
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";
}
// Egy adott oszlop kinyerése
$idColumn = $table->getColumn('id');
Indexek és elsődleges kulcsok
A Reflection az indexekről és az elsődleges kulcsokról nyújt információt:
$listColumnNames = fn(array $columns) => implode(', ', array_map(fn($col) => $col->name, $columns));
// Az összes index felsorolása
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";
}
// Az elsődleges kulcs kinyerése
if ($table->primaryKey) {
echo "Primary key: " . $listColumnNames($table->primaryKey->columns) . "\n";
}
Idegen kulcsok
Az idegen kulcsokról is tájékozódhat:
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";
}