Δρομολόγηση

Ο δρομολογητής είναι υπεύθυνος για τα πάντα σχετικά με τις διευθύνσεις URL, ώστε να μην χρειάζεται πλέον να τις σκέφτεστε. Θα δείξουμε:

  • πώς να ρυθμίσετε τον δρομολογητή ώστε οι διευθύνσεις URL να είναι όπως τις θέλετε
  • μερικές σημειώσεις σχετικά με την ανακατεύθυνση SEO
  • και θα σας δείξουμε πώς να γράψετε τον δικό σας δρομολογητή

Οι πιο ανθρώπινες διευθύνσεις URL (ή cool ή όμορφες διευθύνσεις URL) είναι πιο εύχρηστες, πιο αξιομνημόνευτες και συμβάλλουν θετικά στο SEO. Η Nette έχει αυτό κατά νου και ανταποκρίνεται πλήρως στις επιθυμίες των προγραμματιστών. Μπορείτε να σχεδιάσετε τη δομή URL για την εφαρμογή σας ακριβώς όπως τη θέλετε. Μπορείτε να τη σχεδιάσετε ακόμη και αφού η εφαρμογή είναι έτοιμη, καθώς αυτό μπορεί να γίνει χωρίς αλλαγές στον κώδικα ή στο πρότυπο. Ορίζεται με κομψό τρόπο σε ένα μόνο σημείο, στο δρομολογητή, και δεν είναι διάσπαρτη με τη μορφή σχολίων σε όλους τους παρουσιαστές.

Ο δρομολογητής στη Nette είναι ιδιαίτερος επειδή είναι διπλής κατεύθυνσης, μπορεί τόσο να αποκωδικοποιεί τις διευθύνσεις URL των αιτήσεων HTTP όσο και να δημιουργεί συνδέσμους. Έτσι, παίζει ζωτικό ρόλο στην εφαρμογή Nette, επειδή αποφασίζει ποιος παρουσιαστής και ποια ενέργεια θα εκτελέσει την τρέχουσα αίτηση, και χρησιμοποιείται επίσης για τη δημιουργία URL στο πρότυπο κ.λπ.

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

Συλλογή διαδρομών

Ο πιο ευχάριστος τρόπος για τον ορισμό των διευθύνσεων URL στην εφαρμογή είναι μέσω της κλάσης Nette\Application\Routers\RouteList. Ο ορισμός αποτελείται από μια λίστα των λεγόμενων διαδρομών, δηλαδή μάσκες διευθύνσεων URL και των σχετικών παρουσιαστών και ενεργειών τους με τη χρήση ενός απλού API. Δεν χρειάζεται να δώσουμε όνομα στις διαδρομές.

$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...

Το παράδειγμα λέει ότι αν ανοίξουμε το https://any-domain.com/rss.xml με την ενέργεια rss, αν https://domain.com/article/12 με την ενέργεια view κ.λπ. Εάν δεν βρεθεί κατάλληλη διαδρομή, η εφαρμογή Nette Application ανταποκρίνεται με την απόρριψη μιας εξαίρεσης BadRequestException, η οποία εμφανίζεται στο χρήστη ως σελίδα σφάλματος 404 Not Found.

Σειρά των διαδρομών

Η σειρά με την οποία παρατίθενται οι διαδρομές είναι πολύ σημαντική επειδή αξιολογούνται διαδοχικά από πάνω προς τα κάτω. Ο κανόνας είναι ότι δηλώνουμε τις διαδρομές από τις ειδικές προς τις γενικές:

// ΛΑΘΟΣ: Το 'rss.xml' ταιριάζει με την πρώτη διαδρομή και το παρερμηνεύει ως <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// ΚΑΛΗ
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');

Οι διαδρομές αξιολογούνται επίσης από πάνω προς τα κάτω όταν δημιουργούνται σύνδεσμοι:

// ΛΑΘΟΣ: δημιουργεί έναν σύνδεσμο στο 'Feed:rss' ως 'admin/feed/rss'
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// ΚΑΛΟ
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');

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

Μάσκα και παράμετροι

Η μάσκα περιγράφει τη σχετική διαδρομή με βάση τη ρίζα της τοποθεσίας. Η απλούστερη μάσκα είναι μια στατική διεύθυνση URL:

$router->addRoute('products', 'Products:default');

