Chapter 18. Writing the Project

In the preceding chapter we looked at the design for a simple Customer Relationship Management (CRM) system. This is a fairly simple programming exercise, but we use it to demonstrate some useful tools and techniques that Zend Studio for Eclipse provides. The main goal of this chapter is not to dazzle the world with elegant code, but rather to put the pieces of Zend Studio for Eclipse that we discussed in the other chapters into a working example. All the code for the CRM is available at http://www.informit.com/store/product.aspx?isbn=0672329409 so that you can follow along with the example.

Setting Up

To make use of some extra features and to showcase a new style of PHP programming, we are using a Zend Framework project. To follow along, open Zend Studio for Eclipse and create a new Zend Framework project; then follow the instructions in the Project Creation Wizard. As you can see from Figure 18.1, we call this project Studio_CRM.

Creating the project with the project wizard.

Figure 18.1. Creating the project with the project wizard.

After the framework setup wizard has finished, you should be able to run the default framework “Hello World” script that Zend Studio for Eclipse automatically generates. You have to set up a new Run configuration using the Run dialog. This can be a little tricky the first time because there are a lot of things to set up. You should run the Studio_CRM project as a PHP web page. A Zend Framework project requires the use of a web server for behind the scenes processing, so the best way to run your project is to right-click on the Studio_CRM project and export it to your file system. Launch a new configuration and call it Zend Studio for Eclipse CRM. You can see from Figure 18.2 that a lot of the information in this dialog has been filled in for you already.

Configuring the project to run as a PHP Web Page.

Figure 18.2. Configuring the project to run as a PHP Web Page.

The only thing that you have to set up now is a server. You can use a server that is running locally and just point to it. Keep in mind that you need a copy of the Zend Framework on your server in a location that is in your PHP include path. The location that the Zend Studio for Eclipse server configuration is pointing to also must exist for the server to work properly. You may also want to publish your project files to this location, or remember to re-export your project before you run it.

Writing Some Code

At this point, you should have the standard Zend Framework directory structure created (automatically by the wizard) as well as a bootstrap file located in html/index.php and the indexController.php file. Bootstrap files act as dispatchers for the system. The .htaccess file uses URL rewriting via the apache mod_rewrite module. This example is going to add a few more things to the standard bootstrap.

Note

Bootstrap files can seem a little intimidating, but you should not hesitate to modify them to suit the needs of your particular application. You can set them up to hold helpful items such as defined global constants or database connection information.

Listing 18.1 shows the completed bootstrap file. This file now holds information about some global constants and the database connection that we are going to use. Note the contents of the database connection INI file in Listing 18.2. Other information could also be placed in this file and accessed using the framework INI functions.

Example 18.1. Modified Zend Framework Bootstrap File

<?php

/**
 * Zend Studio for Eclipse CRM bootstrap file
 * /html/index.php
 */

require_once 'Zend/Session/Namespace.php';
$sessionNamespace = new Zend_Session_Namespace();

// global variables can be define here
define('WWW_ROOT', 'http://localhost/Studio_CRM/html/'),
// set up an autoload callback that will load parts of the framework on demand
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

// set up the application directory
$appDir = dirname(dirname(__FILE__)) . '/application';

// add the models directory to the include path to make it easier to load them
set_include_path(
    $appDir . '/default/models'
    . PATH_SEPARATOR
    . get_include_path()
);

// an interesting way to load database connection information via INI file
$config = new Zend_Config_Ini("$appDir/etc/config.ini", 'main'),
Zend_Registry::set('config', $config);
// set up the database object using information from the INI
$db = Zend_Db::factory(
    $config->database->adapter,
    $config->database->params->toArray()
);

//Save this database connection in a place where other classes can find it.
Zend_Registry::set('defaultDb', $db);
Zend_Db_Table::setDefaultAdapter('defaultDb'),

// basic framework setup
require_once 'Zend/Controller/Front.php';

$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('../application/default/controllers'),
$controller->throwExceptions(true);
$controller->dispatch();

You don’t have to understand everything that is going on in the bootstrap file right away. However, it is good to have a general idea of what is going on here and what kinds of things can be held in the bootstrap.

Programmers use different methods to tackle the problem of configuration files. Using PHP or .inc files is a common, and quite useful, way to set up database information and the like. The Zend Framework has special INI processing functions so that INI files can be used to store configuration data instead of .inc files.

