Page 1 of 1

Seperating controller and view using templates

Posted: Wed Dec 26, 2007 3:49 am
by matthijs
There have been quite a few threads on the seperation of the layers in MVC, but most often the central discussion is around the controllers and seperation between the CV and M layers. The most important one to start with, I think.
I haven't found too many taking the approach from the View site. So, working on an application, I managed to get a good separation between the model and controller classes, but I got in trouble dealing with the View. I'll just sum up the process I went through and the problems I encountered.

// PERSPECTIVE 1. How I started.

Code: Select all

<?php
// Controller
class ArtcilesController {
	
	function View(){
		$articles = new ArticleModel();
		$maincontent = $articles->findAll();

		$template = new A_Template_Include('templates/example1.php');
		$template->set('title', 'Mysite - articles');
		$template->set('stylesheet', 'main.css');
		$template->set('content', $maincontent);
		$content = $template->render();
		$this->response->setContent($content);
	}

}
// Template
?>
<html>
<head>
	<title><?php echo $title; ?></title>
	<link href="<?php echo $stylesheet; ?>" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="maincontent">
	<?php echo $maincontent; ?>
</div>
</body>
</html>
This is all fine for a small website with a few pages, but what if the site gets more complex and larger? Say I add one single action:

Code: Select all

<?php
class ArticlesController {
	function View(){
		// ...
	}
	function ViewSummeries(){
		$articles = new ArticleModel();
		$maincontent = $articles->findAllSummeries();

		$template = new A_Template_Include('templates/example1.php');
		$template->set('title', 'Mysite - articlessummeries');
		$template->set('stylesheet', 'main.css');
		$template->set('content', $maincontent);
		$content = $template->render();
		$this->response->setContent($content);
	}
	
}
?>
As you see I'm duplicating the same code over and over again. For hundreds of actions, each time I have to set all the variables (title, stylesheet, etc). Now in this example there are only 3 vars, but in a real webpage there might be 30-100 variables.

So we want to be able to reuse some components. A Composite pattern comes to mind.

Code: Select all

<?php
class ArtcilesController {
	function View(){
		$maincontent = new Template('tpl/body.php');
		$maincontent->set('item','withthis');
		$maincontent->set('item2','withthis');
		$maincontent->set('item3','withthis');
		$body = new Component('body', $maincontent);
		$body->attach(new Component('menu', new Template('tpl/menu.php')));
		$page = new Component('page', new Template('tpl/page.php'));
		$page->attach($body);
		$page->attach(new Component('footer', new Template('tpl/footer.php')));
		$content = $page->render();
		$this->response->setContent($content);
	}
}
?>
Now I can reuse common components like a header, navbar, menu, sidebar, footer, etc And mix and match them in a flexible way. But the problem remains that the action in the
controller has to make sure all content (the variables) has been given to the different components of the view (the templates). For the specific content that action is responsible for, that's ok. But for most content, it's not the responsibility of that action to gather the data.

In my view, let's say the component Mainnavigation is itself responsible for gathering it's data needed to render well. And to complicate matters further, that data depends again on the action/request in some ways! Say you have a navigation list, and depening on which page you are on, one of the items is highlighted.

Another good example is a breadcrumb trail. used in all pages (so its a general element) but it also has to be aware of it's context.

Maybe I need some kind of ViewHelper. Or probably more of them. Then the ViewHelper is responsible for gathering all necessary data for the templates. Of course, I must be careful not to let the ViewHelper get too much responsibility, turning it into a controller. So, read-only for the ViewHelper.

Code: Select all

<?php
class ArtcilesController {
	function View(){
		$articles = new Article;
		$maincontent = $articles->findAll();
		
		$view = new ViewHelper;
		$view->setContent($maincontent);
		
		$content = $view->getContent();
		$this->response->setContent($content);
	}
}
class ViewHelper{
	function getContent(){
		// get together all components for the specific page, also depending on the specific request that's been made.
		// get necessary data from the database
	}
}
?>
PERSPECTIVE 2

Maybe this will work. However, let's take a fresh view from another perspective. The designer/developer building the site, using templates.
In my ideal situation, the front-end developer would be able to do something like this:

Code: Select all

<?php
// Template: main.tpl.php
?>
<?php getHeader(); ?>
<div id="content">
	<!-- more html here .. -->
	<?php if(has_content()) : while (has_content()) : ?>
		<div id="section"><?php the_content(); ?></div>
	<?php endwhile; else: ?>
		<p>Sorry, no content exists for the criteria you searched for</p>
	<?php endif; ?>
	<!-- more html -->
	<!-- more html -->
<?php getSidebar(); ?>
<?php getFooter(); ?>

<?php
// template: header.tpl.php
?>
<html>
<title><?php the_title('optional','arguments'); ?></title>
	<link href="<?php the_stylesheet(); ?>" rel="stylesheet" type="text/css" />
	<!-- more html -->
</head>
<body>
<ul id="mainnav"><?php the_menu('some','arguments'); ?></ul>
Now you are really flexible in the way you design your templates. the_content() just puts in the specific content, depending on the request. Now the controllers are not responsible for the complete views. The views themselves gather the content they need.

