MVC pagina
Pagina: « vorige 1 2 3 4 volgende »
Wouter J op 21/02/2014 12:11:29:
De PHP code heeft helemaal niks te zeggen over wat er op het scherm komt, alleen de view weet wat hij wilt gaan tonen. Je moet dus de subcontrollers aanroepen vanuit de template en niet vanuit de controller.
De view bepaalt niet zelf wat de view gaat tonen. Een view is niet autonoom. Dat bepaalt de controller. En de controller vult ook de view op basis van het model: "Er zijn nog geen reacties" is een andere uitkomst uit het model dan "Er zijn 2 reacties".
Ontvangt de controller een request om een view op te leveren in het Engels in plaats van het Nederlands? Dan verandert ook dat de invulling van de view. Niet de view maar de controller bepaalt dat.
Wat Wouter meer bedoelt, denk ik, is dat HMVC leidt tot "tightly coupled" applicaties. Dat is inherent aan de hiërachie. Daarom is HMVC wel een geschikt model voor webpagina's, want die hebben nu eenmaal zo'n hiërchie, maar kun je voor andere toepassingen veel mooiere OOP-modellen gebruiken.
Alleen al het feit dat er bij MVC en HMVC überhaupt een view uit een module moet rollen, is al sterk output gericht. En dat ben ik met Wouter eens: dan gaat je code bepalen hoe je klassen zijn ingericht, terwijl je het liever omgekeerd doet.
Ozzie PHP:
Als je dat principe begrijpt, dan zul je ook begrijpen dat het niet de bedoeling is om vanuit één zo'n view andere views te gaan aanroepen.
Nee, want dat is juist wel de bedoeling. Het verschil tussen mijn (en die van Vivendi) en jouw denkwijze:
Jouw denkwijze
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
1. BlogController
-> get blog post --> BlogModel::getBlogPost
-> get view for comments for {blog post} --> CommentsController::commentsForPostAction --> CommentsModel::getCommentsForPost({blog post})
-> get view for top 10 posts --> BlogController::top10PostsAction --> BlogModel::getAll(limit=10)
-> inject all views in view layer
2. View Renderer
-> render all the given views
-> get blog post --> BlogModel::getBlogPost
-> get view for comments for {blog post} --> CommentsController::commentsForPostAction --> CommentsModel::getCommentsForPost({blog post})
-> get view for top 10 posts --> BlogController::top10PostsAction --> BlogModel::getAll(limit=10)
-> inject all views in view layer
2. View Renderer
-> render all the given views
Mijn denkwijze
Code (php)
1
2
3
4
5
2
3
4
5
1. BlogController
-> get blog post --> BlogModel::getPost --> CommentsModel::getCommentsForPost
2. View Renderer
-> render view (including blog post + comments)
-> include top 10 posts -> BlogController::top10PostsAction
-> get blog post --> BlogModel::getPost --> CommentsModel::getCommentsForPost
2. View Renderer
-> render view (including blog post + comments)
-> include top 10 posts -> BlogController::top10PostsAction
D Vivendi:
Dit is simpel en duidelijk.
Ja, maar in jouw geval heb je de comments dus vastgebonden aan de blog posts. Dit betekend dat je die comments niet weer kunt hergebruiken voor een eventuele forum module.
Wat dus beter is is om deze 2 te splitsen in 2 verschillende modules. De BlogModel zal vervolgens de CommentsModel gebruiken om de comments te krijgen, je hebt dus alsnog alleen maar:
Vervolgens renderd de template de blog posts + comments.
we hoeven overigens niet zo aanvallend naar elkaar te reageren, iedereen brengt in deze discussie zijn eigen denkwijze en als dat anders is dan die van jouw hoeft dat niet perse fout te zijn. Sta open voor andere ideeën en leer daar ook uit, na afloop weet je dan waarom je jouw idee beter vind dan de andere ideeën of waarom jouw eerste idee fout was. Laten we nu weer verder gaan met deze leuke discussie!
Gewijzigd op 21/02/2014 13:53:27 door Wouter J
+1
Helemaal mee eens :)
>> Mijn denkwijze
Code (php)
1
2
3
4
5
2
3
4
5
1. BlogController
-> get blog post
2. View Renderer
-> render view (including blog post + comments)
-> include top 10 posts -> BlogController::top10PostsAction
-> get blog post
2. View Renderer
-> render view (including blog post + comments)
-> include top 10 posts -> BlogController::top10PostsAction
Ik snap niet helemaal wat nu het verschil is met mijn denkwijze.
Zonder naar de code te grijpen zie ik het hmvc-verhaal als een opeenstapeling van bouwsteentjes (modules). Eén pagina-controller bepaalt van welke bouwsteentjes er op die pagina gebruik wordt gemaakt. Die controller zegt dus als het ware: op deze pagina moet een header komen, nieuwsberichten, contactgegevens en een footer. Al deze bouwsteentjes hebben vervolgens allemaal hun eigen view.
En dat mag een controller dus niet zeggen. Een controller mag helemaal niet weten dat die pagina een header, sidebar, menu, footer bevat. Het enige dat een controller weet is wat er in zijn kleine stukje van de pagina thuishoort. De rest bepaald de view dan weer, die roept voor het andere kleine stukje weer een andere controller aan, etc.
Maar dat is toch geen hmvc? Bij hmvc heeft juist iedere module een EIGEN view. De pagina wordt dus gevuld met allemaal kleine "viewtjes" die telkens voortkomen uit 1 module. Welke modules dit zijn zou dan moeten worden bepaalde vanuit de parent controller:
In mijn methode heeft elke module nog steeds z'n eigen view, het enige verschil is dat niet de parent controller bepaald welke modules er getoond worden, maar een parent view moet bepalen wat er getoond wordt.
Over de dispatch zijn we het dan niet eens. Waar Wouter de view een child-controller laat aanroepen, zou ik de controller een child-controller laten aanroepen. Zoals in het plaatje dat ik eerder postte.
Ik wil namelijk niet dat een view in een module buiten de eigen controller om child-controllers gaat laden. Dan verliest de controller de controle (en komt er mogelijk ook niets van data terug in het model). Ik gebruik liever éénrichtingsverkeer, van controller naar view.
Oké, maar dan zeg ik op mijn beurt dat je daarmee de kracht van het hele gebeuren ondermijnt. De bedoeling is juist de onafhankelijkheid van de componenten.
Voorbeeld. Ik heb een website met een contactpagina. Nu wil jij dat bij website X ook een plattegrond wordt getoond. Zoals jij het nu zegt, zou je die plattegrond (ook een module) aanroepen vanuit de contactmodule (de parent).
Nu wil je bij website Y ook een contactpagina maken, maar zonder plattegrond. Oeps, de plattegrond wordt vanuit de view aangeroepen en zal dus altijd worden getoond, ook als je dit niet wilt.
@Ward:
>> Over de dispatch zijn we het dan niet eens. Waar Wouter de view een child-controller laat aanroepen, zou ik de controller een child-controller laten aanroepen. Zoals in het plaatje dat ik eerder postte.
Maar dit is toch wat ik de hele tijd zeg?
Klopt, dus de discussie gaat over wie de baas is in de HMVC-hiërarchie: de view of de controller.
Ik stem op de controller :-)
Ik dus ook. Zo staat het overigens ook in de dingen die ik erover heb gelezen, dat de controllers onderling met elkaar communiceren.
Ik denk dat de parent controller dan eigenlijk een "page controller" is. En binnen deze page controller kun je dan modules aanroepen. Wat denk jij?
Hier zie je heel erg duidelijk 2 mensen die de approach van ZF gebruiken en ikke die de Symfony2 approach gebruikt. :)
In Symfony2 zijn modules (bundles) zo veel mogelijk standalone en herbruikbaar. Als ik een Contact bundle heb gemaakt voor Website X, dan moet ik die ook voor Website Y kunnen gebruiken. In Symfony2 kun je heel makkelijk een view overriden in een applicatie, door deze te maken in de app directory. Een controller overriden is een stuk moeilijker.
Iedere aparte bundle heeft zijn eigen views en controllers, etc. Dit is allemaal standalone en gebruikt geen andere template. Bijv. een BlogBundle/PostController::showAction heeft een template als:
Vervolgens include je deze in je applicatie. En daar overschrijf je dan in de app directory deze template, zodat hij mooi in je eigen view kan worden geinject:
Eerst maak je een globale applicatie view:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!doctype html>
<html>
<head>
<title><?php echo $view['slots']->render('title', 'Default Title') ?></title>
<!-- ... -->
</head>
<body>
<!-- ... -->
<div id=page__main><?php echo $view['slots']->render('main') ?></div>
<div id=sidebar>
<!-- renderd de BlogBundle/PostController::top10Action controller + zijn template
<?php echo $view['action']->renderController('Blog:Post:top10') ?>
</div>
</body>
</html>
<html>
<head>
<title><?php echo $view['slots']->render('title', 'Default Title') ?></title>
<!-- ... -->
</head>
<body>
<!-- ... -->
<div id=page__main><?php echo $view['slots']->render('main') ?></div>
<div id=sidebar>
<!-- renderd de BlogBundle/PostController::top10Action controller + zijn template
<?php echo $view['action']->renderController('Blog:Post:top10') ?>
</div>
</body>
</html>
En dan override je de template van de bundle, zodat deze wordt geplaatst in de main slot:
Code (php)
Hiermee zie je dat je dus heel simpel kan bepalen hoe je view bestaat en uit welke elementen (modules) hij moet bestaan. Dit bepaal je niet vanuit een controller, maar vanuit de template. Dat geeft je juist meer controle :)
Het mooie is dat als je van de sidebar ook een slot maakt, je ook kunt bepalen deze sidebar niet te laten zien op bijv. de contact pagina, door die slot te overriden met niks.
Verder denk ik dat je naar het grotere geheel moet kijken. Neem bijvoorbeeld je cache: dáárin wil je misschien de complete webpagina opslaan.
Verder moet je, vind ik, niet alles HMVC of MVC bouwen. Zoals gezegd, leent het model zich niet voor alles. Het gevaar daarvan is dat je geforceerd alles in één model propt onder het mom van "standaardiseren", maar daarmee problemen in het keurslijf van één oplossing dwingt in plaats van een betere oplossing te bedenken.
En dat gebeurt naar mijn smaak te vaak: "We hebben een MVC-platform!" Nou en?
Ward, reageer je nu op mij of ozzie? :)
Wouter J op 21/02/2014 14:49:14:
Ward, reageer je nu op mij of ozzie? :)
Op Ozzies vraag over de "page controller".
Hmmm... dat ziet er in mijn optiek maar ingewikkeld uit.
Hoe ik het zie is dat de controller leading is. De controller is het "ding" dat alles regelt. De controller is... even denken of ik een mooie anekdote kan verzinnen...
Oke, stel dat de controller een aannemer is (zo'n kerel die gebouwen bouwt). In de oude MVC situatie als we een pagina wilden oproepen dan kwam dat verzoek bij de controller binnen (het request werd door de controller opgevangen). Stel we wilden de pagina "kathedraal" bekijken. De controller bekeek het verzoek en bepaalde vervolgens welk gebouw (jawel, een view!) moest worden getoond. In dit geval de kathedraal. Dus ook in de oude situatie bepaalde de controller al wat er werd getoond! De view (de kathedraal) werd vanuit de controller aangeroepen.
In de nieuwe HMVC situatie komt het verzoek (request) weer binnen bij de controller. Aha, de bezoeker wil een kathedraal zien. Oké, wat heb ik daar voor nodig. Even kijken... "jongens, ik wil graag 3 platte gebouwen op elkaar gestapeld hebben, daar bovenop een stuk toren, en daar bovenop nog een kerkklok". Hier worden dus de modules aangesproken. De controller bepaalt dus op basis van het verzoek uit welke onderdelen (modules) de kathedraal (de view) moet bestaan.
De controller is als het ware de regelneef, het commandocentrum dat alles aanstuurt. De view is enkel een grafische weergave van hetgeen waar de controller opdracht voor gegeven heeft.
Ozzie, maar jij gaat dus voor elke nieuwe applicatie een PageController en view maken?
Wouter, wat versta je onder "applicatie"?
1 website, project, welke naam je het beestje ook geeft :P
In dat geval, nee... ik maak niet een page controller per applicatie, maar zoals het woord al zegt.. per page/pagina. Iedere pagina krijgt dus een controller. Die controller regelt wat er op de pagina komt te staan. Geeft dat antwoord op jouw vraag, of begrijp ik je vraag niet goed?