Example 18.2. Configuration INI Located at /application/etc/config.ini

[main]
database.adapter         = Pdo_Mysql
database.params.host     = localhost
database.params.dbname   = studio_crm
database.params.username =******
database.params.password =******
database.params.port     = 3306

Now that the bootstrap file is in place, it would be nice to run something to see if it is actually working. The first thing that we want to see is a login screen. For now, we can just create the form and validate any information that has been submitted so that we can get on to the meat of the program.

The Zend Framework works well when a Model-View-Controller design pattern is used (in fact, it is designed to be used with MVC architecture, but the libraries may be used by themselves with other design patterns). The indexController.php file is the default file called when you browse to your site document root. The default action (view) associated with the indexController is a function called indexAction within the controller class. This function is the place where variable handling and form processing happens. A common way to handle a displayed and processed form is to use a structure similar to the following:

if (isset($_POST['btn_submit'])) {
    // process the form
}
    // show the form and any errors

Look at Listing 18.3 for an example of this structure using MVC in the Zend Framework. The Login view is called automatically at the end of the action function. This view is stored in /views/scripts/index/index.phtml and is standard HTML with access to variables set in the controller. Listing 18.4 shows the Login view along with error notification.

Example 18.3. The loginAction Function in indexController.php

public function loginAction()
{

    $frm_data = $this->_request->getPost();
    if($frm_data) {
        // TODO - check login credentials against those stored in user table
    }
    $this->render();

}

Example 18.4. The Login View Called by the loginAction Function in indexController.php

<?php

// Zend Studio for Eclipse CRM login
// application/default/views/scripts/index/login.phtml

?>

<h1>Studio CRM Login</h1>

<form name="frm_login" action="<?php echo WWW_ROOT; ?>index/process"
method="post">
    <table>
        <tr>
            <td>Email Address</td>
            <td><input type="text" name="email" /></td>
        </tr>
        <tr>
            <td>Password</td>
            <td><input type="password" name="password" /></td>
        </tr>

        <tr>
            <td colspan="2" align="right">
                <input type="submit" value="Login" />
            </td>
        </tr>
    </table>
</form>

Now that we have some basic functionality, let’s try to run the program. You should see the login screen, as shown in Figure 18.3, in the Run window. When you click the Login button, the form is submitted to the Login view of the indexController. If you look back to Listing 18.3, you can see the familiar if submit structure in the loginAction function. Our credential checking will go here eventually, but for now we can just set a session variable and pass the user off to a “dashboard” that will act as the launch point for the other functionality. Notice that we make use of the //TODO comment so that Zend Studio for Eclipse will pick up this comment in the Tasks view.

The login screen that appears when running the project.

Figure 18.3. The login screen that appears when running the project.

The dashboard just links to the rest of the system at this point. It sits in the dashboardAction function of the indexController and has its own view in /application/views/scripts/index/dashboard.phtml.

At this point we should look at some of the other functionality and tools that Zend Studio for Eclipse has now that we have some files to work with. If you are following along with this example or have used Zend Studio for Eclipse, you should have made use of the Navigator view (not to be confused with views within the Zend Framework). In the standard Zend Studio for Eclipse PHP perspective, the Outline view appears underneath the Navigator. Be sure to use this view to help you keep track of the different parts of your project. The Zend Framework fits into the Outline view very well because it is object oriented. You should start to see a one-to-one relationship between controller actions and views.

As you saw in some of the code listings, we are using the //TODO commenting system for functionality that we will add later. This is a simple way to make sure that certain functionality isn’t missed later, and it allows us to move on to other parts of the project. All comments that are set up to be Zend Studio for Eclipse tasks are grouped in the Tasks view, which is at the bottom of the PHP perspective by default.

Adding More Functionality

Right now, we have a basic login form and a dashboard. This is a start, especially if you’re just getting into the Zend Framework or MVC architecture. This section is going to walk through the steps of adding to the project. In Chapter 17 we said that we wanted to manage companies, people, events, and users. The dashboard links to these parts already; now we have to put something in those locations. Here, we go through adding a section to handle companies in detail. People, events, and users follow an almost identical pattern as the one we describe for companies, so we don’t explicitly show them here.

