Difference between revisions of "Workshop - week 02"

From mi-linux
Jump to navigationJump to search
Line 1: Line 1:
 
[[Main Page]] >> [[Web Frameworks]] >> [[Web Frameworks - Workbook]] >> Workshop - week 02
 
[[Main Page]] >> [[Web Frameworks]] >> [[Web Frameworks - Workbook]] >> Workshop - week 02
  
===Setting Up Our Default Controller and View===
+
===Setting Up Our Default Controller===
  
Next we need to set up our [http://framework.zend.com/docs/quickstart/create-an-action-controller-and-view default controller and view]. As we are using the Zend Framework defaults, the controller class only requires skeleton code.
+
====Create an Action Controller====
  
===Error Controller and View===
+
In Zend Framework's MVC implementation, both the default action controller and the default action are named 'index'. You should therefore set up the default action controller by creating application/controllers/IndexController.php with the following contents:
  
Because of the change we made by making all requests return the default view, essentially we remove the need for the ErrorHandler that is handling EXCEPTION_NO_CONTROLLER. However, it is good practice to set up the respective error handlers, and so we will continue to [http://framework.zend.com/docs/quickstart/create-an-error-controller-and-view follow the tutorial].
+
<source lang="php">
 +
<?php
 +
// application/controllers/IndexController.php
  
===4. Creating the Layout===
+
/**
 
+
* IndexController is the default controller for this application
We can think of the [http://framework.zend.com/docs/quickstart/create-a-layout layout page] as a template that defines a consistent look and feel for all pages within the MVC application. The controllers provide views that fill in what is unique about a particular page. Combined together, they provide us with a complete HTML/XHTML page. As the tutorial suggests, this is the reason why the layout is a good place to set up headers and footers that we want to remain the same for all our pages.
+
*
 
+
* Notice that we do not have to require 'Zend/Controller/Action.php', this
===5. Configuration and Registry===
+
* is because our application is using "autoloading" in the bootstrap.
 
+
*
In older web programming methodologies, programmers would often create a file to be 'included' by all pages that needed access to database connections. This had many disadvantages, such as providing all the code for database connectivity often to every page, even when a database connection was not required for the page. There was also the security considerations of having a file with all the database connection details in a location where it may be accessible by anonymous users.
+
* @see http://framework.zend.com/manual/en/zend.loader.html#zend.loader.load.autoload
 
+
*/
A more modern take on the concept is to have a [http://framework.zend.com/docs/quickstart/create-a-configuration-and-registry registry / configuration file], and classes to provide access to the data. It works much like an .ini file for desktop applications - providing a place to store application-wide settings, such as database connection strings. It works much like a hashtable, where data can be retrieved by key name. [http://framework.zend.com/docs/quickstart/create-a-configuration-and-registry We need to create] our app.ini file, and modify the boostrap.php file to include a reference to the app.ini. In the case of our QuickStart application, we will then be able to access the database settings from any page we choose.
+
class IndexController extends Zend_Controller_Action
 
+
{
===6. Model and Database===
+
    /**
 
+
    * The "index" action is the default action for all controllers. This  
See [http://framework.zend.com/docs/quickstart/create-a-model-and-database-table here].
+
    * will be the landing page of your application.
 
+
    *
====Database====
+
    * Assuming the default route and default router, this action is dispatched
 
+
    * via the following urls:
=====Database Connection=====
+
    *  /
 +
    *  /index/
 +
    *  /index/index
 +
    *
 +
    * @return void
 +
    */
 +
    public function indexAction()
 +
    {
 +
        /*
 +
          There is nothing inside this action, but it will still attempt to  
 +
          render a view. This is because by default, the front controller
 +
          uses the ViewRenderer action helper to handle auto rendering
 +
          In the MVC grand scheme of things, the ViewRenderer allows us to  
 +
          draw the line between the C and V in the MVC. Also note this action
 +
          helper is optional, but on default.
 +
        */  
 +
    }
 +
}
 +
</source>
  
Next, we need somewhere to store our data. We modify the app.ini file to include the database connection details (We are setting up a SQLite database - it is not a database you will necessarily be familiar with. Rather than having a database program running on a server, SQLite can have an entire database stored within a file - like using an XML file, but with all the benefits of SQL queries.)
+
Because we're using the standard router, requests for http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public will be routed to the indexAction method on the IndexController action controller, as will requests to http://http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public/index and http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public/index/index.
  
We then need to modify the bootstrap.php file, to include the new connection details to the database we are about to create.
+
====Controller and Action Name Conventions====
  
=====Database Schema=====
+
To work correctly with the standard router, action method names should follow the lowerCamelCase convention with the suffix 'Action'. Action controller classes, on the other hand, should follow the UpperCamelCase convention with the suffix 'Controller'. While the standard router should work for almost all applications, routing in Zend Framework is completely customizable. See the Reference Guide for details.
  
We then create two SQL script files - one that defines the structure of the database that we want to set up, and another with some initial test data to be inserted into the new database.
+
It may seem like indexAction() is doing nothing at all, but again Zend Framework is doing useful things for you behind the scenes. In this case it is routing the request to the appropriate view to return the correct response. Unless otherwise configured, the view it will look for is views/scripts/index/index.phtml, where the views directory is contained in the same folder as the controllers directory you passed to Zend_Controller_Front::run(). The file extension for views is yet another best practice recommended for Zend Framework applications; since view templates look suspiciously like PHP scripts in ZF, it is easier to distinguish view templates from other PHP files if you use the .phtml extension instead of .php.
  
=====Setup Script=====
+
===Error Controller===
  
This PHP file will be run just once, and will create our database by executing the first script, and fill it with data as outlined by our second script. Once scripts/load.sqlite.php has been executed successfully, the file can be deleted - but don't do so until you have a fully working application, in case you need to execute the database setup again.
+
Because of the change we made by making all requests return the default view, essentially we remove the need for the ErrorHandler that is handling EXCEPTION_NO_CONTROLLER. However, it is good practice to set up the respective error handlers, and so we will continue.
  
'''Pro Tip:''' Remember that the directories that the database is being set up within - data/db/ - need to have the correct permissions set. At this stage, CHMOD it to 777 (read, write and execute for all) to help get the application running.
+
Create an Error Controller & View
  
====Coding====
+
The last things you want to display to an end user of your site are either a blank page or exceptions. Zend Framework's MVC provides an ErrorHandler plugin that will detect exceptions and dispatch an ErrorController so that you can easily display appropriate content to your end user.
  
=====Data Connector=====
+
Additionally, you need some way to handle 404 errors- i.e., situations when the action or controller are not found.
  
Now we need a small class to define how our normal database queries (update, insert, delete) should be handled. In our system, we only require an insert method, as we'll be only allowing inserts into the guestbook. However, to follow best practice we include an update method, but set it so that it will throw an exception error if the update is called. Our insert method with automatically add the date and time of the insert to the database.
+
In development, you do want to see exceptions, however. Fortunately, ZF MVC can accomodate both situations.
  
 +
The ErrorHandler plugin by default will dispatch the errorAction() of the ErrorController. Let's build our controller.
  
=====Set-up Model File=====
+
<source lang="php">
 +
<?php
 +
// application/controllers/ErrorController.php
  
The model is what establishes business rules within our application. All methods relating to how our page does what it does should be defined here. We need to be able to retrive records from our database, so that the guestbook can display entries. It includes a function to return one entry, all entries - as well as a function that uses the insert we just set up to save new entries to the database.
+
/**
 
+
* ErrorController
We have one more line that needs changing in here:
+
*/
 
+
class ErrorController extends Zend_Controller_Action
<pre>    protected function _getGuestbookForm()
+
{
    {
+
    /**
        require_once APPLICATION_PATH . '/forms/GuestBook.php';
+
    * errorAction() is the action that will be called by the "ErrorHandler"
        $form = new Form_GuestBook();
+
    * plugin. When an error/exception has been encountered
        $form->setAction($this->_helper->url('sign'));
+
    * in a ZF MVC application (assuming the ErrorHandler has not been disabled
        return $form;
+
    * in your bootstrap) - the Errorhandler will set the next dispatchable
    }
+
    * action to come here.  This is the "default" module, "error" controller,
</pre>
+
    * specifically, the "error" action.  These options are configurable.  
 
+
    *
Should become:
+
    * @see http://framework.zend.com/manual/en/zend.controller.plugins.html
 
+
    *
<pre>
+
    * @return void
     protected function _getGuestbookForm()
+
    */
     {
+
     public function errorAction()  
         require_once APPLICATION_PATH . '/forms/GuestBook.php';
+
     {  
         $form = new Form_GuestBook();
+
         // Ensure the default view suffix is used so we always return good
         $form->setAction($this->_request->getBaseUrl() . $this->_helper->url('sign'));
+
         // content
        return $form;
+
         $this->_helper->viewRenderer->setViewSuffix('phtml');
    }
 
</pre>
 
  
=====Guestbook Controller=====
+
        // Grab the error object from the request
 +
        $errors = $this->_getParam('error_handler');
  
[http://framework.zend.com/docs/quickstart/create-a-form In our final steps], we need to create a form through which entries can be inserted.
+
        // $errors will be an object set as a parameter of the request object,
To tie all the functionality together we have just implemented, we need our guestbook controller. This will call functions in the data connector class we just completed, so that data to be saved can be passed to it, and retrieved records can be displayed through our view.
+
        // type is a property
 +
        switch ($errors->type) {
 +
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
 +
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:
  
=====Guestbook View=====
+
                // 404 error -- controller or action not found
 +
                $this->getResponse()->setHttpResponseCode(404);
 +
                $this->view->message = 'Page not found';
 +
                break;
 +
            default:
 +
                // application error
 +
                $this->getResponse()->setHttpResponseCode(500);
 +
                $this->view->message = 'Application error';
 +
                break;
 +
        }
  
And as we have our guestbook controller, we need a guestbook view. This will be used to display our guestbook records, as well as offering a link to set our guestbook action to sign, so that a record can be inserted.
+
        // pass the environment to the view script so we can conditionally
+
        // display more/less information
 +
        $this->view->env      = $this->getInvokeArg('env');
 +
       
 +
        // pass the actual exception object to the view
 +
        $this->view->exception = $errors->exception;
 +
       
 +
        // pass the request to the view
 +
        $this->view->request  = $errors->request;
 +
    }
 +
}
 +
</source>
  
===7. Form===
+
The above may seem a bit esoteric.
  
We now [http://framework.zend.com/docs/quickstart/create-a-form need a form]; somewhere our users can actually insert their entry. We create a function to initialize the class, which includes all the controls needed for the form, such as the text boxes and the captcha. As can be seen, a captcha control exists within the Zend Framework - we simply state what kind we want, how many letters should be displayed, and what the timeout is before it becomes invalid.
+
First, the ErrorHandler plugin stores error information- including the error type, exception caught, and the request that produced it- within a token that is passed in the request. We store that in the $errors variable.
  
We then modify our GuestbookController to include a sign action; providing users with a means of signing our guestbook.
+
Second, we do some switching based on the exception type. There are two types of exceptions that represent 404 errors: controller not found and action not found. For these, we change the HTTP response code to 404 to indicate this. All other exceptions are considered application exceptions, and we'll return the appropriate HTTP response code for these- 500.
  
And as we know by know - new code in the Controller giving us something new to process = new view, so we create a new view for our signing action.
+
Third, remember the env variable you passed to the front controller in the bootstrap file? We're going to get that from the front controller and pass it to the view. More on this later.
  
And with the signing view complete, we should have a fully working GuestBook application. Load it up and test it out.
+
Finally, we pass the exception and request to the view.

Revision as of 08:13, 28 January 2009

Main Page >> Web Frameworks >> Web Frameworks - Workbook >> Workshop - week 02

Setting Up Our Default Controller

Create an Action Controller

In Zend Framework's MVC implementation, both the default action controller and the default action are named 'index'. You should therefore set up the default action controller by creating application/controllers/IndexController.php with the following contents:

<source lang="php"> <?php // application/controllers/IndexController.php

/**

* IndexController is the default controller for this application
* 
* Notice that we do not have to require 'Zend/Controller/Action.php', this
* is because our application is using "autoloading" in the bootstrap.
*
* @see http://framework.zend.com/manual/en/zend.loader.html#zend.loader.load.autoload
*/

class IndexController extends Zend_Controller_Action {

   /**
    * The "index" action is the default action for all controllers. This 
    * will be the landing page of your application.
    *
    * Assuming the default route and default router, this action is dispatched 
    * via the following urls:
    *   /
    *   /index/
    *   /index/index
    *
    * @return void
    */
   public function indexAction() 
   {
       /*
          There is nothing inside this action, but it will still attempt to 
          render a view.  This is because by default, the front controller 
          uses the ViewRenderer action helper to handle auto rendering
          In the MVC grand scheme of things, the ViewRenderer allows us to 
          draw the line between the C and V in the MVC.  Also note this action
          helper is optional, but on default.
       */ 
   }

} </source>

Because we're using the standard router, requests for http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public will be routed to the indexAction method on the IndexController action controller, as will requests to http://http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public/index and http://mi-linux.wlv.ac.uk/~YourStudentNumber/QuickStart/public/index/index.

Controller and Action Name Conventions

To work correctly with the standard router, action method names should follow the lowerCamelCase convention with the suffix 'Action'. Action controller classes, on the other hand, should follow the UpperCamelCase convention with the suffix 'Controller'. While the standard router should work for almost all applications, routing in Zend Framework is completely customizable. See the Reference Guide for details.

It may seem like indexAction() is doing nothing at all, but again Zend Framework is doing useful things for you behind the scenes. In this case it is routing the request to the appropriate view to return the correct response. Unless otherwise configured, the view it will look for is views/scripts/index/index.phtml, where the views directory is contained in the same folder as the controllers directory you passed to Zend_Controller_Front::run(). The file extension for views is yet another best practice recommended for Zend Framework applications; since view templates look suspiciously like PHP scripts in ZF, it is easier to distinguish view templates from other PHP files if you use the .phtml extension instead of .php.

Error Controller

Because of the change we made by making all requests return the default view, essentially we remove the need for the ErrorHandler that is handling EXCEPTION_NO_CONTROLLER. However, it is good practice to set up the respective error handlers, and so we will continue.

Create an Error Controller & View

The last things you want to display to an end user of your site are either a blank page or exceptions. Zend Framework's MVC provides an ErrorHandler plugin that will detect exceptions and dispatch an ErrorController so that you can easily display appropriate content to your end user.

Additionally, you need some way to handle 404 errors- i.e., situations when the action or controller are not found.

In development, you do want to see exceptions, however. Fortunately, ZF MVC can accomodate both situations.

The ErrorHandler plugin by default will dispatch the errorAction() of the ErrorController. Let's build our controller.

<source lang="php"> <?php // application/controllers/ErrorController.php

/**

* ErrorController
*/ 

class ErrorController extends Zend_Controller_Action {

   /**
    * errorAction() is the action that will be called by the "ErrorHandler" 
    * plugin.  When an error/exception has been encountered
    * in a ZF MVC application (assuming the ErrorHandler has not been disabled
    * in your bootstrap) - the Errorhandler will set the next dispatchable 
    * action to come here.  This is the "default" module, "error" controller, 
    * specifically, the "error" action.  These options are configurable. 
    * 
    * @see http://framework.zend.com/manual/en/zend.controller.plugins.html
    *
    * @return void
    */
   public function errorAction() 
   { 
       // Ensure the default view suffix is used so we always return good 
       // content
       $this->_helper->viewRenderer->setViewSuffix('phtml');
       // Grab the error object from the request
       $errors = $this->_getParam('error_handler'); 
       // $errors will be an object set as a parameter of the request object, 
       // type is a property
       switch ($errors->type) { 
           case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER: 
           case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION: 
               // 404 error -- controller or action not found 
               $this->getResponse()->setHttpResponseCode(404); 
               $this->view->message = 'Page not found'; 
               break; 
           default: 
               // application error 
               $this->getResponse()->setHttpResponseCode(500); 
               $this->view->message = 'Application error'; 
               break; 
       } 
       // pass the environment to the view script so we can conditionally 
       // display more/less information
       $this->view->env       = $this->getInvokeArg('env'); 
       
       // pass the actual exception object to the view
       $this->view->exception = $errors->exception; 
       
       // pass the request to the view
       $this->view->request   = $errors->request; 
   } 

} </source>

The above may seem a bit esoteric.

First, the ErrorHandler plugin stores error information- including the error type, exception caught, and the request that produced it- within a token that is passed in the request. We store that in the $errors variable.

Second, we do some switching based on the exception type. There are two types of exceptions that represent 404 errors: controller not found and action not found. For these, we change the HTTP response code to 404 to indicate this. All other exceptions are considered application exceptions, and we'll return the appropriate HTTP response code for these- 500.

Third, remember the env variable you passed to the front controller in the bootstrap file? We're going to get that from the front controller and pass it to the view. More on this later.

Finally, we pass the exception and request to the view.