Page 1 of 2

Separating logic from design

Posted: Tue Jan 09, 2007 1:48 am
by Hurreman
After only coding my own small- to medium-sized projects for myself, relatives of friends and their companies for the past couple of years, I've now been hired as a fulltime webdesigner (80% design, 20% coding), and have been working on a larger project.

Now that I don't have to work more than 8 hours/day, I finally have some time left over to work on my own little "framework".

A while back I came here for advice on making a module-based application, where dependencies could be loaded during run-time instead of using massive amounts of require_once(file) on each page. With some help, I ended up writing a Front Controller and a Service Locator, which ended up doing what I wanted ( for the time being ).

Now however, a few months later, I've noticed some drawbacks in my design decisions, and need some advice to get back on track.

So to get to my question:

The current tree looks something like this:

/root
......./ actions
......./ .........../ inc
......./ lib
......./ images

The root directory contains the index file ( implementing the front controller and service locator to execute actions )
The actions dir is filled with all my actions, which ranges from administrating news to browsing and posting in a forum.
The actions/inc library has a file called pagelayout.php, which is just a class which all actions extend, so that I only have to change one file to affect the layout of the entire site.
The lib directory holds all my classes. MySQL, news, pages, forum, string, user, filehandler, imagehandler...

For a small project, the above was fine. Although as it's growing bigger, the actions directory is getting filled of files. And as if that wasn't enough, these actions are filled with mixed logic and design.

What I'm thinking of doing is renaming the actions folder to addons / modules, and use a template ending to separate the logic from layout.

addons/news/
....................../templates/default.htm
....................../templates/default.css
....................../actions/view.php
....................../actions/delete.php

etc..

Any thoughts or suggestions?

Posted: Tue Jan 09, 2007 2:12 am
by Christopher
It sounds like you may want to take the next step to create the separations defined by the MVC pattern. There are a number of threads about it here and elsewhere. The main thing is to create a separate place for Views (and their associated templates). You pagelayout.php file is probably your first (and outer) View and should not be with the controllers. Likewise you don't want a controller called view.php.

Models are domain logic, Views are presentation logic and Controllers are program flow logic.

Posted: Tue Jan 09, 2007 3:29 am
by Hurreman
So one step further could be replacing my pagelayout.php with a view and templating using smarty?

Posted: Tue Jan 09, 2007 6:21 am
by Maugrim_The_Reaper
Smarty isn't a necessity though it's very popular. ;)

The View layer can have a few parts (some are optional):

1. The Template, which is basically just HTML with some presentation logic. It's often misquoted but presentation logic can be PHP tagged code inside the HTML, so you can use the full range of php statements. It's common to see the alternative if:/else:/endif; format used in templates using PHP (instead of something like the Smarty tag system).

2. The Template Engine, which is given data from some external source (e.g. your Actions), and which processes Templates. For example, I could instantiate a View class (template engine), give it data, then call its render() method to process templates and echo the HTML.

3. The Response Object (optional). This is not always implemented but I like it. A Response object let's you gather up output including HTML and any headers into a central location. Useful if a request can call many actions, each processing many templates, and you need some place to aggregate them before echoing everything.

Views are quite simple to setup, you just need to get your HTML/display elements out of your Controller (Actions) layer and separate them into Templates - you can also carry over any PHP code used to manage presentation of data. If you decide to use Smarty, this can be even more simple since you don't need to design your own small template engine. With Smarty (or your own View engine) you just "assign" data to the engine from your Controller, call its render/process method, and echo the resulting HTML or append it to a Response object (if you want to handle echo'ing elsewhere - like centralising it in your Front Controller.

Posted: Tue Jan 09, 2007 6:31 am
by Hurreman
I installed Smarty during lunch and played around a bit, and I think it'll work like a charm together with the rest of my code.
The file count will probably be doubled, if I end up using a separate template for each action, but at least It'll be more separated by having the actions just append data to the templates.

Have to keep experimenting when I get home from work, but I think this will get me at least one step closer to what I'm aiming for!
I'll post my results later.

Thanks!

Posted: Tue Jan 09, 2007 7:30 am
by Begby
Another part of the view can be individual view wrappers for your model. This can be done with the decorator pattern, separate view objects, or even just using the __toString() method. This works great along with smarty.

Lets say you have a user object, and throughout your pages you display users all over. You want a user to be displayed in a table with first name, last name, contact info, address. One way is to pass a user object to each template and then copy and paste your table all over. Another way is to create a separate template file and include it along with the object on every page. Or you can encapsulate it into a view object and just pass the view to the template. Now if you want to change how the user appears, like add a middle name, you just edit your view class. You can have different views for different things too, like forms, detail view, just first/last name etc.

Code: Select all

$user = new User() ;
$userView = new UserView($user) ;
echo $userView->__toString() ; // Draw a table with the user info

$userForm = new UserForm($user) ;
echo $userForm->__toString() ; // Draw a form for your users

$smarty->assign('user', $userView) ;
in Smarty

