Vlastní router a presenter z db

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

Zdravím,

snažim se napsat svůj vlastní router:
mám tabulku v databázi, kde mám uložený slug a presenter

Mělo by to fungovat asi tímto způsobem:
domain.tld/<slug>/<action>[/<id>]
s tím, že nevím jaký presenter budu chtít použít. Ve vlastním routeru se snažím jen najít presenter k danému slugu.

SlugRouter:

<?php
namespace App;

use Model;
use Nette\Application\Routers;
use Nette\Application as App;
use Nette;
use Nette\Http;


class SlugRouter extends Routers\Route implements Nette\Application\IRouter
{

	/** @var SlugManager */
	private $slugManager;

	public function __construct($mask, $metadata = array(), $flags = 0, Model\SlugManager $slugManager)
	{
		parent::__construct($mask, $metadata, $flags);
		$this->slugManager = $slugManager;
	}

	/**
	 * Maps HTTP request to a Request object.
	 *
	 * @return App\Request|NULL
	 */
	public function match(Http\IRequest $httpRequest)
	{
		$request = parent::match($httpRequest);

		if ($request === NULL) {
			return NULL;
		}

		$slug = $request->getPresenterName();
		$params = $request->parameters;
		$row = $this->slugManager->getPresenterBySlug(strtolower($slug));
		if ($row) {
			$slug = $row;
			$params['slug'] = $row;
		}
		return new App\Request($slug, $httpRequest->getMethod(), $params, $httpRequest->getPost(), $httpRequest->getFiles(), array(App\Request::SECURED => $httpRequest->isSecured()));

	}

	/**
	 * Constructs absolute URL from Request object.
	 *
	 * @return string|NULL
	 */
	public function constructUrl(App\Request $appRequest, Http\Url $refUrl)
	{
		$row = $this->slugManager->getSlugById($appRequest->getPresenterName());
		$appRequest->setParameters(array('slug' => NULL));
		if ($row) {
			$appRequest->setPresenterName($row);
		}

		parent::constructUrl($appRequest, $refUrl);
		//translate presenter into slug
		//call parent
	}
}

SlugManager:

<?php

namespace Model;

use Nette;


class SlugManager extends Nette\Object
{

	/** @var Nette\Database\Context */
	private $tree;


	public function __construct(Nette\Database\Context $database)
	{
		$this->tree = $database->table("tree");
	}

	public function getPresenterBySlug($slug)
	{
		$presenter = $this->tree->where("slug", $slug)->select('presenter')->fetch();
		if($presenter) {
			return $presenter->presenter;
		} else {
			return NULL;
		}
	}

	public function getSlugById($id)
	{
		$slug = $this->tree->where("presenter", $id)->select('slug')->fetch();
		if($slug) {
			return $slug->slug;
		} else {
			return NULL;
		}
	}
}

RouterFactory

<?php

namespace App;

use Nette\Application\Routers\Route;
use Nette;
use Nette\Application\Routers\RouteList;
use Model;


/**
 * Router factory.
 */
class RouterFactory
{
	/** @var SlugManager */
	private $SlugManager;

	public function __construct(Model\SlugManager $SlugManager)
	{
		$this->SlugManager = $SlugManager;
	}

	/**
	 * @return \Nette\Application\IRouter
	 */
	public function createRouter()
	{
		$router = new RouteList();
		$router[] = new SlugRouter('<presenter>/<action>[/<id>]', 'Homepage:default', 0, $this->SlugManager);
		$router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
		return $router;
	}

}

Celé se to chová divně, asi mám nějakou chybu v návrhu.

Nějaké nápady/připomínky? Mám to celé zahodit a udělat jinak?

Díky

David Matějka
Moderator | 6445
+
0
-

zahazovat to nemusis, jen specifikuj, jak divne se to chova :)

BombajS
Člen | 9
+
0
-
  1. když zavolám <slug>/<action>, tak se mi zavolá správný presenter, ale akci bere pořád default.
  2. některé url se mi presměrují z <slug> na <presenter>?slug=<slug>
  3. generování adres mi nefunguje tak, jak by mělo
  • prostě jen chci, aby se při match() zaměnil presenter za presenter nalezený v db u daného slugu a u constructUrl bych chtěl, abych mohl tvořit adresy něco jako <slug>/<action>…

Nějak jsem se do toho zamotal :D

honos
Člen | 109
+
0
-

