Page 1 of 1

MVC, would you do it like this?

Posted: Tue Aug 21, 2007 3:30 am
by ReDucTor
feyd | 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]


Is this good seperation in MVC?

View (Ignore the use of responceXML funcs, as I would make it an array in ajax() if I go through with this method)
[syntax="html"]<script language="text/javascript">
var curpage=0;

function getNews(pagenum) {
  root = ajax("news.php?action=data&page="+pagenum);
  newsdata = root.getElementsByTagName("news");
  newshtml = "";
  for(i=0;i<newsdata.length;i++) {
    newsitem = newsdata[i];
    newshtml += "<h2>"+newsitem.getElementsByTagName("title")[0].firstChild.nodeValue+"</h2>";
    newshtml += newsitem.getElementsByTagName("body")[0].firstChild.nodeValue;
  }
  return newshtml;
}

</script>
<a href="#" onclick="document.news.innerHTML=getNews(--curpage);"><</a> || 
<a href="#" onclick="document.news.innerHTML=getNews(++curpage);"><</a><br>
<div id="news" name="news">
  <script>document.write(getNews(curpage));</script>
</div>
Controller[/syntax]

Code: Select all

<?php
// Includes/Requires here
switch($_GET['action']) {
  case 'data':
    $xmlnews = $news->getNews($_GET['page']);
    $xmlnews->setHandler('NewsXML');
    $xmlnews->header();
    foreach($xmlnews as $post)
      echo $post;
    $xmlnews->footer();
  default:
    require_once('view/news.html');
}
?>
Model

Code: Select all

<?php
define('NEWS_PER_PAGE',15);
class News {
  function getInstance() {
    static $instance;
    if(!isset($instance)) {
      $c = __CLASS__;
      $instance = new $c;
    }
    return $instance;
  }

  function getNews($page) {
    $q = $db->execute('SELECT * FROM news ORDER BY `date` LIMIT '.($page*NEWS_PER_PAGE).','.NEWS_PER_PAGE);
    return $q;
  }
}

class NewsXML {
  function header() {
     echo '<?xml version="1.0" encoding="ISO-8859-1"?>
                  <news>';
  }
  function __toString() {
    return '<newsitem>
                    <title>'.htmlentites($this->title).'</title>
                    <body>'.htmlentites($this->body).'</title>
                </newsitem>';
  }

  function footer() {
     echo '</news>';
  }
}
All code is untested, and probably syntax errors left right and centre, but I want opinions on this method of using MVC before I start building applications like this.

Opinions?


feyd | 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: Tue Aug 21, 2007 7:01 am
by superdezign
The use of MVC is a personal choice, and is a matter of how organized you feel your code should be. If there's one thing I've learned while boggling my brain around MVC is that the best way to understand the way that it works is to think to yourself how separated you have made the elements. The purpose is so that you can make an update to the input handler, the back-end logic, or the output separately without ever having affect the others. When designing, think about if you wanted to add something in particular to any part and if it'd require the modification of more parts than just that one. If not, then you are well on your way.

Posted: Tue Aug 21, 2007 11:00 am
by Begby
The test I like to use on my model is the command line test.

Visualize having to recode your entire application to run solely on the command line through some other scripts with switches and stuff. Since it wouldn't be running through a web page you would get rid of the controller and the view but you should be able to reuse your model without changes.

If your model can do everything that your app needs it to do and can also output all the data to be viewable from a command line in plain text, then your model has been successfully separated from your view / controller logic.

In the example you posted its not, you have view stuff mixed in with your model (the tags, headers, footer, etc.). To solve this you would either split off the specific news stuff into its own view template, or create a separate view object that you would pass a news object to.

Posted: Tue Aug 21, 2007 12:19 pm
by Chris Corbyn
Begby wrote:The test I like to use on my model is the command line test.
Agreed :)
Begby wrote:Visualize having to recode your entire application to run solely on the command line through some other scripts with switches and stuff. Since it wouldn't be running through a web page you would get rid of the controller and the view but you should be able to reuse your model without changes.
Partly agree. Controller is still a layer you want when you're running on command line (my opinion anyway). Apart from anything related to HTTP, a well designed controller can make developing command line apps a really pleasant experience. Just simple things like forwarding from one controller to another allows you to control program flow in real-time in a really streamlined manner. This only works if you communicate soley with a FrontController though. I've recently written a framework which can handle command line and/or HTTP apps. You can still handle things like authentication from the command line too.