Συχνά οι μάσκες περιέχουν τις λεγόμενες παραμέτρους. Αυτές περικλείονται σε αγκύλες (π.χ. <year>) και διαβιβάζονται στον παρουσιαστή-στόχο, για παράδειγμα στη μέθοδο renderShow(int $year) ή στη μόνιμη παράμετρο $year:

$router->addRoute('chronicle/<year>', 'History:show');

Το παράδειγμα λέει ότι αν ανοίξουμε το https://any-domain.com/chronicle/2020 και η ενέργεια show με την παράμετρο year: 2020.

Μπορούμε να καθορίσουμε μια προεπιλεγμένη τιμή για τις παραμέτρους απευθείας στη μάσκα και έτσι γίνεται προαιρετική:

$router->addRoute('chronicle/<year=2020>', 'History:show');

Η διαδρομή θα δέχεται τώρα τη διεύθυνση URL https://any-domain.com/chronicle/ με την παράμετρο year: 2020.

Φυσικά, το όνομα του παρουσιαστή και της ενέργειας μπορεί επίσης να είναι παράμετρος. Για παράδειγμα:

$router->addRoute('<presenter>/<action>', 'Home:default');

Αυτή η διαδρομή δέχεται, για παράδειγμα, μια διεύθυνση URL της μορφής /article/edit ή /catalog/list και τις μεταφράζει σε παρουσιαστές και ενέργειες Article:edit ή Catalog:list.

Δίνει επίσης στις παραμέτρους presenter και action προεπιλεγμένες τιμέςHome και default και επομένως είναι προαιρετικές. Έτσι, η διαδρομή δέχεται επίσης ένα URL /article και το μεταφράζει ως Article:default. Ή αντίστροφα, ένας σύνδεσμος προς το Product:default δημιουργεί μια διαδρομή /product, ένας σύνδεσμος προς την προεπιλεγμένη Home:default δημιουργεί μια διαδρομή /.

Η μάσκα μπορεί να περιγράφει όχι μόνο τη σχετική διαδρομή με βάση τη ρίζα του ιστότοπου, αλλά και την απόλυτη διαδρομή όταν αρχίζει με μια κάθετο, ή ακόμη και ολόκληρη την απόλυτη διεύθυνση URL όταν αρχίζει με δύο κάθετους:

// σχετική διαδρομή προς τη ρίζα του εγγράφου της εφαρμογής
$router->addRoute('<presenter>/<action>', /* ... */);

// απόλυτη διαδρομή, σχετική με το όνομα κεντρικού υπολογιστή του διακομιστή
$router->addRoute('/<presenter>/<action>', /* ... */);

// απόλυτη διεύθυνση URL που περιλαμβάνει το όνομα κεντρικού υπολογιστή (αλλά σχετικό με το σχήμα)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// απόλυτη διεύθυνση URL που περιλαμβάνει το σχήμα
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Εκφράσεις επικύρωσης

Μια συνθήκη επικύρωσης μπορεί να καθοριστεί για κάθε παράμετρο χρησιμοποιώντας κανονική έκφραση. Για παράδειγμα, ας ορίσουμε το id να είναι μόνο αριθμητικό, χρησιμοποιώντας το \d+ regexp:

$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);

Η προεπιλεγμένη κανονική έκφραση για όλες τις παραμέτρους είναι [^/]+, δηλαδή όλα εκτός από την κάθετο. Εάν μια παράμετρος πρέπει να ταιριάζει και με μια κάθετο, ορίζουμε την κανονική έκφραση σε .+.

// δέχεται https://example.com/a/b/c, η διαδρομή είναι 'a/b/c'
$router->addRoute('<path .+>', /* ... */);

Προαιρετικές ακολουθίες

Οι τετράγωνες αγκύλες υποδηλώνουν προαιρετικά μέρη της μάσκας. Οποιοδήποτε μέρος της μάσκας μπορεί να οριστεί ως προαιρετικό, συμπεριλαμβανομένων εκείνων που περιέχουν παραμέτρους:

$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// Αποδεκτές διευθύνσεις URL:      Παράμετροι:
//   /en/download        lang => en, name => download
//   /download           lang => null, name => download

Φυσικά, όταν μια παράμετρος είναι μέρος μιας προαιρετικής ακολουθίας, γίνεται επίσης προαιρετική. Εάν δεν έχει προεπιλεγμένη τιμή, θα είναι null.

Τα προαιρετικά τμήματα μπορούν επίσης να βρίσκονται στον τομέα:

$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);

