Difference between revisions of "6CC001 Workshop - week 04"

From mi-linux
Jump to navigationJump to search
Line 298: Line 298:
 
</pre>
 
</pre>
  
== Checkpoint ==
+
=== Checkpoint ===
  
 
Your website should now display "Blog Index command".
 
Your website should now display "Blog Index command".
Line 449: Line 449:
 
</pre>
 
</pre>
  
= Checkpoint =
+
=== Checkpoint ===
  
 
Your website should now be displaying the data from the database in the form an an array:
 
Your website should now be displaying the data from the database in the form an an array:

Revision as of 16:02, 27 July 2009

Main Page >> Web Application Development >> Workbook >> Week 04

Step 1 - Create and populate database

CREATE TABLE IF NOT EXISTS `messages` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `message` text NOT NULL,
  `date_added` datetime NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=19 ;

INSERT INTO `messages` (`id`, `title`, `message`, `date_added`) VALUES
(1, 'Test 1', 'Hello world!', '2009-07-27 15:06:29'),
(2, 'Test 2', 'Hiya', '2009-07-27 15:06:38');

Step 2 - Create the index.php file, front controller and registry class

Our index file is very simple:

// The index simply calls the front controller
require("Controller.php");
Controller::run();

Next, let's create our Front Controller

require_once("Registry.php");

//############################################################################
// Front Controller class
//############################################################################
class Controller
{
  private function __construct(){}
  
  //############################################################################
  // Main "run" function
  //############################################################################    
  static function run()
  {
    $instance = new Controller;
    $instance->init();
    $instance->handleRequest();
  }
  
  //############################################################################
  // Init config stuff, e.g. database location, etc.
  //############################################################################   
  function init()
  {
    Registry::set("database_dsn", "mysql:host=localhost;dbname=YOURDBNAME");
    Registry::set("database_login", "YOURUSER");
    Registry::set("database_password", "YOURPASSWORD");
  }
  
  //############################################################################
  // Handles request from user
  //############################################################################   
  function handleRequest()
  {
    print("Hello world!");
  }
}

We also need the Registry class:

//############################################################################
// Registry class, used to store values to be used across whole application
//############################################################################
class Registry
{
  private static $values = array();
  
  //############################################################################
  // Store a value
  //############################################################################    
  static function set($key, $val)
  {
    self::$values[$key] = $val;
  }
  
  //############################################################################
  // Get a value
  //############################################################################    
  static function get($key)
  {
    if(isset(self::$values[$key]))
    {
      return self::$values[$key];
    }
    return null;
  }
  
}

Checkpoint

Your page should display "Hello world!"

Step 3 - Handling user request and created appropriate command

We need a Request class:

//############################################################################
// Request class
//############################################################################
class Request
{
  private $properties;

  //############################################################################
  // constructor
  //############################################################################ 
  function __construct()
  {
    $this->init();
  }
  
  //############################################################################
  // Gets all parameters from GET, POST etc...
  //############################################################################   
  function init()
  {
    $this->properties = $_REQUEST;
    return;
  }
  
  //############################################################################
  // Functions to "set" and "get" properties
  //############################################################################   
  function getProperty($key)
  {
    if(isset($this->properties[$key]))
    {
      return $this->properties[$key];
    }
  }
  function setProperty($key, $val)
  {
    $this->properties[$key] = $val;
  }
}

Next, the CommandResolver class:

require_once("Command.php");
require_once("Command_BlogIndex.php");

//############################################################################
// CommandResolver class
//############################################################################
class CommandResolver
{
  private static $base_cmd;
  private static $default_cmd;
  
  //############################################################################
  // Constructor
  //############################################################################    
  function __construct()
  {
    if(!self::$base_cmd)
    {
      // To check if requested command is a valid command
      self::$base_cmd = new ReflectionClass("Command");
      
      // Default command to be returned if an error occurs
      self::$default_cmd = new Command_BlogIndex();
    }
  }
  
