Routy a odkazy – presenter dle slugu (friendly uri) s využitím pole
- flamengo
- Člen | 135
Marně se snažím přijít na banální záležitost a nechce se mi věřit, že to nikdo nemá vyřešeno. Hledal jsem tu a četl a četl až jsem z toho úplně zpitomnělý.
Nechci mít v názvech stránky názvy presenterů, proč taky?
Rád bych, aby si uživatel redakčního systému mohl vytvořit webovou sekci
s články a pojmenovat ji dle sebe: články (www...cz/clanky/), aktuality
(www...cz/aktuality/)
nebo novinky (www...cz/novinky/). Uživatel (jak stránek, tak RS) přeci
vůbec nemusí vědět něco o tom, že existuje nějaký presenter
s nějakým konkrétním jménem, akcema atd.
Ilustratorvní struktura sekcí následující:
- www...cz/ - HomepagePesenter
- www...cz/clanky/ - ArticlesPresenter
- www...cz/produkty/ - ProductsPresenter
V databázi je tabulka webových sekcí, která obsahuje:
- ID webové sekce
- titulek sekce
- slug (friendly uri, cool uri apod.)
- presenter
Mohla by se přidat i akce, ale předpokládám, že se bude vždy jednat o akci „default“.
Zkoušel jsem použít FILTER_IN
a FILTER_OUT
, ale
marně. Kód po mě prosím nechtějte, zkoušel jsem asi trilión možných
variací, spíše hokusů a pokusů.
Ve funkcích filtrů to nechci řešit přes databázi, dle mého to při
větším počtu odkazů zpomalí aplikaci.
Napadlo mě si načíst strukturu sekcí do pole, kde klíč je slug a hodnota
presenter.
$sectionRoutes = array(
'uvod' => 'Homepage',
'clanky' => 'Articles',
'produkty' => 'Products',
);
No a potřeboval bych routu, která by dle slugu a pole určila presenter.
$router[] = new Route('<slug>/', array(
// pro mne bohužel neznámá :(
));
Taky bych vlastně potřeboval poradit, jak budou vypadat odkazy v latte šabloně, zde jsem při hokusech pokusech také narazil.
<a href="{link Homepage:default slug => uvod}">Homepage</a>
<a href="{link Articles:default slug => clanky}">Články</a>
<a href="{link Products:default slug => produkty}">Produkty</a>
Za radu, nasměrování, nakopnutí předem velice díky.
- Pavel Janda
- Člen | 977
https://forum.nette.org/…e-se-nahradi#…
Odkazy budeš dělat normálně (n:href=„Homepage:default“), Nette ti samo převede tuto routu na cool/fancy url
Editoval Pavel Janda (21. 5. 2016 17:40)
- flamengo
- Člen | 135
Díky za radu, ale bohužel uvedený kód nefunguje:
Undefined variable: this,
Route::FILTER_IN => [$this, 'urlIn']
.
Když to trochu předělám, tak mi to stejně hlásí „Undefined variable:
this“ díky $this->urls
a ani se nedivím, to je prosím co za
proměnnou a kde se tu bere?
Odkazy se dle mého musí dělat jinak – potřebuji do nich přce nějak
vložit i ten slug.
Měl jsme upozornit, že také může vzniknout sekce se stejným
presenterem:
- www...cz/clanky/ - ArticlesPresenter
- www...cz/zhave-novinky/ - ArticlesPresenter
$sectionRoutes = array(
'uvod' => 'Homepage',
'clanky' => 'Articles',
'zhave-novinky' => 'Articles',
'produkty' => 'Products',
);
- Myiyk
- Člen | 321
@flamengo Presenter ví jen svůj název a akce, které má. Neví
jaké url to bude generovat.
Když odkazuješ, dáváš odkaz na Presenter a akci. VŽDY
Router je od toho, aby z toho udělal nějakou url adresu. (je jedno
jakou)
A router umí i opačnou funkci, že z url adresy zjistí, jaký to je
presenter a akce.
Routeru se nijak nenapovídá, protože presenter vlastně neví že nějaký router existuje a jak funguje.
- flamengo
- Člen | 135
No tak asi jsem to nakonec vykoumal, budu rád za připomínky a názory, zda
to je správná cesta.
Ještě jsem do databáze jakoby dodělal sloupec home
, který
značí úvodní stránku.
Odkaz na úvodní stránku se vygeneruje bez slugu.
menu.latte
<a href="{link Homepage:default slug => uvod}">Úvod</a>
<a href="{link Articles:default slug => clanky}">Články</a>
<a href="{link Articles:default slug => zhave-novinky}">Žhavé novinky (Články)</a>
<a href="{link Products:default slug => produkty}">Produkty</a>
RouterFactory.php
$router = new RouteList();
$sectionRoutes = array(
'uvod' => 'Homepage',
'clanky' => 'Articles',
'zhave-novinky' => 'Articles',
'produkty' => 'Products',
);
$homeSlug = 'uvod';
$homePresenter = $sectionRoutes[$homeSlug].':default';
$router[] = new Route('[<slug='.$homeSlug.'>/]', array(
NULL => array(
Route::FILTER_IN => function ($params) use ($sectionRoutes) { // dle adresy načítám presenter
if(!isset($params['slug'])){
return NULL;
}
$slug = trim($params['slug'], '/');
if(!isset($sectionRoutes[$slug])){
return NULL;
}
$params['presenter'] = $sectionRoutes[$slug];
return $params;
},
Route::FILTER_OUT => function ($params) use ($sectionRoutes) { // tvořím URL adresu
if(!isset($params['slug']) || !isset($sectionRoutes[$params['slug']])){
return NULL;
}
unset($params['module'], $params['presenter'], $params['action']);
return $params;
}
)
));
$router[] = new Route('<presenter>/<action>/[<id>/]', $homePresenter);
Editoval flamengo (21. 5. 2016 21:33)
- Myiyk
- Člen | 321
Router z odkazu výše https://forum.nette.org/…e-se-nahradi#… nebude fungovat na 100%
Tato verze bude fungovat lépe (ověřeno testy)
<?php
namespace App;
use Nette;
use Nette\Application\Routers\RouteList;
use Nette\Application\Routers\Route;
class RouterFactory
{
private $urls = [
'novinka-jedna' => [
'presenter' => 'Homepage',
'action' => 'detail',
'id' => '4145',
]
];
/**
* @return Nette\Application\IRouter
*/
public function createRouter()
{
$router = new RouteList;
//$router[] = new Route('<url>', 'Homepage:default');
$router[] = new Route('[<url [a-zA-Z0-9-\/]+?>]', [
NULL => [
Route::FILTER_IN => [$this, 'urlIn'],
Route::FILTER_OUT => [$this, 'urlOut']
]
]);
return $router;
}
public function urlIn($params)
{
$url = trim($params['url'], '/');
unset($params['url']);
if (isset($this->urls[$url])) {
return $this->urls[$url] + $params;
}
return NULL;
}
public function urlOut($params)
{
foreach ($this->urls as $url => $url_params) {
$same = true;
$copyParams = $params;
foreach ($url_params as $p => $url_param) {
if (isset($copyParams[$p]) && $url_param == $copyParams[$p]) {
unset($copyParams[$p]);
} else {
$same = false;
break;
}
}
if ($same) {
return ['url' => $url] + $copyParams;
}
}
return NULL;
}
}
Rozdíl je v tom, že tato verze podporuje další parametry. To znamená, že na stránce, kde je seo friendly adresa bude fungovat stránkování atd.
Editoval Myiyk (22. 5. 2016 0:43)
- Myiyk
- Člen | 321
@flamengo tvůj předchozí kód by cca mohl fungovat, ale dělá to jenom převodník názvu presenteru.
Když otevřeš adresy
- www...cz/clanky/ - ArticlesPresenter
- www...cz/zhave-novinky/ - ArticlesPresenter
a mrkneš do debug lišty, uvidíš co to je za Presenter, akce a parametry. A právě tím presenterem akcí a parametry by jsi na tu stránku měl odkazovat z kódu.
Takhle budeš mít v šabloně napevno stanoveny url, na jakých budou ty
stránky. To už rovnou můžeš odkazovat
jako <a href="www...cz/zhave-novinky">
Editoval Myiyk (21. 5. 2016 22:35)
- Myiyk
- Člen | 321
@flamengo to se nedá takhle určit. Otevři si tu stránku a podívej se do debug baru, na jaké jsi adrese.
Příklad
otevřeš v prohlížeči stránku www...cz/clanky/
, v debug baru
bude uvedena adresa jako Articles:show id=123
odkaz z jakékoliv šablony na tu stránku uděláš
jako n:href="Articles:show id=123"
Editoval Myiyk (21. 5. 2016 23:48)
- flamengo
- Člen | 135
@Myiyk
1. Nemůžu si pomoci, ale já mám pocit, že to mám tak, jak naznačuješ. Snad jen že kromě ID tam je parametr SLUG. Ten předám do render metody a dle toho načtu články (respektive dle SLUGu zjistím ID webové sekce a potom mohu zobrazit články vložené k této sekci).
<a href="{link Articles:default slug => clanky}">Články</a>
Request
Articles:default
slug = clanky
<a href="{link Articles:default slug => zhave-novinky}">Žhavé novinky (Články)</a>
Request
Articles:default
slug = zhave-novinky
2. Tvůj kód jsem zkopíroval do RouterFactory.php a bohužel mi hodí chybu :(
Notice
Undefined index: id
File: ...\app\router\RouterFactory.php:75
75: if ($url_param == $copyParams[$p]) {
3. Nevíš prosímtě, jak do RouterFactory.php dostat databázi nebo model? Zkoušel jsem kde co a nejde mi nic :(
Editoval flamengo (22. 5. 2016 0:08)
- Myiyk
- Člen | 321
@flamengo
1. Nemůžu si pomoci, ale já mám pocit, že to mám tak, jak naznačuješ. Snad jen že kromě ID tam je parametr SLUG. Ten předám do render metody a dle toho načtu články (respektive dle SLUGu zjistím ID webové sekce a potom mohu zobrazit články vložené k této sekci).
Ano, máš pravdu. Pokud už v aplikaci nepracuješ s ID, ale se SLUG. Vystačíš si s normálním routerem.
$router[] = new Router("<presenter>/<action>...", ....);
$router[] = new Router("<slug>", "Articles:default");
2. Tvůj kód jsem zkopíroval do RouterFactory.php a bohužel mi hodí chybu :(
Neošetřil jsem neexistenci parametru, správná podmínka
je if (isset($copyParams[$p]) && $url_param == $copyParams[$p])
3. Nevíš prosímtě, jak do RouterFactory.php dostat databázi nebo model? Zkoušel jsem kde co a nejde mi nic :(
Tento příkladový router je statický. Pokud ho chceš dynamicky, bude
vypadat o hodně jinak.
Zkus použít můj doplněk na seo adresy https://componette.org/…/seo-router/
Editoval Myiyk (22. 5. 2016 10:57)
- flamengo
- Člen | 135
Ahoj a díky za odpovědi. Budu to jako minule číslovat.
1.
Ano, máš pravdu. Pokud už v aplikaci nepracuješ s ID, ale se SLUG. Vystačíš si s normálním routerem.
No tím si nejsem jistý. Kdybych si přeci vystačil s tím, co uvádíš, tak by toto vláknu vůbec nevzniklo :)
2. Chyba
Zkusil jsme dnes použít tvůj kód (ten původní s opravou) a dnes mi
z nejde vůbec, nechápu. Včera házel chyby Undefined index: id
,
dneska se k této chybě ani nedopracuje. Dělal jsem tam hodně hokusy pokusy
při snaze dostat do RouterFactory databázi nebo model. Teď jsem vše vrátl
do původního stavu a chyba tato:
Strict standards
Non-static method App\RouterFactory::createRouter() should not be called
statically, assuming $this from incompatible context se
V neonu mám:
services:
router: App\RouterFactory::createRouter
Někde jsem vybrabal toto (nevím, jaký je rozdíl a co to znamená ale zkusil jsem to):
services:
- App\RouterFactory
router: @App\RouterFactory::createRouter
A při tomto kódu se chyba změní na:
Nette\Application\BadRequestException #404
No route for HTTP request.
Dál nevím co s tím.
3. Pokus o nahození Myiyk\SeoRouter
Composer nepoužívám, zatím neumím. Ve složce app/libs/ jsem vytvořil
/Myiyk/SeoRouter/ a do toho nakopíroval obsah složky src/. Do neonu jsem dle
„Basic Usage“ vložil:
services:
- App\Model\SeoRouterSource
routerFactory: App\RouterFactory
router: @routerFactory::createRouter(@seoRouter.router)
extensions:
seoRouter: Myiyk\SeoRouter\Extension
Chyba:
Nette\DI\ServiceCreationException
Class App\Model\SeoRouterSource used in service
‚26_App_Model_SeoRouterSource‘ not found or is not instantiable
Z neonu jsme odstranil - App\Model\SeoRouterSource
Chyba:
Nette\DI\MissingServiceException
Service type Myiyk\SeoRouter\ISource not found, did you register it?
Zkusil sjem do neonu dát:
services:
- Myiyk\SeoRouter\ISource
Chyba:
Nette\DI\ServiceCreationException
Interface Myiyk\SeoRouter\ISource used in service
‚27_Myiyk_SeoRouter_ISource‘ must have just one non-static method create()
or get().
A zde končím, dál nevím. No je mi jasný, že to dost prasím.
Takže dále mám své pátrání zaměřit na dynamický router? Jaký v tom je prosím rozdíl respektive co si mám dostudovat?
Do toho mého příkladu by se nemusela cpát databáze nebo model, stačilo by to pole. To by nějak jednoduše nešlo?
Editoval flamengo (22. 5. 2016 11:43)