Front (actually, Page) Controller design

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
User avatar
Nathaniel
Forum Contributor
Posts: 396
Joined: Wed Aug 31, 2005 5:58 pm
Location: Arkansas, USA

Front (actually, Page) Controller design

Post by Nathaniel »

Hey guys,

McGruff's points in the OOP vs. Procedural threads finally convinced me to try TDD. I think I'm addicted to seeing that green bar now. I don't know how I lived without TDD... but anyway.

I believe I've done a relatively good job keeping tasks seperate, in their own objects, writing tests, and then code to pass the tests, but I'm stuck on my front controller on my Lyrics website project (which I've been working on for several weeks now... I didn't steal your idea, vchris. ;)).

That it's procedural is one problem, but also that I'm not sure how I could test the page itself is another. I plan to write tests for all of the components in the page before writing the cpmponents themselves.

An example URL which would be passed into this script is example.com/artist_name/album_name/song_name/. My OrganizeResourcesRequest uses several classes for validating / formatting... so $request->artist() would return 'artist name'. Then my RetrieveArtist object would "SELECT ... WHERE LOWER(name) = $name". Makes for nice URLs.

Code: Select all

<?PHP
# Lyrics / includes / assembler.php
# Nathaniel Jones
# September 25th, 2005

include('common.php');

$request =& new OrganizeResourcesRequest($_GET['url']); //unit tested, works great
$overall =& new TemplateFile('overall.html');
$failure =& new TemplateFile('404.html'); // will be an extended class which sets 404 header...

// AssembleArtist, AssembleAlbum, AssembleSong extend AssembleModule

if ( $request->albumRequested() == false )
{
	$artist = new AssembleArtist($request->artist());
	$artist->moduleFailureRecovery($failure);

	$overall->assign('titles', $artist->assembledTitles());
	$overall->assign('content', $artist->assembledContent());
}
else if ( $request->songRequested() == false )
{
	$album = new AssembleAlbum($request->artist(), $request->album());
	$album->moduleFailureRecovery($failure);
	
	$overall->assign('titles', $album->assembledTitles());
	$overall->assign('content', $album->assembledContent());
}
else
{
	$song = new AssembleSong($request->artist(), $request->album(), $request->song());
	$song->moduleFailureRecovery($failure);
	
	$overall->assign('titles', $song->assembledTitles());
	$overall->assign('content', $song->assembledContent());
}

$overall->display();
exit;
?>
If there is anything that needs changing (I'm sure there is), or an article relating to what I want to do (I need PoEAA for Christmas), I'm eager to learn.

- Nathaniel
Last edited by Nathaniel on Sun Sep 25, 2005 9:31 pm, edited 1 time in total.
Charles256
DevNet Resident
Posts: 1375
Joined: Fri Sep 16, 2005 9:06 pm

Post by Charles256 »

that's it.i give up.what the hell does TDD stand for?
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Post by feyd »

Test Driven Design or Test Driven Development

most often the latter..
User avatar
patrikG
DevNet Master
Posts: 4235
Joined: Thu Aug 15, 2002 5:53 am
Location: Sussex, UK

Post by patrikG »

tdd = test driven development. For PHP this involves third-party applications such as SimpleTest or PHPUnit.
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

At the risk of posting Sitepoint references...

There's an interesting take on FC at http://www.sitepoint.com/forums/showthread.php?t=184548 . Might give you some insight on thoughts in the area. Please ignore the naggling over the purity of the example as a FrontController - Sitepoint authors can argue for days on the topic of maintaining absolutely pure unmodified patterns...:) I found it fairly interesting at least... The topic title is a little misleading - it only addresses the C in MVC.

Your code above looks fine to me. It may not necessarily be a Front Controller pattern per se - but who cares about that! It looks good, it works - tell the pattern purists to take a leap...:)
User avatar
Buddha443556
Forum Regular
Posts: 873
Joined: Fri Mar 19, 2004 1:51 pm

Re: Front Controller design

Post by Buddha443556 »

Nathaniel wrote:That it's procedural is one problem, but also that I'm not sure how I could test the page itself is another. I plan to write tests for all of the components in the page before writing the cpmponents themselves.
I kind of wonder how you have code and no tests. If your not testing first then it's not TDD. One of the benefit of TDD is 100% test coverage. Is your intention to rewrite your entire script or just apply unit testing? :?
Maugrim_The_Reaper wrote:Sitepoint authors can argue for days on the topic of maintaining absolutely pure unmodified patterns...
I sometimes wonder if they just skipped the Preface and Narratives of PoEAA when they read it. :lol:
User avatar
Nathaniel
Forum Contributor
Posts: 396
Joined: Wed Aug 31, 2005 5:58 pm
Location: Arkansas, USA

Re: Front Controller design

Post by Nathaniel »

Buddha443556 wrote:
Nathaniel wrote:That it's procedural is one problem, but also that I'm not sure how I could test the page itself is another. I plan to write tests for all of the components in the page before writing the cpmponents themselves.
I kind of wonder how you have code and no tests. If your not testing first then it's not TDD. One of the benefit of TDD is 100% test coverage. Is your intention to rewrite your entire script or just apply unit testing? :?
I wasn't sure how to create a unit test for the code I posted (because it is procedural), so I just quickly wrote out what it had to do, then programmed it. It didn't take long. If there is a way I should be doing what I need with an object, I'll write a test for it, then the object, and scratch the code I posted above. If there is a way I should be testing it, even as a procedural script, then I'll write the test and code to the test from scratch.

