[ALG] Hoe omgaan met standaardblokken in frameworks?

Pagina: 1
Acties:

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik ben nu al een tijdje aan het experimenteren met een FrontController en een PageController, gecombineerd met wat simpel MVC werk.

Nu vraag ik me echter het volgende af: stel ik wil alle weergaven voorzien van een standaardlayout, hoe pak ik dit dan aan?
Laten we even uitgaan van de volgende omgeving:
- PHP
- Smarty (of een andere template engine)

Request: /ListCars
mapt naar actions.ListCarsAction, die een lijst met auto's opvraagt uit de DAL.
ListCarsAction verwijst door naar views.ListCarsView:

code:
1
2
3
4
5
6
<html>
<!-- blabla /-->
<div>
{$cars_list}
</div>
</html>


Wat zijn nu nette manieren om iedere View in een standaardlayout weer te geven, ik zit immers met twee soorten templates:
- algemene look & feel
- opmaak voor een bepaalde view
M.a.w.: waar integreer ik de 'overkoepelende' template?

Dit haakt in op een andere vraag: stel dat ik ten alle tijde ergens op de pagina (op iedere pagina in de applicatie) bijvoorbeeld een lijstje met ingelogde users wil laten zien (om maar iets te noemen), hoe handel ik dat af? Het heeft immers niet direct met de huidige View te maken, maar moet wel altijd getoond worden. Op welke manier roep ik dan de bijbehorende Action aan, die deze lijst met ingelogde users opvraagt?
Ik kan me voorstellen dat dit in (bijvoorbeeld) struts op te lossen zou zijn door mbv een custom taglibrary een tag te definieren waarmee ik het lijstje met ingelogde users kan laten zien, maar hoe zou ik zoiets in PHP netjes oplossen, verpakt in een mooi pattern bijvoorbeeld?

Misschien dat ik een beetje de CMS kan opmoet, dat is wat me in deze context het logischste lijkt: een (1) standaardtemplate, met daarin zones die met content gevuld worden.
Na het bekijken van een leuk aantal frameworks heb ik nog niet ontdekt wat voor bovenstaande vragen nu een nette manier is. Met dit topic wil ik graag wat discussie op gang brengen over dit onderwerp.
Ik weet dat over vergelijkbare vragen al een aantal interessante topics voorbij is gekomen, maar miste daarin concrete voorbeelden. Net als het ontbreken van complexere voorbeelden van de meeste frameworks, althans: misschien kijk ik op de verkeerde plaatsen.

  • dingstje
  • Registratie: Augustus 2002
  • Laatst online: 02-01-2024
Wat ik persoonlijk doe is een Controller die zelf voor een action de juiste template bepaalt (het komt erop neer dat één action één template heeft met dezelfde naam). Die template (in Smarty) doet dan zelf een {include file="header.tpl"} en eventueel andere files die ie wilt hebben. De Controller roept dan de Action aan die gewoon ruwe data teruggeeft aan mijn Controller (in een Array). De controller inject deze data dan in de template. De Controller heeft ook dingen die altijd nodig zijn (User object bvb.) door aan de template.

If you can't beat them, try harder


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 07-04 13:41
Wellicht dat je PRADO er voor kan gebruiken, die kent een zelfde idee als de Master pages uit ASP.Net 2.0. (PRADO is op veel meer gebieden op ASP.Net gebaseerd ;)) nadeel is misschien wel dat 't alleen onder PHP5 draait.

  • chris
  • Registratie: September 2001
  • Laatst online: 11-03-2022
Ik zou zeggen, kijk eens naar de manier waarop Ruby On Rails het oplost. Een erg nette manier van een MVC Framework. En voor een php-aftreksel kan je eens kijken naar Cake.

Verwijderd

PrisonerOfPain schreef op zondag 19 juni 2005 @ 10:26:
Wellicht dat je PRADO er voor kan gebruiken, die kent een zelfde idee als de Master pages uit ASP.Net 2.0. (PRADO is op veel meer gebieden op ASP.Net gebaseerd ;)) nadeel is misschien wel dat 't alleen onder PHP5 draait.
En kijk dan ook even naar SiteMesh waar het idee origineel vandaan komt (imho). En sitemesh kan dan ook gelijk wat 'decoreren'...

Maar ik ken niets dat zo flexibel als tiles is, waar je echt componenten ineenpuzzelt in feite...

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 04-05 14:55

