Seperating controller and view using templates
Posted: Wed Dec 26, 2007 3:49 am
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.
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:
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.
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.
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:
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?
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>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);
}
}
?>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);
}
}
?>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
}
}
?>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>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?