That said, I think using an object would be making things more complicated, and since my code works fine and is relatively simple (uses unit tested components and 3 if statements), I think I'll just leave it alone.
User avatar
Buddha443556
Forum Regular
Posts: 873
Joined: Fri Mar 19, 2004 1:51 pm

Re: Front Controller design

Post by Buddha443556 »

Nathaniel wrote:I wasn't sure how to create a unit test for the code I posted (because it is procedural), so I just quickly wrote out what it had to do, then programmed it. It didn't take long. If there is a way I should be doing what I need with an object, I'll write a test for it, then the object, and scratch the code I posted above. If there is a way I should be testing it, even as a procedural script, then I'll write the test and code to the test from scratch.
Looks like you have a web handler, OrganizeResourcesRequest()? Seem to be processing the request data anyway. Probably just need a Dispatcher. The front controller is dependent somewhat on the HTTP server (and PHP which does a lot of the dirty work) for input by creating a dispatcher you'll be able to automate the testing.
PoEAA wrote:Rob Mee showed me an interesting variation of Front Controller using a two stage Web handler separated into a degenerate Web handler and a dispatcher. The degenerate Web handler pulls the basic data out of the http parameters and hands it to the dispatcher in such a way that the dispatcher is completely independent of the Web server framework. This makes testing easier because test code can drive the dispatcher directly without having to run in a Web server.
Microsoft is another good source for pattern information: Front Controller Notice the Testing Consideration section.
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Re: Front Controller design

Post by McGruff »

Nathaniel wrote:McGruff's points in the OOP vs. Procedural threads finally convinced me to try TDD. I think I'm addicted to seeing that green bar now. I don't know how I lived without TDD... but anyway.
Great! I've manged to test-infect someone :)

I think that's a PageController rather than a FrontController. The two patterns are both input controllers ie they receive user input, do some stuff and construct a response (or rather ask the response to build itself). Uniquely, FrontControllers also identify the request type and dispatch the request to something which can handle it. With FrontController, you'd tend to aim all requests at a single file (although you can have multiple FrontControllers) whereas, with PageController, apache or etc would route requests to individual PageControllers - broadly speaking one for each type of site page (eg "view posts").

Strictly speaking, there are almost always a range of possible pages per request type (if you tried to view a topic in the moderators forum you'd get an access denied page) so it's more correct to say that PageControllers map one to one with request types and one to many with possible responses.

Both patterns are valid options: I can't really think of a good reason to choose one over the other. The Wact Wiki is worth a read for more info.

I think PageControllers as brief snippets of procedural code are fine. You can't avoid a few lines to kick everything off. You can test this in a web test case using the Simple Test web tester which allows you to make various assertions about page content - check out the docs for more info.
assertTitle($title) Pass if title is an exact match
assertWantedPattern($pattern) A Perl pattern match against the page content
assertNoUnwantedPattern($pattern) A Perl pattern match to not find content
assertWantedText($text) Pass if matches visible and "alt" text
assertNoUnwantedText($text) Pass if doesn't match visible and "alt" text
assertLink($label) Pass if a link with this text is present
assertNoLink($label) Pass if no link with this text is present
assertLinkById($id) Pass if a link with this id attribute is present
assertNoLinkById($id) Pass if no link with this id attribute is present
assertField($name, $value) Pass if an input tag with this name has this value
assertFieldById($id, $value) Pass if an input tag with this id has this value
assertResponse($codes) Pass if HTTP response matches this list
assertMime($types) Pass if MIME type is in this list
assertAuthentication($protocol) Pass if the current challenge is this protocol
assertNoAuthentication() Pass if there is no current challenge
assertRealm($name) Pass if the current challenge realm matches
assertHeader($header, $content) Pass if a header was fetched matching this value
assertNoUnwantedHeader($header) Pass if a header was not fetched
assertHeaderPattern($header, $pattern) Pass if a header was fetched matching this Perl regex
assertCookie($name, $value) Pass if there is currently a matching cookie
assertNoCookie($name) Pass if there is currently no cookie of this name
McGruff
DevNet Master
Posts: 2893
Joined: Thu Jan 30, 2003 8:26 pm
Location: Glasgow, Scotland

Post by McGruff »

Maugrim_The_Reaper wrote:At the risk of posting Sitepoint references..
No problem here. They've got a great advanced php board - I often recommend it myself.
User avatar
Nathaniel
Forum Contributor
Posts: 396
Joined: Wed Aug 31, 2005 5:58 pm
Location: Arkansas, USA

Post by Nathaniel »

Thanks, McGruff. Reading up on Page Controllers instead of Front Controllers made understanding what I was doing a lot easier... :) I look forward to getting PoEAA.

For anybody running across this thread later with similar concerns to mine: The Page Controller information in the MSDN library (it's in the same directory as the Front Controller link Buddha posted - thanks, Buddha).

- Nathaniel, who's page controller is working perfectly
Post Reply