Uživatelsky přívětivá URL

Rudolf Svátek 2016-07-29 12:15

Zase si můžeš všechno stáhnout: kapitola24.zip.

Nejen kvůli SEO, ale i vzhledem k našim uživatelům je lepší, když redakční systém generuje taková URL, která se snadno čtou, dobře pamatují a nejsou moc dlouhá. Současný stav, kdy v URL vidíš název presenteru, jeho akci a další údaje, není moc uspokojivý a hezký. Proto vytvoříme přesměrování pomocí takzvaných rout. V Nette už máme všechno připraveno a jde jen o to správně nastavit pravidla pro přesměrovávání.

Všechno, nebo skoro všechno, se to bude dít v souboru RouterFactory.php. Ten už máme připravený, jinak by aplikace vůbec neběžela.

Úprava RouterFactory.php

Nerad bych duplikoval nápovědu Nette. O routování si celkem dobře počteš zde:

Nebo sice starší, ale fakt dobré video:

V našem routeru tedy zatím řešíme 2 moduly - $adminRouter a $frontRouter. Je tam přichystán i překlad. Modul Admin asi necháme jak je. Tam nic nebude potřeba měnit, protože uživatelé nám do administrace nepolezou a roboti vyhledávačů tam taky nemají co dělat. Pohrajeme si ale s routováním frontendu.

První, co změníme, je tvar adresy stránek. Budeme k tomu vytvářet další třídu, která bude řešit routování stránek. Bude tedy dědit z třídy Route v Nette a také bude vyžadovat závislost na PageRepository. Budeme totiž hledat url stránky podle jejího id a potřebujeme i opačnou akci, tedy hledání id stránky podle její url.

Vytvoř tedy soubor \app\router\PageRoute.php:

<?php

namespace App;

use App\Model\PagesRepository;

class PageRoute extends \Nette\Application\Routers\Route {

	/** @var PagesRepository @inject */
	public $pagesRepository;

	public function match(\Nette\Http\IRequest $httpRequest) {
		$appRequest = parent::match($httpRequest);
		if(!isset($appRequest->parameters['id']))
			return NULL;
		$id = $appRequest->parameters['id'];
		if (!is_numeric($id)) {
			$page = NULL;
			try {
				$page = $this->pagesRepository->getOneWhere(['url' => $id]);
			} catch (Nette\Application\BadRequestException $exc) {
				return NULL;
			}
			$appRequest->parameters['id'] = $page->id();
		}
		return $appRequest;
	}

}

Třída má jedinou metou match(). Ta nejprve zjistí, zda je v URL parametr 'id'. Pokud prohlížím stránku, tak tam být musí.

Jestli je v parametru 'id' nečíselná hodnota, tak tam bude nějaký text. Budeme chtít, aby tím textem bylo pole url z objektu Page. Požádáme repozitář o to, aby zkusil nalézt stránku podle její url. Pokud najde, vyměný url z dotazu za id.

Nyní upravíme RouterFactory takto:

<?php

namespace App;

use Nette;
use Nette\Application\Routers\RouteList;
use Nette\Application\Routers\Route;
use \App\Model\PagesRepository;

/**
 * Class RouterFactory
 * @package App
 */
class RouterFactory {
	
	/** @var PagesRepository */
	private $pagesRepository;

	public function __construct(PagesRepository $pagesRepository) {
		$this->pagesRepository = $pagesRepository;
	}
	
	/**
	 * @return Nette\Application\IRouter
	 */
	public function createRouter() {
		$router = new RouteList();
		
		$router[] = $adminRouter = new RouteList('Admin');
		$adminRouter[] = new Route('[<locale=cs cs|en>/]admin/<presenter>/<action>', 'Pages:default');

		$router[] = $frontRouter = new RouteList('Front');
		
		$frontRouter[] = new PageRoute('[<locale=cs cs|en>/][stranky/]<id>', array(
			'id' => array(
				Route::FILTER_IN => function ($id) {
					if(is_numeric($id)) {
						return $id;
					} else {
						$page = $this->pagesRepository->getOneWhere(['url' => $id]);
						if($page === NULL)
							return NULL;
						return $page->id();
					}
				},
				Route::FILTER_OUT => function ($id) {
					if(!is_numeric($id)) {
						return $id;
					} else {
						$page = $this->pagesRepository->get($id);
						return $page->url();
					}
				}
			),
			'presenter' => 'Pages',
			'action' => 'view'
		));
			
		$frontRouter[] = new Route('[<locale=cs cs|en>/]<presenter>/<action>[/<id>]', 'Homepage:default');
		$frontRouter[] = new Route('[<locale=cs cs|en>/]index.php', 'Homepage:default', Route::ONE_WAY);
		return $router;
	}

}