In the dashboard, we created a link called Add/Edit Companies. This link pointed to /html/companies. Following the framework convention, we have to create a new controller called companiesController.php with appropriate action functions. We also have to create a view to display something. The default view should list available companies and show a new company link and edit and delete links next to each listing. Displaying this kind of information is going to require using the database connection that we set up in /html/index.php. We’re also going to have to set up our first model.

One way to handle model creation is to set up a model for each entity. This can be an ideal way to implement MVC design patterns, but it can also add a lot of complexity to the program. For this example, because we’re trying to get used to Zend Studio for Eclipse itself, we set up a single model that models a database table. The database table model in Listing 18.5 is used in Zend Framework examples and tutorials released by Zend, and it shows how powerful the framework is.

Example 18.5. A Basic Zend Framework Class That Models a Database Table

<?php

class TableModel extends Zend_Db_Table_Abstract
{
    // don't need code here as it will be inherited from the parent
}

?>

The simple database table model that we have works very well when selecting, inserting, or updating a single table. However, this method does not work if we are trying to do more complex queries joining two or more tables. Complex queries use the database object set up in the bootstrap file directly and assemble the query with Zend Framework query functions. The following code listings use both types of database access. Many of the list view pages in this project use the complex method, whereas the add, edit, and delete pages use the table model.

Let’s look at how we can access data in companiesController.php. Listing 18.6 shows the controller class with an init function that creates an instance of the table model. The init function is run whenever the controller is called. The function that runs after the init depends on arguments that are passed. For this example, we just look at the default action (and view) function, which is called indexAction. When execution reaches the end of indexAction, the companies view is run with access to the data retrieved by the model and controller.

Example 18.6. Init and indexAction Functions in companiesController.php

<?php

/**
 * companiesController.php
 */

require_once "Zend/Controller/Action.php";

class companiesController extends Zend_Controller_Action
{

    public function init()
    {
        // set the table that this controller will handle
        $this->_model = new TableModel(array('name' => 'company'));
        // set up other instances of the TableModel for other database tables
        $this->_model_provinces = new TableModel(array('name' => 'provinces'));
        $this->_model_countries = new TableModel(array('name' => 'countries'));
    }

    /**
     * The default action - show the home page
     */
    public function indexAction()
    {

        // we want to execute the following query using ZF functions:
        //
        //    SELECT *
        //    FROM company, countries, provinces
        //    WHERE company.ProvID = provinces.ProvID
        //    AND company.CountryID = countries.CountryID

        global $db;

        // Create the Zend_Db_Select object
        $select = $db->select();

        // Add a FROM clause (with joins)
        $select->from(array('c' => 'company'))
                ->join(array('co' => 'countries'), 'c.CountryID = co.CountryID')
                ->join(array('p' => 'provinces'), 'c.ProvID = p.ProvID'),

        // execute the query
        $this->view->rowset = $db->fetchAll($select);

    }
// ... continued in Listing 18.8

Listing 18.7 shows the view we are using to display the list of companies in the database.

Example 18.7. The View for companiesController.php

<?php

/**
* application/views/scripts/companies/index.phtml
**/

?>

<h1>View Companies</h1>

<p>
    <a href="index">Return to home</a>
</p>

<p>
    <a href="<?php echo WWW_ROOT; ?>companies/add">New Company</a>
</p>

<table cellspacing="0" cellpadding="2">
    <tr>
        <th>Company Name</th>
        <th>Address 1</th>
        <th>Address 2</th>
        <th>City</th>
        <th>Province</th>
        <th>Postal Code</th>
        <th>Country</th>
               <th>Edit</th>
               <th>Delete</th>
    </tr>

       <?php
               $i = 0;
                $stripeClass = array('rowEven', 'rowOdd'),
       ?>

    <? foreach ($this->rowset as $row): ?>
    <tr class="<?= $stripeClass[$i++%2] ?>">


               <td><?= $this->escape($row['Name']) ?></td>
               <td><?= $this->escape($row['Address1']) ?></td>
               <td><?= $this->escape($row['Address2']) ?></td>
               <td><?= $this->escape($row['City']) ?></td>
               <td><?= $this->escape($row['ProvName']) ?></td>
               <td><?= $this->escape($row['Postal']) ?></td>
               <td><?= $this->escape($row['Country']) ?></td>

