Nette Documentation Preview

syntax
Εξερεύνηση βάσης δεδομένων
**************************

<div class=perex>

Ο Nette Database Explorer απλοποιεί σημαντικά την ανάκτηση δεδομένων από τη βάση δεδομένων χωρίς τη συγγραφή ερωτημάτων SQL.

- χρησιμοποιεί αποδοτικά ερωτήματα
- δεν μεταδίδονται άσκοπα δεδομένα
- διαθέτει κομψή σύνταξη

</div>

Για να χρησιμοποιήσετε την Εξερεύνηση βάσης δεδομένων, ξεκινήστε με έναν πίνακα - καλέστε το `table()` σε ένα αντικείμενο [api:Nette\Database\Explorer]. Ο ευκολότερος τρόπος για να λάβετε μια περίπτωση αντικειμένου πλαισίου [περιγράφεται εδώ |core#Connection and Configuration], ή, για την περίπτωση που ο Nette Database Explorer χρησιμοποιείται ως αυτόνομο εργαλείο, μπορεί να [δημιουργηθεί χειροκίνητα |#Creating Explorer Manually].

```php
$books = $explorer->table('book'); // Το όνομα του πίνακα db είναι 'book'
```

Η κλήση επιστρέφει μια περίπτωση του αντικειμένου [Selection |api:Nette\Database\Table\Selection], η οποία μπορεί να επαναληφθεί για να ανακτηθούν όλα τα βιβλία. Κάθε στοιχείο (μια γραμμή) αναπαρίσταται από μια περίπτωση του [ActiveRow |api:Nette\Database\Table\ActiveRow] με δεδομένα που αντιστοιχίζονται στις ιδιότητές του:

```php
foreach ($books as $book) {
	echo $book->title;
	echo $book->author_id;
}
```

Η απόκτηση μόνο μιας συγκεκριμένης γραμμής γίνεται με τη μέθοδο `get()`, η οποία επιστρέφει απευθείας μια περίπτωση ActiveRow.

```php
$book = $explorer->table('book')->get(2); // επιστρέφει βιβλίο με id 2
echo $book->title;
echo $book->author_id;
```

Ας ρίξουμε μια ματιά σε μια κοινή περίπτωση χρήσης. Πρέπει να ανακτήσετε βιβλία και τους συγγραφείς τους. Πρόκειται για μια κοινή σχέση 1:N. Η συχνά χρησιμοποιούμενη λύση είναι η άντληση δεδομένων με τη χρήση ενός ερωτήματος SQL με ενώσεις πινάκων. Η δεύτερη δυνατότητα είναι να φέρνετε τα δεδομένα ξεχωριστά, να εκτελείτε ένα ερώτημα για τη λήψη βιβλίων και στη συνέχεια να παίρνετε έναν συγγραφέα για κάθε βιβλίο με ένα άλλο ερώτημα (π.χ. στον κύκλο foreach σας). Αυτό θα μπορούσε εύκολα να βελτιστοποιηθεί ώστε να εκτελούνται μόνο δύο ερωτήματα, ένα για τα βιβλία και ένα άλλο για τους απαιτούμενους συγγραφείς - και αυτός ακριβώς είναι ο τρόπος με τον οποίο το κάνει ο Nette Database Explorer.

Στα παραδείγματα που ακολουθούν, θα εργαστούμε με το σχήμα της βάσης δεδομένων του σχήματος. Υπάρχουν σύνδεσμοι OneHasMany (1:N) (συγγραφέας του βιβλίου `author_id` και πιθανός μεταφραστής `translator_id`, ο οποίος μπορεί να είναι `null`) και σύνδεσμος ManyHasMany (M:N) μεταξύ του βιβλίου και των ετικετών του.

[Ένα παράδειγμα, συμπεριλαμβανομένου ενός σχήματος, βρίσκεται στο GitHub |https://github.com/nette-examples/books].

[* db-schema-1-.webp *] *** Δομή βάσης δεδομένων που χρησιμοποιείται στα παραδείγματα .<>

Ο παρακάτω κώδικας παραθέτει το όνομα του συγγραφέα για κάθε βιβλίο και όλες τις ετικέτες του. Θα [συζητήσουμε σε λίγο |#Working with relationships] πώς αυτό λειτουργεί εσωτερικά.

```php
$books = $explorer->table('book');

foreach ($books as $book) {
	echo 'title:      ' . $book->title;
	echo 'written by: ' . $book->author->name; // $book->author is row from table 'author'

	echo 'tags: ';
	foreach ($book->related('book_tag') as $bookTag) {
		echo $bookTag->tag->name . ', '; // $bookTag->tag is row from table 'tag'
	}
}
```

Θα μείνετε ευχαριστημένοι από το πόσο αποτελεσματικά λειτουργεί το επίπεδο της βάσης δεδομένων. Το παραπάνω παράδειγμα πραγματοποιεί έναν σταθερό αριθμό αιτήσεων που μοιάζουν ως εξής:

```sql
SELECT * FROM `book`
SELECT * FROM `author` WHERE (`author`.`id` IN (11, 12))
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 4, 2, 3))
SELECT * FROM `tag` WHERE (`tag`.`id` IN (21, 22, 23))
```

Αν χρησιμοποιείτε [κρυφή μνήμη |caching:] (προεπιλογή on), δεν θα ζητούνται άσκοπα στήλες. Μετά το πρώτο ερώτημα, η κρυφή μνήμη θα αποθηκεύσει τα χρησιμοποιούμενα ονόματα στηλών και ο Nette Database Explorer θα εκτελεί ερωτήματα μόνο με τις απαραίτητες στήλες:

```sql
SELECT `id`, `title`, `author_id` FROM `book`
SELECT `id`, `name` FROM `author` WHERE (`author`.`id` IN (11, 12))
SELECT `book_id`, `tag_id` FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 4, 2, 3))
SELECT `id`, `name` FROM `tag` WHERE (`tag`.`id` IN (21, 22, 23))
```


Επιλογές .[#toc-selections]
===========================

Δείτε τις δυνατότητες φιλτραρίσματος και περιορισμού των γραμμών [api:Nette\Database\Table\Selection]:

.[language-php]
| `$table->where($where[, $param[, ...]])` | Ορισμός WHERE με χρήση του AND ως συγκολλητικού στοιχείου εάν παρέχονται δύο ή περισσότερες συνθήκες
| `$table->whereOr($where)` | Ορισμός WHERE με χρήση OR ως συγκολλητικό στοιχείο εάν παρέχονται δύο ή περισσότερες συνθήκες
| `$table->order($columns)` | Ορισμός ORDER BY, μπορεί να είναι έκφραση `('column DESC, id DESC')`
| `$table->select($columns)` | Ορισμός ανακτημένων στηλών, μπορεί να είναι έκφραση `('col, MD5(col) AS hash')`
| `$table->limit($limit[, $offset])` | Ορισμός LIMIT και OFFSET
| `$table->page($page, $itemsPerPage[, &$lastPage])` | Ενεργοποιεί τη σελιδοποίηση
| `$table->group($columns)` | Ορισμός GROUP BY
| `$table->having($having)` | Ορισμός HAVING

Μπορούμε να χρησιμοποιήσουμε μια λεγόμενη [ρευστή διεπαφή |nette:introduction-to-object-oriented-programming#fluent-interfaces], για παράδειγμα `$table->where(...)->order(...)->limit(...)`. Πολλαπλές συνθήκες `where` ή `whereOr` συνδέονται με τον τελεστή `AND`.


where() .[#toc-where]
---------------------

Ο Nette Database Explorer μπορεί να προσθέσει αυτόματα τους απαραίτητους τελεστές για τις περασμένες τιμές:

.[language-php]
| `$table->where('field', $value)` | field = $value
| `$table->where('field', null)` | field IS NULL
| `$table->where('field > ?', $val)` | πεδίο > $val
| `$table->where('field', [1, 2])` | field IN (1, 2)
| `$table->where('id = ? OR name = ?', 1, $name)` | id = 1 OR name = 'Jon Snow'
| `$table->where('field', $explorer->table($tableName))` | field IN (SELECT $primary FROM $tableName)
| `$table->where('field', $explorer->table($tableName)->select('col'))` | field IN (SELECT col FROM $tableName)

Μπορείτε να δώσετε placeholder ακόμη και χωρίς τον τελεστή στήλης. Αυτές οι κλήσεις είναι οι ίδιες.

```php
$table->where('id = ? OR id = ?', 1, 2);
$table->where('id ? OR id ?', 1, 2);
```

Αυτή η λειτουργία επιτρέπει τη δημιουργία του σωστού τελεστή με βάση την τιμή:

```php
$table->where('id ?', 2);    // id = 2
$table->where('id ?', null); // id IS NULL
$table->where('id', $ids);   // id IN (...)
```

Λειτουργεί και για άδειους πίνακες:

```php
$table->where('id', []);       // id IS NULL AND FALSE
$table->where('id NOT', []);   // id IS NULL OR TRUE
$table->where('NOT (id ?)', $ids);  // NOT (id IS NULL AND FALSE)

// αυτό θα προκαλέσει μια εξαίρεση, αυτή η σύνταξη δεν υποστηρίζεται
$table->where('NOT id ?', $ids);
```


whereOr() .[#toc-whereor]
-------------------------

Παράδειγμα χρήσης χωρίς παραμέτρους:

```php
// WHERE (user_id IS NULL) OR (SUM(`field1`) > SUM(`field2`))
$table->whereOr([
	'user_id IS NULL',
	'SUM(field1) > SUM(field2)',
]);
```

Χρησιμοποιούμε τις παραμέτρους. Εάν δεν καθορίσετε έναν τελεστή, ο Nette Database Explorer θα προσθέσει αυτόματα τον κατάλληλο:

```php
// WHERE (`field1` IS NULL) OR (`field2` IN (3, 5)) OR (`amount` > 11)
$table->whereOr([
	'field1' => null,
	'field2' => [3, 5],
	'amount >' => 11,
]);
```

Το κλειδί μπορεί να περιέχει μια έκφραση που περιέχει ερωτηματικά μπαλαντέρ και στη συνέχεια να περάσετε παραμέτρους στην τιμή:

```php
// WHERE (`id` > 12) OR (ROUND(`id`, 5) = 3)
$table->whereOr([
	'id > ?' => 12,
	'ROUND(id, ?) = ?' => [5, 3],
]);
```


order() .[#toc-order]
---------------------

Παραδείγματα χρήσης:

```php
$table->order('field1');               // ORDER BY `field1`
$table->order('field1 DESC, field2');  // ORDER BY `field1` DESC, `field2`
$table->order('field = ? DESC', 123);  // ORDER BY `field` = 123 DESC
```


select() .[#toc-select]
-----------------------

Παραδείγματα χρήσης:

```php
$table->select('field1');                 // SELECT `field1`
$table->select('col, UPPER(col) AS abc'); // SELECT `col`, UPPER(`col`) AS abc
$table->select('SUBSTR(title, ?)', 3);    // SELECT SUBSTR(`title`, 3)
```


limit() .[#toc-limit]
---------------------

Παραδείγματα χρήσης:

```php
$table->limit(1);     // LIMIT 1
$table->limit(1, 10); // LIMIT 1 OFFSET 10
```


page() .[#toc-page]
-------------------

Ένας εναλλακτικός τρόπος για τον καθορισμό του ορίου και της μετατόπισης:

```php
$page = 5;
$itemsPerPage = 10;
$table->page($page, $itemsPerPage); // LIMIT 10 OFFSET 40
```

 `$lastPage`:

```php
$table->page($page, $itemsPerPage, $lastPage);
```


group() .[#toc-group]
---------------------

Παραδείγματα χρήσης:

```php
$table->group('field1');          // GROUP BY `field1`
$table->group('field1, field2');  // GROUP BY `field1`, `field2`
```


having() .[#toc-having]
-----------------------

Παραδείγματα χρήσης:

```php
$table->having('COUNT(items) >', 100);  // HAVING COUNT(`items`) > 100
```


Φιλτράρισμα με βάση άλλη τιμή πίνακα .[#toc-joining-key]
--------------------------------------------------------

Αρκετά συχνά χρειάζεται να φιλτράρετε τα αποτελέσματα με βάση κάποια συνθήκη που αφορά έναν άλλο πίνακα της βάσης δεδομένων. Αυτού του είδους οι συνθήκες απαιτούν σύνδεση πινάκων. Ωστόσο, δεν χρειάζεται πλέον να τα γράφετε.

Ας υποθέσουμε ότι πρέπει να πάρετε όλα τα βιβλία των οποίων το όνομα του συγγραφέα είναι 'Jon'. Το μόνο που χρειάζεται να γράψετε είναι το κλειδί σύνδεσης της σχέσης και το όνομα της στήλης στον συνδεδεμένο πίνακα. Το κλειδί σύνδεσης προκύπτει από τη στήλη που αναφέρεται στον πίνακα που θέλετε να συνδέσετε. Στο παράδειγμά μας (βλ. το σχήμα db) είναι η στήλη `author_id`, και αρκεί να χρησιμοποιήσετε μόνο το πρώτο μέρος της - `author` (η κατάληξη `_id` μπορεί να παραλειφθεί). `name` είναι μια στήλη του πίνακα `author` που θέλουμε να χρησιμοποιήσουμε. Μια συνθήκη για τον μεταφραστή βιβλίων (η οποία συνδέεται με τη στήλη `translator_id` ) μπορεί να δημιουργηθεί εξίσου εύκολα.

```php
$books = $explorer->table('book');
$books->where('author.name LIKE ?', '%Jon%');
$books->where('translator.name', 'David Grudl');
```

Η λογική του κλειδιού σύνδεσης καθοδηγείται από την εφαρμογή των [συμβάσεων |api:Nette\Database\Conventions]. Σας ενθαρρύνουμε να χρησιμοποιήσετε το [DiscoveredConventions |api:Nette\Database\Conventions\DiscoveredConventions], το οποίο αναλύει τα ξένα κλειδιά σας και σας επιτρέπει να εργάζεστε εύκολα με αυτές τις σχέσεις.

Η σχέση μεταξύ του βιβλίου και του συγγραφέα του είναι 1:N. Η αντίστροφη σχέση είναι επίσης δυνατή. Την ονομάζουμε **backjoin**. Ρίξτε μια ματιά σε ένα άλλο παράδειγμα. Θα θέλαμε να αντλήσουμε όλους τους συγγραφείς, οι οποίοι έχουν γράψει περισσότερα από 3 βιβλία. Για να κάνουμε την αντίστροφη σύνδεση χρησιμοποιούμε τη δήλωση `:` (colon). Colon means that the joined relationship means hasMany (and it's quite logical too, as two dots are more than one dot). Unfortunately, the Selection class isn't smart enough, so we have to help with the aggregation and provide a `GROUP BY`, επίσης η συνθήκη πρέπει να γραφτεί με τη μορφή της δήλωσης `HAVING`.

```php
$authors = $explorer->table('author');
$authors->group('author.id')
	->having('COUNT(:book.id) > 3');
```

Ίσως έχετε παρατηρήσει ότι η έκφραση σύνδεσης αναφέρεται στο βιβλίο, αλλά δεν είναι σαφές, αν κάνουμε σύνδεση μέσω του `author_id` ή του `translator_id`. Στο παραπάνω παράδειγμα, η Selection κάνει σύνδεση μέσω της στήλης `author_id` επειδή βρέθηκε μια αντιστοιχία με τον πίνακα προέλευσης - τον πίνακα `author`. Αν δεν υπήρχε τέτοια αντιστοιχία και υπήρχαν περισσότερες δυνατότητες, η Nette θα έριχνε [AmbiguousReferenceKeyException |api:Nette\Database\Conventions\AmbiguousReferenceKeyException].

Για να πραγματοποιήσετε μια σύνδεση μέσω της στήλης `translator_id`, δώστε μια προαιρετική παράμετρο εντός της έκφρασης σύνδεσης.

```php
$authors = $explorer->table('author');
$authors->group('author.id')
	->having('COUNT(:book(translator).id) > 3');
```

Ας ρίξουμε μια ματιά σε μερικές πιο δύσκολες εκφράσεις σύνδεσης.

Θα θέλαμε να βρούμε όλους τους συγγραφείς που έχουν γράψει κάτι για την PHP. Όλα τα βιβλία έχουν ετικέτες, οπότε θα πρέπει να επιλέξουμε εκείνους τους συγγραφείς που έχουν γράψει κάποιο βιβλίο με την ετικέτα PHP.

```php
$authors = $explorer->table('author');
$authors->where(':book:book_tags.tag.name', 'PHP')
	->group('author.id')
	->having('COUNT(:book:book_tags.tag.id) > 0');
```


Συγκεντρωτικά ερωτήματα .[#toc-aggregate-queries]
-------------------------------------------------

| `$table->count('*')` | Λήψη αριθμού γραμμών
| `$table->count("DISTINCT $column")` | Λήψη αριθμού διαφορετικών τιμών
| `$table->min($column)` | Λήψη ελάχιστης τιμής
| `$table->max($column)` | Λήψη μέγιστης τιμής
| `$table->sum($column)` | Λήψη του αθροίσματος όλων των τιμών
| `$table->aggregation("GROUP_CONCAT($column)")` | Εκτέλεση οποιασδήποτε συνάρτησης συνάθροισης

.[caution]
Η μέθοδος `count()` χωρίς καθορισμένες παραμέτρους επιλέγει όλες τις εγγραφές και επιστρέφει το μέγεθος του πίνακα, κάτι που είναι πολύ αναποτελεσματικό. Για παράδειγμα, αν πρέπει να υπολογίσετε τον αριθμό των γραμμών για τη σελιδοποίηση, καθορίζετε πάντα το πρώτο όρισμα.


Διαφυγή & παραπομπή .[#toc-escaping-quoting]
============================================

Ο Database Explorer είναι έξυπνος και αποφεύγει τις παραμέτρους και τα αναγνωριστικά εισαγωγικά για εσάς. Ωστόσο, πρέπει να τηρούνται αυτοί οι βασικοί κανόνες:

- λέξεις-κλειδιά, συναρτήσεις, διαδικασίες πρέπει να είναι κεφαλαία.
- οι στήλες και οι πίνακες πρέπει να είναι πεζά
- να περνάτε μεταβλητές ως παραμέτρους, να μην τις συνυπολογίζετε

```php
->where('name like ?', 'John'); // ΛΑΘΟΣ! γενιές: `name` `like` ?
->where('name LIKE ?', 'John'); // ΣΩΣΤΟ

->where('KEY = ?', $value); // ΛΑΘΟΣ! Το KEY είναι λέξη κλειδί
->where('key = ?', $value); // ΣΩΣΤΟ: `key` = ?

->where('name = ' . $name); // ΛΑΘΟΣ! sql injection!
->where('name = ?', $name); // ΣΩΣΤΟ

->select('DATE_FORMAT(created, "%d.%m.%Y")'); // Περνάτε μεταβλητές ως παραμέτρους, μην κάνετε συνένωση
->select('DATE_FORMAT(created, ?)', '%d.%m.%Y'); // ΣΩΣΤΟ
```

.[warning]
Η λανθασμένη χρήση μπορεί να δημιουργήσει κενά ασφαλείας


Λήψη δεδομένων .[#toc-fetching-data]
====================================

| `foreach ($table as $id => $row)` | Επανάληψη σε όλες τις γραμμές του αποτελέσματος
| `$row = $table->get($id)` | Λήψη μεμονωμένης γραμμής με αναγνωριστικό $id από τον πίνακα
| `$row = $table->fetch()` | Λήψη της επόμενης γραμμής από το αποτέλεσμα
| `$array = $table->fetchPairs($key, $value)` | Λήψη όλων των τιμών σε συσχετιστικό πίνακα
| `$array = $table->fetchPairs($key)` | Λήψη όλων των γραμμών σε συσχετιστικό πίνακα
| `count($table)` | Λήψη αριθμού γραμμών στο σύνολο αποτελεσμάτων


Εισαγωγή, ενημέρωση και διαγραφή .[#toc-insert-update-delete]
=============================================================

Η μέθοδος `insert()` δέχεται πίνακα αντικειμένων Traversable (για παράδειγμα [ArrayHash |utils:arrays#ArrayHash] που επιστρέφει [μορφές |forms:]):

```php
$row = $explorer->table('users')->insert([
	'name' => $name,
	'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)
```

Εάν έχει οριστεί πρωτεύον κλειδί στον πίνακα, επιστρέφεται ένα αντικείμενο ActiveRow που περιέχει την εισαγόμενη γραμμή.

Πολλαπλή εισαγωγή:

```php
$explorer->table('users')->insert([
	[
		'name' => 'Jim',
		'year' => 1978,
	], [
		'name' => 'Jack',
		'year' => 1987,
	],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)
```

DateTime μπορούν να περάσουν ως παράμετροι:

```php
$explorer->table('users')->insert([
	'name' => $name,
	'created' => new DateTime, // ή $explorer::literal('NOW()')
	'avatar' => fopen('image.gif', 'r'), // εισάγει το αρχείο
]);
```

Ενημέρωση (επιστρέφει τον αριθμό των επηρεαζόμενων γραμμών):

```php
$count = $explorer->table('users')
	->where('id', 10) // πρέπει να καλείται πριν από την update()
	->update([
		'name' => 'Ned Stark'
	]);
// UPDATE `users` SET `name`='Ned Stark' WHERE (`id` = 10)
```

Για την ενημέρωση μπορούμε να χρησιμοποιήσουμε τους τελεστές `+=` a `-=`:

```php
$explorer->table('users')
	->update([
		'age+=' => 1, // see +=
	]);
// UPDATE users SET `age` = `age` + 1
```

Διαγραφή (επιστρέφει τον αριθμό των διαγραμμένων γραμμών):

```php
$count = $explorer->table('users')
	->where('id', 10)
	->delete();
// DELETE FROM `users` WHERE (`id` = 10)
```


Εργασία με σχέσεις .[#toc-working-with-relationships]
=====================================================


Έχει μία σχέση .[#toc-has-one-relation]
---------------------------------------
Το Has one relation είναι μια συνηθισμένη περίπτωση χρήσης. Το βιβλίο *έχει έναν* συγγραφέα. Το βιβλίο *έχει έναν* μεταφραστή. Η λήψη σχετικής σειράς γίνεται κυρίως με τη μέθοδο `ref()`. Δέχεται δύο ορίσματα: το όνομα του πίνακα-στόχου και τη στήλη σύνδεσης της πηγής. Βλέπε παράδειγμα:

```php
$book = $explorer->table('book')->get(1);
$book->ref('author', 'author_id');
```

 `author` Το πρωτεύον κλειδί του συγγραφέα αναζητείται από τη στήλη `book.author_id`. Η μέθοδος Ref() επιστρέφει την περίπτωση ActiveRow ή null αν δεν υπάρχει κατάλληλη εγγραφή. Η γραμμή που επιστρέφεται είναι μια περίπτωση της ActiveRow, οπότε μπορούμε να εργαστούμε με αυτήν με τον ίδιο τρόπο όπως με την εγγραφή του βιβλίου.

```php
$author = $book->ref('author', 'author_id');
$author->name;
$author->born;

// ή απευθείας
$book->ref('author', 'author_id')->name;
$book->ref('author', 'author_id')->born;
```

Το βιβλίο έχει επίσης έναν μεταφραστή, οπότε το να πάρουμε το όνομα του μεταφραστή είναι αρκετά εύκολο.
```php
$book->ref('author', 'translator_id')->name
```

Όλα αυτά είναι μια χαρά, αλλά είναι κάπως δυσκίνητα, δεν νομίζετε; Η Εξερεύνηση βάσης δεδομένων περιέχει ήδη τους ορισμούς των ξένων κλειδιών, οπότε γιατί να μην τους χρησιμοποιήσετε αυτόματα; Ας το κάνουμε αυτό!

Αν καλέσουμε ιδιότητα, η οποία δεν υπάρχει, το ActiveRow προσπαθεί να επιλύσει το όνομα της καλούσας ιδιότητας ως σχέση 'έχει μία'. Η λήψη αυτής της ιδιότητας είναι το ίδιο με την κλήση της μεθόδου ref() με ένα μόνο όρισμα. Θα ονομάσουμε το μοναδικό όρισμα **key**. Το κλειδί θα επιλυθεί σε συγκεκριμένη σχέση ξένου κλειδιού. Το περασμένο κλειδί συγκρίνεται με τις στήλες της γραμμής, και αν ταιριάζει, το ξένο κλειδί που ορίζεται στην αντίστοιχη στήλη χρησιμοποιείται για τη λήψη δεδομένων από τον σχετικό πίνακα-στόχο. Βλέπε παράδειγμα:

```php
$book->author->name;
// το ίδιο με
$book->ref('author')->name;
```

Η περίπτωση ActiveRow δεν έχει στήλη author. Όλες οι στήλες βιβλίων αναζητούνται για μια αντιστοιχία με το *key*. Η αντιστοίχιση σε αυτή την περίπτωση σημαίνει ότι το όνομα της στήλης πρέπει να περιέχει το κλειδί. Έτσι, στο παραπάνω παράδειγμα, η στήλη `author_id` περιέχει τη συμβολοσειρά 'author' και επομένως ταιριάζει με το κλειδί 'author'. Αν θέλετε να βρείτε τον μεταφραστή του βιβλίου, μπορείτε απλώς να χρησιμοποιήσετε π.χ. το 'translator' ως κλειδί, επειδή το κλειδί 'translator' θα ταιριάζει με τη στήλη `translator_id`. Μπορείτε να μάθετε περισσότερα για τη λογική αντιστοίχισης κλειδιών στο κεφάλαιο [Joining expressions |#joining-key].

```php
echo $book->title . ': ';
echo $book->author->name;
if ($book->translator) {
	echo ' (translated by ' . $book->translator->name . ')';
}
```

Αν θέλετε να αντλήσετε πολλά βιβλία, θα πρέπει να χρησιμοποιήσετε την ίδια προσέγγιση. Ο Nette Database Explorer θα αντλήσει συγγραφείς και μεταφραστές για όλα τα βιβλία που θα αντλήσετε ταυτόχρονα.

```php
$books = $explorer->table('book');
foreach ($books as $book) {
	echo $book->title . ': ';
	echo $book->author->name;
	if ($book->translator) {
		echo ' (translated by ' . $book->translator->name . ')';
	}
}
```

Ο κώδικας θα εκτελέσει μόνο αυτά τα 3 ερωτήματα:
```sql
SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- ids of fetched books from author_id column
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- ids of fetched books from translator_id column
```


Has Many Relation .[#toc-has-many-relation]
-------------------------------------------

Η σχέση 'Έχει πολλούς' είναι απλώς η αντίστροφη σχέση 'έχει έναν'. Ο συγγραφέας *έχει* γράψει *πολλά* βιβλία. Ο συγγραφέας *έχει* μεταφράσει *πολλά* βιβλία. Όπως μπορείτε να δείτε, αυτός ο τύπος σχέσης είναι λίγο πιο δύσκολος επειδή η σχέση είναι 'ονομαστική' ('έγραψε', 'μετέφρασε'). Η περίπτωση ActiveRow έχει τη μέθοδο `related()`, η οποία θα επιστρέψει πίνακα σχετικών καταχωρήσεων. Οι εγγραφές είναι επίσης περιπτώσεις ActiveRow. Δείτε το παράδειγμα παρακάτω:

```php
$author = $explorer->table('author')->get(11);
echo $author->name . ' has written:';

foreach ($author->related('book.author_id') as $book) {
	echo $book->title;
}

echo 'and translated:';
foreach ($author->related('book.translator_id') as $book) {
	echo $book->title;
}
```

Η μέθοδος `related()` δέχεται την πλήρη περιγραφή join που περνάει ως δύο ορίσματα ή ως ένα όρισμα ενωμένο με τελεία. Το πρώτο όρισμα είναι ο πίνακας-στόχος, το δεύτερο είναι η στήλη-στόχος.

```php
$author->related('book.translator_id');
// το ίδιο με
$author->related('book', 'translator_id');
```

Μπορείτε να χρησιμοποιήσετε τις ευρετικές λειτουργίες του Nette Database Explorer που βασίζονται σε ξένα κλειδιά και να δώσετε μόνο το όρισμα **key**. Το κλειδί θα συγκριθεί με όλα τα ξένα κλειδιά που δείχνουν στον τρέχοντα πίνακα (`author` table). Εάν υπάρχει ταύτιση, ο Nette Database Explorer θα χρησιμοποιήσει αυτό το ξένο κλειδί, διαφορετικά θα πετάξει [Nette\InvalidArgumentException |api:Nette\InvalidArgumentException] ή [AmbiguousReferenceKeyException |api:Nette\Database\Conventions\AmbiguousReferenceKeyException]. Μπορείτε να βρείτε περισσότερα για τη λογική αντιστοίχισης κλειδιών στο κεφάλαιο [Joining expressions |#joining-key].

Φυσικά, μπορείτε να καλέσετε τις σχετικές μεθόδους για όλους τους συγγραφείς που ανακτήθηκαν, ο Nette Database Explorer θα ανακτήσει και πάλι τα κατάλληλα βιβλία ταυτόχρονα.

```php
$authors = $explorer->table('author');
foreach ($authors as $author) {
	echo $author->name . ' has written:';
	foreach ($author->related('book') as $book) {
		$book->title;
	}
}
```

Το παραπάνω παράδειγμα θα εκτελέσει μόνο δύο ερωτήματα:

```sql
SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- ids of fetched authors
```


Δημιουργία Explorer χειροκίνητα .[#toc-creating-explorer-manually]
==================================================================

Μια σύνδεση βάσης δεδομένων μπορεί να δημιουργηθεί χρησιμοποιώντας τη διαμόρφωση της εφαρμογής. Σε τέτοιες περιπτώσεις δημιουργείται μια υπηρεσία `Nette\Database\Explorer` και μπορεί να περάσει ως εξάρτηση χρησιμοποιώντας το DI container.

Ωστόσο, εάν ο Nette Database Explorer χρησιμοποιείται ως αυτόνομο εργαλείο, πρέπει να δημιουργηθεί χειροκίνητα μια περίπτωση του αντικειμένου `Nette\Database\Explorer`.

```php
// $storage implements Nette\Caching\Storage:
$storage = new Nette\Caching\Storages\FileStorage($tempDir);
$connection = new Nette\Database\Connection($dsn, $user, $password);
$structure = new Nette\Database\Structure($connection, $storage);
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);
```

Εξερεύνηση βάσης δεδομένων

Ο Nette Database Explorer απλοποιεί σημαντικά την ανάκτηση δεδομένων από τη βάση δεδομένων χωρίς τη συγγραφή ερωτημάτων SQL.

  • χρησιμοποιεί αποδοτικά ερωτήματα
  • δεν μεταδίδονται άσκοπα δεδομένα
  • διαθέτει κομψή σύνταξη

Για να χρησιμοποιήσετε την Εξερεύνηση βάσης δεδομένων, ξεκινήστε με έναν πίνακα – καλέστε το table() σε ένα αντικείμενο Nette\Database\Explorer. Ο ευκολότερος τρόπος για να λάβετε μια περίπτωση αντικειμένου πλαισίου περιγράφεται εδώ, ή, για την περίπτωση που ο Nette Database Explorer χρησιμοποιείται ως αυτόνομο εργαλείο, μπορεί να δημιουργηθεί χειροκίνητα.

$books = $explorer->table('book'); // Το όνομα του πίνακα db είναι 'book'

Η κλήση επιστρέφει μια περίπτωση του αντικειμένου Selection, η οποία μπορεί να επαναληφθεί για να ανακτηθούν όλα τα βιβλία. Κάθε στοιχείο (μια γραμμή) αναπαρίσταται από μια περίπτωση του ActiveRow με δεδομένα που αντιστοιχίζονται στις ιδιότητές του:

foreach ($books as $book) {
	echo $book->title;
	echo $book->author_id;
}

Η απόκτηση μόνο μιας συγκεκριμένης γραμμής γίνεται με τη μέθοδο get(), η οποία επιστρέφει απευθείας μια περίπτωση ActiveRow.

$book = $explorer->table('book')->get(2); // επιστρέφει βιβλίο με id 2
echo $book->title;
echo $book->author_id;

Ας ρίξουμε μια ματιά σε μια κοινή περίπτωση χρήσης. Πρέπει να ανακτήσετε βιβλία και τους συγγραφείς τους. Πρόκειται για μια κοινή σχέση 1:N. Η συχνά χρησιμοποιούμενη λύση είναι η άντληση δεδομένων με τη χρήση ενός ερωτήματος SQL με ενώσεις πινάκων. Η δεύτερη δυνατότητα είναι να φέρνετε τα δεδομένα ξεχωριστά, να εκτελείτε ένα ερώτημα για τη λήψη βιβλίων και στη συνέχεια να παίρνετε έναν συγγραφέα για κάθε βιβλίο με ένα άλλο ερώτημα (π.χ. στον κύκλο foreach σας). Αυτό θα μπορούσε εύκολα να βελτιστοποιηθεί ώστε να εκτελούνται μόνο δύο ερωτήματα, ένα για τα βιβλία και ένα άλλο για τους απαιτούμενους συγγραφείς – και αυτός ακριβώς είναι ο τρόπος με τον οποίο το κάνει ο Nette Database Explorer.

Στα παραδείγματα που ακολουθούν, θα εργαστούμε με το σχήμα της βάσης δεδομένων του σχήματος. Υπάρχουν σύνδεσμοι OneHasMany (1:N) (συγγραφέας του βιβλίου author_id και πιθανός μεταφραστής translator_id, ο οποίος μπορεί να είναι null) και σύνδεσμος ManyHasMany (M:N) μεταξύ του βιβλίου και των ετικετών του.

Ένα παράδειγμα, συμπεριλαμβανομένου ενός σχήματος, βρίσκεται στο GitHub.

Δομή βάσης δεδομένων που χρησιμοποιείται στα παραδείγματα

Ο παρακάτω κώδικας παραθέτει το όνομα του συγγραφέα για κάθε βιβλίο και όλες τις ετικέτες του. Θα συζητήσουμε σε λίγο πώς αυτό λειτουργεί εσωτερικά.

$books = $explorer->table('book');

foreach ($books as $book) {
	echo 'title:      ' . $book->title;
	echo 'written by: ' . $book->author->name; // $book->author is row from table 'author'

	echo 'tags: ';
	foreach ($book->related('book_tag') as $bookTag) {
		echo $bookTag->tag->name . ', '; // $bookTag->tag is row from table 'tag'
	}
}

Θα μείνετε ευχαριστημένοι από το πόσο αποτελεσματικά λειτουργεί το επίπεδο της βάσης δεδομένων. Το παραπάνω παράδειγμα πραγματοποιεί έναν σταθερό αριθμό αιτήσεων που μοιάζουν ως εξής:

SELECT * FROM `book`
SELECT * FROM `author` WHERE (`author`.`id` IN (11, 12))
SELECT * FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 4, 2, 3))
SELECT * FROM `tag` WHERE (`tag`.`id` IN (21, 22, 23))

Αν χρησιμοποιείτε κρυφή μνήμη (προεπιλογή on), δεν θα ζητούνται άσκοπα στήλες. Μετά το πρώτο ερώτημα, η κρυφή μνήμη θα αποθηκεύσει τα χρησιμοποιούμενα ονόματα στηλών και ο Nette Database Explorer θα εκτελεί ερωτήματα μόνο με τις απαραίτητες στήλες:

SELECT `id`, `title`, `author_id` FROM `book`
SELECT `id`, `name` FROM `author` WHERE (`author`.`id` IN (11, 12))
SELECT `book_id`, `tag_id` FROM `book_tag` WHERE (`book_tag`.`book_id` IN (1, 4, 2, 3))
SELECT `id`, `name` FROM `tag` WHERE (`tag`.`id` IN (21, 22, 23))

Επιλογές

Δείτε τις δυνατότητες φιλτραρίσματος και περιορισμού των γραμμών Nette\Database\Table\Selection:

$table->where($where[, $param[, ...]]) Ορισμός WHERE με χρήση του AND ως συγκολλητικού στοιχείου εάν παρέχονται δύο ή περισσότερες συνθήκες
$table->whereOr($where) Ορισμός WHERE με χρήση OR ως συγκολλητικό στοιχείο εάν παρέχονται δύο ή περισσότερες συνθήκες
$table->order($columns) Ορισμός ORDER BY, μπορεί να είναι έκφραση ('column DESC, id DESC')
$table->select($columns) Ορισμός ανακτημένων στηλών, μπορεί να είναι έκφραση ('col, MD5(col) AS hash')
$table->limit($limit[, $offset]) Ορισμός LIMIT και OFFSET
$table->page($page, $itemsPerPage[, &$lastPage]) Ενεργοποιεί τη σελιδοποίηση
$table->group($columns) Ορισμός GROUP BY
$table->having($having) Ορισμός HAVING

Μπορούμε να χρησιμοποιήσουμε μια λεγόμενη ρευστή διεπαφή, για παράδειγμα $table->where(...)->order(...)->limit(...). Πολλαπλές συνθήκες where ή whereOr συνδέονται με τον τελεστή AND.

where()

Ο Nette Database Explorer μπορεί να προσθέσει αυτόματα τους απαραίτητους τελεστές για τις περασμένες τιμές:

$table->where('field', $value) field = $value
$table->where('field', null) field IS NULL
$table->where('field > ?', $val) πεδίο > $val
$table->where('field', [1, 2]) field IN (1, 2)
$table->where('id = ? OR name = ?', 1, $name) id = 1 OR name = ‚Jon Snow‘
$table->where('field', $explorer->table($tableName)) field IN (SELECT $primary FROM $tableName)
$table->where('field', $explorer->table($tableName)->select('col')) field IN (SELECT col FROM $tableName)

Μπορείτε να δώσετε placeholder ακόμη και χωρίς τον τελεστή στήλης. Αυτές οι κλήσεις είναι οι ίδιες.

$table->where('id = ? OR id = ?', 1, 2);
$table->where('id ? OR id ?', 1, 2);

Αυτή η λειτουργία επιτρέπει τη δημιουργία του σωστού τελεστή με βάση την τιμή:

$table->where('id ?', 2);    // id = 2
$table->where('id ?', null); // id IS NULL
$table->where('id', $ids);   // id IN (...)

Λειτουργεί και για άδειους πίνακες:

$table->where('id', []);       // id IS NULL AND FALSE
$table->where('id NOT', []);   // id IS NULL OR TRUE
$table->where('NOT (id ?)', $ids);  // NOT (id IS NULL AND FALSE)

// αυτό θα προκαλέσει μια εξαίρεση, αυτή η σύνταξη δεν υποστηρίζεται
$table->where('NOT id ?', $ids);

whereOr()

Παράδειγμα χρήσης χωρίς παραμέτρους:

// WHERE (user_id IS NULL) OR (SUM(`field1`) > SUM(`field2`))
$table->whereOr([
	'user_id IS NULL',
	'SUM(field1) > SUM(field2)',
]);

Χρησιμοποιούμε τις παραμέτρους. Εάν δεν καθορίσετε έναν τελεστή, ο Nette Database Explorer θα προσθέσει αυτόματα τον κατάλληλο:

// WHERE (`field1` IS NULL) OR (`field2` IN (3, 5)) OR (`amount` > 11)
$table->whereOr([
	'field1' => null,
	'field2' => [3, 5],
	'amount >' => 11,
]);

Το κλειδί μπορεί να περιέχει μια έκφραση που περιέχει ερωτηματικά μπαλαντέρ και στη συνέχεια να περάσετε παραμέτρους στην τιμή:

// WHERE (`id` > 12) OR (ROUND(`id`, 5) = 3)
$table->whereOr([
	'id > ?' => 12,
	'ROUND(id, ?) = ?' => [5, 3],
]);

order()

Παραδείγματα χρήσης:

$table->order('field1');               // ORDER BY `field1`
$table->order('field1 DESC, field2');  // ORDER BY `field1` DESC, `field2`
$table->order('field = ? DESC', 123);  // ORDER BY `field` = 123 DESC

select()

Παραδείγματα χρήσης:

$table->select('field1');                 // SELECT `field1`
$table->select('col, UPPER(col) AS abc'); // SELECT `col`, UPPER(`col`) AS abc
$table->select('SUBSTR(title, ?)', 3);    // SELECT SUBSTR(`title`, 3)

limit()

Παραδείγματα χρήσης:

$table->limit(1);     // LIMIT 1
$table->limit(1, 10); // LIMIT 1 OFFSET 10

page()

Ένας εναλλακτικός τρόπος για τον καθορισμό του ορίου και της μετατόπισης:

$page = 5;
$itemsPerPage = 10;
$table->page($page, $itemsPerPage); // LIMIT 10 OFFSET 40

$lastPage:

$table->page($page, $itemsPerPage, $lastPage);

group()

Παραδείγματα χρήσης:

$table->group('field1');          // GROUP BY `field1`
$table->group('field1, field2');  // GROUP BY `field1`, `field2`

having()

Παραδείγματα χρήσης:

$table->having('COUNT(items) >', 100);  // HAVING COUNT(`items`) > 100

Φιλτράρισμα με βάση άλλη τιμή πίνακα

Αρκετά συχνά χρειάζεται να φιλτράρετε τα αποτελέσματα με βάση κάποια συνθήκη που αφορά έναν άλλο πίνακα της βάσης δεδομένων. Αυτού του είδους οι συνθήκες απαιτούν σύνδεση πινάκων. Ωστόσο, δεν χρειάζεται πλέον να τα γράφετε.

Ας υποθέσουμε ότι πρέπει να πάρετε όλα τα βιβλία των οποίων το όνομα του συγγραφέα είναι ‚Jon‘. Το μόνο που χρειάζεται να γράψετε είναι το κλειδί σύνδεσης της σχέσης και το όνομα της στήλης στον συνδεδεμένο πίνακα. Το κλειδί σύνδεσης προκύπτει από τη στήλη που αναφέρεται στον πίνακα που θέλετε να συνδέσετε. Στο παράδειγμά μας (βλ. το σχήμα db) είναι η στήλη author_id, και αρκεί να χρησιμοποιήσετε μόνο το πρώτο μέρος της – author (η κατάληξη _id μπορεί να παραλειφθεί). name είναι μια στήλη του πίνακα author που θέλουμε να χρησιμοποιήσουμε. Μια συνθήκη για τον μεταφραστή βιβλίων (η οποία συνδέεται με τη στήλη translator_id ) μπορεί να δημιουργηθεί εξίσου εύκολα.

$books = $explorer->table('book');
$books->where('author.name LIKE ?', '%Jon%');
$books->where('translator.name', 'David Grudl');

Η λογική του κλειδιού σύνδεσης καθοδηγείται από την εφαρμογή των συμβάσεων. Σας ενθαρρύνουμε να χρησιμοποιήσετε το DiscoveredConventions, το οποίο αναλύει τα ξένα κλειδιά σας και σας επιτρέπει να εργάζεστε εύκολα με αυτές τις σχέσεις.

Η σχέση μεταξύ του βιβλίου και του συγγραφέα του είναι 1:N. Η αντίστροφη σχέση είναι επίσης δυνατή. Την ονομάζουμε backjoin. Ρίξτε μια ματιά σε ένα άλλο παράδειγμα. Θα θέλαμε να αντλήσουμε όλους τους συγγραφείς, οι οποίοι έχουν γράψει περισσότερα από 3 βιβλία. Για να κάνουμε την αντίστροφη σύνδεση χρησιμοποιούμε τη δήλωση : (colon). Colon means that the joined relationship means hasMany (and it's quite logical too, as two dots are more than one dot). Unfortunately, the Selection class isn't smart enough, so we have to help with the aggregation and provide a GROUP BY, επίσης η συνθήκη πρέπει να γραφτεί με τη μορφή της δήλωσης HAVING.

$authors = $explorer->table('author');
$authors->group('author.id')
	->having('COUNT(:book.id) > 3');

Ίσως έχετε παρατηρήσει ότι η έκφραση σύνδεσης αναφέρεται στο βιβλίο, αλλά δεν είναι σαφές, αν κάνουμε σύνδεση μέσω του author_id ή του translator_id. Στο παραπάνω παράδειγμα, η Selection κάνει σύνδεση μέσω της στήλης author_id επειδή βρέθηκε μια αντιστοιχία με τον πίνακα προέλευσης – τον πίνακα author. Αν δεν υπήρχε τέτοια αντιστοιχία και υπήρχαν περισσότερες δυνατότητες, η Nette θα έριχνε AmbiguousReferenceKeyException.

Για να πραγματοποιήσετε μια σύνδεση μέσω της στήλης translator_id, δώστε μια προαιρετική παράμετρο εντός της έκφρασης σύνδεσης.

$authors = $explorer->table('author');
$authors->group('author.id')
	->having('COUNT(:book(translator).id) > 3');

Ας ρίξουμε μια ματιά σε μερικές πιο δύσκολες εκφράσεις σύνδεσης.

Θα θέλαμε να βρούμε όλους τους συγγραφείς που έχουν γράψει κάτι για την PHP. Όλα τα βιβλία έχουν ετικέτες, οπότε θα πρέπει να επιλέξουμε εκείνους τους συγγραφείς που έχουν γράψει κάποιο βιβλίο με την ετικέτα PHP.

$authors = $explorer->table('author');
$authors->where(':book:book_tags.tag.name', 'PHP')
	->group('author.id')
	->having('COUNT(:book:book_tags.tag.id) > 0');

Συγκεντρωτικά ερωτήματα

$table->count('*') Λήψη αριθμού γραμμών
$table->count("DISTINCT $column") Λήψη αριθμού διαφορετικών τιμών
$table->min($column) Λήψη ελάχιστης τιμής
$table->max($column) Λήψη μέγιστης τιμής
$table->sum($column) Λήψη του αθροίσματος όλων των τιμών
$table->aggregation("GROUP_CONCAT($column)") Εκτέλεση οποιασδήποτε συνάρτησης συνάθροισης

Η μέθοδος count() χωρίς καθορισμένες παραμέτρους επιλέγει όλες τις εγγραφές και επιστρέφει το μέγεθος του πίνακα, κάτι που είναι πολύ αναποτελεσματικό. Για παράδειγμα, αν πρέπει να υπολογίσετε τον αριθμό των γραμμών για τη σελιδοποίηση, καθορίζετε πάντα το πρώτο όρισμα.

Διαφυγή & παραπομπή

Ο Database Explorer είναι έξυπνος και αποφεύγει τις παραμέτρους και τα αναγνωριστικά εισαγωγικά για εσάς. Ωστόσο, πρέπει να τηρούνται αυτοί οι βασικοί κανόνες:

  • λέξεις-κλειδιά, συναρτήσεις, διαδικασίες πρέπει να είναι κεφαλαία.
  • οι στήλες και οι πίνακες πρέπει να είναι πεζά
  • να περνάτε μεταβλητές ως παραμέτρους, να μην τις συνυπολογίζετε
->where('name like ?', 'John'); // ΛΑΘΟΣ! γενιές: `name` `like` ?
->where('name LIKE ?', 'John'); // ΣΩΣΤΟ

->where('KEY = ?', $value); // ΛΑΘΟΣ! Το KEY είναι λέξη κλειδί
->where('key = ?', $value); // ΣΩΣΤΟ: `key` = ?

->where('name = ' . $name); // ΛΑΘΟΣ! sql injection!
->where('name = ?', $name); // ΣΩΣΤΟ

->select('DATE_FORMAT(created, "%d.%m.%Y")'); // Περνάτε μεταβλητές ως παραμέτρους, μην κάνετε συνένωση
->select('DATE_FORMAT(created, ?)', '%d.%m.%Y'); // ΣΩΣΤΟ

Η λανθασμένη χρήση μπορεί να δημιουργήσει κενά ασφαλείας

Λήψη δεδομένων

foreach ($table as $id => $row) Επανάληψη σε όλες τις γραμμές του αποτελέσματος
$row = $table->get($id) Λήψη μεμονωμένης γραμμής με αναγνωριστικό $id από τον πίνακα
$row = $table->fetch() Λήψη της επόμενης γραμμής από το αποτέλεσμα
$array = $table->fetchPairs($key, $value) Λήψη όλων των τιμών σε συσχετιστικό πίνακα
$array = $table->fetchPairs($key) Λήψη όλων των γραμμών σε συσχετιστικό πίνακα
count($table) Λήψη αριθμού γραμμών στο σύνολο αποτελεσμάτων

Εισαγωγή, ενημέρωση και διαγραφή

Η μέθοδος insert() δέχεται πίνακα αντικειμένων Traversable (για παράδειγμα ArrayHash που επιστρέφει μορφές):

$row = $explorer->table('users')->insert([
	'name' => $name,
	'year' => $year,
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978)

Εάν έχει οριστεί πρωτεύον κλειδί στον πίνακα, επιστρέφεται ένα αντικείμενο ActiveRow που περιέχει την εισαγόμενη γραμμή.

Πολλαπλή εισαγωγή:

$explorer->table('users')->insert([
	[
		'name' => 'Jim',
		'year' => 1978,
	], [
		'name' => 'Jack',
		'year' => 1987,
	],
]);
// INSERT INTO users (`name`, `year`) VALUES ('Jim', 1978), ('Jack', 1987)

DateTime μπορούν να περάσουν ως παράμετροι:

$explorer->table('users')->insert([
	'name' => $name,
	'created' => new DateTime, // ή $explorer::literal('NOW()')
	'avatar' => fopen('image.gif', 'r'), // εισάγει το αρχείο
]);

Ενημέρωση (επιστρέφει τον αριθμό των επηρεαζόμενων γραμμών):

$count = $explorer->table('users')
	->where('id', 10) // πρέπει να καλείται πριν από την update()
	->update([
		'name' => 'Ned Stark'
	]);
// UPDATE `users` SET `name`='Ned Stark' WHERE (`id` = 10)

Για την ενημέρωση μπορούμε να χρησιμοποιήσουμε τους τελεστές += a -=:

$explorer->table('users')
	->update([
		'age+=' => 1, // see +=
	]);
// UPDATE users SET `age` = `age` + 1

Διαγραφή (επιστρέφει τον αριθμό των διαγραμμένων γραμμών):

$count = $explorer->table('users')
	->where('id', 10)
	->delete();
// DELETE FROM `users` WHERE (`id` = 10)

Εργασία με σχέσεις

Έχει μία σχέση

Το Has one relation είναι μια συνηθισμένη περίπτωση χρήσης. Το βιβλίο έχει έναν συγγραφέα. Το βιβλίο έχει έναν μεταφραστή. Η λήψη σχετικής σειράς γίνεται κυρίως με τη μέθοδο ref(). Δέχεται δύο ορίσματα: το όνομα του πίνακα-στόχου και τη στήλη σύνδεσης της πηγής. Βλέπε παράδειγμα:

$book = $explorer->table('book')->get(1);
$book->ref('author', 'author_id');

author Το πρωτεύον κλειδί του συγγραφέα αναζητείται από τη στήλη book.author_id. Η μέθοδος Ref() επιστρέφει την περίπτωση ActiveRow ή null αν δεν υπάρχει κατάλληλη εγγραφή. Η γραμμή που επιστρέφεται είναι μια περίπτωση της ActiveRow, οπότε μπορούμε να εργαστούμε με αυτήν με τον ίδιο τρόπο όπως με την εγγραφή του βιβλίου.

$author = $book->ref('author', 'author_id');
$author->name;
$author->born;

// ή απευθείας
$book->ref('author', 'author_id')->name;
$book->ref('author', 'author_id')->born;

Το βιβλίο έχει επίσης έναν μεταφραστή, οπότε το να πάρουμε το όνομα του μεταφραστή είναι αρκετά εύκολο.

$book->ref('author', 'translator_id')->name

Όλα αυτά είναι μια χαρά, αλλά είναι κάπως δυσκίνητα, δεν νομίζετε; Η Εξερεύνηση βάσης δεδομένων περιέχει ήδη τους ορισμούς των ξένων κλειδιών, οπότε γιατί να μην τους χρησιμοποιήσετε αυτόματα; Ας το κάνουμε αυτό!

Αν καλέσουμε ιδιότητα, η οποία δεν υπάρχει, το ActiveRow προσπαθεί να επιλύσει το όνομα της καλούσας ιδιότητας ως σχέση ‚έχει μία‘. Η λήψη αυτής της ιδιότητας είναι το ίδιο με την κλήση της μεθόδου ref() με ένα μόνο όρισμα. Θα ονομάσουμε το μοναδικό όρισμα key. Το κλειδί θα επιλυθεί σε συγκεκριμένη σχέση ξένου κλειδιού. Το περασμένο κλειδί συγκρίνεται με τις στήλες της γραμμής, και αν ταιριάζει, το ξένο κλειδί που ορίζεται στην αντίστοιχη στήλη χρησιμοποιείται για τη λήψη δεδομένων από τον σχετικό πίνακα-στόχο. Βλέπε παράδειγμα:

$book->author->name;
// το ίδιο με
$book->ref('author')->name;

Η περίπτωση ActiveRow δεν έχει στήλη author. Όλες οι στήλες βιβλίων αναζητούνται για μια αντιστοιχία με το key. Η αντιστοίχιση σε αυτή την περίπτωση σημαίνει ότι το όνομα της στήλης πρέπει να περιέχει το κλειδί. Έτσι, στο παραπάνω παράδειγμα, η στήλη author_id περιέχει τη συμβολοσειρά ‚author‘ και επομένως ταιριάζει με το κλειδί ‚author‘. Αν θέλετε να βρείτε τον μεταφραστή του βιβλίου, μπορείτε απλώς να χρησιμοποιήσετε π.χ. το ‚translator‘ ως κλειδί, επειδή το κλειδί ‚translator‘ θα ταιριάζει με τη στήλη translator_id. Μπορείτε να μάθετε περισσότερα για τη λογική αντιστοίχισης κλειδιών στο κεφάλαιο Joining expressions.

echo $book->title . ': ';
echo $book->author->name;
if ($book->translator) {
	echo ' (translated by ' . $book->translator->name . ')';
}

Αν θέλετε να αντλήσετε πολλά βιβλία, θα πρέπει να χρησιμοποιήσετε την ίδια προσέγγιση. Ο Nette Database Explorer θα αντλήσει συγγραφείς και μεταφραστές για όλα τα βιβλία που θα αντλήσετε ταυτόχρονα.

$books = $explorer->table('book');
foreach ($books as $book) {
	echo $book->title . ': ';
	echo $book->author->name;
	if ($book->translator) {
		echo ' (translated by ' . $book->translator->name . ')';
	}
}

Ο κώδικας θα εκτελέσει μόνο αυτά τα 3 ερωτήματα:

SELECT * FROM `book`;
SELECT * FROM `author` WHERE (`id` IN (1, 2, 3)); -- ids of fetched books from author_id column
SELECT * FROM `author` WHERE (`id` IN (2, 3));    -- ids of fetched books from translator_id column

Has Many Relation

Η σχέση ‚Έχει πολλούς‘ είναι απλώς η αντίστροφη σχέση ‚έχει έναν‘. Ο συγγραφέας έχει γράψει πολλά βιβλία. Ο συγγραφέας έχει μεταφράσει πολλά βιβλία. Όπως μπορείτε να δείτε, αυτός ο τύπος σχέσης είναι λίγο πιο δύσκολος επειδή η σχέση είναι ‚ονομαστική‘ (‚έγραψε‘, ‚μετέφρασε‘). Η περίπτωση ActiveRow έχει τη μέθοδο related(), η οποία θα επιστρέψει πίνακα σχετικών καταχωρήσεων. Οι εγγραφές είναι επίσης περιπτώσεις ActiveRow. Δείτε το παράδειγμα παρακάτω:

$author = $explorer->table('author')->get(11);
echo $author->name . ' has written:';

foreach ($author->related('book.author_id') as $book) {
	echo $book->title;
}

echo 'and translated:';
foreach ($author->related('book.translator_id') as $book) {
	echo $book->title;
}

Η μέθοδος related() δέχεται την πλήρη περιγραφή join που περνάει ως δύο ορίσματα ή ως ένα όρισμα ενωμένο με τελεία. Το πρώτο όρισμα είναι ο πίνακας-στόχος, το δεύτερο είναι η στήλη-στόχος.

$author->related('book.translator_id');
// το ίδιο με
$author->related('book', 'translator_id');

Μπορείτε να χρησιμοποιήσετε τις ευρετικές λειτουργίες του Nette Database Explorer που βασίζονται σε ξένα κλειδιά και να δώσετε μόνο το όρισμα key. Το κλειδί θα συγκριθεί με όλα τα ξένα κλειδιά που δείχνουν στον τρέχοντα πίνακα (author table). Εάν υπάρχει ταύτιση, ο Nette Database Explorer θα χρησιμοποιήσει αυτό το ξένο κλειδί, διαφορετικά θα πετάξει Nette\InvalidArgumentException ή AmbiguousReferenceKeyException. Μπορείτε να βρείτε περισσότερα για τη λογική αντιστοίχισης κλειδιών στο κεφάλαιο Joining expressions.

Φυσικά, μπορείτε να καλέσετε τις σχετικές μεθόδους για όλους τους συγγραφείς που ανακτήθηκαν, ο Nette Database Explorer θα ανακτήσει και πάλι τα κατάλληλα βιβλία ταυτόχρονα.

$authors = $explorer->table('author');
foreach ($authors as $author) {
	echo $author->name . ' has written:';
	foreach ($author->related('book') as $book) {
		$book->title;
	}
}

Το παραπάνω παράδειγμα θα εκτελέσει μόνο δύο ερωτήματα:

SELECT * FROM `author`;
SELECT * FROM `book` WHERE (`author_id` IN (1, 2, 3)); -- ids of fetched authors

Δημιουργία Explorer χειροκίνητα

Μια σύνδεση βάσης δεδομένων μπορεί να δημιουργηθεί χρησιμοποιώντας τη διαμόρφωση της εφαρμογής. Σε τέτοιες περιπτώσεις δημιουργείται μια υπηρεσία Nette\Database\Explorer και μπορεί να περάσει ως εξάρτηση χρησιμοποιώντας το DI container.

Ωστόσο, εάν ο Nette Database Explorer χρησιμοποιείται ως αυτόνομο εργαλείο, πρέπει να δημιουργηθεί χειροκίνητα μια περίπτωση του αντικειμένου Nette\Database\Explorer.

// $storage implements Nette\Caching\Storage:
$storage = new Nette\Caching\Storages\FileStorage($tempDir);
$connection = new Nette\Database\Connection($dsn, $user, $password);
$structure = new Nette\Database\Structure($connection, $storage);
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);