Sunday, May 23, 2010

Changing from Procedural PHP to Zend MVC - Hello World

Having worked with procedural PHP (non OO), I decided this spring to learn the Zend Framework MVC system.  Through making various PHP sites, I always found it unclear how to tie the structure together so that it's clear what is going on.  Certainly for small sites, this is not a big issue, but for a larger site, a redirector was important.

I would typically code the following into index.php

<php
if(isset($_POST['form']){
 // deal with $_POST['form']
if(isset($_GET['page']){
  include $_GET['page'];
}
else {
 // send out the default stuff
}
?>

Of course, this is insecure as hell because you're allowing someone to grab any file from your system, it's wise to define an array of allowable pages here... I'm not going to get into that here - you get the point. This allows for a basic redirector model for selecting different pages; however, it doesn't provide a smart interface for a controller / model difference.

The Zend Framework MVC system is much more sophisticated.  I work primarily in a FreeBSD environment; so we start by installing with a command like:

root@aztec~# cd /usr/ports/www/zend-framework && make install

We can now setup a skeleton directory structure for our Zend project

gate@aztec~# zf create project zendPlay

This creates a directory structure

gate@aztec~# tree zendPlay/
zendPlay/
|-- application
|   |-- Bootstrap.php
|   |-- configs
|   |   `-- application.ini
|   |-- controllers
|   |   |-- ErrorController.php
|   |   `-- IndexController.php
|   |-- models
|   `-- views
|       |-- helpers
|       `-- scripts
|           |-- error
|           |   `-- error.phtml
|           `-- index
|               `-- index.phtml
|-- library
|-- public
|   `-- index.php
`-- tests
    |-- application
    |   `-- bootstrap.php
    |-- library
    |   `-- bootstrap.php
    `-- phpunit.xml

14 directories, 10 files


The part that's a little unclear with Zend right away is that the controller automagically calls the view script.  Notice above how we have controllers IndexController and ErrorController, and we have error and index directories in the application/views/scripts/ directory.  This is no coincidence.  The controller calls the view script and together they form the page.  It's important that the names are associated the way they are so that the router works properly.

In Zend, a url re-writer is used to determine which page to load.  In apache, we include something like

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

into zendPlay/public/.htaccess  so that a request like http://zendhost/ is sent to the controller IndexController (a class found in application/controllers/IndexController.php) with method indexAction (in the IndexController class).  Similarly, the request http://zendhost/foo/bar would be sent to the controller FooController with method barAction which in turn uses the view application/views/scripts/foo/bar.phtml.

Now create a vhost with apache and a free domain from dyndns (or wherever) with the following in /usr/local/etc/apache22/extras/httpd-vhosts.conf

<virtualhost zend.thruhere.net:80="">
  DocumentRoot "/home/gate/zendPlay"
  ServerName zendhost
  <directory gate="" home="" zendplay="">
    DirectoryIndex index.php
    AllowOverride All
    Order allow,deny
    Allow from all
  </directory>
</virtualhost>

and restart apache.

root@aztec~# /usr/local/etc/rc.d/apache22 restart

So the following code would give us a hello world script right off the bat.

edit application/controllers/IndexController.php
<?php

class IndexController extends Zend_Controller_Action
{

    public function init()
    {
        /* Initialize action controller here */
    }

    public function indexAction()
    {
        // action body
    }

    public function hiAction()
    {
      $this->view->myVar = "Hellow World";
    }
}

so that it has the additional action hiAction (notice how there is no ?> bracket). Try loading http://zendhost/ now. We get an error message!

Message: script 'index/hi.phtml' not found in path (/usr/home/gate/zendPlay/application/views/scripts/)

Fortunately the solution is quite clear: create the file application/views/scripts/index/hi.phtml and add the following

<?php

echo $this->myvar;

Wonderful! it's quite an advanced 'hello world' script. But it at least explains how Zend's router works. There's a lot more to cover here; but I'll finish this howto off with some justification for this much more code.

The purpose of separating the .phtml files from the action is that our controller can act as a traffic marshall between the model and the view.  The controller has no `echo` or `print` functions, and the view only has output functions (with some loops).   The value in this is that our .phtml files are very simple and the application logic is kept in the rest of the code.




No comments:

Post a Comment