Janoz

Moderator Devschuur®

!litemod

Verwijderd schreef op maandag 20 juni 2005 @ 09:35:
Maar ik ken niets dat zo flexibel als tiles is, waar je echt componenten ineenpuzzelt in feite...
Het probleem dat tiles echter niet oplost is eventueel toegevoegde acties. De overerving is heel mooi en hierdoor zou je in een template wel een who's online in kunnen bouwen, maar dan heb je alleen het weergeef deel opgelost. Die gegevens moeten nog wel ergens in je request terecht komen. Ikzelf heb dit een tijdje terug opgelost door een configuratie bestand te maken waarmee ik meerdere acties aan een actie kon koppelen. Hierdoor kon een enkele actie meerdere acties triggeren. De resultaten van deze acties werden vervolgens gecombineerd verstuurd naar het view gedeelte. Dit was echter een from scratch geschreven oplossing waarbij geen bestaand framework werd gebruikt en dit is dan ook niet zomaar toepasbaar in reeds bestaande frameworks.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik zal zometeen eens gaan kijken naar PRADO (stond al in mijn bookmarks, ben het dus al eens tegengekomen), Cake, Ruby on Rails, en SiteMesh.

Janoz: Ik wil eigenlijk niet dat ik per actie moet definiëren dat er ook een 'who's online' actie uitgevoerd moet worden, die content voor de view genereerd. Ik wil juist dat ik in een generieke template de genoemde tiles kan gebruiken, waarvoor automagisch ook actions uitgevoerd worden, die in principe 'losstaan' van de actie die de gebruiker triggered.

PRADO doet weer een belletje rinkelen, zal er nogmaals naar kijken. Ik heb zelf al een basic framework liggen, waar ik dus feitelijk nog tiles support in wil hebben. Tiles was de naam die ik zocht, nu kan ik ten minste wat gerichter zoeken, merci!
Verder zal ik de genoemde frameworks vooral bekijken om ideeen op te doen, ik vind de meeste frameworks onder PHP nl. wat overkill, en tevens 'zwaar' aangezien het gehele framework geladen wordt voor iedere request.

Ik had zelf ook al een lijst(je) gemaakt met frameworks voor PHP (voor het project dat deze conrete vragen opriep het ontwikkelplatform):
- Studs
- Mojavi (nu even offline zie ik)
- phpWACT (biedt (deels) mogelijkheden die ik zoek, in de vorm van custom tags in een template, die gekoppeld kunnen worden aan de BL laag)
- Fusebox
- Midgard

  • B-Man
  • Registratie: Februari 2000
  • Niet online
Na het bekijken van de door jullie genoemde projecten, zit ik aan het volgende te denken: een Tile klasse, die door daadwerkelijke tiles ge-extend wordt.

In een template de mogelijkheid bieden om een tile te integreren.

De flow wordt als volgt (om en nabij):
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Request /Client/List
 V
FrontController (mapt op basis van eerste param naar een PageController)
 V
ClientController extends PageController (met als input /List)
 - Selecteer op basis van config de default Layout (overkoepelende template)
 - Selecteer op basis van de input een Action
 V
ClientListAction
 - actie geeft aan welke view getoond moet worden
 - kan eventueel de te gebruiken Layout nog veranderen
 V
ClientListView
 - wordt in een variabele geladen
 V
Render layout
 - Voer (indien nodig) Tiles uit die in de layout staan (kan bijvoorbeeld door in eerste instantie gewoon een functie te gebruiken: tile(UsersOnlineTile))
 - Integreer de output van de View (hier dus ClientListView) in de layout, op een standaardplek (bijv. %content%, of gewoon PHP vars)
 - Integreer eventueel meta informatie van de View


Een Tile is feitelijk een Action, die niet vanuit een Request aangeroepen wordt, maar vanuit een Layout of View. De Tile kan zelf weer een view aanroepen. Als ik ervoor kies om gewoon met PHP vars te werken, kan een tile dmv een functie geintegreerd worden, zoals ik hierboven voorstel. Een tile kan dan ook in een andere tile geintegreerd worden (bijv: in de UsersOnlineTileView de UsersOnlineCountTile aanroepen).

