Routy závislé na databázi

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

Protože jsem dosud nenašel nikde k dispozici žádný použitelný kus kódu na routování s databází, napsal jsem si vlastní, inspiroval jsem se přitom trochu od Davida – https://forum.nette.org/…ovnove-routy?…

Používám prefixed verzi Nette, pokud někdo používá klasickou, musí si umazat Nka. ;-)

Snad to někomu přijde vhod:

<?php
/**
 * Shop route
 *
 * Example of use:
 * <code>
 * $router[] = new ShopRoute('Categories', 'list',
 *      '#([a-z0-9-]+)#i',
 *      array(
 *              'table' => 'categories',
 *              'column' => 'url_key'
 *      ));
 * </code>
 *
 * @author	Radek Simko <radek.simko@gmail.com>
 */
class ShopRoute extends NObject implements IRouter
{
	private $presenterName;
	private $actionName;
	private $regex;
	private $sqlQuery;
	private $paramName;

	public function __construct (
		$presenterName, $actionName,
		$regex, $sqlQuery, $paramName = 'id')
	{
		$this->presenterName = $presenterName;
		$this->actionName = $actionName;
		$this->regex = $regex;
		$this->sqlQuery = $sqlQuery;
		$this->paramName = $paramName;
	}

	/**
     * Maps HTTP request to a NPresenterRequest object.
     * @param  Nette\Web\IHttpRequest
     * @return PresenterRequest|NULL
     */
    public function match(IHttpRequest $context)
    {
	$uri = preg_replace (
		'#^'.$context->getUri()->scriptPath.'(.*)$#i',
		'\\1', $context->getUri()->path);
	preg_match($this->regex, $uri, $matches);

	if (!isset ($matches[1])) {
		return NULL;
	}

	if (dibi::fetchSingle(
		'SELECT COUNT(*) FROM ['.$this->sqlQuery['table'].
		'] WHERE '.$this->sqlQuery['column'].'=%s',
		$matches[1]) === '0') {
		return NULL;
	}

        $params = $context->getQuery();
        $params[$this->paramName] = $matches[1];
		$params['action'] = $this->actionName;

        return new NPresenterRequest(
                    $this->presenterName,
                    $context->getMethod(),
                    $params,
                    $context->getPost(),
                    $context->getFiles(),
                    array('secured' => $context->isSecured())
            );
    }



    /**
     * Constructs URL path from NPresenterRequest object.
     * @param  Nette\Web\IHttpRequest
     * @param  PresenterRequest
     * @return string|NULL
     */
    public function constructUrl(NPresenterRequest $request, IHttpRequest $context)
    {
	$actualPresenter = $request->getPresenterName();
	$actualParams = $request->getParams();

	if ($actualPresenter != $this->presenterName &&
		$actualParams['action'] != $this->actionName) {
    		return NULL;
        }

        if (!isset($actualParams[$this->paramName])) {
	        return NULL;
        }

        $uri = $context->getUri()->basePath.
			rawurlencode($actualParams[$this->paramName]);
        unset($actualParams[$this->paramName], $actualParams['action']);

        $query = http_build_query($actualParams, '', '&');
        if ($query !== '') $uri .= '?' . $query;

		return $uri;
    }
}

Mám v plánu napsat ještě podobnou záležitost, resp. Davidovo 2.alternativu, kdy všechny URL keys budou uložené v databázi v tabulce a u nich budou patřičné presentery a actions. Faktem je, že by to bylo znatelně rychlejší.

Editoval maarlin (9. 1. 2010 20:48)

Panda
Člen | 569
+
0
-

Ta rychlost je dost relativní. Kdyby jsi dělal v každém match a constructUrl dotaz na databázi, tak tím veškeré pokusy o rychlost spolehlivě zabiješ. A pokud by jsi to zase načetl při prvním požadavku celé do PHP, tak tím tu rychlost u většího počtu položek zabiješ taky.

Jinak v podstatě totéž jde udělat jednoduše například takto:

<?php
class DatabaseRoute extends Route
{
	// Parametry si nadefinuješ někde v konstruktoru,
	// případně si třídu podědíš a přepíšeš
	protected $column = 'uri';
	protected $table = 'table';

	public function match(IHttpRequest $httpRequest)
	{
		$request = parent::match($httpRequest);

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

		$params = $request->getParams();

		if (dibi::fetchSingle(
				'SELECT COUNT(*) FROM %n', $this->table,
				'WHERE %n = %s', $this->column, $params[$this->column]
			) == 0)
			return NULL;

		return $request;
	}
}
?>

A routu si pak nadefinuješ například takto:

$router[] = new DatabaseRoute('<uri>/<action>', array(
	'presenter' => 'Presenter',
	'action' => 'action'
));

Značnou výhodou je fakt, že můžeš využívat veškeré schopnosti routování z Nette.

Petr Mašát
Člen | 101
+
0
-

Ahoj,
chci se jenom zeptat Maarlina zda nějak dořešil to že bude mít všechny názvy presenterů s actions v databázi a u toho patřičné url?
Právě řeším něco podobného a toto by se mi docela i hodilo.
Díky moc,
pm*

Honza Kuchař
Člen | 1662
+
0
-

@Panda: Nechceš to dát do extras?