               <td>
            <a href="<?php echo WWW_ROOT; ?>companies/edit/id/
                <?php echo $row['CompanyID']; ?>">edit</a>
        </td>
        <td>
            <a href="<?php echo WWW_ROOT; ?>companies/delete/id/
                <?php echo $row['CompanyID']; ?>">delete</a>
        </td>
    </tr>
        <? endforeach; ?>

</table>

Our project is finally starting to take shape. All that we have to do is add the add, edit, and delete functions to the companiesController and create the views. Listings 18.8 to 18.10 show the remainder of the controller and the views needed to complete this part of the project. Notice that although there is a function in the controller for deleting, we don’t actually need a view because there is no graphical component for deleting. Brace yourself for a few pages of code examples.

Example 18.8. Remaining Functions for companiesController.php

// ... continued from Listing 18.6

// create a company
public function addAction()
{
    if($this->_request->isPost()) {

            Zend_Loader::loadClass('Zend_Filter_StripTags'),
            $filter = new Zend_Filter_StripTags();

            // filter posted content
            $name = $filter->filter($this->_request->getPost('Name'));
            $address1 = $filter->filter($this->_request->getPost('Address1'));
            $address2 = $filter->filter($this->_request->getPost('Address2'));
            $city = $filter->filter($this->_request->getPost('City'));
            $provID = $filter->filter($this->_request->getPost('ProvID'));
            $postal = $filter->filter($this->_request->getPost('Postal'));
            $countryID = $filter->filter($this->_request->getPost('CountryID'));

            $data = array(
                'Name'         => $name,
                'Address1'     => $address1,
                'Address2'     => $address2,
                'City'         => $city,
                'ProvID'     => $provID,
                'Postal'     => $postal,
                'CountryID' => $countryID
            );

            $this->_model->insert($data);
            $this->_redirect(WWW_ROOT.'companies'),
            return;
        }

        // get province and country info for select boxes in the view
        $this->view->prov_row = $this->_model_provinces->fetchAll();
        $this->view->country_row = $this->_model_countries->fetchAll();
}

// delete a company
public function deleteAction()
{

        $id = $this->_request->getParam('id', 0);
        if($id > 0) {
            $this->_model->delete('CompanyID='.$id);
            $this->_redirect(WWW_ROOT.'companies'),
            return;
        }
}

// edit a company
public function editAction()
{

        $id = $this->_request->getParam('id', 0);

        // if submitted, update the table
        if($this->_request->isPost()) {

            Zend_Loader::loadClass('Zend_Filter_StripTags'),
            $filter = new Zend_Filter_StripTags();

            // filter posted content
            $name = $filter->filter($this->_request->getPost('Name'));
            $address1 = $filter->filter($this->_request->getPost('Address1'));
            $address2 = $filter->filter($this->_request->getPost('Address2'));
            $city = $filter->filter($this->_request->getPost('City'));
            $provID = $filter->filter($this->_request->getPost('ProvID'));
            $postal = $filter->filter($this->_request->getPost('Postal'));
            $countryID = $filter->filter($this->_request->getPost('CountryID'));

            $data = array(
                'Name'         => $name,
                'Address1'     => $address1,
                'Address2'     => $address2,
                'City'         => $city,
                'ProvID'     => $provID,
                'Postal'     => $postal,
                'CountryID' => $countryID
            );


            $this->_model->update($data, 'CompanyID='.$id);
            $this->_redirect(WWW_ROOT.'companies'),
            return;
        }

        // query the db for the id being edited
        if($id > 0) {
           $this->view->company = $this->_model->fetchRow('CompanyID='.$id);
       }
       // set vars for the view to display
       $this->view->companyID = $id;

       // get province and country info for select boxes in the view
       $this->view->prov_row = $this->_model_provinces->fetchAll();
       $this->view->country_row = $this->_model_countries->fetchAll();

    }

 

Example 18.9. add.phtml—The View for Adding a Company

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>New Zend Framework Project</title>
</head>

<body>
     <h1>Add a new Company</h1>

     <form name="frm_add_company" action="<?php echo WWW_ROOT; ?>companies/add"
        method="post">

         <table>
             <tr>
                 <td>Company Name</td>
                 <td><input type="text" name="Name" value="" /></td>
             </tr>

             <tr>
                 <td>Address 1</td>
                 <td><input type="text" name="Address1" value="" /></td>
             </tr>

