Advice needed for MVC, FrontController, etc
Moderator: General Moderators
Advice needed for MVC, FrontController, etc
Hi,
I'm new to these forums but have been working on a PHP framework for myself to use in various projects for a while now.
Until now I've been working with a SugarBean style OO framework but it's never seemed to do things the way I wanted.
I've been reading various threads on these forums regarding the MVC Pattern and FrontController and this looks like exactly what I was looking for.
I have created a basic setup now using examples from these forums (in particular Arborint's FrontController) and it is working quite well.
I was hoping that someone here could elaborate a little more about Views and Models for me in terms of their usage/structure in a PHP app.
I sort of understand the basic principles of these but am unsure of how to implement them for maximum reusability.
Any advice would be appreciated.
Thanks
I'm new to these forums but have been working on a PHP framework for myself to use in various projects for a while now.
Until now I've been working with a SugarBean style OO framework but it's never seemed to do things the way I wanted.
I've been reading various threads on these forums regarding the MVC Pattern and FrontController and this looks like exactly what I was looking for.
I have created a basic setup now using examples from these forums (in particular Arborint's FrontController) and it is working quite well.
I was hoping that someone here could elaborate a little more about Views and Models for me in terms of their usage/structure in a PHP app.
I sort of understand the basic principles of these but am unsure of how to implement them for maximum reusability.
Any advice would be appreciated.
Thanks
Its hard to elaborate on the Model as that is almost completely dependant on your application. Some people will consider their database access as part of the model; others consider the database access as its own seperate layer. Many php applications use a very simple model, almost entirely data transfer objects (very little business logic, just get/setter) with a lot of the business logic residing in the page controllers. If you've moved to the front controller system, then the business logic migrates to either the model or the Command hierarchy depending on the complexity of your application, in my opinion. (Simple applications often find it easier to place it in the Command, more complex applications push it into the model.) The way I make the decision is based on how many Commands need to know about the business logic -- once you pass 2 the duplication is too much and if you can use command chains or subclassing to remove the duplication into the model it goes.
Of course as you move business logic into the model, the database access issue starts to beome more involved and you'll see a possible evolution Active Records or [Row|Table]Gateways into DataMappers, etc. This evolution is also affected by the granuality of your object model -- if you have lots of smaller cooperating classes the push for data mapping becomes greater.
In my experience if your application is mainly of the "List lots of stuff" "show details of stuff" with some simple administration pages you'll do fine with the simpler options for everything -- RowGateways or ActiveRecords, most business logic in the command, etc. If you start to have more complex user-interactions then it'll push for the more complex options -- normally I find that the demands for either administration activities or "power users" are what will push the decisions more than the regular operations.
For the View its often a choice between using an explicit Templating Engine, an HTML generator, or php as a templating engine. Of these the HTML generator is probably the least popular by far. The other twwo are both very popular and it comes down to personal preference.
Lately I've been using Smarty, thus my View component is split between an almost trivial View class in PHP and the Smarty templates.
My View class is simply
It has some minor error handling to deal with missing templates and otherwise handles all the assignments of PHP variables to Smarty template variables using a convetion that the Command will stick whatever data is required by the View into the context's data store. The CIB_Smarty class references in the View class is the simple subclass of the third-party Smarty class with all the directories configured, following the Smarty Best Practices document.
Of course as you move business logic into the model, the database access issue starts to beome more involved and you'll see a possible evolution Active Records or [Row|Table]Gateways into DataMappers, etc. This evolution is also affected by the granuality of your object model -- if you have lots of smaller cooperating classes the push for data mapping becomes greater.
In my experience if your application is mainly of the "List lots of stuff" "show details of stuff" with some simple administration pages you'll do fine with the simpler options for everything -- RowGateways or ActiveRecords, most business logic in the command, etc. If you start to have more complex user-interactions then it'll push for the more complex options -- normally I find that the demands for either administration activities or "power users" are what will push the decisions more than the regular operations.
For the View its often a choice between using an explicit Templating Engine, an HTML generator, or php as a templating engine. Of these the HTML generator is probably the least popular by far. The other twwo are both very popular and it comes down to personal preference.
Lately I've been using Smarty, thus my View component is split between an almost trivial View class in PHP and the Smarty templates.
My View class is simply
Code: Select all
class View {
/**
* The filename, minus extension, of Smarty Template to display.
* @var string
*/
private $_smartyTemplate;
/**
* An instance of the Smarty template engine.
* @var Smarty
*/
private $_smarty;
function View() {
$this->_smartyTemplate="";
}
/**
* Loads the selected Smarty template into the View
*
* Adds some error handling in case of the selected view not existing.
* @param string $templateName the name of the Smarty Template minus extension
*/
function setSmartyTemplate($templateName) {
require_once("classes/utilities/CIB_Smarty.inc");
$this->_smarty = new CIB_Smarty();
$fullPath = CIB_BASE."smarty/templates/$templateName.tpl";
if (file_exists($fullPath)) {
$this->_smarty->assign('templateFile',$fullPath);
$this->_smartyTemplate=CIB_BASE.'smarty/templates/StandardLayout.tpl';
}
else {
$this->_smarty->assign('uriRequest',$_SERVER["REQUEST_URI"]);
$this->_smarty->assign('templateName',$templateName);
$this->_smartyTemplate=CIB_BASE."smarty/templates/InvalidTemplate.tpl";
}
}
/**
* Returns the rendered webpage
* @param Context $context Contains the request specific information
* @return string
*/
function showSmarty($context) {
$this->_smarty->assign("siteHome",SITE_URL);
$this->_smarty->assign("siteName",SITE_NAME);
$this->_smarty->assign("title",SITE_NAME.
($context->lookup("SubTitle")!=NULL ?
": ".$context->lookup("SubTitle") :
""));
/** @todo Change to an iterator pattern, paired todo */
$contextVariables = $context->getData();
if (count($contextVariables))
foreach($contextVariables as $key=>$name) {
$this->_smarty->assign($key,$name);
}
return $this->_smarty->fetch($this->_smartyTemplate);
}
}- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Well I had not heard of SugarBean before, so I looked it up. It seems it is a Data Gateway class for SugarCRM. That would essentially act as the Model in SugarCRM. You would build something similar for you own code.
A good place to start is Table Data Gateway. I use it a lot because most Data Gateways for web apps are just to one table and it makes creating a model trivial. It has an easy to understand interface of finders plus insert/update/delete and it works on a whole table. If you want it can return a Record Set or Active Records, but having it return an array keeps itereating simple in the View.
Here is a basic one that works for MySQL and Postgres. It has some basic query builder methods, and does some best practices like escaping output. You an add more methods to a base class like this if you use them regularly, or inherit this class by a Model class that has real business logic.
A good place to start is Table Data Gateway. I use it a lot because most Data Gateways for web apps are just to one table and it makes creating a model trivial. It has an easy to understand interface of finders plus insert/update/delete and it works on a whole table. If you want it can return a Record Set or Active Records, but having it return an array keeps itereating simple in the View.
Here is a basic one that works for MySQL and Postgres. It has some basic query builder methods, and does some best practices like escaping output. You an add more methods to a base class like this if you use them regularly, or inherit this class by a Model class that has real business logic.
Code: Select all
class TableDataGateway {
var $db;
var $table = '';
var $key = '';
var $fields = '*';
var $errmsg = '';
var $where = '';
var $orderby = '';
var $limit = '';
var $sql = '';
function TableDataGateway(&$db, $table, $key='id') {
$this->db =& $db;
$this->table = $table;
$this->key = $key;
}
function findByKey($id) {
$id = $this->db->escape($id);
$this->where = '';
$this->isEqual($this->key, $id);
$allrows = $this->find();
if (isset($allrows)) {
return $allrows[0];
}
}
function & findWhere($where='', $sort='') {
if ($where) {
$where = ' WHERE ' . $where;
}
if ($sort) {
$sort = ' ORDER BY ' . $sort;
}
$this->sql = "SELECT {$this->fields} FROM {$this->table}$where$sort";
return $this->db->query($this->sql);
}
function & find() {
$allrows = array();
$where = ($this->where ? ' WHERE ' . implode(' AND ', $this->where) : '');
$orderby = ($this->orderby ? ' ORDER BY ' . implode(', ', $this->orderby) : '');
$this->sql = "SELECT {$this->fields} FROM {$this->table}$where$orderby{$this->limit}";
$result = $this->db->query($this->sql);
if ($result->isError()) {
$this->errmsg = $result->getMessage();
} else {
while ($row = $result->fetchRow()) {
$allrows[] = $row;
}
}
$this->where = '';
return $allrows;
}
function isEqual($field, $value) {
$this->where[] = "$field=" . $this->quoteValue($value);
}
function isNotEqual($field, $value) {
$this->where[] = "$field<>" . $this->quoteValue($value);
}
function isLike($field, $value) {
$this->where[] = "$field LIKE '%$value%'";
}
function isNotLike($field, $value) {
$this->where[] = "$field NOT LIKE '%$value%'";
}
function quoteValue($value) {
if (preg_match('/^[A-Z\_]*\(/', $value) == 0) { // not a function
$value = "'$value'";
}
return $value;
}
function sortOrder($orderby) {
$this->orderby[] = $orderby;
}
function limit($start, $size) {
$this->limit = " LIMIT $start, $size";
}
function update($id, $data) {
if ($id && $data) {
foreach ($data as $field => $value) {
$sets[] = $field . "='" . $this->db->escape($value) . "'";
}
$this->sql = "UPDATE {$this->table} SET " . implode(',', $sets) . " WHERE {$this->key}='$id'";
$this->db->query($this->sql);
}
}
function insert($data) {
if ($data) {
foreach ($data as $field => $value) {
$cols[] = $field;
$values[] = "'" . $this->db->escape($value) . "'";
}
$this->sql = "INSERT INTO {$this->table} (" . implode(',', $cols) . ') VALUES (' . implode(',', $values) . ')';
$this->db->query($this->sql);
}
}
function delete($id) {
if ($id) {
$this->sql = "DELETE FROM {$this->table} WHERE {$this->key}='$id'";
$this->db->query($this->sql);
}
}
}(#10850)
First of all thanks for the responses. 
nielsene - Thanks for the explaination. That helps a lot.
I understand that CIB_Smarty in your example is likely an extension of the Smarty class. The only bit I don't quite understand (and this may just be me being dumb) is the $context object that you pass to the showSmarty method.
arborint - I'm a little confused by your example. I normally work directly with ADOdb and don't recognise your db object.
nielsene - Thanks for the explaination. That helps a lot.
I understand that CIB_Smarty in your example is likely an extension of the Smarty class. The only bit I don't quite understand (and this may just be me being dumb) is the $context object that you pass to the showSmarty method.
arborint - I'm a little confused by your example. I normally work directly with ADOdb and don't recognise your db object.
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
ADOdb would be the connection object you would pass to this Gateway object. You might need to modify it a little the make the $this->db-> methods work for ADOdb. The Table Data Gateway is the next level up in abstraction. It lets you deal with arrays of data which makes it simpler to integrate with a View object as there is no need for the the View to know about the connection iterator (Record Set).Hades wrote:arborint - I'm a little confused by your example. I normally work directly with ADOdb and don't recognise your db object.
(#10850)
Yes, the CIB_Smarty is a trivial class:Hades wrote: nielsene - Thanks for the explaination. That helps a lot.
I understand that CIB_Smarty in your example is likely an extension of the Smarty class. The only bit I don't quite understand (and this may just be me being dumb) is the $context object that you pass to the showSmarty method.
Code: Select all
class CIB_Smarty extends Smarty {
function CIB_Smarty() {
$this->Smarty();
$this->template_dir = CIB_BASE.'smarty/templates/';
$this->compile_dir = CIB_BASE.'smarty/templates_c/';
$this->config_dir = CIB_BASE.'smarty/configs/';
$this->cache_dir = CIB_BASE.'smarty/cache/';
$this->caching = false;
$this->assign('app_name','CompInaBox');
}
}The Context class on the other hand is a little bit of a hodge-podge at present. Its likely to get refactored soon as I triangulate in on the different functionalities. The Context class is created relatively early in the flow through the Front Controller, currently it acts as both a "View Helper" and a Service Locater/Registry.
The View Helper role is filled by the $_data member and the lookup/store/getData member functions. This could basically be replaced by any standard HashTable/HashMap data class. Its a place for the program logic to store information that will be needed by the Smarty Template in a standard fashion.
The other features of the Context are as the locator for the reference to the database. (I'm in the process of wrapping all access to the DB through the DatabaseGateway; in time the centralDB and its associated getCentralDB function will be removed with everything going through the DatabaseGateway.) The PhraseBook was part of my first solution and will also be phased out.
You can also see the stubs in the Context class as present (in the middle of some TDD right now).
Finally, at present as you can see the Database and phrase book as created within the Context's constructor, which is bad from a testing point of view and I probably should fix that at some point.
Here's what it looks like at present:
Code: Select all
class Context {
private $_centralDB;
private $_corePB;
private $_data;
private $_gateway;
function Context() {
$this->_centralDB=&new DB(CIB_CENTRAL_DB,CIB_UNAUTH_USER, CIB_UNAUTH_PASS);
$this->_corePB=&new PhraseBook(PHRASEBOOKS."infrastructure.sql");
$this->_data=array();
$this->_gateway=& new DatabaseGateway($this->_centralDB);
}
function getUserName() {
}
function getUserPerms() {
}
function &getCentralDB() {
return $this->_centralDB;
}
function &getCorePhraseBook() {
return $this->_corePB;
}
function setGateway(&$gateway) {
$this->_gateway=& $gateway;
}
function &getGateway() {
return $this->_gateway;
}
function lookup($key) {
if (isset($this->_data[$key]))
return $this->_data[$key];
}
function store($key,&$val) {
$this->_data[$key]=& $val;
}
function getData() {
return $this->_data;
}
}Re: Advice needed for MVC, FrontController, etc
I also swear by Smarty and find the following simple guidelines very helpful:Hades wrote:I was hoping that someone here could elaborate a little more about Views and Models for me in terms of their usage/structure in a PHP app.
Infrastructure
Extension: .php
Role: Behavioral Logic; communication between model, view and controller
What it should never contain: SQL queries or HTML
Model
Extension: .class.php
Role: PHP Classes
What it should never contain: HTML
View/Controller
Extension: .tpl.php
Role: Render results; accept input
What it should never contain: SQL queries, no PHP logic except iterators and conditions
As you have noticed, it is particularly difficult to truly split view and controller functions in Web Applications because HTML both renders data and input fields. I don't have any experience with MVC outside of Web Applications, so I'm not sure what a separate view and controller looks like. I guess maybe a command line or a cell phone?
Re: Advice needed for MVC, FrontController, etc
Well, in many cases people begin to consider a large part of the "infrastructure" as part of the controller. The controller component of MVC is about responding to the user, not about rendering the user interface.tr0gd0rr wrote: As you have noticed, it is particularly difficult to truly split view and controller functions in Web Applications because HTML has both renders data and input fields. I don't have any experience with MVC outside of Web Applications, so I'm not sure what a separate view and controller looks like. I guess maybe a command line or a cell phone?
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
Re: Advice needed for MVC, FrontController, etc
This is true and I am not sure pure separation between View and Controller is required. The purest MVC puts all the dependencies into the Controller. But as long as you have clean Model / Presentation layering then you can be creative in the Presentation layer (View + Controller). I personally don't think View / Controller separation is that necessary until a certain level of complexity is reached.tr0gd0rr wrote:As you have noticed, it is particularly difficult to truly split view and controller functions in Web Applications because HTML both renders data and input fields. I don't have any experience with MVC outside of Web Applications, so I'm not sure what a separate view and controller looks like. I guess maybe a command line or a cell phone?
However, that said, in tr0gd0rr's breakdown the section Infrastructure is really the Controller and the section View/Controller is just the View. The prohibition against business logic in the View is often taken to mean no logic in the View. A useful View, in my opinion, often needs complex logic to create the output. This idea that the template system is the View is common in PHP because powerful template systems came before MVC was popular in PHP.
(#10850)
Seperation between View and Controller is absolutely required if you want to follow MVC standards..
There is a book called "Ajax by Example" that the original author should look at. They explain how MVC works very well and things you should do to prepare for writing with this standard in mind.
XP (extreme programming) has methods that counter what MVC says to use, and I've heard of a new standard (sorry, i can't remember the name exactly) that one of Zend's programmers stand by that is beginning a new course of standards. Wish I could remember it , but I was not at the conference that it was explain more ... It was the conference in Florida just a few weeks ago.
Anyways, the point is that each standard has it's positives and negatives. You should learn the one that best fits your needs and follow it's standards solidly. If you find yourself bending the rules, you may want to just forget it and move on to the next standard.
The point of this action is because whatever standard it is you choose, it should be the standard for you and everyone else who develop in your code base. This method not only makes development easier, but it also gives you and your organization a set of guidelines that every developer should adheir to. This makes for a universal understanding of each other's code, and a much easier enviornment for a new comer to jump into without screwing up any existing code out there by them (the new comer) implimenting his/her own standards.
It's just good coding practice.
But back to the topic at hand.
MVC, again, is a very powerful standard. The point of it is to break down server side code (php), your model (or DB/files/whatever you store data with), and the UI (javascript, xhtml, whatever) and seperate them from each other. And then the most important part is to seperate the UI design from the back-end programming code.
For instance. Using MVC, one could easily create a pure HTML page with very, VERY minimum php at all included in the file (aside from some includes here and there). Instead, the designer would use a combination of CSS, HTML, and maybe some javascript to create the page.
The developer would then create universal functions that are not laid out specifically for one page, but for all pages.
IE : he/she would create a function that returns a ruleset, an array set, or some other form of data that could be used not only on 1 page, but all pages within the app.
Anyways, this discussion requires so much more indepth commentating that it is best just to buy a book that explains this more such as "Ajax By Example" as i stated above. Hope this helps, and good luck..
There is a book called "Ajax by Example" that the original author should look at. They explain how MVC works very well and things you should do to prepare for writing with this standard in mind.
XP (extreme programming) has methods that counter what MVC says to use, and I've heard of a new standard (sorry, i can't remember the name exactly) that one of Zend's programmers stand by that is beginning a new course of standards. Wish I could remember it , but I was not at the conference that it was explain more ... It was the conference in Florida just a few weeks ago.
Anyways, the point is that each standard has it's positives and negatives. You should learn the one that best fits your needs and follow it's standards solidly. If you find yourself bending the rules, you may want to just forget it and move on to the next standard.
The point of this action is because whatever standard it is you choose, it should be the standard for you and everyone else who develop in your code base. This method not only makes development easier, but it also gives you and your organization a set of guidelines that every developer should adheir to. This makes for a universal understanding of each other's code, and a much easier enviornment for a new comer to jump into without screwing up any existing code out there by them (the new comer) implimenting his/her own standards.
It's just good coding practice.
But back to the topic at hand.
MVC, again, is a very powerful standard. The point of it is to break down server side code (php), your model (or DB/files/whatever you store data with), and the UI (javascript, xhtml, whatever) and seperate them from each other. And then the most important part is to seperate the UI design from the back-end programming code.
For instance. Using MVC, one could easily create a pure HTML page with very, VERY minimum php at all included in the file (aside from some includes here and there). Instead, the designer would use a combination of CSS, HTML, and maybe some javascript to create the page.
The developer would then create universal functions that are not laid out specifically for one page, but for all pages.
IE : he/she would create a function that returns a ruleset, an array set, or some other form of data that could be used not only on 1 page, but all pages within the app.
Anyways, this discussion requires so much more indepth commentating that it is best just to buy a book that explains this more such as "Ajax By Example" as i stated above. Hope this helps, and good luck..
- Christopher
- Site Administrator
- Posts: 13596
- Joined: Wed Aug 25, 2004 7:54 pm
- Location: New York, NY, US
While I agree that MVC specifies a separation between the View and Controller, I have found that there is not much agreement as to exactly what that separation is. My point was actually not that MVC does not have VC separation -- it was that you may not need or want VC separation (an hence not MVC).
I think the book you mention is called "Ajax In Action" and it does have a lot of excellent information about implementing MVC both client side and server side. It also has its own opinion about what MVC means.
MVC is a pattern, not a standard as you state. It is a high level pattern (of which there are fewer) and in my opinion defines some separations and possible dependencies, but little in the way of actual implementation. Unfortuantely much of the history of MVC web implementations is in Java, and PHP ends up being different. Only recently have enough PHP MVC implementations existed to give much direction on implementatiing it in PHP.
Also, MVC does not separate the PHP from the HTML as you seem to imply. There is often PHP in the View.
Finally, you say, "XP (extreme programming) has methods that counter what MVC says to use..." I really don't understand that because XP is a methology and MVC is a pattern. They really have very little to do with each other and I can't see how XP is counter to MVC in any way.
I think the book you mention is called "Ajax In Action" and it does have a lot of excellent information about implementing MVC both client side and server side. It also has its own opinion about what MVC means.
MVC is a pattern, not a standard as you state. It is a high level pattern (of which there are fewer) and in my opinion defines some separations and possible dependencies, but little in the way of actual implementation. Unfortuantely much of the history of MVC web implementations is in Java, and PHP ends up being different. Only recently have enough PHP MVC implementations existed to give much direction on implementatiing it in PHP.
Also, MVC does not separate the PHP from the HTML as you seem to imply. There is often PHP in the View.
Finally, you say, "XP (extreme programming) has methods that counter what MVC says to use..." I really don't understand that because XP is a methology and MVC is a pattern. They really have very little to do with each other and I can't see how XP is counter to MVC in any way.
(#10850)
Hello,
I used them for few commercial sites and it's working fine.
If you would like to look on my MVC-solution then visit project site on:
http://www.jetfish.net
--
Regards,
Juliusz
I have been already witten one of MVC framework for PHP.Hades wrote:I think I've finally got the theory into my head. As I mentioned, I'm working on a framework for some personal sites and things are going slow but steady now.
I used them for few commercial sites and it's working fine.
If you would like to look on my MVC-solution then visit project site on:
http://www.jetfish.net
--
Regards,
Juliusz
- CoderGoblin
- DevNet Resident
- Posts: 1425
- Joined: Tue Mar 16, 2004 10:03 am
- Location: Aachen, Germany
OK I know I am late but I found this link useful when looking at MVC:
Understanding MVC in PHP by Joe Stump.
Understanding MVC in PHP by Joe Stump.
Standard and Pattern are loose terms in general. If you use, what you call a Pattern, as the direction in which your code will be built, it actually does become a standard...
So I think both of you are correct. MVC may be a pattern, but it's also a standard. A standard is a set of patterns you use to create code.
So I think both of you are correct. MVC may be a pattern, but it's also a standard. A standard is a set of patterns you use to create code.