FILTER_IN, FILTER_OUT závislý na více parametrech

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

Zdravím,

potřebuji vyřešit problém s kategoriemi/podkategoriemi a jejich routami, požadavek na URL je například:

http://kategorie.example.com/podkategorie/

Aktuálně mám routování funkční jen s kategoriemi, a to způsobem:

<?php
$categories = array(1 => "auta", ....);

new Route("//<category_id>.example.com/", array("presenter" => "Category", "action" => "category",
"category_id" => array(
Route::FILTER_OUT => function($category_id) use ($categories) { return $categories[$category_id]; },
Route::FILTER_IN => function($seo) use ($categories) { return array_search($seo, $categories); }
)));
?>

Avšak podkategorie mohou být v daných kategoriích totožné, čili bych potřeboval zavolat onen callback FILTER_IN/FILTER_OUT i s parametrem části URL, kategorie nejen podkategorie,
něco jako:

<?php
$categories = array(1 => "auta", ....);
$subcategories = array("auta" => array(1 => "osobní", 2 => "tanky"), "motorky" => array(...));

new Route("//<category_id>.example.com/<subcategory_id>/", array("presenter" => "Category", "action" => "category",
"category_id" => array(
   Route::FILTER_OUT => function($category_id) use ($categories) { return $categories[$category_id]; },
   Route::FILTER_IN => function($seo) use ($categories) { return array_search($seo, $categories); }),
"subcategory_id" => array(
  // Ted ta důležitá část
  Route::FILTER_OUT => function($category_id, $subcategory_id) { return $subcategories[$category_id][$subcategory_id];  },
Route::FILTER_IN => function($category, $subcategory) { return array_search($subcategories[$category],$subcategory );  }
  // EOF
)
));
?>

BTW: kategorií/podkategorií bude opravdu mnoho

Děkuji za nějaké nasměrování

Editoval frosty22 (29. 7. 2011 13:47)

frosty22
Člen | 373
+
0
-

Děkuji, děkuji, už též narazil a omlouvám se za zbytečný dotaz, případně mám ještě jeden menší – je možné změnit presenter/action na základě parametru? Aktuálně mám routy:

<?php
$frontRouter[] = $filterRoute = new FilterRoute("//<category_id>.{$domain}/<subcategory_id>/", array("presenter" => "Filter", "action" => "default", "category_id" => array(
Route::FILTER_OUT => function($category_id) use ($categories) { return $categories[$category_id]; },
Route::FILTER_IN => function($seo) use ($categories) { return array_search($seo, $categories); }
)));

$filterRoute->addFilter("subcategory_id", function($seo, $request) use ($subcategories) {
  if (empty($request->params['category_id'])) return null;
  return array_search($seo, $subcategories[$request->params['category_id']]);
},
function($subcategory_id, $request) use ($subcategories) {
  if (empty($request->params['category_id'])) return null;
  return @$subcategories[$request->params['category_id'[$subcategory_id]; 				});

$frontRouter[] = new Route("//<category_id>.{$domain}/", array("presenter" => "Category", "action" => "category",
"category_id" => array(
Route::FILTER_OUT => function($category_id) use ($categories) { return $categories[$category_id]; },
Route::FILTER_IN => function($seo) use ($categories) { return array_search($seo, $categories); }
)));
?>

A jak je vidět, tak v případě, že existuje podkategorie chci zobrazit Filter:default, a pokud není a je pouze kategorie, tak Category:category … předpokládám, že toto asi vyřešit jednou routu nelze a musí být využity, takto dvě?

PS: onoho zavináče si nevšímat (pole je veliké a @ bude rychlejší než isset)

Filip Procházka
Moderator | 4668
+
0
-

Bolí mě z toho tvého kódu oči, opravdu :) (neříkám že je špatný, ale je strašně nepřehledný)

Takže ti k tomu řeknu toto: Objekt Nette\Application\Request $request, který se předává filtrům, se nikde neklonuje. Pokud ho ve filtru změníš, předá se změněný do Application a ta ho zpracuje.

$route->addFilter('id', function ($param, $request) {
	$request->setPresenterName('Homepage:default');
});

PS: napadá mě, že bych to mohl přepsat na Nette 2 :)

frosty22
Člen | 373
+
0
-

Super díky, jinak tedy ono zkopírované sem, to je dost nepřehledné, jinak v netbeans to docela je v pohodě :)