Een vaste functie binnen een Tile wordt aangeroepen, met als scope de huidige request, van waaruit de Tile toegang krijgt tot alle Request-scope variabelen, en alle controller variabelen (User objecten, enz).

Om mijn idee concreet te maken, wat bestanden ter illustratie:
DefaultLayout.php:
PHP:
1
2
3
4
5
6
7
8
9
10
<html>
<body>
Hallo! Dit is de standaardlayout.
<div id="whosonline">
<?php tile( 'UsersOnlineTile' ); ?>
</div>

<?php echo $content; ?>
</body>
</html>


Een request naar /Client/List zorgt ervoor dat $content gevuld wordt, wat dus losstaat van de aangeroepen Tile in de layout.

UsersOnlineTile.php
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import( 'mvc.Tile' );

class UsersOnlineTile extends Tile
{
    /**
     * Execute the action
     *
     * @param mapping   Action mapping, allows us to e.g. redirect
     * @param request   Request object, contains all request variables
     * @param context   Context for passing info between Actions, Views
     */
    function execute( &$mapping, &$request, &$context )
    {
        // .. do stuff .. (query database)
        $context->set( 'users', array( 'B-man' ) );

        // Ik definieer alle mogelijke forwards in een extern XML bestand (gecached als een serialized php array uiteraard)
        $mapping->findForward( 'list' );
    }
}


UsersOnlineTileView.php
PHP:
1
2
3
4
Users die op dit moment online zijn:
<p>
<?php implode( ', ', $users ); ?>
</p>


Graag jullie reactie/ideeen. Hiermee heb ik in ieder geval exact de flexibiliteit die ik voor deze applicatie wil hebben, maar mogelijk zijn er nog nettere/betere manieren.

  • djluc
  • Registratie: Oktober 2002
  • Laatst online: 06-05 13:54
Je hebt het zelf al eens aangehaald:
Misschien dat ik een beetje de CMS kant opmoet,
Als je naar een CMS als Typo3 kijkt zie je mogelijkheden om een pagina onder te verdelen in meerdere gebieden. Je maakt dus een gebied aan voor de header, een gebied voor een banner, een gebied voor de echte content, eentje voor het menu en eentje voor je who is online stukje. In de database staat aangegeven welke items in welke gebieden moeten staan. Dit kan je op 2 manieren doen:
1. op de pagina zelf een stukje content zoals een scriptje plaatsen in het juiste vak
2. middels een template-configuratie bestandje aangeven wat er in het veld moet.

Daardoor worden de scripts aangeroepen.

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 04-05 14:55

Janoz

Moderator Devschuur®

!litemod

Dat (wat djluc verteld) is in principe ook de manier die Nukes gebruikt.

Wat ik een beetje vind schorten aan het ontwerp (van B_Man) is dat je nu in je vormgeving bepaald welke buisness acties er uitgevoerd gaan worden. Sowieso heb je twee mogelijke manieren waarop een actie uitgevoerd zou kunnen worden. Waarom zou je bijvoorbeeld de content anders gaan behandelen dan een tile? Tot slot blijf je het probleem houden dat ook in mijn implementatie zat, alleen heb je het nu niet per actie, maar per pagina.

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • B-Man
  • Registratie: Februari 2000
  • Niet online
Ik snap precies wat jullie bedoelen, maar worstel nog steeds wat met het hele gebeuren, omdat ik inderdaad de CMS kant opga op dit soort manieren, maar ik geen CMS-achtige functies (a la Typo3 of xNuke) nodig heb.

Ik zit simpelweg met een hoofdsjabloon dat gevuld wordt met basiscontent, en tevens een variabel gebied, dat gevuld wordt afhankelijk van een URL parameter.
Het verschil tussen:
• in de BL definieren dat in hoofdsjabloon X 'whosonline' nodig is, en dit weergeven in het sjabloon, en
• in datzelfde sjabloon middels een functie de benodigde BL laten uitvoeren voor 'whosonline', wat resulteert in een weergave

Ik heb vandaag uitgebreid zitten stoeien met het door mijzelf voorgestelde principe, en moet zeggen: dat bevalt prima. Na het lezen van een groot stuk documentatie rondom RubyOnRails, en ermee gespeeld te hebben, werd duidelijk dat patterns zeker interessant zijn, maar dat het er met webapps vooral omgaat dat ik als ontwikkelaar er gemakkelijk mee wil kunnen werken.

