Problém s nastavením modulu u databázově závislého routeru
- Čelo
- Člen | 42
Zdravím,
Celý večer procházím forum a dokumentaci, ale už nevím, jak si poradit
s tímto problémem.
Mám v bootstrapu
<?php
$router = $application->getRouter();
$router[] = new DatabaseRoute('<lang cs|en>/<id [0-9]+>-<uri>', array(
'presenter' => 'Default',
));
?>
a DatabaseRoute definovaný:
<?php
class DatabaseRoute extends Route implements IRouter
{
protected $table = 'router';
public function match(IHttpRequest $httpRequest)
{
$request = parent::match($httpRequest);
if ($request === NULL)
return NULL;
$params = $request->getParams();
$data = dibi::fetch(
'SELECT [modul],[presenter] FROM %n', $this->table,
'WHERE %n = %s', 'id', $params['id']
);
if (empty($data)) return NULL;
return new PresenterRequest(
$data->modul.':'.$data->presenter,
$httpRequest->getMethod(),
$params,
$httpRequest->getPost(),
$httpRequest->getFiles(),
array('secured' => $httpRequest->isSecured())
);
}
public function constructUrl(PresenterRequest $httpRequest, IHttpRequest $context)
{
$actualParams = $httpRequest->getParams();
$data = dibi::fetch(
'SELECT [uri],[lang] FROM %n', $this->table,
'WHERE %n = %s', 'id', $actualParams['id']
);
if (empty($data)) return NULL;
$uri = $context->getUri()->basePath.$data['lang'].'/'.$actualParams['id'].'-'.$data['uri'];
//unset($actualParams['lang'], $actualParams['id'],$actualParams['uri']);
//$query = http_build_query($actualParams, '', '&');
//if ($query !== '') $uri .= '?' . $query;
return $uri;
}
}
?>
A tabulka „router“ v databázi má dva testovací záznamy [id=2,lang=cs,uri=novinky,modul=Novinky,presenter=Default] a [id=2,lang=cs,uri=fotogalerie,modul=Fotogalerie,presenter=Default]
Snahou tedy je, aby se zavolal definovaný modul a presenter. Jenže při
zavolání url adres
http://www.example.com/cs/2-novinky a http://www.example.com/…-fotogalerie laděnka zahlásí
No route for Novinky:Default:default(lang=cs, id=2,
uri=novinky) respektive No route for
Fotogalerie:Default:default(lang=cs, id=3, uri=fotogalerie)
Při hledání řešení jsem zjistil, že když v bootstrapu zavolám:
<?php
$router[] = new DatabaseRoute('<lang cs|en>/<id [0-9]+>-<uri>', array(
'presenter' => 'Novinky:Default',
));
?>
či
<?php
$router[] = new DatabaseRoute('<lang cs|en>/<id [0-9]+>-<uri>', array(
'module' => 'Fotogalerie',
'presenter' => 'Default',
));
?>
tak pro daný modul adresa začne být funkční a stránka se správně
zobrazí. Jenže jen právě a pouze pro modul.
Pomůžete mě navést k nalezení řešení, které nastaví DatabaseRoute tak
aby zavolal modul a presenter definovaný v databázové tabulce?
- Vyki
- Člen | 388
Mrkni se jak funguje překladový slovník k routám. A pak se mrkni volitelné sekvence v route a pomocí toho to s trochou snahy dáš dohromady i bez vlastní route.
- Čelo
- Člen | 42
Tak se mi podařilo zprovoznit takovou polofunkční verzi, která potřebné adresy zpracuje tak jak potřebuji. Nemám v tom moc jasno a asi to není úplně přesně to, co chci, protože to zpracovává (či neopravuje) adresy, které nechci (např. http://www.example.com/cs/2-blabla či http://www.example.com/cs/test-blabla ). Obě adresy hodí na Default presenter. Ten DatabaseRouter měl výhodu v tom, že to zpracovávalo jen správně zadaný formát a navíc to automaticky opravovalo uri i lang, protože se to řídilo id.
<?php
function getModulePresenterFromId($Id=0)
{
$data = dibi::fetch(
'SELECT [modul],[presenter] FROM %n', 'router',
'WHERE %n = %s', 'id', $Id
);
if (empty($data)) return NULL;
return $data['modul'].':'.$data['presenter'];
}
function getIdFromModulePresenter($Name='')
{
if (!preg_match('~^([^:]+):([^:]+)$~',$Name,$matches)) return NULL;
$data = dibi::fetch(
'SELECT [id] FROM %n', 'router',
'WHERE %n = %s', 'modul', $matches[1],
' AND %n = %s', 'presenter', $matches[2]
);
if (empty($data)) return NULL;
return $data['id'];
}
Route::addStyle('#id', NULL);
Route::setStyleProperty('#id', Route::PATTERN, '[0-9]');
Route::setStyleProperty('#id', Route::FILTER_IN, 'getModulePresenterFromId');
Route::setStyleProperty('#id', Route::FILTER_OUT, 'getIdFromModulePresenter');
$router[] = new Route('<lang '.implode('|',$langs).'>/<presenter #id>-<uri>[/<action>]', array(
'presenter' => 'Default',
'action' => 'default',
));
?>
- Čelo
- Člen | 42
Tak mi to nedalo a chvíli jsem laboroval s tím, jak zprovoznit ten
DatabaseRouter a přitom jsem pochopil, že na to nejdu dobře.
Hlavní problém byl v tom, že MultiRouter při zavolání fce constructUrl a
následně getTargetPresenter vracel Default, což bylo logické, ale
nežádoucí.
Stačilo DatabaseRouteru do fce match před vrácením PresenterRequest uložit do nějaké proměnné z databáze zjištěný modul a presenter a pak vytvořit v DatabaseRouteru vlastní fci getTargetPresenter a vrátit jej.
<?php
public function getTargetPresenter() {
return isset($this->presenter)?$this->presenter:NULL;
}
?>
pak stačilo ještě pořešit constructUrl v DatabaseRouteru pro předávání dalších parametrů, ale jak jsem psal, při rozcházení funkčnosti jsem pochopil, že na to nejdu dobře ačkoliv to už funguje :)
EDIT: Samozřejmě to funguje jen na oko, protože vytváření adres je pak úplně mimo :)
Editoval Čelo (1. 2. 2010 10:25)
- Vyki
- Člen | 388
Protože příklad je lepší než tisíc slov:
<?php
class RouteSubs extends Object
{
const ROUTESUBS = 'RouteSubs';
const PERMALINKOUT = 'outPermalink';
const IDIN = 'inId';
public static function inId($url)
{
$convertor = array( //zde bys měl ten dotaz na DB, pole tam mám pro zjednodušení
'kontakt' => 4,
'uvod' => 1,
'informace' => 2,
'aktuality' => 3,
);
if(isset($convertor[$url]))
return $convertor[$url];
else
return NULL;
}
public static function outPermalink($id)
{
$convertor = array(
4 => 'kontakt',
1 => 'uvod',
2 => 'informace',
3 => 'aktuality',
);
if(isset($convertor[$id]))
return $convertor[$id];
else
return NULL;
}
}
Route::addStyle('#id');
Route::setStyleProperty('#id', Route::FILTER_IN, callback(RouteSubs::ROUTESUBS, RouteSubs::IDIN));
Route::setStyleProperty('#id', Route::FILTER_OUT, callback(RouteSubs::ROUTESUBS, RouteSubs::PERMALINKOUT));
$router = $application->getRouter();
$router[] = new Route('index.php', array(
'presenter' => 'Front',
'action' => 'default',
'id' => '1',
), Route::ONE_WAY);
$router[] = new Route('[[<presenter>/]<id #id>/][<action>/]', array(
'presenter' => 'Front:Page',
'action' => 'default',
'id' => '1',
));
//akceptuje cesty:
// domena.cz/kontakt/ -> vede na Front:Page:Default (modul:presenter:action)
// domena.cz/front.editpage/kontakt/ -> vede na Front:Editpage:Default
// domena.cz/front.editpage/kontakt/save/ -> vede na Front:Editpage:Save
?>
a například ve výchozím page presenteru mám potom:
<?php
class Front_PagePresenter extends BasePresenter
{
public function actionDefault($id)
{
//$id = $this->getParam('id');
if($id === NULL)
throw new BadRequestException('Stránka neexistuje'); //ten překladový slovník ti místo čísla můžě vrátit i NULL! Je potřeba to nějak takto ošetřit.
$page = DbModel::FetchOnePage($id);
if($page){
$this->template->html = $page->html;
$this->template->id = $id;
} else {
throw new BadRequestException('Stránka neexistuje');
}
}
}
?>
Editoval Vyki (31. 1. 2010 22:39)