Οι ακολουθίες μπορούν να φωλιάζουν και να συνδυάζονται ελεύθερα:

$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// Αποδεκτές διευθύνσεις URL:
//  /en/hello
//  /en-us/hello
//  /hello
//  /hello/page-12

Η γεννήτρια URL προσπαθεί να κρατήσει τη διεύθυνση URL όσο το δυνατόν συντομότερη, οπότε ό,τι μπορεί να παραλειφθεί παραλείπεται. Επομένως, για παράδειγμα, μια διαδρομή index[.html] παράγει μια διαδρομή /index. Μπορείτε να αντιστρέψετε αυτή τη συμπεριφορά γράφοντας ένα θαυμαστικό μετά την αριστερή τετράγωνη αγκύλη:

// δέχεται τόσο το /hello όσο και το /hello.html, παράγει το /hello
$router->addRoute('<name>[.html]', /* ... */);

// δέχεται τόσο το /hello όσο και το /hello.html, παράγει το /hello.html
$router->addRoute('<name>[!.html]', /* ... */);

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

$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// ισούται με:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);

Για να αλλάξετε τον τρόπο με τον οποίο παράγεται η δεξιότερη κάθετος, δηλαδή αντί για /home/ να πάρετε ένα /home, προσαρμόστε τη διαδρομή με αυτόν τον τρόπο:

$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);

Αγριόχαρτα

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

  • %tld% = τομέας ανώτατου επιπέδου, π.χ. com ή org
  • %sld% = τομέας δεύτερου επιπέδου, π.χ. example
  • %domain% = τομέας χωρίς υποτομείς, π.χ. example.com
  • %host% = ολόκληρος κεντρικός υπολογιστής, π.χ. www.example.com
  • %basePath% = διαδρομή προς τον ριζικό κατάλογο
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);

Σύνθετος συμβολισμός

Ο στόχος μιας διαδρομής, που συνήθως γράφεται με τη μορφή Presenter:action, μπορεί επίσης να εκφραστεί με τη χρήση ενός πίνακα που ορίζει τις επιμέρους παραμέτρους και τις προεπιλεγμένες τιμές τους:

$router->addRoute('<presenter>/<action>[/<id \d+>]', [
	'presenter' => 'Home',
	'action' => 'default',
]);

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

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>[/<id>]', [
	'presenter' => [
		Route::Value => 'Home',
	],
	'action' => [
		Route::Value => 'default',
	],
	'id' => [
		Route::Pattern => '\d+',
	],
]);

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

Φίλτρα και μεταφράσεις

Είναι μια καλή πρακτική να γράφετε τον πηγαίο κώδικα στα αγγλικά, αλλά τι γίνεται αν θέλετε ο ιστότοπός σας να έχει μεταφρασμένο URL σε διαφορετική γλώσσα; Απλές διαδρομές, όπως: “Η γλώσσα που θα χρησιμοποιηθεί για να μεταφραστεί σε άλλη γλώσσα”:

$router->addRoute('<presenter>/<action>', 'Home:default');

θα δημιουργήσουν αγγλικές διευθύνσεις URL, όπως /product/123 ή /cart. Αν θέλουμε να έχουμε παρουσιαστές και ενέργειες στη διεύθυνση URL μεταφρασμένες στα γερμανικά (π.χ. /produkt/123 ή /einkaufswagen), μπορούμε να χρησιμοποιήσουμε ένα μεταφραστικό λεξικό. Για να το προσθέσουμε, χρειαζόμαστε ήδη μια “πιο ομιλητική” παραλλαγή της δεύτερης παραμέτρου:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// συμβολοσειρά στη διεύθυνση URL => παρουσιαστής
			'produkt' => 'Product',
			'einkaufswagen' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'liste' => 'list',
		],
	],
]);

Πολλαπλά κλειδιά λεξικού μπορούν να χρησιμοποιηθούν για τον ίδιο παρουσιαστή. Θα δημιουργήσουν διάφορα ψευδώνυμα για αυτόν. Το τελευταίο κλειδί θεωρείται ως η κανονική παραλλαγή (δηλαδή αυτή που θα υπάρχει στο παραγόμενο URL).

Ο πίνακας μεταφράσεων μπορεί να εφαρμοστεί σε οποιαδήποτε παράμετρο με αυτόν τον τρόπο. Ωστόσο, εάν η μετάφραση δεν υπάρχει, λαμβάνεται η αρχική τιμή. Μπορούμε να αλλάξουμε αυτή τη συμπεριφορά προσθέτοντας το Route::FilterStrict => true και τότε η διαδρομή θα απορρίπτει το URL αν η τιμή δεν υπάρχει στο λεξικό.

