Datenbank-Kern
Nette Database Core ist eine Datenbankabstraktionsschicht und bietet Kernfunktionen.
Installation
Laden Sie das Paket herunter und installieren Sie es mit Composer:
composer require nette/database
Verbindung und Konfiguration
Um eine Verbindung zur Datenbank herzustellen, erstellen Sie einfach eine Instanz der Klasse Nette\Database\Connection:
$database = new Nette\Database\Connection($dsn, $user, $password);
Der Parameter $dsn
(Name der Datenquelle) ist derselbe, der auch von PDO verwendet
wird, z. B. host=127.0.0.1;dbname=test
. Im Falle eines Fehlers wird
Nette\Database\ConnectionException
ausgelöst.
Eine anspruchsvollere Methode bietet jedoch die Anwendungskonfiguration. Wir fügen einen Abschnitt
database
hinzu, der die erforderlichen Objekte und ein Datenbank-Panel in der Tracy-Leiste erstellt.
database:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: password
Das Verbindungsobjekt erhalten wir z. B. als Dienst von einem DI-Container:
class Model
{
// übergibt Nette\Database\Explorer, um mit der Datenbank-Explorer-Schicht zu arbeiten
public function __construct(
private Nette\Database\Connection $database,
) {
}
}
Weitere Informationen finden Sie unter Datenbankkonfiguration.
Abfragen
Für Datenbankabfragen verwenden Sie die Methode query()
, die ein ResultSet zurückgibt.
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
}
echo $result->getRowCount(); // gibt die Anzahl der Zeilen zurück, wenn diese bekannt ist
Über die ResultSet
ist es möglich, nur einmal zu iterieren, wenn wir mehrere Male iterieren
müssen, ist es notwendig, das Ergebnis in das Array über fetchAll()
Methode zu konvertieren.
Sie können der Abfrage einfach Parameter hinzufügen, beachten Sie das Fragezeichen:
$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 ist array
WARNUNG, verketten Sie niemals Zeichenketten, um eine SQL-Injection-Schwachstelle zu vermeiden!
$db->query('SELECT * FROM users WHERE name = ' . $name); // WRONG!!!
Im Falle eines Fehlers löst query()
entweder Nette\Database\DriverException
oder einen seiner
Abkömmlinge aus:
- ConstraintViolationException – Verletzung einer Beschränkung
- ForeignKeyConstraintViolationException – ungültiger Fremdschlüssel
- NotNullConstraintViolationException – Verstoß gegen die NOT NULL-Bedingung
- UniqueConstraintViolationException – Konflikt eines eindeutigen Index
Zusätzlich zu query()
gibt es weitere nützliche Methoden:
// gibt das assoziative Array id => name zurück
$pairs = $database->fetchPairs('SELECT id, name FROM users');
// gibt alle Zeilen als Array zurück
$rows = $database->fetchAll('SELECT * FROM users');
// gibt einzelne Zeile zurück
$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);
// gibt einzelnes Feld zurück
$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);
Im Falle eines Fehlers werfen alle diese Methoden Nette\Database\DriverException.
Einfügen, Aktualisieren & Löschen
Der Parameter, den wir in die SQL-Abfrage einfügen, kann auch das Array sein (in diesem Fall ist es möglich, die
Platzhalteranweisung ?
), which may be useful for the INSERT
zu überspringen:
$database->query('INSERT INTO users ?', [ // hier kann das Fragezeichen weggelassen werden
'name' => $name,
'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
$id = $database->getInsertId(); // gibt das automatische Inkrement der eingefügten Zeile zurück
$id = $database->getInsertId($sequence); // oder Sequenzwert
Mehrfaches Einfügen:
$database->query('INSERT INTO users', [
[
'name' => 'Jim',
'year' => 1978,
], [
'name' => 'Jack',
'year' => 1987,
],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
Wir können auch Dateien, DateTime-Objekte oder Aufzählungen übergeben:
$database->query('INSERT INTO users', [
'name' => $name,
'created' => new DateTime, // oder $datenbank::literal('NOW()')
'avatar' => fopen('image.gif', 'r'), // fügt Dateiinhalt ein
'status' => State::New, // enum State
]);
Zeilen aktualisieren:
$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(); // gibt die Anzahl der betroffenen Zeilen zurück
Für UPDATE können wir die Operatoren +=
und -=
verwenden:
$database->query('UPDATE users SET', [
'age+=' => 1, // note +=
], 'WHERE id = ?', $id);
// UPDATE users SET `age` = `age` + 1
Löschen:
$result = $database->query('DELETE FROM users WHERE id = ?', $id);
echo $result->getRowCount(); // gibt die Anzahl der betroffenen Zeilen zurück
Erweiterte Abfragen
Einfügen oder Aktualisieren, wenn sie bereits existiert:
$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
Beachten Sie, dass Nette Database den SQL-Kontext erkennt, in den der Array-Parameter eingefügt wird, und den SQL-Code
entsprechend aufbaut. So erzeugt er aus dem ersten Array (id, name, year) VALUES (123, 'Jim', 1978)
, während der
zweite in name = 'Jim', year = 1978
umgewandelt wird.
Wir können auch die Sortierung mit Hilfe eines Arrays beschreiben, wobei die Schlüssel Spaltennamen und die Werte Boolesche Werte sind, die festlegen, ob in aufsteigender Reihenfolge sortiert werden soll:
$database->query('SELECT id FROM author ORDER BY', [
'id' => true, // aufsteigend
'name' => false, // absteigend
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC
Wenn die Erkennung nicht funktioniert hat, können Sie die Form der Baugruppe mit einem Platzhalter ?
gefolgt von
einem Hinweis angeben. Diese Hinweise werden unterstützt:
?values | (key1, key2, …) VALUES (value1, value2, …) |
?set | key1 = wert1, key2 = wert2, … |
?and | Schlüssel1 = Wert1 AND Schlüssel2 = Wert2 … |
?or | key1 = value1 OR key2 = value2 … |
?order | key1 ASC, key2 DESC |
Die WHERE-Klausel verwendet den Operator ?and
, so dass die Bedingungen durch AND
verknüpft
werden:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
Das kann leicht in OR
geändert werden, indem man den Platzhalter ?or
verwendet:
$result = $database->query('SELECT * FROM users WHERE ?or', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978
Wir können Operatoren in Bedingungen verwenden:
$result = $database->query('SELECT * FROM users WHERE', [
'name <>' => $name,
'year >' => $year,
]);
// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978
Und auch Aufzählungen:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => ['Jim', 'Jack'],
'role NOT IN' => ['admin', 'owner'], // Aufzählung + Operator NOT IN
]);
// SELECT * FROM users WHERE
// `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')
Wir können auch ein Stück benutzerdefinierten SQL-Code einfügen, indem wir das so genannte SQL-Literal verwenden:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
Alternativ dazu:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
SQL-Literal kann auch seine Parameter haben:
$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)
Dadurch können wir interessante Kombinationen erstellen:
$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')
Variable Name
Es gibt einen ?name
Platzhalter, den Sie verwenden, wenn der Tabellen- oder Spaltenname eine Variable ist.
(Achtung, erlauben Sie dem Benutzer nicht, den Inhalt einer solchen Variablen zu manipulieren):
$table = 'blog.users';
$column = 'name';
$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
Vorgänge
Es gibt drei Methoden für den Umgang mit Transaktionen:
$database->beginTransaction();
$database->commit();
$database->rollback();
Einen eleganten Weg bietet die Methode transaction()
. Sie übergeben den Callback, der in der Transaktion
ausgeführt wird. Wenn während der Ausführung eine Ausnahme auftritt, wird die Transaktion abgebrochen, wenn alles gut geht,
wird die Transaktion bestätigt.
$id = $database->transaction(function ($database) {
$database->query('DELETE FROM ...');
$database->query('INSERT INTO ...');
// ...
return $database->getInsertId();
});
Wie Sie sehen können, gibt die Methode transaction()
den Rückgabewert des Callbacks zurück.
Die Transaktion() kann auch verschachtelt werden, was die Implementierung unabhängiger Repositories vereinfacht.
Reflexion
Nette Database bietet mit der Klasse Nette\Database\Reflection Werkzeuge für die Introspektion der Datenbankstruktur. Mit dieser Klasse können Sie Informationen über Tabellen, Spalten, Indizes und Fremdschlüssel abrufen. Sie können Reflection verwenden, um Schemata zu generieren, flexible Anwendungen zu erstellen, die mit Datenbanken arbeiten, oder allgemeine Datenbank-Tools zu entwickeln.
Sie können ein Reflection-Objekt von einer Datenbankverbindungsinstanz abrufen:
$reflection = $database->getReflection();
Arbeiten mit Tabellen
Mit Hilfe von Reflection können Sie über alle Tabellen in der Datenbank iterieren:
// Auflisten der Namen aller Tabellen
foreach ($reflection->tables as $tableName => $table) {
echo $tableName . "\n";
}
// Prüfen, ob eine Tabelle existiert
if ($reflection->hasTable('users')) {
echo "The 'users' table exists";
}
// Abrufen einer bestimmten Tabelle
$table = $reflection->getTable('users');
Informationen zur Säule
Für jede Tabelle können Sie detaillierte Informationen über ihre Spalten erhalten:
// Iterieren über alle Spalten
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";
}
// Eine bestimmte Spalte abrufen
$idColumn = $table->getColumn('id');
Indizes und Primärschlüssel
Reflection bietet Informationen zu Indizes und Primärschlüsseln:
$listColumnNames = fn(array $columns) => implode(', ', array_map(fn($col) => $col->name, $columns));
// Alle Indizes auflisten
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";
}
// Abrufen des Primärschlüssels
if ($table->primaryKey) {
echo "Primary key: " . $listColumnNames($table->primaryKey->columns) . "\n";
}
Ausländische Schlüssel
Sie können auch Informationen über Fremdschlüssel erhalten:
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";
}