Posted: Tue Aug 21, 2007 1:31 pm
by Christopher
This is an updated classic example of how to apply MVC. I say updated because it uses Ajax. The reason that using Ajax makes it a little interesting is because if you really want to follow MVC you will need to do it twice -- on the server and in the client.

In answer to your broad question:
ReDucTor wrote:Is this good seperation in MVC?
The answer is no.

Start with the Ajax portion. An Ajax client will have both Controller and View code -- the Model being on the other end of the Ajax Request. You could also wrap that request in a Model on the client side if you needed to do additional processing of the data for the View. If we add a Controller/View separation it wold look like this:

Code: Select all

<script language="text/javascript">
var curpage=0;

function newsController(pagenum) {
  root = ajax("news.php?action=data&page="+pagenum);
  newsdata = root.getElementsByTagName("news");
  return newsView(newsdata);
}

function newsView(newsdata) {
  newshtml = "";
  for(i=0;i<newsdata.length;i++) {
    newsitem = newsdata[i];
    newshtml += "<h2>"+newsitem.getElementsByTagName("title")[0].firstChild.nodeValue+"</h2>";
    newshtml += newsitem.getElementsByTagName("body")[0].firstChild.nodeValue;
  }
  return newshtml;
}

</script>
<a href="#" onclick="document.news.innerHTML=newsController(--curpage);"><</a> || 
<a href="#" onclick="document.news.innerHTML=newsController(++curpage);"><</a><br>
<div id="news" name="news">
  <script>document.write(newsController(curpage));</script>
</div>
Notice that the Controller could now display different views of the same data depending on the parameter(s) and the request.

On to the Controller. It uses a switch -- urgh. It is fine though.

Code: Select all

<?php
// Includes/Requires here
switch($_GET['action']) {
  case 'data':
    $xmlnews = $news->getNews($_GET['page']);
    $xmlnews->setHandler('NewsXML');
    echo $newsview->render($xmlnews);
  default:
    require_once('view/news.html');
}
?>
Now onto your Model. Hey, what are those echo's doing in your Model? It's actually not bad though. You have good instincts and even though you didn't admit it -- you separated the Model and View into separate classes. It is important to remember that with Ajax you still need a View on the server side. The View is what generates the XML (or HTML or RSS ...). The Model should just be providing data:

Code: Select all

<?php
define('NEWS_PER_PAGE',15);
class NewsModel {
  function getNews($page) {
    $q = $db->execute('SELECT * FROM news ORDER BY `date` LIMIT '.($page*NEWS_PER_PAGE).','.NEWS_PER_PAGE);
    return $q;
  }
}
And the View that generates the XML

Code: Select all

<?php
define('NEWS_PER_PAGE',15);
class NewsViewXML {
  function render($data) {
    $str = $xmlnews->header();
    foreach($data as $post) {
      $str .= $this->item($post);
    }
    $str .= $xmlnews->footer();
    return $str;
  }

  function header() {
     return '<?xml version="1.0" encoding="ISO-8859-1"?>
                  <news>';
  }

  function item() {
    return '<newsitem>
                    <title>'.htmlentites($row->title).'</title>
                    <body>'.htmlentites($row->body).'</title>
                </newsitem>';
  }

  function footer() {
     return '</news>';
  }
}
And ultimately your response (echo) should be at some central location and hopefully as late as possible. That lets you redirect and make other changes in a modular fashion before output is sent.

Posted: Wed Aug 22, 2007 10:51 am
by Begby
d11wtq wrote:Partly agree. Controller is still a layer you want when you're running on command line (my opinion anyway). Apart from anything related to HTTP, a well designed controller can make developing command line apps a really pleasant experience. Just simple things like forwarding from one controller to another allows you to control program flow in real-time in a really streamlined manner. This only works if you communicate soley with a FrontController though. I've recently written a framework which can handle command line and/or HTTP apps. You can still handle things like authentication from the command line too.
However you are probably going to want to use a different controller or a different implentation of your controller. I didn't say not to use any controller at all. The thing to be careful of is that you don't have your business logic within the controller.

For instance if you have all your data validation in the controller, you would have to rewrite it in your command line controller (which means that your domain logic is overlapping into your controller).

So mentally visualizing your model meeting the requirements of your domain sans a controller is a good idea when working on separation. If it is truly separated then you can reuse your model in a variety of controllers without having to rewrite domain logic.