Code: Select all

{$user}  <!-- calls __toString() -->

Posted: Tue Jan 09, 2007 7:45 am
by Jenk
It took me quite a while to click onto the differences between model logic (aka business logic) and presentation logic.

A quick summary is that presentation logic, only deal with presentation ("duh" I hear you say!) however, I often got muddled with adding iterative loops for tables and other such presentation, which then got me into a mess with adding calculations and mutators etc, which soon made the line between presentation and model somewhat blurry.

An example of presentation logic, from perhaps a search results page, or article page could be:

Code: Select all

<?php
if ($data->numberOfArticles > 0)
{
    foreach ($data->article as $article)
    {
        echo "<div class=\"article\">{$article->body}</div>\n";
    }
}
else
{
    echo "<div class=\"article\">No articles to display.</div>\n";
}
?>
If anything gets more into the nitty gritty of fetching articles and so forth, I would consider that part of the model. Certainly anything connecting to the database/source, or if there are any calculations for percentages (such as relevance to search criteria.)

This may or may not be useful to you, my reading comprehension is somewhat sucky. :)

Posted: Tue Jan 09, 2007 8:42 am
by Hurreman
Jenk wrote: An example of presentation logic, from perhaps a search results page, or article page could be:

Code: Select all

<?php
if ($data->numberOfArticles > 0)
{
    foreach ($data->article as $article)
    {
        echo "<div class="article">{$article->body}</div>\n";
    }
}
else
{
    echo "<div class="article">No articles to display.</div>\n";
}
?>

This is the kind of problem I ended up with in my current setup. I want to get rid of as many tags as possible from within the php code, so I kind of like how the smarty {foreach} seems to work. But it probably has a few pitfalls that I'm unaware of :)

Posted: Tue Jan 09, 2007 10:33 am
by Jenk
Actually, fwiw, using Smarty helped me click on BIG time as far as clarification of what is what. Now I just tend to use PHP instead of Smarty, but limit it's use in views to that of what would be available to smarty.

If you like, you can use:

Code: Select all

<?php if ($data->numberOfArticles > 0) 
{
    foreach ($data->articles as $article) { ?>
        <div class="article"><?php echo $article->body; ?></div>
    <?php }
}
else
{ ?>
    <div class="article">No articles found</div>
<?php } ?>
Or any form of hybrid in between the two, etc.

The choice is, of course, yours to make.

Posted: Tue Jan 09, 2007 11:18 am
by Hurreman
It all come down to personal preference (unless working in a team enviroment I suppose :roll:).

I've tried several ways of "separating" logic from presentation, and havn't found any ways I really like.
Looking at Smarty does however give me a small tickle in the private areas, but I'll have to try it out before I can say anything.

Until then, all suggestions are, as always, welcome.

At least I'm getting past the "I'm never going to use a library written by someone else" way of thinking.

Posted: Tue Jan 09, 2007 11:35 am
by Jenk
Zend's Zend_View package of their framework also provides a good insight for handling views :)

Posted: Tue Jan 09, 2007 11:49 am
by Maugrim_The_Reaper
Zend_View's simplicity is nice for a change from Smarty. It's even nicer now it extends ArrayObject, and I currently have it subclasses to handle automated escaping of assigned data. Lots of other things like Helpers you can also plugin or write for your needs.

Posted: Tue Jan 09, 2007 12:51 pm
by AKA Panama Jack
Hurreman wrote:I installed Smarty during lunch and played around a bit, and I think it'll work like a charm together with the rest of my code.
The file count will probably be doubled, if I end up using a separate template for each action, but at least It'll be more separated by having the actions just append data to the templates.

Have to keep experimenting when I get home from work, but I think this will get me at least one step closer to what I'm aiming for!
I'll post my results later.

Thanks!
If you like Smarty you might want to look into my Smarty Compatible Template Lite. It is considerably smaller and faster while offering most of the features of Smarty. Plus is has a few extra template tags like {for} and {while} that make creating templates much easier. You can find the full documentation here. :)

Posted: Tue Jan 09, 2007 2:31 pm
by Hurreman
Jenk and Reaper:

I've had a brief look at the Zend Framework before, but it's contains a lot more than what I'm looking for. ( Already have a lot of the work done, just need to get the separation part improved )

Panama Jack:

Ah, I remember reading about Template Lite when a few months back, and I actually thought "what was that light-weight template engine called.." before I just went and downloaded Smarty. I think I'll d/l TPL and give it a try, cheers!

Posted: Tue Jan 09, 2007 4:49 pm
by Hurreman
Sometimes I'm moving too fast for my own good, and I suppose this might be one of those occations. But hey, I'm curious and eager to learn :roll:

So I've had a look at Template_Lite, and must say it seems to be just what I'm looking for. However, I've been browsing both the TPL and Smarty forums for answers, but since I'm not entirely sure what my question is, it's hard searching for the answer. However, while describing my problem to a friend on IRC, I narrowed it down a bit.

