Having read the article on The MVC Pattern and PHP, I decided to try and write a bit of code using MVC. I'll add my code at the bottom of the article, if anyone wants to look through it and tell me everything I'm doing wrong, that would be great!

The code is very simple so far, it is just a form with a checkbox and a textbox for a date. When submitted the controller pushes the submitted data to the model, which stores it in a property $data ($formData might actually be a better description). The model also validates the date input. Then the view just displays the page again, including any error messages.

Particular questions I have are:
  • If the data formatting is performed in the template, what purpose does the view have? Or should the view be doing the formatting, e.g. should it be one view calling different templates depending on the wanted data format (HTML, JSON, XML etc.), or should it be a different view for each format?
  • According to the article
    The View is really much more than just a template
    , but this is not really expanded on. The only info I could find on anything else the view does is that it might cache the output. What else can the view be used for?
  • I am a bit confused on whether the controller should call the view or not. The article indicates it shouldn't, but if the controller handles user interactions, then isn't one of those user interactions requesting the view (web page)?
  • In my code I am copying the data from the model to the view, and the view implements methods for the template to access the data. Is this correct, or should the data stay in the model?


Thanks

Dave

The page:
PHP Code:
<?php
include('globalFuncs.inc.php');
$imageLicenseModel = new imageLicenseModel($conn);
$imageLicenseController = new imageLicenseController($imageLicenseModel);
if(isset(
$_POST['submit'])){
    
$imageLicenseController->process($_POST);
}
$imageLicenseView = new imageLicenseView($imageLicenseModel);
$title="License an image";
$metaDescription 'Purchase a license to use a photo on a website or in print';
$h1="<h1>$title</h1>";
include(
'pageTop.php');
$imageLicenseView->process('imageLicenseTemplateHTML.php');
include(
'pageBottom.php');
Controller:
PHP Code:
class imageLicenseController{

    
/**
     *Creates an instance of the imageLicenseController class
     *@param imageLicenseModel $imageLicenseModel The $imageLicenseModel to pull data from
     */
    
public function __construct(&$imageLicenseModel){
        
$this->_imageLicenseModel = &$imageLicenseModel;
    }

    
/**
     *Passes the data to the model for processing
     *If data okay pushes the data to paypal for payment processing
     */
    
public function process($data){
        
//pass the data to the model for processing
        
if($this->_imageLicenseModel->setData($data)){
            
//if success post on to paypal
        
}
    }

View:
PHP Code:
<?php
class imageLicenseView{

    
//The model
    
protected $_imageLicenseModel;
    
//The data (retrieved from the model)
    
protected $_data;
    
//The errors (retrieved from the model)
    
protected $_errors;

    
/**
     *constructs an instance of the imageLicenseView object
     *@param imageLicenseModel $imageLicenseModel The $imageLicenseModel to pull data from
     */
    
public function __construct(&$imageLicenseModel){
        
$this->_imageLicenseModel = &$imageLicenseModel;
    }
    
    
/**
     *Returns the value of a named data property
     *@param string $property The name of the property to get the value of
     *@return mixed The value of the property
     */
    
public function get($property){
        if(isset(
$this->_data[$property])){
            return 
$this->_data[$property];
        }
    }
    
    
/**
     *Returns the value of a named error property
     *@param string $property The name of the property to get the value of
     *@return string The value of the property
     */
    
public function getError($property){
        if(isset(
$this->_errors[$property])){
            return 
$this->_errors[$property];
        }
    }    
    
    
/**
     *Includes the specified template
     *@param string $template The template file to include
     */
    
public function process($template){
        
$this->_data $this->_imageLicenseModel->getData();
        
$this->_errors $this->_imageLicenseModel->getErrors();
        include(
$template);
    }
}
Model:
PHP Code:
<?php
class imageLicenseModel{

    
//holds the db connection
    
protected $_conn;
    
//holds the data
    
protected $_data=array();
    
//holds any errors that occur
    
protected $_errors=array();

    
    
/**
     *constructs an instance of the imageLicenseModel object
     *@param mysqli $conn The database connection
     */
    
public function __construct(&$conn){
        
$this->_conn = &$conn;
    }
    
    
    
/**
     *Gets the data
     *@return array The data
     */
    
public function getData(){
        return 
$this->_data;
    }
    
    
    
/**
     *Sets and validates the provided data
     *@param array $data The data to set
     *@return bool Whether the data was updated or not
     */
    
public function setData($data){
        
$this->_data $data;
        
//validate
        
if($this->_validate($data)){
            
//update the db
        
}
        if(!empty(
$this->_errors)){
            return 
false;
        }
        return 
true;
    }
    
    
    
/**
     *Validates the provided data
     *@param array $data The data to validate
     *@return bool Whether the data validated or not
     */
    
protected function _validate($data){
        
//date-start
        //It would be nice to validate the date by using the DateTime object's inbuilt validation, but if a date is invalid this will throw a Fatal exception rather than a lower exception level - http://stackoverflow.com/questions/11343403/php-exception-handling-on-datetime-object
        
if(strlen($data['date-start']) != 10 ||
           ((
$dateSplit explode('-'$data['date-start'])) && count($dateSplit) != 3) ||
           !
is_numeric($dateSplit[0]) ||  !is_numeric($dateSplit[1]) || !is_numeric($dateSplit[2]) ||
           !
checkdate($dateSplit[1], $dateSplit[2], $dateSplit[0]) ||
           
date('Y-m-d'strtotime($data['date-start'])) != $data['date-start']){
            
$this->_errors['date-start']='Date must be a valid date in format YYYY-MM-DD';
        }
        elseif(
strtotime($data['date-start']) < mktime (0,0,0)){
            
$this->_errors['date-start']='Start date cannot be in the past';
        }
        if(!empty(
$this->_errors)){
            return 
false;
        }
        return 
true;
    }
    
    
/**
     *Gets the errors
     *@return array The errors
     */
    
public function getErrors(){
        return 
$this->_errors;
    }
}
Template:
PHP Code:
<?php
//If there were any errors, print them out above the form
if(!empty($this->_errors)){
    
?><div class="msgBox error"><p>The request could not be processed, the following errors occurred:</p>
    <ul>
        <?php foreach ($this->_errors as $error){
            echo 
"<li>$error</li>";
        }
?>
    </ul></div><?php
}?>
<form action="" method="post">
<ul>
<li><label for="non-commercial">Non commercial use</label><input type="checkbox" name="non-commercial" value="1" <?php if($this->get('non-commercial')){echo 'checked="checked"';} ?> /><span class="info">Please note that usage on a page that contains adverts constitutes commercial use.</span></li>
<li class="<?php if($this->getError('date-start')){echo 'error';}?>"><label for="date-start">License start date (YYYY-MM-DD)</label><input type="date" id="date-start" name="date-start" min="<?php echo gmdate('Y-m-d'); ?>" value="<?php echo $this->get('date-start');?>" /></li>
</ul>
<div><input type="submit" value="submit" name="submit" /></div>
</form>