Odkazy ve tavru: clanky/7-titulek-clanku

- motorcb
 - Člen | 552
 
Zdravim.
Chtěl bych mít odkazy ve tvaru: clanky/7-titulek-clanku
Nyní je mám ve tvaru: clanky/id:
$router[] = new Route('clanek/<id>', 'Clanek:detail');
Jak udělám aby v adrese byl i titulek článku?
Titulek si zakoduju funkci webalize
return Strings::webalize($titulek);
Ale jak ho v routeru dostat do adresy?
Adresy mam ve tvaru:
<a href="{link Clanek:detail, 7}">Titulek článku/a>
				
- Michal Vyšinský
 - Člen | 608
 
Příklad, který mám použitý na svých webech:
/*
 * Pages route - /<id>-<title>
 */
Route::addStyle("title");
Route::setStyleProperty("title", Route::FILTER_OUT, function($url) {
            return \Nette\Utils\Strings::webalize($url);
        });
Route::setStyleProperty("title", Route::FILTER_IN, function($url) {
            return \Nette\Utils\Strings::webalize($url);
        });
$router[] = new Route("clanky/<id [0-9]+>-<title>", array(
    "presenter" => "Clanek",
    "action" => "detail",
    "id" => NULL
        ));
EDIT:
Link pak vypadá takto:
<a href="{link Clanek:detail, id=>7, title=>'Titulek článku'}">Titulek článku/a>
EDIT 2:
Titulek si zakoduju funkci webalize
Toto pak není potřeba. Do linku můžeš dávat přímo titulek, maska se ti postará o správnou funkčnost.
Editoval CherryBoss (18. 2. 2013 13:13)

- motorcb
 - Člen | 552
 
2CherryBoss:
Díky :)
Jenže to budu muset předělat všechny odkazy aby v nich bylo title=>‚…‘ :(
<a href="{link Clanek:detail, id=>7, title=>'Titulek článku'}">Titulek článku/a>
Nebylo by možné ten titulek nastavovat přímo v routeru?
Představoval bych si přes FILTER_OUT?. Vytáhnout informace o článku a
titulek přidat do adresy

- Michal Vyšinský
 - Člen | 608
 
Tak to už musí poradit zkušenější. Ale obávám se, že to nijak
jednoduše nepůjde.
Edit: proč si myslím, že to nelze: Nemáš se jak dostat
k parametru „id“.
Editoval CherryBoss (18. 2. 2013 13:40)

- tomas.lang
 - Člen | 53
 
motorcb: Možné to je, nejlépe pak jít cestou vlastního routeru (i když možná je to v tomto případě kanón na vrabce… :) ):
<?php
class MySpecialRouter extends Nette\Application\Routers\Route
{
	protected $database = NULL;
	public function injectDatabase(Nette\Connection\Database $database)
	{
		$this->database = $database;
	}
	public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl)
	{
		// upravím parametry
		$params = $appRequest->getParameters();
		if ($this->database !== NULL) {
			$articleRecord = $this->database->table('clanky')->get($params['id']);
			if ($articleRecord !== FALSE)
				$params['title'] = Nette\Utils\Strings::webalize($articleRecord->title);
			else
				$params['title'] = NULL;
		}
		$appRequest->setParameters($params);
		// vytvořím url
		return parent::constructUrl($appRequest, $refUrl);
	}
}
?>
No a routování pak provedu následovně:
<?php
$route = new MySpecialRouter('clanek/<id>[-<title>]', 'Clanek:detail')
$route->injectDatabase($this->context->database);
?>
Píšu to teď z patra, takže to nemám ozkoušené, ale mělo by to fungovat… :-)
Editoval tomas.lang (18. 2. 2013 14:32)

- Michal Vyšinský
 - Člen | 608
 
Inject přes konstruktor?
Edit: vidím, že už vyřešeno :-)
Editoval CherryBoss (18. 2. 2013 16:21)

- Lumeriol
 - Generous Backer | 68
 
tomas.lang napsal(a):
motorcb: Možné to je, nejlépe pak jít cestou vlastního routeru (i když možná je to v tomto případě kanón na vrabce… :) ):
No a routování pak provedu následovně:
<?php $route = new MySpecialRouter('clanek/<id>[-<title>]', 'Clanek:detail') $route->injectDatabase($this->context->database); ?>
Píšu to teď z patra, takže to nemám ozkoušené, ale mělo by to fungovat… :-)
A jak jednoduše přidat tyto routy do RouterFactory bys netušil? Tam se ke contextu Nette nedostane.

- Lumeriol
 - Generous Backer | 68
 
Lopata napsal(a):
Ta metoda
createRouter(viz sandbox) může přijímat jakýkoliv argument, přičemž normálně funguje autowiring. Tak si tam prostě injectni database. Nebo lépe, nějakou obálku, která pracuje s cache a nebude se ptát databáze při každém odkazu.
A nebyl by nějaký drobný praktický příklad? Zkoušel jsem hledat vytvoření injectu tímto způsobem, ale marně a myslím že by to ocenili i další.

- Lopata
 - Člen | 139
 
Když se v sandboxu podíváš do souboru config.neon , uvidíš, že se tam ta RouterFactory deklaruje jako service. Díky tomu na ní funguje autowiring.
Autowiring je mechanismus, který u konstruktorů (i u jiných metod, ale to zatím asi neřeš) podle type hintů pozná, co ten či onen objekt chce a pokud najde odpovídající službu, předá ji jako příslušný argument, a to aniž bys to ty musel explicitně příkázat. Od toho se to jmenuje autowiring.
My tedy chceme router naučit překládat ty adresy. Víme, že aplikace se
může kdykoliv rozrůst, a že tudíž hrozí rozsáhlost a složitost té
třídy RouterFactory. Uděláme si proto nějaký pomocný objekt,
který nám s tím překladem pomůže, abychom odlehčili oné nebohé
továrničce na routy. Ten pomocný objekt tedy deklarujeme jako službu
přidáním tohoto řádku do config.neon:
services: # Tento řádek už tam je. Neduplikuj to, jen tam přidej další záznam.
	cachedDatabaseRouterHelper: my\Namespace\CachedDatabaseRouterHelper
