Nette Documentation Preview

syntax
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}}

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:

$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:

$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:

$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:

$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:

// 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:

$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:

// 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.

İsteğe bağlı bölümler alan adında da olabilir:

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

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:

$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
$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:

$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:

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:

$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:

$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:

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.

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.

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 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:

$router->addRoute('<lang cs|en>', function (string $lang, Nette\Http\Request $httpRequest, NetteModule\MicroPresenter $presenter) {
	// ...
});

Modüller

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:

// 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:

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

Alan adında Joker karakterler de kullanılabilir:

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

Yol ön eki

Rota koleksiyonları URL'deki yola göre bölünebilir:

$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:

$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:

// '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:

$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:

$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:

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 şunu yazarız:

services:
	- App\Core\RouterFactory::createRouter

Ö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.

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.

// 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:

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 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:

// 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'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:

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

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:

Yine router'ı bizim için kuracak bir metot oluştururuz, örneğin:

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:

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

Ya da nesneleri doğrudan oluşturun:

$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:

$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:

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());