BombajS napsal(a):

Nějak jsem se do toho zamotal :D

Jsem se do toho ted take zamotal, jak jsem to chtel rozlustit k cemu to vlastne mas..

Osobne mam na localhostu/u rozdelanou aplikaci vyuzivajici moduly kde kazdy modul definuje vlastni routy ulozenim do tabulky v db pod foreign_key k tabulce definujici modul. Ale jako router mam pouzit obycejny RouteList a funguje mi to bez problemu. Mel jsem tam rozdelany 4 moduly: galery, presentation, blog a slug ale ponechal jsem si ted tam prave pouze ten slug abych to vystiloval. Ale taky jsem mel problemy ze se volal spatny presenter. Vyresil jsem to tak ze jsem musel definovat vahu jednotlivych modulu ale i vahu jednotlivych rout v ramci mmodulu. Co se tyce routovani tak ted mi to funguje bezvadne.

P.S. Vlastne jsem zapomel jeste na to ze administraci systemu(ne modulu jako takovych), prihlaseni/registraci, ACL mam reseny jako moduly a funguje bez problemu.

EDIT: Jinak ti v tom RoterManager chybi RouteList(‚index.php‘…

Editoval honos (12. 1. 2014 11:17)

BombajS
Člen | 9
+
0
-

O co se snažím:

/vitejte
⇒ presenter = page, action = default, slug = vitejte

/domy
⇒ presenter = reality, aciton = default, slug = domy

/domy/detail/36
⇒ presenter = reality, aciton = detail, slug = domy, id = 36

tzn. že presenter nechci mít uveden v url, ale získám ho pomocí slugu z db a slug si dám jako parametr (který ale také nemá být vidět v url)

honos
Člen | 109
+
0
-

BombajS napsal(a):

tzn. že presenter nechci mít uveden v url, ale získám ho pomocí slugu z db a slug si dám jako parametr (který ale také nemá být vidět v url)

Presne tak to mam taky. Z DB se vytahnou roury k modulum setridene podle vahy a zaregistrujise do routeru. Ale pokud to chapu dobre tak si vlastne chces zrychlit aplikaci tim ze omezis pocet rout?

To ti nevim čeče. Ale mohl bych tě snad odkazat na přednasku o routovani ale popravde mi byla k nicemu, ja jsem to vyresil jak jiz jsem psal vyse..

Jan Suchánek
Člen | 404
+
0
-

A musis tam mit <presenter>/<action>[/<id>]? Co misto presenteru dat slug?

BombajS
Člen | 9
+
0
-

honos napsal(a):

Ale pokud to chapu dobre tak si vlastne chces zrychlit aplikaci tim ze omezis pocet rout?

Jo, přesně, nechce se mi je tam všechny vypisovat, protože jich může být opravdu hodně.

jenicek napsal(a):

A musis tam mit <presenter>/<action>[/<id>]? Co misto presenteru dat slug?

To jsem zkusil, ale jak potom budu generovat url?

Jan Suchánek
Člen | 404
+
0
-

Jednoduše, vytahes podle id ci jinych parametru (kombinace presenter+id apod.) A zahodis presenter a nechas vygenerovat url, me to tak maka s tim rozdilem ze predavam manager pres filter metodu.
Jo a pouzivam cache taky.

BombajS
Člen | 9
+
+2
-

Tak jsem to celé přepsal a funguje:

/<slug>/<action>/<id>?query
slug = router z db podle slugu zjistí, jaký presenter zavolat. V url ale presenter nesmí být vidět!
action = zavolá akci daného presenteru
id = id…
ostatní parametry = query a také slug, který se předá do presenteru

Vytváření url:
Slug:Action, … – parametry

Router a ostatní soubory:

RouterFactory:

<?php

namespace App;

use Nette\Application\Routers\Route;
use Nette;
use Nette\Application\Routers\RouteList;
use Model;


/**
 * Router factory.
 */
class RouterFactory
{
	/** @var SlugManager */
	private $SlugManager;

	public function __construct(Model\SlugManager $SlugManager)
	{
		$this->SlugManager = $SlugManager;
	}

	/**
	 * @return \Nette\Application\IRouter
	 */
	public function createRouter()
	{
		$router = new RouteList();
		$router[] = new SlugRouter($this->SlugManager);
		$router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
		return $router;
	}

}

SlugRouter:

<?php
namespace App;

use Model;
use Nette\Application\Routers;
use Nette\Application as App;
use Nette;
use Nette\Http;


class SlugRouter extends Nette\Object implements Nette\Application\IRouter
{

	/** @var SlugManager */
	private $slugManager;

	public function __construct(Model\SlugManager $slugManager)
	{
		$this->slugManager = $slugManager;
	}

	/**
	 * Maps HTTP request to a Request object.
	 *
	 * @param \Nette\Http\IRequest $httpRequest
	 * @throws \Nette\Application\BadRequestException
	 * @return App\Request|NULL
	 */
	public function match(Http\IRequest $httpRequest)
	{
		// 1) PARSE URL
		$url = $httpRequest->getUrl();
		$path = trim($url->path, $url->scriptPath);
		if ($path !== '') {
			$parts = explode($url->scriptPath, $path, 4);
			//get row by slug
			$row = $this->slugManager->getRowBySlug($parts[0]);
		} else {
			$parts = array('Homepage', 'default');
			$row = $this->slugManager->getDefault();
		}

		if(!$row) {
			throw new Nette\Application\BadRequestException('Page does not exist');
		}


		// 2) PARAMS
		$params = array();

		//action
		if(isset($parts[1])) {
			$action = $parts[1];
		} else {
			$action = 'default';
		}

		//id
		if(isset($parts[2])) {
			$id = $parts[2];
		}

		//params
		$params['action'] = $action;
		$params['page_id'] = $row->id;
		if(isset($id)) {
			$params['id'] = $id;
		}

		//$url->query into params
		if($url->getQuery() !== '') {
			$query = explode('&', $url->getQuery());
			foreach($query as $singlequery) {
				$result = explode('=', $singlequery);
				$params[$result[0]] = $result[1];
			}
		}


		// 3) GET PRESENTER
		$presenter = $row->presenter;


		return new App\Request($presenter, $httpRequest->getMethod(), $params, $httpRequest->getPost(), $httpRequest->getFiles(), array(App\Request::SECURED => $httpRequest->isSecured()));

	}

	/**
	 * Constructs absolute URL from Request object.
	 *
	 * @return string|NULL
	 */
	public function constructUrl(App\Request $appRequest, Http\Url $refUrl)
	{
		$params = $appRequest->getParameters();

		$query = $params;
		unset($query['action'], $query['page_id'], $query['slug'], $query['id']);

		if(isset($params['slug'])) {
			$slug = strtolower($params['slug']);
		} else {
			if(isset($params['page_id'])) {
				$row = $this->slugManager->getSlugById($params['page_id']);
				if($row) {
					$slug = $row->slug;
				} else {
					return NULL;
				}
			} else {
				return NULL;
			}
		}
		$url = $refUrl->getScheme() . '://' . $refUrl->getHost() . $refUrl->getPath() . $slug;
		$params = $appRequest->getParameters();
		if(isset($params['action']) && $params['action'] !== 'default') {
			$url .= $refUrl->getPath() . $params['action'];
		}
		if(isset($params['id'])) {
			if($params['action'] == 'default' && isset($params['action'])) {
				$url .= $refUrl->getPath() . $params['action'];
			}
			$url .= $refUrl->getPath() . $params['id'];
		}

		if(count($query)>0){

			$queryString = '?';

			foreach($query as $key=>$parameter){
				$queryString .= $key . '=' . $parameter . '&';
			}

			$finalQueryString = substr($queryString,0,-1);

			$url .= $finalQueryString;
		}

		return $url;
	}
}

SlugManager:

<?php

namespace Model;

use Nette;


class SlugManager extends Nette\Object
{

	/** @var Nette\Database\Context */
	private $database;


	public function __construct(Nette\Database\Context $database)
	{
		$this->database = $database;
	}

	public function getRowBySlug($slug)
	{
		$row = $this->database->table("tree")->where(array("slug" => $slug))->fetch();
		if($row) {
			return $row;
		} else {
			return NULL;
		}
	}

	public function getDefault()
	{
		$row = $this->database->table("tree")->where(array("default" => 1))->fetch();
		if($row) {
			return $row;
		} else {
			return NULL;
		}
	}

	public function getSlugById($id)
	{
		$row = $this->database->table("tree")->get($id);
		if($row) {
			return $row;
		} else {
			return NULL;
		}
	}
}

Tak třeba se bude někomu hodit

Editoval BombajS (12. 1. 2014 22:06)