Πυρήνας βάσης δεδομένων
Το Nette Database Core είναι ένα επίπεδο αφαίρεσης βάσεων δεδομένων και παρέχει βασική λειτουργικότητα.
Εγκατάσταση
Κατεβάστε και εγκαταστήστε το πακέτο χρησιμοποιώντας το Composer:
composer require nette/database
Composer: Σύνδεση και διαμόρφωση
Για να συνδεθείτε στη βάση δεδομένων, απλώς δημιουργήστε μια περίπτωση της κλάσης Nette\Database\Connection:
$database = new Nette\Database\Connection($dsn, $user, $password);
Η παράμετρος $dsn
(όνομα πηγής δεδομένων) είναι η ίδια που
χρησιμοποιείται από το PDO, π.χ. host=127.0.0.1;dbname=test
. Σε περίπτωση
αποτυχίας πετάει το Nette\Database\ConnectionException
.
Ωστόσο, ένας πιο εξελιγμένος τρόπος προσφέρει διαμόρφωση της εφαρμογής. Θα προσθέσουμε ένα τμήμα
database
και αυτό δημιουργεί τα απαιτούμενα αντικείμενα και ένα
πάνελ βάσης δεδομένων στη γραμμή Tracy.
database:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: root
password: password
Το αντικείμενο σύνδεσης που λαμβάνουμε ως υπηρεσία από ένα δοχείο DI, για παράδειγμα:
class Model
{
// περάστε το Nette\Database\Explorer για να εργαστείτε με το επίπεδο Database Explorer
public function __construct(
private Nette\Database\Connection $database,
) {
}
}
Για περισσότερες πληροφορίες, ανατρέξτε στη διαμόρφωση της βάσης δεδομένων.
Ερωτήματα
Για να κάνετε ερώτημα στη βάση δεδομένων χρησιμοποιήστε τη μέθοδο
query()
που επιστρέφει ResultSet.
$result = $database->query('SELECT * FROM users');
foreach ($result as $row) {
echo $row->id;
echo $row->name;
}
echo $result->getRowCount(); // επιστρέφει τον αριθμό των γραμμών αν είναι γνωστός
Πάνω από το ResultSet
είναι δυνατόν να γίνει επανάληψη μόνο
μία φορά, αν χρειαστεί να γίνει επανάληψη πολλές φορές, είναι
απαραίτητο να μετατρέψουμε το αποτέλεσμα σε πίνακα μέσω της μεθόδου
fetchAll()
.
Μπορείτε εύκολα να προσθέσετε παραμέτρους στο ερώτημα, σημειώστε το ερωτηματικό:
$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 είναι πίνακας
ΠΡΟΕΙΔΟΠΟΙΗΣΗ, μην συνδέετε ποτέ συμβολοσειρές για να αποφύγετε την ευπάθεια SQL injection!
$db->query('SELECT * FROM users WHERE name = ' . $name); // WRONG!!!
Σε περίπτωση αποτυχίας το query()
πετάει είτε το
Nette\Database\DriverException
είτε έναν από τους απογόνους του:
- ConstraintViolationException – παραβίαση οποιουδήποτε περιορισμού
- ForeignKeyConstraintViolationException – μη έγκυρο ξένο κλειδί
- NotNullConstraintViolationException – παραβίαση της συνθήκης NOT NULL
- UniqueConstraintViolationException – σύγκρουση μοναδικού δείκτη
Εκτός από το query()
, υπάρχουν και άλλες χρήσιμες μέθοδοι:
// επιστρέφει τον συσχετιστικό πίνακα id => name
$pairs = $database->fetchPairs('SELECT id, name FROM users');
// επιστρέφει όλες τις γραμμές ως πίνακα
$rows = $database->fetchAll('SELECT * FROM users');
// επιστρέφει μία μόνο γραμμή
$row = $database->fetch('SELECT * FROM users WHERE id = ?', $id);
// επιστρέφει ένα μόνο πεδίο
$name = $database->fetchField('SELECT name FROM users WHERE id = ?', $id);
Σε περίπτωση αποτυχίας, όλες αυτές οι μέθοδοι
ρίχνουν Nette\Database\DriverException.
Εισαγωγή, ενημέρωση και διαγραφή
Η παράμετρος που εισάγουμε στο ερώτημα SQL μπορεί επίσης να είναι ο
πίνακας (σε αυτή την περίπτωση είναι δυνατόν να παραλείψουμε τη δήλωση
μπαλαντέρ ?
), which may be useful for the INSERT
:
$database->query('INSERT INTO users ?', [ // εδώ μπορεί να παραλειφθεί το ερωτηματικό
'name' => $name,
'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
$id = $database->getInsertId(); // επιστρέφει την αυτόματη αύξηση της εισαγόμενης γραμμής
$id = $database->getInsertId($sequence); // ή τιμή ακολουθίας
Πολλαπλή εισαγωγή:
$database->query('INSERT INTO users', [
[
'name' => 'Jim',
'year' => 1978,
], [
'name' => 'Jack',
'year' => 1987,
],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
DateTime ή απαριθμήσεις:
$database->query('INSERT INTO users', [
'name' => $name,
'created' => new DateTime, // ή $database::literal('NOW()')
'avatar' => fopen('image.gif', 'r'), // Εισάγει τα περιεχόμενα του αρχείου
'status' => State::New, // enum Κατάσταση
]);
Ενημέρωση γραμμών:
$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(); // επιστρέφει τον αριθμό των επηρεαζόμενων γραμμών
Για UPDATE, μπορούμε να χρησιμοποιήσουμε τους τελεστές +=
και
-=
:
$database->query('UPDATE users SET', [
'age+=' => 1, // note +=
], 'WHERE id = ?', $id);
// UPDATE users SET `age` = `age` + 1
Διαγραφή:
$result = $database->query('DELETE FROM users WHERE id = ?', $id);
echo $result->getRowCount(); // επιστρέφει τον αριθμό των επηρεαζόμενων γραμμών
Ερωτήματα για προχωρημένους
Εισαγωγή ή ενημέρωση, εάν υπάρχει ήδη:
$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
Σημειώστε ότι η Nette Database αναγνωρίζει το πλαίσιο SQL στο οποίο εισάγεται
η παράμετρος του πίνακα και δημιουργεί τον κώδικα SQL ανάλογα. Έτσι, από
τον πρώτο πίνακα παράγει το (id, name, year) VALUES (123, 'Jim', 1978)
, ενώ ο
δεύτερος μετατρέπεται σε name = 'Jim', year = 1978
.
Μπορούμε επίσης να περιγράψουμε την ταξινόμηση χρησιμοποιώντας array, στο keys είναι ονόματα στηλών και values είναι boolean που καθορίζει αν θα γίνει ταξινόμηση σε αύξουσα σειρά:
$database->query('SELECT id FROM author ORDER BY', [
'id' => true, // αύξουσα
'name' => false, // φθίνουσα
]);
// SELECT id FROM author ORDER BY `id`, `name` DESC
Εάν η ανίχνευση δεν λειτούργησε, μπορείτε να καθορίσετε τη μορφή της
συνέλευσης με ένα μπαλαντέρ ?
ακολουθούμενο από μια υπόδειξη.
Αυτές οι υποδείξεις υποστηρίζονται:
?values | (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 |
Η ρήτρα WHERE χρησιμοποιεί τον τελεστή ?and
έτσι ώστε οι συνθήκες
να συνδέονται με το AND
:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' AND `year` = 1978
Το οποίο μπορεί εύκολα να αλλάξει σε OR
με τη χρήση του
μπαλαντέρ ?or
:
$result = $database->query('SELECT * FROM users WHERE ?or', [
'name' => $name,
'year' => $year,
]);
// SELECT * FROM users WHERE `name` = 'Jim' OR `year` = 1978
Μπορούμε να χρησιμοποιήσουμε τελεστές σε συνθήκες:
$result = $database->query('SELECT * FROM users WHERE', [
'name <>' => $name,
'year >' => $year,
]);
// SELECT * FROM users WHERE `name` <> 'Jim' AND `year` > 1978
Και επίσης απαριθμήσεις:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => ['Jim', 'Jack'],
'role NOT IN' => ['admin', 'owner'], // απαρίθμηση + τελεστής NOT IN
]);
// SELECT * FROM users WHERE
// `name` IN ('Jim', 'Jack') AND `role` NOT IN ('admin', 'owner')
Μπορούμε επίσης να συμπεριλάβουμε ένα κομμάτι προσαρμοσμένου κώδικα SQL χρησιμοποιώντας το λεγόμενο SQL literal:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
'year >' => $database::literal('YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (`year` > YEAR())
Εναλλακτικά:
$result = $database->query('SELECT * FROM users WHERE', [
'name' => $name,
$database::literal('year > YEAR()'),
]);
// SELECT * FROM users WHERE (`name` = 'Jim') AND (year > YEAR())
SQL literal μπορεί επίσης να έχει τις παραμέτρους του:
$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)
Χάρη σε αυτό μπορούμε να δημιουργήσουμε ενδιαφέροντες συνδυασμούς:
$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')
Όνομα μεταβλητής
Υπάρχει ένα μπαλαντέρ ?name
που χρησιμοποιείτε αν το όνομα του
πίνακα ή της στήλης είναι μεταβλητή. (Προσοχή, μην επιτρέψετε στο
χρήστη να χειριστεί το περιεχόμενο μιας τέτοιας μεταβλητής):
$table = 'blog.users';
$column = 'name';
$database->query('SELECT * FROM ?name WHERE ?name = ?', $table, $column, $name);
// SELECT * FROM `blog`.`users` WHERE `name` = 'Jim'
Συναλλαγές
Υπάρχουν τρεις μέθοδοι για την αντιμετώπιση των συναλλαγών:
$database->beginTransaction();
$database->commit();
$database->rollback();
Η μέθοδος transaction()
προσφέρει έναν κομψό τρόπο. Περνάτε το callback
που εκτελείται στη συναλλαγή. Εάν κατά την εκτέλεση εκσφενδονιστεί μια
εξαίρεση, η συναλλαγή εγκαταλείπεται, ενώ εάν όλα πάνε καλά, η
συναλλαγή δεσμεύεται.
$id = $database->transaction(function ($database) {
$database->query('DELETE FROM ...');
$database->query('INSERT INTO ...');
// ...
return $database->getInsertId();
});
Όπως μπορείτε να δείτε, η μέθοδος transaction()
επιστρέφει την τιμή
επιστροφής του callback.
Η transaction() μπορεί επίσης να είναι εμφωλευμένη, γεγονός που απλοποιεί την υλοποίηση ανεξάρτητων αποθετηρίων.