Problém s nastavením modulu u databázově závislého routeru

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Čelo
Člen | 42
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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)