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:

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";
}
Version: 4.0