Εκτός από το μεταφραστικό λεξικό με τη μορφή πίνακα, είναι δυνατό να ορίσετε δικές σας μεταφραστικές συναρτήσεις:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>/<id>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterIn => function (string $s): string { /* ... */ },
		Route::FilterOut => function (string $s): string { /* ... */ },
	],
	'action' => 'default',
	'id' => null,
]);

Η συνάρτηση Route::FilterIn μετατρέπει μεταξύ της παραμέτρου στη διεύθυνση URL και της συμβολοσειράς, η οποία στη συνέχεια διαβιβάζεται στον παρουσιαστή, η συνάρτηση FilterOut εξασφαλίζει τη μετατροπή προς την αντίθετη κατεύθυνση.

Οι παράμετροι presenter, action και module έχουν ήδη προκαθορισμένα φίλτρα που μετατρέπουν μεταξύ του στυλ PascalCase ή camelCase και του kebab-case που χρησιμοποιείται στο URL. Η προεπιλεγμένη τιμή των παραμέτρων είναι ήδη γραμμένη στη μετασχηματισμένη μορφή, έτσι, για παράδειγμα, στην περίπτωση ενός παρουσιαστή, γράφουμε <presenter=ProductEdit> αντί για <presenter=product-edit>.

Γενικά φίλτρα

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

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => 'Home',
	'action' => 'default',
	null => [
		Route::FilterIn => function (array $params): array { /* ... */ },
		Route::FilterOut => function (array $params): array { /* ... */ },
	],
]);

Τα γενικά φίλτρα σας δίνουν τη δυνατότητα να προσαρμόσετε τη συμπεριφορά της διαδρομής με απολύτως οποιονδήποτε τρόπο. Μπορούμε να τα χρησιμοποιήσουμε, για παράδειγμα, για να τροποποιήσουμε παραμέτρους με βάση άλλες παραμέτρους. Για παράδειγμα, η μετάφραση <presenter> και <action> με βάση την τρέχουσα τιμή της παραμέτρου <lang>.

Εάν για μια παράμετρο έχει οριστεί ένα προσαρμοσμένο φίλτρο και υπάρχει ταυτόχρονα ένα γενικό φίλτρο, το προσαρμοσμένο FilterIn εκτελείται πριν από το γενικό και αντίστροφα το γενικό FilterOut εκτελείται πριν από το προσαρμοσμένο. Έτσι, εντός του γενικού φίλτρου βρίσκονται οι τιμές των παραμέτρων presenter και action γραμμένες σε PascalCase και camelCase.

Σημαία μονής κατεύθυνσης

Οι μονόδρομες διαδρομές χρησιμοποιούνται για τη διατήρηση της λειτουργικότητας παλαιών διευθύνσεων URL που η εφαρμογή δεν παράγει πλέον, αλλά εξακολουθεί να δέχεται. Τις επισημαίνουμε με το OneWay:

// παλιά διεύθυνση URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// νέα διεύθυνση URL /product/123
$router->addRoute('product/<id>', 'Product:detail');

Κατά την πρόσβαση στην παλιά διεύθυνση URL, ο παρουσιαστής ανακατευθύνει αυτόματα στη νέα διεύθυνση URL, ώστε οι μηχανές αναζήτησης να μην ευρετηριάζουν αυτές τις σελίδες δύο φορές (βλ. SEO και κανονικοποίηση).

Δυναμική δρομολόγηση με Callbacks

Η δυναμική δρομολόγηση με callbacks σας επιτρέπει να αναθέτετε απευθείας λειτουργίες (callbacks) σε διαδρομές, οι οποίες θα εκτελούνται όταν επισκέπτεστε την καθορισμένη διαδρομή. Αυτή η ευέλικτη λειτουργία σας επιτρέπει να δημιουργείτε γρήγορα και αποτελεσματικά διάφορα τελικά σημεία για την εφαρμογή σας:

$router->addRoute('test', function () {
	echo 'You are at the /test address';
});

Μπορείτε επίσης να ορίσετε παραμέτρους στη μάσκα, οι οποίες θα μεταβιβαστούν αυτόματα στο callback σας:

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Welcome to the Czech version of our website!',
		'en' => 'Welcome to the English version of our website!',
	};
});