Filtry FILTER_IN a FILTER_OUT slouží k překládání toho, co jde do prezenteru a z něj. Prostě přehazujeme mezi ID a URL.

Takže pokud si to takto upravíš a zobrazíš si stránky, zkus zi z menu vybrat nějakou stránku a kliknout. Například já mám v menu stránku "Kontakty". Má id=6 a url=kontaty. Takže původní

http://localhost:8000/pages/view/6

se změnilo na

http://localhost:8000/kontakty

Skvělé je, že funguje i původní tvar a dokonce i tvar http://localhost:8000/stranky/kontakty. Ale ještě skvělejší je, že Nette zařídí, že pouze jeden tvar je kanonický a ostatní na něj přesměruje.

A takhle je to možné provést i s dalšími presentery. Nabízí se třeba novinky. Odkaz na novinku má v URL např:

http://localhost:8000/news/view/1

Bylo by hezké také vyměnit ID za URL. Dokonce můžeme zařídit, že zmzne i to otravné news. Je tam ale nebezpečí, že bude existovat textová stránka se stejným URL jako nějaká novinka. To by se pak ukázala stránka místo novinky a novinku by zobrazit nešlo. Pokud se to ale pohlídá, je to krása.

Podobně jako u stránek, vytvoříme router pro novinky:

<?php

namespace App;

use App\Model\NewsRepository;
use Nette\Http\IRequest;
use Nette\Application\Routers\Route;
use Nette\Application\BadRequestException;

class NewsRoute extends Route
{

	/** @var NewsRepository @inject */
	public $newsRepository;

	/**
	 * @return Nette\Application\IRouter
	 * @param IRequest $httpRequest
	 */
	public function match(IRequest $httpRequest) {
		$appRequest = parent::match($httpRequest);
		if (!isset($appRequest->parameters['id']))
			return NULL;
		$id = $appRequest->parameters['id'];
		if (!is_numeric($id)) {
			$news = NULL;
			try {
				$news = $this->newsRepository->getOneWhere(['url' => $id]);
			} catch (BadRequestException $exc) {
				return NULL;
			}
			$appRequest->parameters['id'] = $news->id();
		}
		return $appRequest;
	}

}

Dělá vlastně to samé jako v případě stránek, jen se ptá tabulky pro novinky. Mám cukání nevypisovat tu další úpravu toho RouterFactory, protože je to celkem stejné jako u stránek, jen doplníš závislost na NewsRepository, upravíš kontruktor a vlastně opíšeš tu routu.

Další routa mě už nenapadá, ale princip by byl pokaždé stejný. Důležité je řazení, protože se bere vždy první vyhovující routa. Proto se taky zobrazí stránka a ne novinka, když mají obě stejnou URL.

Takže dnes je to všechno. Příště si ukážeme v posledním dílu ještě pár drobností jako mapu stránek a rss kanály. Zase si můžeš všechno stáhnout: kapitola24.zip.

Redakční systém RS::RS Předchozí kapitola

Využití modulu Settings

Redakční systém RS::RS Celý seriál

Vývoj redakčního systému v PHP

Redakční systém RS::RS Následující kapitola

Mapa stránek a rss

Komentáře (0)

Přidej svůj komentář

O mně

Jmenuji se Rudolf Svátek. Jsem lektor výpočetní techniky a PHP programátor. Stavím firemní stránky a eshopy. Aby se mi to dělalo pohodlně, vytvořil jsem redakční systém RS::RS, který ti tu nabízím k použití.

Rychlý kontakt na mně

  • Rudolf Svátek
  • Telefon:
    +420 777 828 353
  • Email:
  • Adresa:
    Josefa Hory 1097/5
    736 01 Havířov
    ČR



Tyto stránky používají Cookies. Používáním stránek s tím souhlasíte Další informace