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 | 63
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 | 63
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).