Ενότητες

Αν έχουμε περισσότερες διαδρομές που ανήκουν σε ένα module, μπορούμε να χρησιμοποιήσουμε το withModule() για να τις ομαδοποιήσουμε:

$router = new RouteList;
$router->withModule('Forum') // οι ακόλουθοι δρομολογητές αποτελούν μέρος της ενότητας Forum
	->addRoute('rss', 'Feed:rss') // παρουσιαστής είναι το Forum:Feed
	->addRoute('<presenter>/<action>')

	->withModule('Admin') // οι ακόλουθοι δρομολογητές αποτελούν μέρος της ενότητας Forum:Admin
		->addRoute('sign:in', 'Sign:in');

Μια εναλλακτική λύση είναι να χρησιμοποιήσουμε την παράμετρο module:

// URL διαχείριση/πίνακας/προεπιλογή χαρτών στον παρουσιαστή Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Υποτομείς

Οι συλλογές διαδρομών μπορούν να ομαδοποιηθούν ανά υποτομέα:

$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');

Μπορείτε επίσης να χρησιμοποιήσετε μπαλαντέρ στο όνομα του τομέα σας:

$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...

Πρόθεμα διαδρομής

Οι συλλογές διαδρομών μπορούν να ομαδοποιηθούν με βάση τη διαδρομή στη διεύθυνση URL:

$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // ταιριάζει με τη διεύθυνση URL /eshop/rss
	->addRoute('<presenter>/<action>'); // ταιριάζει με τη διεύθυνση URL /eshop/<presenter>/<action>

Συνδυασμοί

Η παραπάνω χρήση μπορεί να συνδυαστεί:

$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...

Παράμετροι ερώτησης

Οι μάσκες μπορούν επίσης να περιέχουν παραμέτρους ερωτήματος (παράμετροι μετά το ερωτηματικό στη διεύθυνση URL). Δεν μπορούν να ορίσουν μια έκφραση επικύρωσης, αλλά μπορούν να αλλάξουν το όνομα με το οποίο μεταβιβάζονται στον παρουσιαστή:

// χρήση της παραμέτρου ερωτήματος 'cat' ως 'categoryId' στην εφαρμογή
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Foo Parameters

Πάμε πιο βαθιά τώρα. Οι παράμετροι Foo είναι βασικά μη ονομαστικές παράμετροι που επιτρέπουν να ταιριάζουν με μια κανονική έκφραση. Η παρακάτω διαδρομή ταιριάζει με τις /index, /index.html, /index.htm και /index.php:

$router->addRoute('index<? \.html?|\.php|>', /* ... */);

Είναι επίσης δυνατό να ορίσετε ρητά μια συμβολοσειρά η οποία θα χρησιμοποιηθεί για τη δημιουργία URL. Η συμβολοσειρά πρέπει να τοποθετηθεί αμέσως μετά το ερωτηματικό. Η παρακάτω διαδρομή είναι παρόμοια με την προηγούμενη, αλλά παράγει το /index.html αντί για το /index επειδή η συμβολοσειρά .html έχει οριστεί ως “παραγόμενη τιμή”.

$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);

Ενσωμάτωση

Για να συνδέσουμε τον δρομολογητή μας στην εφαρμογή, πρέπει να ενημερώσουμε το DI container σχετικά με αυτόν. Ο ευκολότερος τρόπος είναι να προετοιμάσουμε το εργοστάσιο που θα κατασκευάσει το αντικείμενο του δρομολογητή και να πούμε στη διαμόρφωση του δοχείου να το χρησιμοποιήσει. Ας πούμε λοιπόν ότι γράφουμε μια μέθοδο για το σκοπό αυτό App\Core\RouterFactory::createRouter():

namespace App\Core;

use Nette\Application\Routers\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute(/* ... */);
		return $router;
	}
}

Στη συνέχεια, γράφουμε στη διαμόρφωση:

services:
	- App\Core\RouterFactory::createRouter

Οποιεσδήποτε εξαρτήσεις, όπως μια σύνδεση βάσης δεδομένων κ.λπ., περνούν στη μέθοδο factory ως παράμετροι με τη χρήση αυτόματης σύνδεσης:

public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}

SimpleRouter

