Yönlendirme
***********
<div class=perex>
Router, URL adresleriyle ilgili her şeyi üstlenir, böylece onları düşünmek zorunda kalmazsınız. Size şunları göstereceğiz:
- URL'lerin istediğiniz gibi görünmesi için router nasıl yapılandırılır
- SEO ve yönlendirme üzerine konuşacağız
- ve kendi router'ınızı nasıl yazacağınızı göstereceğiz
</div>
İnsana daha dostça URL'ler (havalı veya güzel URL de denir) daha kullanışlıdır, akılda daha iyi kalır ve SEO'ya olumlu katkı yapar. Nette bunu göz önünde bulundurur ve geliştiricilerin ihtiyaçlarını tam olarak karşılar. Uygulamanız için istediğiniz URL yapısını tam olarak tasarlayabilirsiniz. Üstelik uygulama zaten tamamlandığında bile tasarlayabilirsiniz, çünkü kodda veya şablonlarda hiçbir değişiklik gerektirmez. Tüm presenter'lara anotasyon olarak dağılmak yerine [tek bir yerde |#Entegrasyon], router'da zarif biçimde tanımlanır.
Nette'teki router olağanüstüdür, çünkü **çift yönlüdür**. Hem HTTP isteklerinden URL'leri çözebilir hem de bağlantı oluşturabilir. Böylece [Nette Application |how-it-works#Nette Application] içinde çok önemli bir rol oynar: yalnızca geçerli isteği hangi presenter'ın ve eylemin yürüteceğine karar vermekle kalmaz, şablonlarda vb. [URL üretmek |creating-links] için de kullanılır.
Ancak router'ın kullanımı bununla sınırlı değildir; onu presenter'ların hiç kullanılmadığı uygulamalarda, REST API'lerde vb. de kullanabilirsiniz. Daha fazla ayrıntı [#Bağımsız kullanım] bölümünde.
Rota koleksiyonu
================
Bir uygulamadaki URL adreslerinin yapısını tanımlamanın en hoş yolunu [api:Nette\Application\Routers\RouteList] sınıfı sunar. Tanım, rota denilen şeylerin listesinden oluşur; yani basit bir API ile URL adreslerinin maskeleri ve onlara bağlı presenter'lar ile eylemler. Rotaları hiçbir şekilde adlandırmamız gerekmez.
```php
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
```
Örnek şunu gösterir: tarayıcıda `https://domain.com/rss.xml` açarsak, `rss` eylemiyle `Feed` presenter'ı gösterilir. `https://domain.com/article/12` ise `view` eylemiyle `Article` presenter'ını gösterir vb. Uygun bir rota bulunamazsa Nette Application, kullanıcıya 404 Not Found hata sayfası olarak gösterilen bir [BadRequestException |api:Nette\Application\BadRequestException] fırlatarak yanıt verir.
Rotaların sırası
----------------
Rotaların listelenme **sırası** kesinlikle **çok önemlidir**, çünkü yukarıdan aşağıya sırayla değerlendirilirler. Kural şudur: rotaları **özelden genele** doğru bildiririz:
```php
// YANLIŞ: 'rss.xml' ilk rota tarafından yakalanır ve bu dize <slug> sanılır
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// DOĞRU
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
```
Bağlantı üretilirken de rotalar yukarıdan aşağıya değerlendirilir:
```php
// YANLIŞ: 'Feed:rss' bağlantısı 'admin/feed/rss' olarak üretilir
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');
// DOĞRU
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
```
Rotaları doğru kurmanın biraz beceri gerektirdiğini sizden saklamayacağız. Bunda ustalaşana kadar [yönlendirme paneli |#Router'ın hata ayıklaması] işinize yarayacak bir araç olacak.
Maske ve parametreler
---------------------
Maske, sitenin kök dizininden itibaren göreli yolu tanımlar. En basit maske statik bir URL'dir:
```php
$router->addRoute('products', 'Products:default');
```
Maskeler sıklıkla **parametre** denilen şeyleri içerir. Bunlar açılı parantez içine alınır (örneğin `<year>`) ve hedef presenter'a, örneğin `renderShow(int $year)` metoduna veya `$year` kalıcı parametresine aktarılır:
```php
$router->addRoute('chronicle/<year>', 'History:show');
```
Örnek şunu gösterir: tarayıcıda `https://example.com/chronicle/2020` açarsak, `show` eylemiyle ve `year: 2020` parametresiyle `History` presenter'ı gösterilir.
Parametreler için doğrudan maskede varsayılan bir değer belirtebiliriz; bu onları isteğe bağlı yapar:
```php
$router->addRoute('chronicle/<year=2020>', 'History:show');
```
Rota artık `https://example.com/chronicle/` URL'sini de kabul edecek ve yine `year: 2020` parametresiyle `History:show`'u gösterecektir.
Elbette presenter ve eylem adları da parametre olabilir. Örneğin:
```php
$router->addRoute('<presenter>/<action>', 'Home:default');
```
Belirtilen rota örneğin `/article/edit` veya `/catalog/list` biçimindeki URL'leri kabul eder ve onları sırasıyla `Article:edit` ile `Catalog:list` presenter'ları ve eylemleri olarak anlar.
Aynı zamanda `presenter` ve `action` parametrelerine `Home` ve `default` varsayılan değerlerini verir, bu da onları da isteğe bağlı yapar. Böylece rota `/article` gibi bir URL'yi de kabul eder ve onu `Article:default` olarak anlar. Ya da tersine, `Product:default`'a giden bir bağlantı `/product` yolunu, varsayılan `Home:default`'a giden bir bağlantı ise `/` yolunu üretir.
Maske yalnızca sitenin kök dizininden itibaren göreli yolu değil, eğik çizgiyle başlıyorsa mutlak yolu, hatta iki eğik çizgiyle başlıyorsa tüm mutlak URL'yi de tanımlayabilir:
```php
// document root'a göreli
$router->addRoute('<presenter>/<action>', /* ... */);
// mutlak yol (alan adına göreli)
$router->addRoute('/<presenter>/<action>', /* ... */);
// alan adı dahil mutlak URL (şemaya göreli)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// şema dahil mutlak URL
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
```
Doğrulama ifadeleri
-------------------
Her parametre için bir [düzenli ifadeyle|https://www.php.net/manual/en/reference.pcre.pattern.syntax.php] doğrulama koşulu belirtilebilir. Örneğin `id` parametresi için, `\d+` regex'iyle yalnızca rakam içerebileceğini belirtiriz:
```php
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
```
Tüm parametreler için varsayılan düzenli ifade `[^/]+`'dir, yani eğik çizgi dışındaki her şey. Bir parametrenin eğik çizgi de kabul etmesi gerekiyorsa ifadeyi `.+` yaparız:
```php
// https://example.com/a/b/c kabul eder, path 'a/b/c' olur
$router->addRoute('<path .+>', /* ... */);
```
İsteğe bağlı diziler
--------------------
Maskede isteğe bağlı bölümler köşeli parantezle işaretlenebilir. Maskenin herhangi bir bölümü isteğe bağlı olabilir ve parametre içerebilir:
```php
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// Şu yolları kabul eder:
// /en/download => lang => en, name => download
// /download => lang => null, name => download
```
Bir parametre isteğe bağlı bir dizinin parçası olduğunda, doğal olarak kendisi de isteğe bağlı olur. Belirtilmiş bir varsayılan değeri yoksa null olacaktır.
İsteğe bağlı bölümler alan adında da olabilir:
```php
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
```
Diziler iç içe geçirilebilir ve istenildiği gibi birleştirilebilir:
```php
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// Şu yolları kabul eder:
// /en/hello
// /en-us/hello
// /hello
// /hello/page-12
```
URL üretilirken en kısa seçenek yeğlenir, bu yüzden atlanabilecek her şey atlanır. Bu nedenle örneğin `index[.html]` rotası `/index` yolunu üretir. Bu davranış, sol köşeli parantezin ardına ünlem işareti konarak tersine çevrilebilir:
```php
// /hello ve /hello.html kabul eder, /hello üretir
$router->addRoute('<name>[.html]', /* ... */);
// /hello ve /hello.html kabul eder, /hello.html üretir
$router->addRoute('<name>[!.html]', /* ... */);
```
Köşeli parantezsiz isteğe bağlı parametreler (yani varsayılan değeri olanlar) aslında şöyle sarılmış gibi davranır:
```php
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// buna karşılık gelir:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
```
Sondaki eğik çizginin davranışını etkilemek, örneğin `/home/` yerine `/home` üretilmesini istemek istersek, bu şöyle sağlanabilir:
```php
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
```
Joker karakterler
-----------------
Mutlak URL maskesinde, örneğin geliştirme ve üretim ortamları arasında farklı olabilecek alan adını maskeye yazmak zorunda kalmamak için şu joker karakterleri kullanabiliriz:
- `%tld%` = üst düzey alan adı, örneğin `com` veya `org`
- `%sld%` = ikinci düzey alan adı, örneğin `example`
- `%domain%` = alt alan adları olmadan alan adı, örneğin `example.com`
- `%host%` = tüm host, örneğin `www.example.com`
- `%basePath%` = kök dizine giden yol
```php
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action>', /* ... */);
```
Gelişmiş yazım
--------------
Genellikle `Presenter:eylem` biçiminde yazılan rota hedefi, tek tek parametreleri ve varsayılan değerlerini tanımlayan bir dizi kullanılarak da yazılabilir:
```php
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
'presenter' => 'Home',
'action' => 'default',
]);
```
Daha ayrıntılı belirtim için, varsayılan değerlerin yanı sıra başka parametre özelliklerini de (örneğin bir doğrulama düzenli ifadesini, bkz. `id` parametresi) ayarlayabildiğimiz daha da genişletilmiş bir biçim kullanılabilir:
```php
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>[/<id>]', [
'presenter' => [
Route::Value => 'Home',
],
'action' => [
Route::Value => 'default',
],
'id' => [
Route::Pattern => '\d+',
],
]);
```
Şunu belirtmek önemlidir: dizide tanımlanan parametreler yol maskesinde listelenmiyorsa, değerleri değiştirilemez; URL'de soru işaretinden sonra belirtilen sorgu parametreleriyle bile.
Bu, **sabit parametreler** için yararlıdır; belirli bir sayfaya kısa, akılda kalan bir URL vermek. Örneğin `/tos`'un her zaman `id: 123` ile `Article:view`'u açması için:
```php
$router->addRoute('tos', [
'presenter' => 'Article',
'action' => 'view',
'id' => 123,
]);
```
Filtreler ve çeviriler
----------------------
Uygulamanın kaynak kodunu İngilizce yazarız, ama sitenin Çekçe URL'leri olması gerekiyorsa, şöyle basit bir yönlendirme:
```php
$router->addRoute('<presenter>/<action>', 'Home:default');
```
İngilizce URL'ler üretecektir, örneğin `/product/123` veya `/cart`. URL'deki presenter'ların ve eylemlerin Çekçe sözcüklerle temsil edilmesini istiyorsak (örneğin `/produkt/123` veya `/kosik`), bir çeviri sözlüğü kullanabiliriz. Onu yazmak için ikinci parametrenin "daha ayrıntılı" seçeneğine ihtiyacımız var:
```php
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterTable => [
// URL'deki dize => presenter
'produkt' => 'Product',
'kosik' => 'Cart',
'katalog' => 'Catalog',
],
],
'action' => [
Route::Value => 'default',
Route::FilterTable => [
'seznam' => 'list',
],
],
]);
```
Çeviri sözlüğündeki birden fazla anahtar aynı presenter'a gidebilir. Bu, onun için çeşitli takma adlar oluşturur. Son anahtar kanonik seçenek sayılır (yani üretilen URL'de yer alacak olan).
Çeviri tablosu bu şekilde herhangi bir parametre için kullanılabilir. Bir çeviri yoksa özgün değer alınır. Bu davranışı `Route::FilterStrict => true` ekleyerek değiştirebiliriz; o zaman rota, değer sözlükte yoksa URL'yi reddeder.
Dizi biçimindeki çeviri sözlüğünün yanı sıra kendi çeviri fonksiyonlarınız da devreye sokulabilir.
```php
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` fonksiyonu, URL'deki parametre ile sonra presenter'a aktarılan dize arasında dönüşüm yapar; `FilterOut` fonksiyonu ise ters yöndeki dönüşümü sağlar.
`presenter`, `action` ve `module` parametrelerinin, PascalCase veya camelCase stili ile URL'lerde kullanılan kebab-case arasında dönüşüm yapan önceden tanımlı filtreleri vardır. Parametrelerin varsayılan değeri, uygulamaya aktarıldığı biçimde yazılır (presenter ve module için PascalCase, action için camelCase); yani örneğin bir presenter söz konusuysa `<presenter=ProductEdit>` yazarız, `<presenter=product-edit>` değil.
Genel filtreler
---------------
Belirli parametreler için tasarlanmış filtrelerin yanı sıra, tüm parametrelerin ilişkisel dizisini alan, onu istediği gibi değiştirip döndürebilen genel filtreler de tanımlayabiliriz. Genel filtreler boş anahtar altında tanımlanır.
```php
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => 'Home',
'action' => 'default',
'' => [
Route::FilterIn => function (array $params): array { /* ... */ },
Route::FilterOut => function (array $params): array { /* ... */ },
],
]);
```
Genel filtreler, rotanın davranışını kesinlikle her şekilde değiştirme olanağı sunar. Onları örneğin parametreleri başka parametrelere göre değiştirmek için kullanabiliriz. Örneğin `<presenter>` ve `<action>`'ı `<lang>` parametresinin geçerli değerine göre çevirmek için.
Bir parametrenin kendi filtresi tanımlıysa ve bir de genel filtre varsa, kendi `FilterIn`'i genelden önce, buna karşılık genel `FilterOut` kendi filtreden önce çalıştırılır. Böylece genel filtrenin içinde `presenter` ve `action` parametrelerinin değerleri sırasıyla PascalCase ve camelCase stilinde yazılıdır.
Bu filtrelerin pratik bir kullanımı için, hiçbir şablonu değiştirmeden `/article/123-ekmek-nasil-pisirilir` gibi SEO dostu URL'ler üretmeyi anlatan [Slug'lu güzel URL'ler |best-practices:pretty-urls] bölümüne bakın.
OneWay bayrağı
--------------
Tek yönlü rotalar, uygulamanın artık üretmediği ama hâlâ kabul ettiği eski URL'lerin işlevselliğini korumaya yarar. Onları `OneWay` bayrağıyla işaretleriz:
```php
// eski URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', oneWay: true);
// yeni URL /product/123
$router->addRoute('product/<id>', 'Product:detail');
```
Eski URL'ye erişildiğinde presenter otomatik olarak yeni URL'ye yönlendirir, böylece arama motorları bu sayfaları iki kez indekslemez (bkz. [#SEO ve kanonikleştirme]).
Callback'lerle dinamik yönlendirme
----------------------------------
Callback'lerle dinamik yönlendirme, rotalara doğrudan, ilgili yol ziyaret edildiğinde çalıştırılan fonksiyonlar (callback'ler) atamanızı sağlar. Bu esnek işlevsellik, uygulamanız için çeşitli uç noktaları hızlı ve verimli biçimde oluşturmanıza olanak tanır:
```php
$router->addRoute('test', function () {
echo '/test adresindesiniz';
});
```
Maskede, callback'inize otomatik aktarılan parametreler de tanımlayabilirsiniz:
```php
$router->addRoute('<lang cs|en>', function (string $lang) {
echo match ($lang) {
'cs' => 'Sitemizin Çekçe sürümüne hoş geldiniz!',
'en' => 'Sitemizin İngilizce sürümüne hoş geldiniz!',
};
});
```
Maskeden gelen parametrelerin yanı sıra callback, DI konteynerinden servisler de alabilir. Bunlar parametre tipine göre aktarılır. Ayrıca `$presenter` parametresi, rotayı işleyen [MicroPresenter |api:NetteModule\MicroPresenter] örneğini alır:
```php
$router->addRoute('<lang cs|en>', function (string $lang, Nette\Http\Request $httpRequest, NetteModule\MicroPresenter $presenter) {
// ...
});
```
Modüller
--------
Ortak bir [modüle |directory-structure#Presenter'lar ve şablonlar] ait birden fazla rotamız varsa `withModule()` kullanırız. Verilen modül, gruptaki her rotanın presenter'ının önüne otomatik eklenir ve URL'den tamamen kaybolur:
```php
$router = new RouteList;
$router->withModule('Forum') // sonraki rotalar Forum modülünün parçasıdır
->addRoute('rss', 'Feed:rss') // presenter Forum:Feed olacak
->addRoute('<presenter>/<action>')
->withModule('Admin') // sonraki rotalar Forum:Admin modülünün parçasıdır
->addRoute('sign:in', 'Sign:in');
```
Bir alternatif de `module` parametresidir; o da sabit bir modül belirler ve onu URL'nin dışında tutar:
```php
// manage/dashboard/default URL'si Admin:Dashboard presenter'ına eşlenir
$router->addRoute('manage/<presenter>/<action>', [
'module' => 'Admin',
]);
```
Her presenter adı ancak modülüyle birlikte tamdır, örneğin `Front:Admin:ProductList`. Böyle bir tam ad bir URL parametresine düştüğünde, router onu iki basit kuralla kodlar: her iki nokta üst üste `:` (modül ayırıcısı) bir **noktaya**, PascalCase bir addaki her sözcük sınırı ise bir **kısa çizgiye** dönüşür. Yani `Front:Admin:ProductList`, URL'de `front.admin.product-list` olarak görünür ve aynı şekilde geri çözülür. Modüler bir uygulamanın, yukarıdaki araçlar olmadan, noktalarla dolu URL'ler üretmesinin nedeni budur.
Hem `withModule()` hem de `module` parametresi bundan tam olarak şu yüzden kaçınır: presenter adı URL'ye ulaşmadan önce bilinen modül ön ekini ondan soyarlar; modül sabit olduğundan hiç kodlanması gerekmez.
Bazen modülün kendisinin değişmesini ve URL'de görünmesini isteriz, bu yüzden doğrudan maskede `<module>`'e başvururuz. Çok önemli bir ayrıntıya dikkat edin: **`<module>` tüm modül yolunu yakalar**, yani presenter adındaki son iki nokta üst üsteye kadar olan her şeyi. `Shop:Admin:Product` presenter'ı için bu, `Shop:Admin` modülü ve `Product` presenter'ı demektir; iki nokta üst üste noktaya dönüştüğünden şunu elde ederiz:
Alt alan adları
---------------
Rota koleksiyonları alt alan adlarına göre bölünebilir:
```php
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
```
Alan adında [#Joker karakterler] de kullanılabilir:
```php
$router = new RouteList;
$router->withDomain('example.%tld%')
// ...
```
Yol ön eki
----------
Rota koleksiyonları URL'deki yola göre bölünebilir:
```php
$router = new RouteList;
$router->withPath('eshop')
->addRoute('rss', 'Feed:rss') // /eshop/rss URL'siyle eşleşir
->addRoute('<presenter>/<action>'); // /eshop/<presenter>/<action> URL'siyle eşleşir
```
Bileşimler
----------
Yukarıdaki gruplamalar birbirleriyle birleştirilebilir:
```php
$router = (new RouteList)
->withDomain('admin.example.com')
->withModule('Admin')
->addRoute(/* ... */)
->addRoute(/* ... */)
->end()
->withModule('Images')
->addRoute(/* ... */)
->end()
->end()
->withDomain('example.com')
->withPath('export')
->addRoute(/* ... */)
// ...
```
Sorgu parametreleri
-------------------
Maskeler sorgu parametreleri de (URL'de soru işaretinden sonraki parametreler) içerebilir. Bunlar için doğrulama ifadesi tanımlanamaz, ama presenter'a aktarıldıkları ad değiştirilebilir:
```php
// 'cat' sorgu parametresini uygulamada 'categoryId' adıyla kullanmak istiyoruz
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
```
Foo parametreleri
-----------------
Şimdi daha derine iniyoruz. Foo parametreleri aslında bir düzenli ifadeyle eşleşmeyi sağlayan adsız parametrelerdir. Bir örnek, `/index`, `/index.html`, `/index.htm` ve `/index.php` kabul eden bir rotadır:
```php
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
```
URL üretilirken kullanılacak dizeyi açıkça tanımlamak da mümkündür. Dize doğrudan soru işaretinin ardına konmalıdır. Aşağıdaki rota öncekine benzer, ama `/index` yerine `/index.html` üretir, çünkü üretim değeri olarak `.html` dizesi ayarlanmıştır:
```php
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
```
Entegrasyon
===========
Oluşturduğumuz router'ı uygulamaya entegre etmek için DI konteynerine ondan söz etmemiz gerekir. En kolayı, router nesnesini oluşturacak bir factory hazırlamak ve yapılandırmada konteynere onu kullanmasını söylemektir. Bunun için `App\Core\RouterFactory::createRouter()` metodunu yazdığımızı varsayalım:
```php
namespace App\Core;
use Nette\Application\Routers\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute(/* ... */);
return $router;
}
}
```
Sonra [yapılandırmaya |dependency-injection:services] şunu yazarız:
```neon
services:
- App\Core\RouterFactory::createRouter
```
Örneğin bir veritabanına olan bağımlılıklar, factory metoduna [autowiring|dependency-injection:autowiring] ile parametreleri olarak aktarılır:
```php
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
```
SimpleRouter
============
Rota koleksiyonundan çok daha basit bir router [SimpleRouter |api:Nette\Application\Routers\SimpleRouter]'dır. Onu, URL biçimiyle ilgili özel gereksinimlerimiz olmadığında, `mod_rewrite` (veya alternatifleri) kullanılamıyorsa ya da henüz güzel URL'lerle uğraşmak istemiyorsak kullanırız.
Aşağı yukarı şu biçimde adresler üretir:
```
http://example.com/?presenter=Product&action=detail&id=123
```
`SimpleRouter` constructor'ının parametresi varsayılan bir presenter ve eylemdir; yani örneğin `http://example.com/` adresini ek parametre olmadan açtığımızda çalıştırılacak eylem.
```php
// varsayılan presenter 'Home', eylem 'default' olacak
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
```
SimpleRouter'ı doğrudan [yapılandırmada |dependency-injection:services] tanımlamanızı öneririz:
```neon
services:
- Nette\Application\Routers\SimpleRouter('Home:default')
```
SEO ve kanonikleştirme
======================
Framework, aynı içeriğin farklı URL'lerde bulunmasını engelleyerek SEO'ya (arama motoru optimizasyonuna) katkı yapar. Belirli bir hedefe birden fazla adres gidiyorsa, örneğin `/index` ve `/index.html`, framework ilkini birincil (kanonik) sayar ve diğerlerini HTTP kodu 301 ile ona yönlendirir. Bu sayede arama motorları sayfaları iki kez indekslemez ve sayfa sıralamalarını seyreltmez.
Bu sürece kanonikleştirme denir. Kanonik URL, router tarafından üretilen, yani koleksiyondaki OneWay bayrağı olmayan ilk uyan rotanın URL'sidir. Bu yüzden koleksiyonda **önce birincil rotaları** listeleriz.
Kanonikleştirmeyi presenter gerçekleştirir; daha fazlası [kanonikleştirme |presenters#Kanonikleştirme] bölümünde.
HTTPS
=====
HTTPS protokolünü kullanmak için onu hosting'de etkinleştirmek ve sunucuyu doğru yapılandırmak gerekir.
Tüm sitenin HTTPS'e yönlendirilmesi sunucu düzeyinde, örneğin uygulamamızın kök dizinindeki `.htaccess` dosyasıyla ve HTTP kodu 301 ile ayarlanmalıdır. Ayarlar hosting'e göre değişebilir ve aşağı yukarı şöyle görünür:
```
<IfModule mod_rewrite.c>
RewriteEngine On
...
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
...
</IfModule>
```
Router, URL'leri sayfanın yüklendiği protokolün aynısıyla üretir, bu yüzden başka bir şey ayarlamaya gerek yoktur.
Ancak istisnai olarak farklı rotaların farklı protokoller altında çalışmasına ihtiyaç duyarsak, bunu rota maskesinde belirtiriz:
```php
// HTTP adresi üretecek
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);
// HTTPS adresi üretecek
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
```
Router'ın hata ayıklaması
=========================
[Tracy Bar |tracy:]'da görünen yönlendirme paneli, rotaların listesini ve ayrıca router'ın URL'den elde ettiği parametreleri gösteren yararlı bir yardımcıdır.
✓ simgeli yeşil çubuk, geçerli URL'yi işleyen rotayı temsil eder; mavi renk ve ≈ simgesi ise, yeşil olan onların önüne geçmeseydi URL'yi işleyecek olan rotaları gösterir. Ayrıca geçerli presenter'ı ve eylemi görürüz.
[* routing-debugger.webp *]
Aynı zamanda [kanonikleştirme |#SEO ve kanonikleştirme] nedeniyle beklenmedik bir yönlendirme olursa, panelin *redirect* çubuğuna bakmak yararlıdır; orada router'ın URL'yi başlangıçta nasıl anladığını ve neden yönlendirdiğini öğrenebilirsiniz.
.[note]
Router'ın hata ayıklamasını yaparken, yönlendirmelerin orada saklanmaması için tarayıcıda Developer Tools'u açmanızı (Ctrl+Shift+I veya Cmd+Option+I) ve Network panelinde önbelleği kapatmanızı öneririz.
Performans
==========
Rotaların sayısı router'ın hızını etkiler. Sayıları kesinlikle birkaç düzineyi aşmamalıdır. Sitenizin URL yapısı fazla karmaşıksa, kendi [#Kendi router'ınız]'ınızı yazabilirsiniz.
Router'ın örneğin bir veritabanına bağımlılığı yoksa ve factory'si argüman almıyorsa, derlenmiş biçimini doğrudan DI konteynerine serileştirebilir ve böylece uygulamayı biraz hızlandırabiliriz.
```neon
routing:
cache: true
```
Kendi router'ınız
=================
Aşağıdaki satırlar çok ileri düzey kullanıcılar içindir. Kendi router'ınızı oluşturabilir ve onu doğal olarak rota koleksiyonuna entegre edebilirsiniz. Router, iki metotlu [api:Nette\Routing\Router] arayüzünün bir uygulamasıdır:
```php
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` metodu, yalnızca URL'nin değil header'ların vb. de elde edilebildiği geçerli istek [$httpRequest |http:request]'i, presenter adını ve parametrelerini içeren bir diziye işler. İsteği işleyemezse null döndürür. İsteği işlerken en azından presenter'ı döndürmemiz gerekir; eylem isteğe bağlıdır ve belirtilmezse `default` olur. Presenter adı tamdır ve varsa modülleri de içerir:
```php
[
'presenter' => 'Front:Home',
'action' => 'default',
]
```
`constructUrl` metodu ise tam tersine, parametre dizisinden sonuçtaki mutlak URL'yi kurar. Geçerli URL olan [`$refUrl`|api:Nette\Http\UrlScript] parametresindeki bilgilerden yararlanabilir.
Onu rota koleksiyonuna `add()` ile ekleyin:
```php
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
```
Bağımsız kullanım
=================
Bağımsız kullanımdan kastımız, router'ın yeteneklerinden Nette Application ve presenter'ları kullanmayan bir uygulamada yararlanmaktır. Bu bölümde gösterdiğimiz hemen her şey onun için de geçerlidir, şu farklarla:
- rota koleksiyonları için [api:Nette\Routing\RouteList] sınıfını kullanırız
- basit router olarak [api:Nette\Routing\SimpleRouter] sınıfını
- `Presenter:eylem` çifti var olmadığından [#Gelişmiş yazım] kullanırız
Yine router'ı bizim için kuracak bir metot oluştururuz, örneğin:
```php
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;
}
}
```
Önerdiğimiz gibi bir DI konteyneri kullanıyorsanız, metodu yine yapılandırmaya ekleyin ve sonra router'ı HTTP isteğiyle birlikte konteynerden alın:
```php
$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);
```
Ya da nesneleri doğrudan oluşturun:
```php
$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
```
Şimdi geriye yalnızca router'ın işini yapmasına izin vermek kalıyor:
```php
$params = $router->match($httpRequest);
if ($params === null) {
// uyan rota bulunamadı, 404 hatası gönder
exit;
}
// elde edilen parametreleri işle
$controller = $params['controller'];
// ...
```
Ve tersine, bir bağlantı kurmak için router'ı kullanın:
```php
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());
```
{{composer: nette/routing}}
{{repo: nette/routing}}
{{api: https://api.nette.org/routing/}}
Yönlendirme
Router, URL adresleriyle ilgili her şeyi üstlenir, böylece onları düşünmek zorunda kalmazsınız. Size şunları
göstereceğiz:
URL'lerin istediğiniz gibi görünmesi için router nasıl yapılandırılır
SEO ve yönlendirme üzerine konuşacağız
ve kendi router'ınızı nasıl yazacağınızı göstereceğiz
İnsana daha dostça URL'ler (havalı veya güzel URL de denir) daha kullanışlıdır, akılda daha iyi kalır ve SEO'ya
olumlu katkı yapar. Nette bunu göz önünde bulundurur ve geliştiricilerin ihtiyaçlarını tam olarak karşılar. Uygulamanız
için istediğiniz URL yapısını tam olarak tasarlayabilirsiniz. Üstelik uygulama zaten tamamlandığında bile
tasarlayabilirsiniz, çünkü kodda veya şablonlarda hiçbir değişiklik gerektirmez. Tüm presenter'lara anotasyon olarak
dağılmak yerine tek bir yerde, router'da zarif biçimde tanımlanır.
Nette'teki router olağanüstüdür, çünkü çift yönlüdür. Hem HTTP isteklerinden URL'leri çözebilir hem de
bağlantı oluşturabilir. Böylece Nette
Application içinde çok önemli bir rol oynar: yalnızca geçerli isteği hangi presenter'ın ve eylemin yürüteceğine
karar vermekle kalmaz, şablonlarda vb. URL üretmek için de
kullanılır.
Ancak router'ın kullanımı bununla sınırlı değildir; onu presenter'ların hiç kullanılmadığı uygulamalarda, REST
API'lerde vb. de kullanabilirsiniz. Daha fazla ayrıntı Bağımsız kullanım
bölümünde.
Rota koleksiyonu
Bir uygulamadaki URL adreslerinin yapısını tanımlamanın en hoş yolunu Nette\Application\Routers\RouteList
sınıfı sunar. Tanım, rota denilen şeylerin listesinden oluşur; yani basit bir API ile URL adreslerinin maskeleri ve onlara
bağlı presenter'lar ile eylemler. Rotaları hiçbir şekilde adlandırmamız gerekmez.
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
Örnek şunu gösterir: tarayıcıda https://domain.com/rss.xml açarsak, rss eylemiyle
Feed presenter'ı gösterilir. https://domain.com/article/12 ise view eylemiyle
Article presenter'ını gösterir vb. Uygun bir rota bulunamazsa Nette Application, kullanıcıya 404 Not Found hata
sayfası olarak gösterilen bir BadRequestException fırlatarak
yanıt verir.
Rotaların sırası
Rotaların listelenme sırası kesinlikle çok önemlidir, çünkü yukarıdan aşağıya sırayla
değerlendirilirler. Kural şudur: rotaları özelden genele doğru bildiririz:
// YANLIŞ: 'rss.xml' ilk rota tarafından yakalanır ve bu dize <slug> sanılır
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// DOĞRU
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
Bağlantı üretilirken de rotalar yukarıdan aşağıya değerlendirilir:
// YANLIŞ: 'Feed:rss' bağlantısı 'admin/feed/rss' olarak üretilir
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');
// DOĞRU
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
Rotaları doğru kurmanın biraz beceri gerektirdiğini sizden saklamayacağız. Bunda ustalaşana kadar yönlendirme paneli işinize yarayacak bir araç olacak.
Maske ve parametreler
Maske, sitenin kök dizininden itibaren göreli yolu tanımlar. En basit maske statik bir URL'dir:
Maskeler sıklıkla parametre denilen şeyleri içerir. Bunlar açılı parantez içine alınır (örneğin
<year>) ve hedef presenter'a, örneğin renderShow(int $year) metoduna veya $year
kalıcı parametresine aktarılır:
Örnek şunu gösterir: tarayıcıda https://example.com/chronicle/2020 açarsak, show eylemiyle ve
year: 2020 parametresiyle History presenter'ı gösterilir.
Parametreler için doğrudan maskede varsayılan bir değer belirtebiliriz; bu onları isteğe bağlı yapar:
Belirtilen rota örneğin /article/edit veya /catalog/list biçimindeki URL'leri kabul eder ve
onları sırasıyla Article:edit ile Catalog:list presenter'ları ve eylemleri olarak anlar.
Aynı zamanda presenter ve action parametrelerine Home ve default
varsayılan değerlerini verir, bu da onları da isteğe bağlı yapar. Böylece rota /article gibi bir URL'yi de
kabul eder ve onu Article:default olarak anlar. Ya da tersine, Product:default'a giden bir bağlantı
/product yolunu, varsayılan Home:default'a giden bir bağlantı ise / yolunu üretir.
Maske yalnızca sitenin kök dizininden itibaren göreli yolu değil, eğik çizgiyle başlıyorsa mutlak yolu, hatta iki eğik
çizgiyle başlıyorsa tüm mutlak URL'yi de tanımlayabilir:
// document root'a göreli
$router->addRoute('<presenter>/<action>', /* ... */);
// mutlak yol (alan adına göreli)
$router->addRoute('/<presenter>/<action>', /* ... */);
// alan adı dahil mutlak URL (şemaya göreli)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// şema dahil mutlak URL
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
Doğrulama ifadeleri
Her parametre için bir düzenli ifadeyle
doğrulama koşulu belirtilebilir. Örneğin id parametresi için, \d+ regex'iyle yalnızca rakam
içerebileceğini belirtiriz:
Tüm parametreler için varsayılan düzenli ifade [^/]+'dir, yani eğik çizgi dışındaki her şey. Bir
parametrenin eğik çizgi de kabul etmesi gerekiyorsa ifadeyi .+ yaparız:
// https://example.com/a/b/c kabul eder, path 'a/b/c' olur
$router->addRoute('<path .+>', /* ... */);
İsteğe bağlı diziler
Maskede isteğe bağlı bölümler köşeli parantezle işaretlenebilir. Maskenin herhangi bir bölümü isteğe bağlı
olabilir ve parametre içerebilir:
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// Şu yolları kabul eder:
// /en/download => lang => en, name => download
// /download => lang => null, name => download
Bir parametre isteğe bağlı bir dizinin parçası olduğunda, doğal olarak kendisi de isteğe bağlı olur. Belirtilmiş bir
varsayılan değeri yoksa null olacaktır.
Diziler iç içe geçirilebilir ve istenildiği gibi birleştirilebilir:
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// Şu yolları kabul eder:
// /en/hello
// /en-us/hello
// /hello
// /hello/page-12
URL üretilirken en kısa seçenek yeğlenir, bu yüzden atlanabilecek her şey atlanır. Bu nedenle örneğin
index[.html] rotası /index yolunu üretir. Bu davranış, sol köşeli parantezin ardına ünlem
işareti konarak tersine çevrilebilir:
// /hello ve /hello.html kabul eder, /hello üretir
$router->addRoute('<name>[.html]', /* ... */);
// /hello ve /hello.html kabul eder, /hello.html üretir
$router->addRoute('<name>[!.html]', /* ... */);
Köşeli parantezsiz isteğe bağlı parametreler (yani varsayılan değeri olanlar) aslında şöyle sarılmış gibi
davranır:
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// buna karşılık gelir:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
Sondaki eğik çizginin davranışını etkilemek, örneğin /home/ yerine /home üretilmesini
istemek istersek, bu şöyle sağlanabilir:
Mutlak URL maskesinde, örneğin geliştirme ve üretim ortamları arasında farklı olabilecek alan adını maskeye yazmak
zorunda kalmamak için şu joker karakterleri kullanabiliriz:
%tld% = üst düzey alan adı, örneğin com veya org
%sld% = ikinci düzey alan adı, örneğin example
%domain% = alt alan adları olmadan alan adı, örneğin example.com
Genellikle Presenter:eylem biçiminde yazılan rota hedefi, tek tek parametreleri ve varsayılan değerlerini
tanımlayan bir dizi kullanılarak da yazılabilir:
Daha ayrıntılı belirtim için, varsayılan değerlerin yanı sıra başka parametre özelliklerini de (örneğin bir
doğrulama düzenli ifadesini, bkz. id parametresi) ayarlayabildiğimiz daha da genişletilmiş bir biçim
kullanılabilir:
Şunu belirtmek önemlidir: dizide tanımlanan parametreler yol maskesinde listelenmiyorsa, değerleri değiştirilemez; URL'de
soru işaretinden sonra belirtilen sorgu parametreleriyle bile.
Bu, sabit parametreler için yararlıdır; belirli bir sayfaya kısa, akılda kalan bir URL vermek. Örneğin
/tos'un her zaman id: 123 ile Article:view'u açması için:
İngilizce URL'ler üretecektir, örneğin /product/123 veya /cart. URL'deki presenter'ların ve
eylemlerin Çekçe sözcüklerle temsil edilmesini istiyorsak (örneğin /produkt/123 veya /kosik), bir
çeviri sözlüğü kullanabiliriz. Onu yazmak için ikinci parametrenin “daha ayrıntılı” seçeneğine
ihtiyacımız var:
Çeviri sözlüğündeki birden fazla anahtar aynı presenter'a gidebilir. Bu, onun için çeşitli takma adlar oluşturur. Son
anahtar kanonik seçenek sayılır (yani üretilen URL'de yer alacak olan).
Çeviri tablosu bu şekilde herhangi bir parametre için kullanılabilir. Bir çeviri yoksa özgün değer alınır. Bu
davranışı Route::FilterStrict => true ekleyerek değiştirebiliriz; o zaman rota, değer sözlükte yoksa
URL'yi reddeder.
Dizi biçimindeki çeviri sözlüğünün yanı sıra kendi çeviri fonksiyonlarınız da devreye sokulabilir.
Route::FilterIn fonksiyonu, URL'deki parametre ile sonra presenter'a aktarılan dize arasında dönüşüm yapar;
FilterOut fonksiyonu ise ters yöndeki dönüşümü sağlar.
presenter, action ve module parametrelerinin, PascalCase veya camelCase stili ile
URL'lerde kullanılan kebab-case arasında dönüşüm yapan önceden tanımlı filtreleri vardır. Parametrelerin varsayılan
değeri, uygulamaya aktarıldığı biçimde yazılır (presenter ve module için PascalCase, action için camelCase); yani
örneğin bir presenter söz konusuysa <presenter=ProductEdit> yazarız,
<presenter=product-edit> değil.
Genel filtreler
Belirli parametreler için tasarlanmış filtrelerin yanı sıra, tüm parametrelerin ilişkisel dizisini alan, onu istediği
gibi değiştirip döndürebilen genel filtreler de tanımlayabiliriz. Genel filtreler boş anahtar altında tanımlanır.
Genel filtreler, rotanın davranışını kesinlikle her şekilde değiştirme olanağı sunar. Onları örneğin parametreleri
başka parametrelere göre değiştirmek için kullanabiliriz. Örneğin <presenter> ve
<action>'ı <lang> parametresinin geçerli değerine göre çevirmek için.
Bir parametrenin kendi filtresi tanımlıysa ve bir de genel filtre varsa, kendi FilterIn'i genelden önce, buna
karşılık genel FilterOut kendi filtreden önce çalıştırılır. Böylece genel filtrenin içinde
presenter ve action parametrelerinin değerleri sırasıyla PascalCase ve camelCase stilinde
yazılıdır.
Bu filtrelerin pratik bir kullanımı için, hiçbir şablonu değiştirmeden /article/123-ekmek-nasil-pisirilir
gibi SEO dostu URL'ler üretmeyi anlatan Slug'lu güzel URL'ler
bölümüne bakın.
OneWay bayrağı
Tek yönlü rotalar, uygulamanın artık üretmediği ama hâlâ kabul ettiği eski URL'lerin işlevselliğini korumaya yarar.
Onları OneWay bayrağıyla işaretleriz:
// eski URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', oneWay: true);
// yeni URL /product/123
$router->addRoute('product/<id>', 'Product:detail');
Eski URL'ye erişildiğinde presenter otomatik olarak yeni URL'ye yönlendirir, böylece arama motorları bu sayfaları iki kez
indekslemez (bkz. SEO ve kanonikleştirme).
Callback'lerle dinamik yönlendirme
Callback'lerle dinamik yönlendirme, rotalara doğrudan, ilgili yol ziyaret edildiğinde çalıştırılan fonksiyonlar
(callback'ler) atamanızı sağlar. Bu esnek işlevsellik, uygulamanız için çeşitli uç noktaları hızlı ve verimli biçimde
oluşturmanıza olanak tanır:
$router->addRoute('test', function () {
echo '/test adresindesiniz';
});
Maskede, callback'inize otomatik aktarılan parametreler de tanımlayabilirsiniz:
$router->addRoute('<lang cs|en>', function (string $lang) {
echo match ($lang) {
'cs' => 'Sitemizin Çekçe sürümüne hoş geldiniz!',
'en' => 'Sitemizin İngilizce sürümüne hoş geldiniz!',
};
});
Maskeden gelen parametrelerin yanı sıra callback, DI konteynerinden servisler de alabilir. Bunlar parametre tipine göre
aktarılır. Ayrıca $presenter parametresi, rotayı işleyen MicroPresenter örneğini alır:
Ortak bir modüle ait
birden fazla rotamız varsa withModule() kullanırız. Verilen modül, gruptaki her rotanın presenter'ının önüne
otomatik eklenir ve URL'den tamamen kaybolur:
$router = new RouteList;
$router->withModule('Forum') // sonraki rotalar Forum modülünün parçasıdır
->addRoute('rss', 'Feed:rss') // presenter Forum:Feed olacak
->addRoute('<presenter>/<action>')
->withModule('Admin') // sonraki rotalar Forum:Admin modülünün parçasıdır
->addRoute('sign:in', 'Sign:in');
Bir alternatif de module parametresidir; o da sabit bir modül belirler ve onu URL'nin dışında tutar:
Her presenter adı ancak modülüyle birlikte tamdır, örneğin Front:Admin:ProductList. Böyle bir tam ad bir
URL parametresine düştüğünde, router onu iki basit kuralla kodlar: her iki nokta üst üste : (modül
ayırıcısı) bir noktaya, PascalCase bir addaki her sözcük sınırı ise bir kısa çizgiye dönüşür. Yani
Front:Admin:ProductList, URL'de front.admin.product-list olarak görünür ve aynı şekilde geri
çözülür. Modüler bir uygulamanın, yukarıdaki araçlar olmadan, noktalarla dolu URL'ler üretmesinin nedeni budur.
Hem withModule() hem de module parametresi bundan tam olarak şu yüzden kaçınır: presenter adı
URL'ye ulaşmadan önce bilinen modül ön ekini ondan soyarlar; modül sabit olduğundan hiç kodlanması gerekmez.
Bazen modülün kendisinin değişmesini ve URL'de görünmesini isteriz, bu yüzden doğrudan maskede
<module>'e başvururuz. Çok önemli bir ayrıntıya dikkat edin: <module> tüm modül
yolunu yakalar, yani presenter adındaki son iki nokta üst üsteye kadar olan her şeyi. Shop:Admin:Product
presenter'ı için bu, Shop:Admin modülü ve Product presenter'ı demektir; iki nokta üst üste
noktaya dönüştüğünden şunu elde ederiz:
Alt alan adları
Rota koleksiyonları alt alan adlarına göre bölünebilir:
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
Maskeler sorgu parametreleri de (URL'de soru işaretinden sonraki parametreler) içerebilir. Bunlar için doğrulama ifadesi
tanımlanamaz, ama presenter'a aktarıldıkları ad değiştirilebilir:
Şimdi daha derine iniyoruz. Foo parametreleri aslında bir düzenli ifadeyle eşleşmeyi sağlayan adsız parametrelerdir. Bir
örnek, /index, /index.html, /index.htm ve /index.php kabul eden bir
rotadır:
URL üretilirken kullanılacak dizeyi açıkça tanımlamak da mümkündür. Dize doğrudan soru işaretinin ardına
konmalıdır. Aşağıdaki rota öncekine benzer, ama /index yerine /index.html üretir, çünkü
üretim değeri olarak .html dizesi ayarlanmıştır:
Oluşturduğumuz router'ı uygulamaya entegre etmek için DI konteynerine ondan söz etmemiz gerekir. En kolayı, router
nesnesini oluşturacak bir factory hazırlamak ve yapılandırmada konteynere onu kullanmasını söylemektir. Bunun için
App\Core\RouterFactory::createRouter() metodunu yazdığımızı varsayalım:
namespace App\Core;
use Nette\Application\Routers\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute(/* ... */);
return $router;
}
}
Örneğin bir veritabanına olan bağımlılıklar, factory metoduna autowiring ile parametreleri olarak aktarılır:
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
SimpleRouter
Rota koleksiyonundan çok daha basit bir router SimpleRouter'dır. Onu, URL
biçimiyle ilgili özel gereksinimlerimiz olmadığında, mod_rewrite (veya alternatifleri) kullanılamıyorsa ya da
henüz güzel URL'lerle uğraşmak istemiyorsak kullanırız.
SimpleRouter constructor'ının parametresi varsayılan bir presenter ve eylemdir; yani örneğin
http://example.com/ adresini ek parametre olmadan açtığımızda çalıştırılacak eylem.
// varsayılan presenter 'Home', eylem 'default' olacak
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
SimpleRouter'ı doğrudan yapılandırmada
tanımlamanızı öneririz:
Framework, aynı içeriğin farklı URL'lerde bulunmasını engelleyerek SEO'ya (arama motoru optimizasyonuna) katkı yapar.
Belirli bir hedefe birden fazla adres gidiyorsa, örneğin /index ve /index.html, framework ilkini
birincil (kanonik) sayar ve diğerlerini HTTP kodu 301 ile ona yönlendirir. Bu sayede arama motorları sayfaları iki kez
indekslemez ve sayfa sıralamalarını seyreltmez.
Bu sürece kanonikleştirme denir. Kanonik URL, router tarafından üretilen, yani koleksiyondaki OneWay bayrağı olmayan ilk
uyan rotanın URL'sidir. Bu yüzden koleksiyonda önce birincil rotaları listeleriz.
Kanonikleştirmeyi presenter gerçekleştirir; daha fazlası kanonikleştirme bölümünde.
HTTPS
HTTPS protokolünü kullanmak için onu hosting'de etkinleştirmek ve sunucuyu doğru yapılandırmak gerekir.
Tüm sitenin HTTPS'e yönlendirilmesi sunucu düzeyinde, örneğin uygulamamızın kök dizinindeki .htaccess
dosyasıyla ve HTTP kodu 301 ile ayarlanmalıdır. Ayarlar hosting'e göre değişebilir ve aşağı yukarı şöyle
görünür:
<IfModule mod_rewrite.c>
RewriteEngine On
...
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
...
</IfModule>
Router, URL'leri sayfanın yüklendiği protokolün aynısıyla üretir, bu yüzden başka bir şey ayarlamaya gerek
yoktur.
Ancak istisnai olarak farklı rotaların farklı protokoller altında çalışmasına ihtiyaç duyarsak, bunu rota maskesinde
belirtiriz:
Tracy Bar'da görünen yönlendirme paneli, rotaların listesini ve ayrıca
router'ın URL'den elde ettiği parametreleri gösteren yararlı bir yardımcıdır.
✓ simgeli yeşil çubuk, geçerli URL'yi işleyen rotayı temsil eder; mavi renk ve ≈ simgesi ise, yeşil olan onların
önüne geçmeseydi URL'yi işleyecek olan rotaları gösterir. Ayrıca geçerli presenter'ı ve eylemi görürüz.
Aynı zamanda kanonikleştirme nedeniyle beklenmedik bir yönlendirme olursa, panelin
redirect çubuğuna bakmak yararlıdır; orada router'ın URL'yi başlangıçta nasıl anladığını ve neden
yönlendirdiğini öğrenebilirsiniz.
Router'ın hata ayıklamasını yaparken, yönlendirmelerin orada saklanmaması için tarayıcıda Developer
Tools'u açmanızı (Ctrl+Shift+I veya Cmd+Option+I) ve Network panelinde önbelleği kapatmanızı öneririz.
Performans
Rotaların sayısı router'ın hızını etkiler. Sayıları kesinlikle birkaç düzineyi aşmamalıdır. Sitenizin URL
yapısı fazla karmaşıksa, kendi Kendi router'ınız'ınızı yazabilirsiniz.
Router'ın örneğin bir veritabanına bağımlılığı yoksa ve factory'si argüman almıyorsa, derlenmiş biçimini
doğrudan DI konteynerine serileştirebilir ve böylece uygulamayı biraz hızlandırabiliriz.
routing:
cache: true
Kendi router'ınız
Aşağıdaki satırlar çok ileri düzey kullanıcılar içindir. Kendi router'ınızı oluşturabilir ve onu doğal olarak
rota koleksiyonuna entegre edebilirsiniz. Router, iki metotlu Nette\Routing\Router arayüzünün bir
uygulamasıdır:
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 metodu, yalnızca URL'nin değil header'ların vb. de elde edilebildiği geçerli istek $httpRequest'i, presenter adını ve parametrelerini içeren bir diziye işler.
İsteği işleyemezse null döndürür. İsteği işlerken en azından presenter'ı döndürmemiz gerekir; eylem isteğe
bağlıdır ve belirtilmezse default olur. Presenter adı tamdır ve varsa modülleri de içerir:
constructUrl metodu ise tam tersine, parametre dizisinden sonuçtaki mutlak URL'yi kurar. Geçerli URL olan $refUrl parametresindeki bilgilerden
yararlanabilir.
Onu rota koleksiyonuna add() ile ekleyin:
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
Bağımsız kullanım
Bağımsız kullanımdan kastımız, router'ın yeteneklerinden Nette Application ve presenter'ları kullanmayan bir uygulamada
yararlanmaktır. Bu bölümde gösterdiğimiz hemen her şey onun için de geçerlidir, şu farklarla: