[ZF] Zend_Navigation i.c.m. Zend Router

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • StephanVierkant
  • Registratie: Mei 2003
  • Laatst online: 08-09 16:22
Ik ben bezig met het opzetten van een website met het Zend Framework. Ik wil een aantal dynamische pagina's maken (inloggen, bestellen, etc.) en een aantal 'statische' pagina's dat in een database zit. In dat laatste geval gaat het dan om de gebruikelijke pagina's zoals 'contact', 'routebeschrijving', 'disclaimer', etc.

Om een goede menustructuur, breadcrumb e.d. te krijgen, maak ik gebruik van Zend_Navigation. Dat is dankzij een tutorial al voor een groot deel gelukt. Door middel van een navigation.xml-bestand wordt de navigatie geladen.

Ik loop nu echter vast op het gedeelte waarbij ik de url's mooier wil maken. Momenteel is de contactpagina te bereiken via /index/contact, maar dat '/index' wil ik er uiteraard graag tussenuit hebben. Ook wil ik een url kunnen hebben als /contact/route. Bovendien wil ik één functie hebben die de pagina's uit de database haalt, en niet alles in een .phtml-bestand.

Ik heb al veel tutorials en de Zend-documentatie doorgelezen en vermoed dat ik met Routers moet gaan werken. het is me echter nog niet gelukt dit aan de praat te krijgen. Ook met de andere tutorial is het nog niet gelukt.

Via deze link kom ik iets verder, maar in dat geval wordt alles overruled. De pagina 'bestellen' moet echter niet middels die controller gebeuren, maar door de BestellenController.

Hoe kan ik dit het beste aanpakken? Is het opzetten van zo'n systeem inderdaad erg eenvoudig met Zend, of mis ik een addertje onder het gras?

[ Voor 8% gewijzigd door StephanVierkant op 20-02-2012 14:44 ]


Acties:
  • 0 Henk 'm!

  • StephanVierkant
  • Registratie: Mei 2003
  • Laatst online: 08-09 16:22
De laatste oplossing komt tot nu toe het dichtste bij. Door middel van een uitzondering op de regex, kan ik de bestelpagina uitzonderen:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
$route = new Zend_Controller_Router_Route_Regex(
    '(?(?=^index$|^bestellen$)|([a-z0-9-_.]+))',
    array(
        'controller' => 'page',
        'action' => 'view',
        'slug' => null
    ),
    array(
        1 => 'slug'
    ),
    '%s'
);

Wat ik nu nog wil is subpagina's kunnen maken. Bijvoorbeeld /contact/routebeschrijving/locatieA/openbaarvervoer.

Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 11:48
Ik heb zelf ook dit probleem gehad. Ik los dit anders op.

Mijn routing ziet er als volgt uit in de application.ini:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;;;;;;;;;;;;;;;;;;;;;;;;
; ROUTES
;;;;;;;;;;;;;;;;;;;;;;;;

resources.router.routes.view.type                               = Zend_Controller_Router_Route
resources.router.routes.view.route                              = "*"
resources.router.routes.view.defaults.controller                = "index"
resources.router.routes.view.defaults.action                    = "view"

resources.router.routes.index.type                              = Zend_Controller_Router_Route
resources.router.routes.index.route                             = "zoeken/*"
resources.router.routes.index.defaults.controller               = "index"
resources.router.routes.index.defaults.action                   = "search"

resources.router.routes.admin.type                              = Zend_Controller_Router_Route
resources.router.routes.admin.route                             = "admin/:action/*"
resources.router.routes.admin.defaults.controller               = "admin"
resources.router.routes.admin.defaults.action                   = "index"


Alles wat buiten de 'statische' content valt, geef ik een route. De 'statische' content wordt via een alias ingeladen in de IndexController::viewAction(). In de database sla ik bij elke statische pagina een alias op. Door middel van een query haal ik de juiste pagina op. Op deze manier kun je in de database de aliases beheren en eventueel hier een unique constraint op zetten.

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • StephanVierkant
  • Registratie: Mei 2003
  • Laatst online: 08-09 16:22
storeman schreef op maandag 20 februari 2012 @ 23:15:
Alles wat buiten de 'statische' content valt, geef ik een route. De 'statische' content wordt via een alias ingeladen in de IndexController::viewAction(). In de database sla ik bij elke statische pagina een alias op. Door middel van een query haal ik de juiste pagina op. Op deze manier kun je in de database de aliases beheren en eventueel hier een unique constraint op zetten.
Dank! Dit werkt inderdaad. Hoe vang je waarden in je url (bijv. '/over-ons/contact/routebeschrijving/') nu af?

Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 11:48
Ik draai een query op de database:

code:
1
2
3
4
5
6
$alias = $this->_request->getRequestUri();
        
        $select = $mdlPages->select()
                            ->setIntegrityCheck( false )
                            ->from ($mdlPages)
                            ->where('alias = ?', $alias);


Indien niet gevonden natuurlijk een keurige 404 tonen.

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
TS: Je moet je bedenken dat je twee soorten routes hebt: een default en verder alle eigen aangemaakte routes. Alle eigen aangemaakte routes kan je op verschillende manieren injecteren in de router. De default route is nu ook degene die je gebruikt: index/contact verwijst nu dus naar IndexController::contactAction().

Wat je wilt is een set van eigen routes gebruiken die je injecteert in de router. De router baseert op basis van de request welke route de beste match is en selecteert die route. Een dispatcher pakt vervolgens de juiste controller, selecteert juiste actie en voert die uit.

Je kan nog steeds via /bestellen bij de BestellenController uitkomen als je de default route niet verwijdert. Als je extra routes toevoegt, blijft die functionaliteit bestaan. Wat je kan doen zijn alle tekstpagina's verwijzen naar dezelfde controller, zelfde actie. Bijvoorbeeld de ContentController::indexAction(): alle pagina's die met teksten uit de database komen, worden gevormd door deze controller/actie. In die actie kijk je naar een parameter, die je vervolgens gebruikt om je content op te halen.

De makkelijkste manier is het specificeren van die parameter in de route zelf. Een voorbeeld voor de application.ini:
resources.router.routes.contact.route                              = "/contact"
resources.router.routes.contact.defaults.controller                = "content"
resources.router.routes.contact.defaults.action                    = "index"
resources.router.routes.contact.defaults.id                        = "contact"
Je hebt hier naast een controller (ContentController) en actie (IndexAction) voor de route "/contact" ook een extra parameter "id". Daar kan je bijvoorbeeld je database op querien om de content op te halen.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function indexAction ()
{
    $id   = $this->getRequest()->getParam('id');
    $page = $this->getPage($id);

    if (false === $page) {
      throw new Zend_Controller_Action_Exception('Cannot find page for id ' . $id);
    }

    $this->view->page = $page;
}

protected function getPage ($id)
{
    // haal pagina op
}

Mocht uiteindelijk het aantal pagina's te groot worden, dan wil je dit wellicht niet meer in de application.ini vermelden. Je kan hiervoor een frontcontroller plugin schrijven die de routes injecteert, bijvoorbeeld op basis van de database.

Dat is ook hoe ik het altijd doe: in de database staan alle pagina's, die ik allemaal ophaal. Deze "parse" ik tot een lijst met routes en een navigatiestructuur. De routes gaan naar de router, de navigatiestructuur in Zend_Navigate. De database is uiteraard te beheren om vervolgens pagina's aan te maken, te verwijderen en te verslepen.

Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 11:48
@Mithras: Je router opbouwen met zeer veel pagina's is volgens mij niet zo'n goed idee. Ik heb de ervaring dat dit toch wel je performance kan beinvloeden. Wellicht dat je door requirements aan de routes dit kan versnellen. De router parsed alle regels en voert deze in omgekeerde volgorde uit en blijft hangen bij de eerste match en voert deze uit.

Daarnaast wil de TS niet matchen op id, maar op een "statisch adres". Als het slechts met ID's is, dan heb je aan 1 route-regel genoeg, met aliases heb je hier helaas niets aan.