Jinak tedy, já už jsem si přepsal do Nette2, tedy zaměnil jen na:

Nette\Application\Request
Nette\Http\IRequest

frosty22
Člen | 373
+
0
-

Ještě by mě zajímalo, zda-li jde na základě parametru přeskočit routu, raději na příkladě:

Mám-li routu:

<?php
new FilterRoute("//<category_id>.{$domain}/<subcategory_id>[/<advert_type_id>][/<locality_id>]/", ...);
?>

následně přidám nějaký filtr:

<?php
$filterRoute->addFilter("subcategory_id", function($seo, $request) use ($subcategories) {
  if (empty($request->params['category_id'])) return null;
  return array_search($seo, $subcategories[$request->params['category_id']]);
},
function($subcategory_id, $request) use ($subcategories) { ... });
?>

avšak v případě, že subcategory_id nebude nalezeno, tak tato routa už je vybraná a nepřeskočí se ..
jedno řešení, které funguje je pomocí masky:

<?php
new FilterRoute("//<category_id>.{$domain}/<subcategory_id kone|psi|kocky>[/<advert_type_id>][/<locality_id>]/", ...);
?>

avšak jelikož je těchto závislých položek na stovky, tak to není jistě ideální zde vypisovat, zároveň i mám několik dalších rout, podobně složitých, kde potřebuji vyřešit tuto problematiku.

Děkuji za nějaký nápad.

Filip Procházka
Moderator | 4668
+
0
-

To je divné, měl jsem za to, že přeskakování rout funguje. Zkus to pořádně prostudovat, po zběžné kontrole si troufám tvrdit, že můj router funguje dle očekávání.

frosty22
Člen | 373
+
0
-

Tady jde předpokládám o obecnou vlastnost, nikoliv o chybu, při matchování rout, záleží pouze na masce, nikoliv na tom, co vrátí callback filteru, či pokud vrátí null tak se přeskočí? Samozřejmě to zkusím, jen to mám na větší úpravy (vyřešil jsem to nakonec jinak).

Pokud mám následující routu:

<?php
$a = new FilterRoute("<subcategory>", array("presenter" => "Foo", "action" => "Bar"));
$a->addFilter("subcategory", function($a){ return $a == "auta" ? 1 : null });
?>

Tak URL:
http://www.example.com/auta/
→ odpovídá masce routě, a parametr bude subcategory = 1

,ale například url:
http://www.example.com/tanky/
→ odpovídá masce routě, a parametr bude subcategory = false,
A NEBO se routa přeskočí?


Pokud jsem nedělal chybu, tak se nepřeskočila právě ,že ale pouze byla hodnota parametru subcategory = false, fungovalo to snad jen, když jsem upravil onu masku:

<?php
$a = new FilterRoute("<subcategory auta>", ...);
?>

Zkusím to tedy ještě takto na jednoduchém příkladě, to jsem netestoval, předtím jsem měl rozsáhlejší routování, takže pokud jsem měl jinde chybu, tak je možné, že se to takto chovalo, ale myslím, že nikoliv. Ale jak říkám, asi to není chyba, nýbrž vlastnost.

Filip Procházka
Moderator | 4668
+
0
-

Pokud filtr vrátí NULL, routa se musí přeskočit.

frosty22
Člen | 373
+
0
-

Otestováno a pravda, přeskočilo to korektně, čili problém byl z mé strany.

frosty22
Člen | 373
+
0
-

Tak již jsem zjistil, kde jsem měl původně problém, mám-li routu s více volitelnými parametry, např:

[/<locality_id>][/<advert_type_id>][/subtype_id]

tak vynechám-li některý z posledních elementů, např subtype:

www.example.com/praha/poptavka

potom je to v Ok a funguje, ale vynechám-li např. lokalitu, např:

www.example.com/poptavka

pak se onomu FILTER_IN předá u locality_id řetězec poptavka, který samozřejmě vrátí null, ale již ho nezkusí dál do dalších parametrů praha, což sice akceptuje onu routu, ale ignoruje mi tento parametr

Lze nějakým způsobem docílit tohoto chování? Aby v případě volitelného parametru zkusil zda neodpovídá další?

Děkuji moc za pomoc

Filip Procházka
Moderator | 4668
+
0
-

Jedině patterny, nebo výčet přímo v routě. Pomocí filtrů to nejspíše nepůjde.