But, of course, depending on the request, the controllers should still do their job of putting together the correct response depending on the request. I think that you have two kinds of logic here: controller logic and view logic. Controller logic is "intelligent": deciding what to do depending on the request. View logic is "dumb": only deciding what to show depending on the request

Example controller logic:
GET: mysite/page
controller decides the view should display that page
POST: mysite/page
A form has been submitted to the page. The controller handles that POST request, maybe updating something in the db (done by the model of course), etc
GET: mysite/blog/
The View decides to show a list of recent posts
GET: mysite/blog/2006/10/
The View decides to show all posts from 2006/10.

All in all a long story and a lot of questions. I guess the main question is: how do you guys put together the View component (in MVC) in more complex websites?

Posted: Wed Dec 26, 2007 8:21 am
by Maugrim_The_Reaper
The system I prefer relies more on the View to be more self-sufficient. It is not the only way, but it is a popular way in PHP and Java. The main concept is that the V and C are in the same layer of the application (Presentation) and that since they're related almost any View logic you put in a controller, could be transferred to a view. One example of this is a View Helper - where the View, and not the Controller, is responsible for collecting data from the database (in read-only style) it may need. This is useful where the controller can't easily determine common data requirements.

This style is implemented in two frameworks - Symfony, and the Zend Framework (as of mid-December). I co-proposed the final ZF solution: http://blog.astrumfutura.com/archives/2 ... anced.html after a fairly long campaign (the 6 prior blog entries, countless emails and IRC chats, and discussion with the leading MVC Zend Tech. developer).

In Java similar ideas are present in J2EE, JSP, and the Tiles tag library.

In PHP it's actually hard to find simpler examples. We've been spoiled with Smarty and co. to such a degree it's automatic to assume a controller must handle all View data setup and initialisation. Maybe the core ideas of the above ZVE post will shed a little light on possible strategy.

Posted: Wed Dec 26, 2007 3:57 pm
by blueyon
scottayy | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]


I have set mine up like this:

Code: Select all

<?php
class ControllerDefault extends Action {
	var $id     = 'content';
	var $parent = 'layout';

	function execute() {
		$this->view->set('title', 'Default');
		
		$view = new View('default.tpl');
		
		$view->set('title', 'Default');
		
		$this->render($view);
	}
}
?>

<?php
class ControllerLayout extends Action {
	var $id       = 'layout';
	var $children = array(
		'header',
		'footer',
		'column'
	);
	
	function execute() {
		$this->view = new View('layout.tpl');
		
		$this->render($this->view);
	}
}
?>
I think zend havn't got a clue how to set their views up properly otherwise they would have had a good solution at the begining.


scottayy | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]

Posted: Thu Dec 27, 2007 5:55 am
by matthijs
Maugrim_The_Reaper wrote:The system I prefer relies more on the View to be more self-sufficient. It is not the only way, but it is a popular way in PHP and Java. The main concept is that the V and C are in the same layer of the application (Presentation) and that since they're related almost any View logic you put in a controller, could be transferred to a view. One example of this is a View Helper - where the View, and not the Controller, is responsible for collecting data from the database (in read-only style) it may need. This is useful where the controller can't easily determine common data requirements.
I agree with this. A navigation menu list for example, should be set up from the View. It has to be aware of the context, but without having to be set up by the controllers/actions.

My two main concerns are:
First, duplication. If each action has to set up all data, you get a massive amount of duplicated code.
Second, responsibility. Say I have an action ViewListOfArticles, used for a webpage /site.com/articles/. That action has nothing to do with showing the sidebar on that page, neither with the header, the footer, the calendar in the sidebar, the list of widgets, etc etc etc. That action has only one concern, getting the right list of articles.
Maugrim_The_Reaper wrote:I co-proposed the final ZF solution: http://blog.astrumfutura.com/archives/2 ... anced.html after a fairly long campaign (the 6 prior blog entries, countless emails and IRC chats, and discussion with the leading MVC Zend Tech. developer).
I've quickly reread your article. Seems like you go in the right direction, but I'll have to study it some more.

Viewing an example like this:

Code: Select all

//./scripts/article.phtml:
<? $this->headTitle($this->article['name']) ?>
<? $this->placeholder()->set('layout_license', 'GNU Free Documentation License'); ?>
<div id="article">
<?php echo $this->article->text ?>
</div>
I'd like to go even further then this. Maybe combine your idea of a View gathering it's own data with simple template-system. So that you end up with templates like:

Code: Select all

<h1><?php $the_title(); ?></h1>
<div id="article">
<h2><?php $the_article_title(); ?></h2>
<p><?php $the_date(); ?></p>
<p><?php $the_content(); ?></p>
</div>
But without having to set each element in the controllers. The functions you see here are simple functions, echoing the specific content, depending on the request. However, somewhere there has to be a connection between the controller layer and this View. Because, when someone visits
/thesite.com/article/my-blog-post
the functions in the template display something else then when someone visits
/thesite.com/article/my-other-blog-post