Nyní musíme tu třídu vytvořit.
namespace my\Namespace;
use Nette;
class CachedDatabaseRouterHelper extends Nette\Object {
	protected $connection;
	protected $cache;
	public function __construct(\DibiConnection $connection, Nette\Caching\Storages\FileStorage $storage) {
		$this->connection = $connection;
		$this->cache = new Nette\Caching\Cache($storage, 'CachedDatabaseRouterHelper'); // Klidně té Cache dej lepší namespace… ;)
	}
	public function filterIn($param) {
		// tady regulárem vybereš to ID
	}
	public function filterOut($param) {
		// tady k ID nalepíš webalizovaný string
	}
}
Nezapomeň smazat obsah složky temp/cache, až tyto změny
uděláš!
Všimni si, že autowiring sám předá ty dva argumenty.
Teď už jen vysvětlit RouterFactory, že to má použít.
Když si o tu novou třídu řekneš v konstruktoru, dostaneš ji tam, nebudu
se opakovat. Vlastní router psát netřeba,
Nette\Application\Routers\Route umí vše, co teď
potřebujeme.
$cachedDatabaseRouterHelper = $this->cachedDatabaseRouterHelper;
$router[] = new Route('clanky[/<id [a-z0-9\-]+>]', array (
	'categoryId' => array (
		Route::FILTER_IN => $cachedDatabaseRouterHelper->filterIn, // protože ta třída dědí Nette\Object
		Route::FILTER_OUT => $cachedDatabaseRouterHelper->filterOut
	),
	'action' => 'whateverAction',
	'presenter' => 'whateverPresenter'
));
Doporučuji tu routu realizovat jen podle toho čísla a ten titulek tam mít jen pro okrasu, aby nemusela být jména článků unikátní. Podobně to má např. i idnes, viz např zpravy.idnes.cz/kocka-leze-dirou-pes-oknem-pes-oknem-ffl-/domaci.aspx?c=A130714_113159_domaci_maq . :D
Jinak ten kód jsem psal teď z hlavy, tak k tomu tak prosím přistupuj. Mělo by Tě to ale postrčit správným směrem.

- Snikwah
 - Člen | 6
 
Lumeriol: Funguje ti kód, který přidal Lopata (příspěvěk č. 11)?
Mně při použití háže tuto chybu:
Notice
Undefined property: RouterFactory::$myRouterFilter
Poradí mně někdo co s tím?
config.neon
...
	services:
		myRouterFilter: MyCode\Router\MyRouterFilter
...
RouterFactory.php
use Nette\Application\Routers\RouteList,
	Nette\Application\Routers\Route,
	Nette\Application\Routers\SimpleRouter;
class RouterFactory{
	public function createRouter(){
		$router = new RouteList();
		$routerFilter = $this->myRouterFilter;
...
MyRouterFilter.php
namespace MyCode\Router;
use Nette;
class MyRouterFilter extends Nette\Object {
    protected $connection;
    protected $cache;
    public function __construct(Nette\Database\Connection $db, Nette\Caching\Storages\FileStorage $storage) {
        $this->connection = $db;
        $this->cache = new Nette\Caching\Cache($storage, 'MyRouterFilter');
    }
	public function filterInCategory($param){
		...
	}
    	public function filterOutCategory($param) {
        ...
    }
}
					Editoval Snikwah (27. 8. 2013 0:04)

- David Matějka
 - Moderator | 6445
 
jelikoz v tom kodu v 11 nevidim zadny myRouterFilter, tak predpokladam, ze sis to tam dopsal sam – muzes ukazat cely kod a ladenku?

- Snikwah
 - Člen | 6
 
Pokusil jsem se použít kód z přednášky Jan Smitka: Routování podrobně a takhle to dopadlo:
RouterFactory.php
class RouterFactory
{
	/**
	 * @return Nette\Application\IRouter
	 */
	public function createRouter(Nette\Database\Connection $db)
	{
		$router = new RouteList();
		$router[] = $frontRouter = new RouteList('Front');
		$frontRouter[] = new Route('kategorie/<id>', array(
							'id' => array (
							  Route::FILTER_IN => function ($id) use ($db) {
							  				if(is_numeric($id)) {
							  					return $id;
							  				} else {
							  					$param = $db->table('category')->where('uri',$id)->fetch()->id;
							  					return $param;
								  			}
							  			},
							  Route::FILTER_OUT => function ($id) use ($db) {
							  				if(!is_numeric($id)) {
							  					return $id;
							  				} else {
							  					$param = $db->table('category')->where('id',$id)->fetch()->uri;
							  					return $param;
								  			}
							  			}
							  ),
							'presenter' => 'Category',
							'action' => 'show'
							));
...
Funguje při zadání /kategorie/<uri> se vyhledá id a pokud někdo zadá /kategorie/<id> automaticky se přepíše adresa na /kategorie/<uri>.
Editoval Snikwah (31. 8. 2013 23:06)

- duke
 - Člen | 650
 
Snikwah napsal(a):
No service of type FrontModule\Nette\Database\Connection found. Make sure the type hint in Method FrontModule\CategoryPresenter::injectCategory() is written correctly and service of this type is registered.
Z toho je přeci jasné, o co jde… Chybí ti tam:
use Nette;
Alternativně můžeš použít \Nette\Database\Connection
(počáteční backslash).