My site is built around a FrontController and Service Locator (both heavily inspired by arborint's examples), executing and loading modules on demand (well, kind of anyway. Not happy with how my locator turned out).

Example index.php:

Code: Select all

<?php
require_once('Lib/Locator.php');
require_once('Lib/FrontController.php');

$locator = new Locator();


// Give the locator the required args for the MySQL Module
$locator->setArgs('MySQL','host','database','user','pwd');

$controller = new FrontController($locator,'actions/','home','error');

$controller->execute();
?>

All my actions inherit from a base class with the basic layout.

Example pageLayout.php:

Code: Select all

<?php
class pagelayout
{
    var $db;
    var $page;
    var $locator;

    public function __construct(&$locator)
    {
        $this->db = $locator->get('MySQL');
        $this->user = $locator->get('User',$locator);
        $this->locator = $locator;
    }
    
    public function printNavigation()
    {
        echo '<div id="navigation">';
        // Logic to print out page titles from the database etc...
        echo '</div>';
    }

    public function printHead()
    {
        echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
        echo '<html><head>';
        echo '<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />';
        echo '<title>Website</title>';
        echo '<link rel="stylesheet" href="includes/css/style.css"/>';
        echo '</head>';
        echo '<body>';
        echo '<div id="container">';
    }

    public function printHeader()
    {
        echo '<div id="header">';
        echo '</div>';
    }

    public function printFooter()
    {
        echo '<div id="footer"></div>';
        echo '</body></html>';
    }

    public function printRightDiv()
    {
        echo '<div id="rightdiv">';
        echo '</div>';
    }
}
?>

And the actions then contain more specific presentation and logic for the current action.


Example home.php:

Code: Select all

<?php
include_once('inc/pagelayout.php');

class home extends pagelayout
{
    // Content
    public function printContent()
    {
        echo '<div id="content">';
        $this->locator->setArgs('News',$this->locator);
        $news = $this->locator->get('News');
        $news->printNews();
        echo '</div>';
    }  

    // Execute
    public function execute()
    {
        $this->printHead();
        $this->printHeader();
        $this->printNavigation();
        $this->printContent();
        $this->printRightDiv();
        $this->printFooter();
    }
}
?>

And to show what's going on inside the news class, here it is:


Example News.php:

Code: Select all

<?php
class News
{
    private $db;
    private $user;

    public function __construct(&$locator)
    {
        $this->db = $locator->get('MySQL');
        $this->user = $locator->get('User');
    }

    public function printNews()
    {
        $stmt = $this->db->createQuery('SELECT * FROM news ORDER BY newsDate DESC, newsID DESC');
        $rs = $stmt->execute();
        while($rs->next())
        {
            echo '<div id="newscontent">';
            echo '<p class="newsTitle">' . stripslashes($rs->getField('newsTitle')) . '</p>';
            if($rs->getField('newsImage'))
            {
                echo '<img class="newsimage" src="images/pageimages/' . $rs->getField('newsImage') . '"/>';
            }
            $body = stripslashes($rs->getField('newsText'));
            $bodyArr = explode(chr(13),$body);
            foreach($bodyArr as $content)
            {
                if(trim($content=='') || !isset($content))
                {
                    echo '<br/>';
                }
                else
                {
                    echo '<p class="newsText">' . $this->getPreparedString($content) . '</p>';
                }
            }
            echo '<p class="newsAuthor">Written by <i>' . $this->user->getUserName($rs->getField('newsAuthor')) . '</i></p>';
            echo '</div>';
        }
    }

    public function getPreparedString($str)
    {
        $str = strip_tags($str);
		$str = str_replace('[b]','<b>',$str);
		$str = str_replace('[/b]','</b>',$str);
		$str = str_replace('[i]','<i>',$str);
		$str = str_replace('[/i]','</i>',$str);
		$str = str_replace('[u]','<u>',$str);
		$str = str_replace('[/u]','</u>',$str);
        $str = str_replace('[list]','<ul>',$str);
        $str = str_replace('[/list]','</ul>',$str);
        $str = str_replace('[item]','<li>',$str);
        $str = str_replace('[/item]','</li>',$str);
        $str = preg_replace("!\\[url=(.*?)\\](.*?)\\[/url\\]!i", "<a target=\"_blank\" href=\"\\1\">\\2</a>", $str);
        return $str;
    }
}
?>


As you all can see, it's really one big mess, even with such a small example. However this can be cleaned up a lot by implementing the use of templates, which, for example, means that my News class could just return the result array, and let the presentation logic be in a News_Template.tpl.

However, I'm not sure how to reach the end-result that I want. All pages will have the same basic layout, but specific styles may be needed for each page. Could I directly from the main template do something like "{ include file="calendar.tpl" }", which fetches data from the action file calendar.php, whenever I want to include my calendar on the current page?

I hope I'm at least making a little bit of sense. Because I think I just got lot inside my own head from trying to explain my question :roll:

/ Fredrik