  //############################################################################
  // getCommand creates the appropriate command class, 
  // depending on parameter passed in URL
  //############################################################################    
  function getCommand(Request $request)
  {
    // Get "cmd" parameter from request
    $cmd = $request->getProperty('cmd');

    // If no command passed in, use default one    
    if(!$cmd)
      return self::$default_cmd;
      
    $filepath = "Command_{$cmd}.php";
    $classname = "Command_{$cmd}";
    
    // Check if file containing command class exists
    if(file_exists($filepath))
    {
      require_once($filepath);
      
      // Check if command class exists
      if(class_exists($classname))
      {
        $cmd_class = new ReflectionClass($classname);
        
        // Check of class is a valid command
        if($cmd_class->isSubClassOf(self::$base_cmd))
        {
          // Return new instance of command
          return $cmd_class->newInstance();
        }
      }
    }
    
    // Couldn't find command, return default command
    return clone self::$default_cmd;
  }
}

Our parent Command class:

//############################################################################
// Command class
//############################################################################
abstract class Command
{
  // Make constructor final so sub-classes can't add parameters to it
  final function __construct(){}
  
  //############################################################################
  // Execute command
  //############################################################################   
  function execute(Request $request)
  {
    // General setup for all commands
    // ...
    
    // Then call specific command code
    $this->doExecute($request);
  }
  
  // Specific command code, to be implemented by each sub-class
  abstract function doExecute(Request $request);
}

And our first command: the Command_BlogIndex class

//############################################################################
// SearchVenueCommand class
//############################################################################
class Command_BlogIndex extends Command
{
  //############################################################################
  // doExecute
  //############################################################################ 
  function doExecute(Request $request)
  {
    print("Blog Index command");
  }
}

Let's update our Controller class. We now read the request from the user, and use the CommandResolver object to create a Command.

Add these at the top of the file:

require_once("Request.php");
require_once("CommandResolver.php");

And implement the handleRequest function:

  //############################################################################
  // Handles request from user
  //############################################################################   
  function handleRequest()
  {
    // Create object that handles request from user
    $request = new Request();
    
    // Create object that decides which command to create depending on request
    $cmd_r = new CommandResolver();
    
    // This function return the appropriate command
    $cmd = $cmd_r->getCommand($request);
    
    // Execute command
    $cmd->execute($request);
  }

Checkpoint

Your website should now display "Blog Index command".

Step 4 - Accessing the database

First let's create our Manager class:

require_once("Registry.php");

//############################################################################
// Manager class
//############################################################################
abstract class Manager
{
  static $DB;
  static $stmts = array();
  
  //############################################################################
  // Constructor
  //############################################################################   
  function __construct()
  {
    // Get DB details from registry
    $database_dsn = Registry::get("database_dsn");
    $database_login = Registry::get("database_login");
    $database_password = Registry::get("database_password");
    
    // Create connection
    $pdo = new PDO($database_dsn, $database_login, $database_password);
    self::$DB = $pdo;

    // Set connection attributes    
    self::$DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }

  //############################################################################
  // prepareStatement: prepares anc caches SQL statement
  //############################################################################   
  function prepareStatement($stmt_s)
  {
    // Check if statement is already in cache
    if(isset(self::$stmts[$stmt_s]))
    {
      return self::$stmts[$stmt_s];
    }
    
    // Prepare stement, store in cache and return
    $stmt_handle = self::$DB->prepare($stmt_s);
    self::$stmts[$stmt_s] = $stmt_handle;
    return $stmt_handle;
  }
  
  //############################################################################
  // doStatement: runs SQL statement
  //############################################################################   
  protected function doStatement($stmt_s, $values_a)
  {
    $sth = $this->prepareStatement($stmt_s);
    
    // Closes the cursor, enabling the statement to be executed again.
    $sth->closeCursor();
    
    // Runs the statement
    $db_result = $sth->execute($values_a);
    return $sth;
  }
}

Next, our ManagerMessage class:

require_once("Manager.php");

//############################################################################
// ManagerMessage class
//############################################################################
class ManagerMessage extends Manager
{
  static $add_message = "INSERT INTO messages(title,message,date_added) values(?,?,?)";
  static $list_messages = "SELECT id,title,message,date_added FROM messages ORDER BY date_added DESC";
  static $search_messages = "SELECT id,title,message,date_added FROM messages WHERE title LIKE ? ORDER BY date_added DESC";
  static $delete_message = "DELETE FROM messages WHERE id = ?";
  