Tevens is het zo dat de sjablonen waar ik hier over spreek zeer statisch van aard zijn.

In mijn huidige systeem, waarin de Controllers (zowel FrontController als PageControllers) op basis van een XML bestand geconfigureerd worden, kan ik ook reeds meerdere acties aan een 'entry point' hangen (deel van het XML bestand dat de PageController voor het 'Client' object regelt):
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<config>

    <routes>
        <route name="default" path="/:action/*">
            <default name="action" value="List"/>
        </route>
    </routes>

    <actions>
        <action 
            name="List"
            type="actions.ClientListAction"
        >
            <forward name="list" view="views.ClientListView"/>
        </action>

        <action
            name="Display"
            type="actions.ClientRetrieveAction"
            params="/:id/*"
        >
            <forward name="display" view="views.ClientDetailView"/>
        </action>

        <action
            name="Edit"
            type="actions.ClientRetrieveAction"
            params="/:id"
        >
            <forward name="edit" view="views.ClientEditView"/>
        </action>

        <action
            name="EditSubmit"
            type="actions.ClientEditSubmitAction"
        >
            <forward name="error" view="views.ClientEditView"/>
            <forward name="list" action="List" redirect="true"/>
        </action>
    </actions>
</config>

Ik kan m.b.v. een <execute action="xyz"/> binnen een <action/> tag reeds meerdere acties daisy-chainen, die ieder hun data aan de context van de request kunnen toevoegen. Vanuit de view zijn alle zaken in de request context vervolgens beschikbaar.

Nu zou ik het zo kunnen uitbreiden dat ik via de XML config ga aangeven welke acties de 'whosonline' actie moeten uitvoeren, zodat ik de benodigde data in de view heb, maar dat voelt niet erg intuitief aan.
Dan neig ik meer naar mijn manier, waar ik me overigens wel bij voorstel dat een tile nooit een error zal gooien, maar in geval van (bijvoorbeeld) onvoldoende rechten dat netjes weergeeft, ipv de verwachtte content.

Het wordt wel een smakelijke mix van Struts, RubyOnRails, Cake en eigen ideeen zo :9

De manier waarop RoR met variabelen via PATH_INFO omgaat spreekt me overigens erg aan: het mappen van posities naar variabelen middels een syntax als '/:var_naam_x/:var_naam:y/*' Ik heb (basis) validatie van input meteen meegenomen, wat in RoR weer via een aparte parameter gaat.
Middels mijn aanpassing kan het via '/:var_naam_x{regex_expressie}/*'

  • djluc
  • Registratie: Oktober 2002
  • Laatst online: 06-05 13:54
Ik zit simpelweg met een hoofdsjabloon dat gevuld wordt met basiscontent, en tevens een variabel gebied, dat gevuld wordt afhankelijk van een URL parameter.
Dat kan precies met een dergelijke structuur welke Typo3 e.d. gebruiken.
Het verschil tussen:
# in de BL definieren dat in hoofdsjabloon X 'whosonline' nodig is, en dit weergeven in het sjabloon, en
# in datzelfde sjabloon middels een functie de benodigde BL laten uitvoeren voor 'whosonline', wat resulteert in een weergave
Wat je misschien een beetje verward in mijn stukje is dat de template configuratie file niet specifiek van de sjabloon is. Het configuratiebestand zorgt er voor dat de juiste zaken in de business layer uitgevoerd worden. Daar staan regels in als:
Plaats module whoisonline in vak rechts en laat daar de eerste 10 bezoekers zien.

Volgens mij bedoel je iets dergelijks toch?

  • B-Man
  • Registratie: Februari 2000
  • Niet online
djluc: Ik wil dat type constructie juist vermijden, omdat dat me teveel de CMS kant opgaat. De sjablonen mogen zelf aangeven waar ze wat willen laten zien. M.a.w.: ik wil geen compleet generieke sjablonen.

Ik bedacht me na jullie feedback nog een andere oplossing. Ik wil de sjablonen beschikbaar stellen als 'Layouts' in de Controllers. Via de XML config zou ik dan aan zo'n Layout ook een stuk BL kunnen hangen, wat afhankelijk van de context zaken opvraagt. Op die manier is de content niet meer pull-based (met tiles dus), maar weer push based: de BL achter een Layout bepaald wat er opgevraagd wordt.
Pagina: 1