Ένας πολύ απλούστερος δρομολογητής από το Route Collection είναι ο SimpleRouter. Μπορεί να χρησιμοποιηθεί όταν δεν υπάρχει ανάγκη για μια συγκεκριμένη μορφή URL, όταν το mod_rewrite (ή εναλλακτικές λύσεις) δεν είναι διαθέσιμες ή όταν απλά δεν θέλουμε να ασχοληθούμε ακόμα με φιλικές προς το χρήστη διευθύνσεις URL.

Παράγει διευθύνσεις με αυτή περίπου τη μορφή:

http://example.com/?presenter=Product&action=detail&id=123

Η παράμετρος του κατασκευαστή SimpleRouter είναι ένας προεπιλεγμένος παρουσιαστής & ενέργεια, δηλαδή ενέργεια που θα εκτελεστεί αν ανοίξουμε π.χ. το http://example.com/ χωρίς πρόσθετες παραμέτρους.

// προεπιλογή παρουσιαστή 'Home' και δράσης 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Συνιστούμε τον ορισμό του SimpleRouter απευθείας στη διαμόρφωση:

services:
	- Nette\Application\Routers\SimpleRouter('Home:default')

SEO και Canonization

Το πλαίσιο αυξάνει το SEO (βελτιστοποίηση μηχανών αναζήτησης) αποτρέποντας την επανάληψη περιεχομένου σε διαφορετικές διευθύνσεις URL. Εάν πολλαπλές διευθύνσεις παραπέμπουν στον ίδιο προορισμό, π.χ. /index και /index.html, το πλαίσιο καθορίζει την πρώτη ως κύρια (κανονική) και ανακατευθύνει τις υπόλοιπες σε αυτήν χρησιμοποιώντας τον κωδικό HTTP 301. Χάρη σε αυτό, οι μηχανές αναζήτησης δεν ευρετηριάζουν τις σελίδες δύο φορές και δεν χαλάει η κατάταξη της σελίδας τους. .

Αυτή η διαδικασία ονομάζεται κανονικοποίηση. Η κανονική διεύθυνση URL είναι αυτή που δημιουργείται από τον δρομολογητή, δηλαδή από την πρώτη αντίστοιχη διαδρομή στη συλλογή χωρίς τη σημαία OneWay. Επομένως, στη συλλογή, παραθέτουμε πρώτα τις πρωτεύουσες διαδρομές.

Η κανονικοποίηση εκτελείται από τον παρουσιαστή, περισσότερα στο κεφάλαιο κανονικοποίηση.

HTTPS

Για να χρησιμοποιήσετε το πρωτόκολλο HTTPS, είναι απαραίτητο να το ενεργοποιήσετε στη φιλοξενία και να ρυθμίσετε τις παραμέτρους του διακομιστή.

Η ανακατεύθυνση ολόκληρου του ιστότοπου σε HTTPS πρέπει να πραγματοποιηθεί σε επίπεδο διακομιστή, για παράδειγμα με τη χρήση του αρχείου .htaccess στον ριζικό κατάλογο της εφαρμογής μας, με κωδικό HTTP 301. Οι ρυθμίσεις μπορεί να διαφέρουν ανάλογα με τη φιλοξενία και μοιάζουν κάπως έτσι:

<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>

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

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

// Θα δημιουργήσει μια διεύθυνση HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Θα δημιουργήσει μια διεύθυνση HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Αποσφαλμάτωση δρομολογητή

Η μπάρα δρομολόγησης που εμφανίζεται στο Tracy Bar είναι ένα χρήσιμο εργαλείο που εμφανίζει μια λίστα με τις διαδρομές και επίσης τις παραμέτρους που έχει λάβει ο δρομολογητής από τη διεύθυνση URL.

Η πράσινη μπάρα με το σύμβολο ✓ αντιπροσωπεύει τη διαδρομή που ταιριάζει με την τρέχουσα διεύθυνση URL, οι μπλε μπάρες με τα σύμβολα ≈ υποδεικνύουν τις διαδρομές που θα ταιριάζουν επίσης με τη διεύθυνση URL αν δεν τις προσπεράσει το πράσινο. Βλέπουμε περαιτέρω τον τρέχοντα παρουσιαστή & τη δράση.

Ταυτόχρονα, αν υπάρχει μια απροσδόκητη ανακατεύθυνση λόγω κανονικοποίησης, είναι χρήσιμο να κοιτάξουμε στη μπάρα ανακατεύθυνση για να δούμε πώς ο δρομολογητής αρχικά κατανόησε τη διεύθυνση URL και γιατί ανακατεύθυνε.