             <tr>
                 <td>Address 2</td>
                 <td><input type="text" name="Address2" value="" /></td>
             </tr>

             <tr>
                 <td>City</td>
                 <td><input type="text" name="City" value="" /></td>
             </tr>

             <tr>
                 <td>Province</td>
                 <td>
                     <select name="ProvID">
                         <option value="0">Select province</option>
                         <? foreach ($this->prov_row as $row): ?>
                         <option value="<?php echo $row->ProvID; ?>">
                             <?php echo $row->ProvName; ?>
                         </option>
                         <? endforeach; ?>
                     </select>
                 </td>
             </tr>

             <tr>
                 <td>Postal Code</td>
                 <td><input type="text" name="Postal" value="" /></td>
             </tr>

             <tr>
                 <td>Country</td>
                 <td>
                     <select name="CountryID">
                         <option value="0">Select country</option>
                         <? foreach ($this->country_row as $row): ?>
                         <option value="<?php echo $row->CountryID; ?>">
                             <?php echo $row->Country; ?>
                         </option>
                         <? endforeach; ?>
                     </select>
                 </td>
             </tr>

             <tr>
                <td colspan="2" align="right">
                    <input type="submit" value="Add" />
             </td>
            </tr>
         </table>

     </form>

</body>

</html>

 

Example 18.10. edit.phtml—The View for Editing a Company

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>New Zend Framework Project</title>
</head>

<body>
     <h1>Edit a Company</h1>

     <form name="frm_edit_company"
        action="<?php echo WWW_ROOT; ?>companies/edit/id/<?php echo $this-
>companyID; ?>"
        method="post">

         <table>
             <tr>
                 <td>Company Name</td>
                 <td>
                     <input type="text" name="Name"
                         value="<?php echo $this->company->Name; ?>" />
                 </td>
             </tr>

             <tr>
                 <td>Address 1</td>
                 <td>
                     <input type="text" name="Address1"
                         value="<?php echo $this->company->Address1; ?>" />
                 </td>
             </tr>
             <tr>
                  <td>Address 2</td>
                  <td>
                      <input type="text" name="Address2"
                         value="<?php echo $this->company->Address2; ?>" />
                  </td>
             </tr>

             <tr>
                 <td>City</td>
                 <td>
                     <input type="text" name="City"
                         value="<?php echo $this->company->City; ?>" />
                 </td>
             </tr>

             <tr>
                 <td>Province</td>
                 <td>
                     <select name="ProvID">
                         <option value="0">Select province</option>
                         <? foreach ($this->prov_row as $row): ?>
                         <option value="<?php echo $row->ProvID; ?>"
                             <?php if($this->company->ProvID == $row->ProvID) {
                                 echo 'selected="selected"'; } ?>>
                             <?php echo $row->ProvName; ?>
                         </option>
                         <? endforeach; ?>
                     </select>
                 </td>
             </tr>

             <tr>
                 <td>Postal Code</td>
                 <td>
                     <input type="text" name="Postal"
                         value="<?php echo $this->company->Postal; ?>" />
                 </td>
             </tr>

             <tr>
                 <td>Country</td>
                 <td>
                     <select name="CountryID">
                         <option value="0">Select country</option>
                         <? foreach ($this->country_row as $row): ?>
                         <option value="<?php echo $row->CountryID; ?>"
                             <?php if($this->company->CountryID == $row-
>CountryID) {
                                 echo 'selected="selected"'; } ?>>
                             <?php echo $row->Country; ?>
                         </option>
                         <? endforeach; ?>
                     </select>
                 </td>
             </tr>

             <tr>
            <td colspan="2" align="right">
                <input type="submit" value="Update" />
            </td>
        </tr>
         </table>

     </form>

</body>

</html>

 

The companies part of the project is finally complete. The events, people, and users parts of the site are almost identical to the companies files. You can see that copying and pasting and then changing table names, fields, and queries is all you would need to do to get the same functionality on another table.

Summary

This chapter took the design notes from Chapter 17 and turned them into a working project. The project was built using the Zend Framework, so a large portion of the chapter extended concepts and theories discussed in Chapter 16. However, the goal of this chapter was to give you a more in-depth example of code so that you can explore Zend Studio for Eclipse’s functionality.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.139.224