Wat betreft je Zend_Navigation heb je gelijk, deze kun je af en toe vernieuwen en opnieuw opbouwen. Denk er wel aan om deze te cachen bij wat grotere sites, anders krijg je performance problemen. Als je het over een site hebt met een 30-tal pagina's zal het allemaal nog niet zo'n probleem zijn.

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
storeman schreef op dinsdag 21 februari 2012 @ 19:57:
@Mithras: Je router opbouwen met zeer veel pagina's is volgens mij niet zo'n goed idee. Ik heb de ervaring dat dit toch wel je performance kan beinvloeden. Wellicht dat je door requirements aan de routes dit kan versnellen. De router parsed alle regels en voert deze in omgekeerde volgorde uit en blijft hangen bij de eerste match en voert deze uit.
Definieer als eerste "zeer veel". Uit de TS maak ik niet op dat het om meer dan 10 pagina's gaat (anders was de use case wel anders geweest denk ik). Bij een tiental pagina's heeft het aantal routes écht geen invloed op de performance als je niet naar micro-optimalisaties gaat kijken. Dan kan je beter Zend_Application eruit slopen, de ViewRenderer zelf construeren, Zend_Form niet gebruiken voor de view kant en zoveel mogelijk i18n features als Zend_Date en Zend_Currency vermijden.

Dat gezegd hebbende, zie ik alle statische pagina's met een wildcard afvangen ook nog eens als niet toekomst-bestendig en meer als een anti-pattern. Routing is om te bepalen of je request een geldig pad heeft om te gaan dispatchen, nu accepteer jij alles wat geen "zoeken" of "admin" is en kijkt in je controller of het vervolgens allemaal wel klopt.
Daarnaast wil de TS niet matchen op id, maar op een "statisch adres". Als het slechts met ID's is, dan heb je aan 1 route-regel genoeg, met aliases heb je hier helaas niets aan.
Je matcht ook helemaal niet op een id, maar op een literal. Elke statische pagina is is een literal, komt dat even goed uit ;)

Feit blijft dat je de route kan uitbreiden met extra parameters, die je zo uit je db kan trekken. Id's van pagina's (hier toevallig een name gekozen in plaats van een numeric, maar je kan alles ermee doen natuurlijk), maar als je gelijk Zend_Acl koppelt en de groep/categorie van de pagina's meeneemt, kan je met een frontcontroller plugin direct toegang bepalen op basis van de route match, om maar eens een voorbeeld te noemen.
Wat betreft je Zend_Navigation heb je gelijk, deze kun je af en toe vernieuwen en opnieuw opbouwen. Denk er wel aan om deze te cachen bij wat grotere sites, anders krijg je performance problemen. Als je het over een site hebt met een 30-tal pagina's zal het allemaal nog niet zo'n probleem zijn.
Cache lijkt me evident, maar dat was hier ook niet het onderwerp van discussie :)

Acties:
  • 0 Henk 'm!

  • StephanVierkant
  • Registratie: Mei 2003
  • Laatst online: 08-09 16:22
Ik moet eerlijk bekennen dat ik jullie nog maar gedeeltelijk kan volgen, gezien mijn Zend-ervaring.

Het gaat mij erom dat het allemaal zo flexibel mogelijk is. Wellicht dat /over-ons/contact/ het beste via de cmsController* (dus uit de db) kan, maar /over-ons/routebeschrijving/vestigingA met de routebeschrijvingController die de gegevens over 'vestigingA' weer uit de db haalt.

*allemaal fictieve url's/controllers, ik probeer de namen wat algemeen te houden.

Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 11:48
Alles zo flexibel mogelijk houden is een utopie ;). Je moet op de een of andere manier toch een stramien aanhouden om mee aan de slag te gaan. Hiermee leg je jezelf beperkingen op, je moet echter zorgen dat die beperkingen je niet snel in de weg gaan zitten.

Je kunt met de genoemde opties nog steeds een heel aantal wegen inslaan. Hierbij is er simpelweg niet een beste optie te kiezen. Elke mogelijkheid heeft voors en tegens, vrijheden en beperkingen. Je dit voor jezelf op een rijtje zetten en een onderbouwde keuze maken.

Wat volg je niet precies van de opmerkingen van Mithras en mij?

"Chaos kan niet uit de hand lopen"

Pagina: 1