Κατά την αποσφαλμάτωση του δρομολογητή, συνιστάται να ανοίγετε τα Εργαλεία ανάπτυξης στο πρόγραμμα περιήγησης (Ctrl+Shift+I ή Cmd+Option+I) και να απενεργοποιείτε την προσωρινή μνήμη στο πάνελ Δικτύου, ώστε να μην αποθηκεύονται σε αυτήν οι ανακατευθύνσεις.

Απόδοση

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

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

routing:
	cache: true

Προσαρμοσμένος δρομολογητής

Οι ακόλουθες γραμμές προορίζονται για πολύ προχωρημένους χρήστες. Μπορείτε να δημιουργήσετε το δικό σας δρομολογητή και φυσικά να τον προσθέσετε στη συλλογή δρομολογίων σας. Ο δρομολογητής είναι μια υλοποίηση της διεπαφής Nette\Routing\Router με δύο μεθόδους:

use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;

class MyRouter implements Nette\Routing\Router
{
	public function match(HttpRequest $httpRequest): ?array
	{
		// ...
	}

	public function constructUrl(array $params, UrlScript $refUrl): ?string
	{
		// ...
	}
}

Η μέθοδος match επεξεργάζεται το τρέχον $httpRequest, από το οποίο μπορεί να ανακτηθεί όχι μόνο η διεύθυνση URL, αλλά και οι επικεφαλίδες κ.λπ., σε έναν πίνακα που περιέχει το όνομα του παρουσιαστή και τις παραμέτρους του. Εάν δεν μπορεί να επεξεργαστεί το αίτημα, επιστρέφει null. Κατά την επεξεργασία του αιτήματος, πρέπει να επιστρέψουμε τουλάχιστον τον παρουσιαστή και την ενέργεια. Το όνομα του παρουσιαστή είναι πλήρες και περιλαμβάνει τυχόν ενότητες:

[
	'presenter' => 'Front:Home',
	'action' => 'default',
]

Η μέθοδος constructUrl, από την άλλη πλευρά, παράγει μια απόλυτη διεύθυνση URL από τον πίνακα παραμέτρων. Μπορεί να χρησιμοποιήσει τις πληροφορίες από την παράμετρο $refUrl, η οποία είναι η τρέχουσα διεύθυνση URL.

Για να προσθέσετε προσαρμοσμένο δρομολογητή στη συλλογή διαδρομών, χρησιμοποιήστε τη μέθοδο add():

$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...

Ξεχωριστή χρήση

Με τον όρο διαχωρισμένη χρήση εννοούμε τη χρήση των δυνατοτήτων του δρομολογητή σε μια εφαρμογή που δεν χρησιμοποιεί την εφαρμογή Nette και τους παρουσιαστές. Σχεδόν όλα όσα παρουσιάσαμε σε αυτό το κεφάλαιο ισχύουν γι' αυτήν, με τις ακόλουθες διαφορές:

Έτσι και πάλι θα δημιουργήσουμε μια μέθοδο που θα κατασκευάσει ένα δρομολογητή, για παράδειγμα:

namespace App\Core;

use Nette\Routing\RouteList;

class RouterFactory
{
	public static function createRouter(): RouteList
	{
		$router = new RouteList;
		$router->addRoute('rss.xml', [
			'controller' => 'RssFeedController',
		]);
		$router->addRoute('article/<id \d+>', [
			'controller' => 'ArticleController',
		]);
		// ...
		return $router;
	}
}

Εάν χρησιμοποιείτε ένα DI container, το οποίο συνιστούμε, προσθέστε ξανά τη μέθοδο στη διαμόρφωση και στη συνέχεια λάβετε το δρομολογητή μαζί με την αίτηση HTTP από το container:

$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);

Ή θα δημιουργήσουμε αντικείμενα απευθείας:

$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();

Τώρα πρέπει να αφήσουμε τον δρομολογητή να λειτουργήσει:

$params = $router->match($httpRequest);
if ($params === null) {
	// δεν βρέθηκε αντίστοιχη διαδρομή, θα στείλουμε ένα σφάλμα 404
	exit;
}

// επεξεργαζόμαστε τις παραμέτρους που λάβαμε
$controller = $params['controller'];
// ...

Και αντίστροφα, θα χρησιμοποιήσουμε το δρομολογητή για να δημιουργήσουμε τη σύνδεση:

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());
έκδοση: 4.0