  //############################################################################
  // addMessage: adds a message to the database
  //############################################################################ 
  function addMessage($title, $message)
  {
    $values = array($title, $message, date("Y/m/d H:i:s"));
    $this->doStatement(self::$add_message, $values);
  }
  
  //############################################################################
  // getAllMessages: selects all messages from the database
  //############################################################################ 
  function getAllMessages()
  {
    $stmt = $this->doStatement(self::$list_messages, null);
    $result = $stmt->fetchAll();
    return $result;
  }
  
  //############################################################################
  // searchMessages: searches for a message
  //############################################################################ 
  function searchMessages($keywords)
  {
    $values = array($keywords);
    $stmt = $this->doStatement(self::$search_messages, $values);
    $result = $stmt->fetchAll();
    return $result;
  }
  
  //############################################################################
  // deleteMessage: deletes a message
  //############################################################################ 
  function deleteMessage($id)
  {
    $values = array($id);
    $stmt = $this->doStatement(self::$delete_message, $values);
  }  
  
}
<pre>

Now we can implement the doExecute function in Command_BlogIndex, so it call our database classes:

<pre>
  //############################################################################
  // doExecute
  //############################################################################ 
  function doExecute(Request $request)
  {
    // Create manager object
    $manager = new ManagerMessage();
    
    // Are we searching?
    $keywords = trim($request->getProperty('search'));
    
    if($keywords=="")
      $data = $manager->getAllMessages();
    else
      $data = $manager->searchMessages($keywords);
      
    print_r($data);
  }

Checkpoint

Your website should now be displaying the data from the database in the form an an array:

Array ( [0] => Array ( [id] => 18 [0] => 18 [title] => Test 2 [1] => Test 2 [message] => Hiya [2] => Hiya [date_added] => 2009-07-27 15:06:38 [3] => 2009-07-27 15:06:38 ) [1] => Array ( [id] => 17 [0] => 17 [title] => Test 1 [1] => Test 1 [message] => Hello world! [2] => Hello world! [date_added] => 2009-07-27 15:06:29 [3] => 2009-07-27 15:06:29 ) )

Step 5 - Create our first view

All we need now is to pass the data above to a view. Let's create all our views in a "views" sub-folder.

<?require_once("view_helper.php");?>

<?ViewHelper::DisplayHeader("Blog index");?>

<h1>Blog index</h1>

<p>You can <a href="index.php?cmd=BlogAddForm">add messages</a> !</p>

<form action="index.php" method="post">
  Search: <input name="search"> 
  <input type="submit" value="Search!"> 
</form>

<?
  foreach($data as $message)
    echo("<div class=\"message\">
            <h2>{$message['title']}</h2>
            <p>{$message['message']}</p>
            <a href=\"index.php?cmd=BlogDelete&id={$message['id']}\">Delete this message</a>
          </div>");
?>

<?ViewHelper::DisplayFooter();?>

As you can see, we have chosen to use a helper:

//############################################################################
// ViewHelper
//############################################################################
class ViewHelper
{
  //############################################################################
  // Displays HTML Header
  //############################################################################ 
  static function DisplayHeader($pageTitle = "")
  {
    echo("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
    echo("<html>");
    echo("  <head>");
    echo("    <title>{$pageTitle}</title>");
    echo("    <link type=\"text/css\" href=\"views/style.css\" rel=\"stylesheet\">");
    echo("  </head>");
    echo("  <body>");
  }
  
  //############################################################################
  // Displays HTML Footer
  //############################################################################   
  static function DisplayFooter()
  {
    echo("  </body>");
    echo("</html>");  
  }
}

All you need now is to include your view from your command:

  //############################################################################
  // doExecute
  //############################################################################ 
  function doExecute(Request $request)
  {
    // Create manager object
    $manager = new ManagerMessage();
    
    // Are we searching?
    $keywords = trim($request->getProperty('search'));
    
    if($keywords=="")
      $data = $manager->getAllMessages();
    else
      $data = $manager->searchMessages($keywords);
      
    // Include view
    include("views/index.php");
  }