Chapter 4. Managing Data with Zend Framework

Current web applications utilize a wide range of data sources, including flat files, web services, feeds, and databases. Content management systems exist that are based on each of these sources, or combinations of them, but most of them store their content in databases.

The CMS you are developing in this book will use a MySQL database. What MySQL lacks in enterprise features is more than made up for by its simplicity and speed. The vast majority of LAMP [2] hosting packages include at least one MySQL database.

In this chapter, you will learn how to interact with databases using Zend Framework's database abstraction layer, Zend_Db. Zend_Db is an object-oriented interface to SQL database systems. You will add onto the example you created in the previous chapter, saving the bug reports that the previous demo's form submits.

Setting Up the Database

Before you can start working with the database demos, you need to create the CMS database and configure its connection.

Creating the CMS Database

First you need to create your database. So, create a new database named zf_cms on your testing server, and create a user who has access to this database. This user will need to have permission to select, insert, update, and delete data from the database.

Configuring the Database Connection

Next you need to configure your application's database connection. Zend Framework uses adapters to connect to database servers. These adapters provide a standardized interface to a range of commercially available database systems, including the following:

  • IBM DB2

  • MySQL

  • Microsoft SQL Server

  • Oracle

  • PostgreSQL

  • SQLite

This common interface makes it easy to switch from one back-end database server to another. A common use case is where an application starts with a file-based SQLite database but is scaled up to a full RDBMS, such as MySQL, when necessary.

Zend_Application_Resource_Db is one of the default resource plug-ins that ships with Zend Framework. All you need to do is add the database connection properties to the application.ini configuration file; the resource plug-in creates the connection and then registers it with Zend_Db_Table as the default database connection. Add the database settings to your application.ini file, as shown in Listing 4-1.

Example 4.1. The Database Connection Information in application/configs/application.ini

resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "your username"
resources.db.params.password = "your password"
resources.db.params.dbname = "zf_cms"
resources.db.isDefaultTableAdapter = true

Creating the bugs Table

In the previous chapter, you created a form for the users to submit bug reports. Now that you have created the database, you need to create a table to store these bug reports in. Table 4-1 describes the bugs table that you'll create.

Table 4.1. The bugs Table Description

Field

Type

Length

Notes

id

int

11

This is the primary key for the table.

author

varchar

250

This is the name of the person who is submitting the bug.

email

varchar

250

This is the email address of the person who is submitting the bug.

date

int

11

This is the timestamp that the bug was reported on.

url

varchar

250

This is the URL the bug occurred on.

description

text

--

This is the description of the bug.

priority

varchar

550

This is how critical the bug is.

status

varchar

50

This is the current status of the bug.

To create this table, run the SQL statement shown in Listing 4-2 on your database.

Example 4.2. The SQL Statement to Create the bugs Table

CREATE TABLE `bugs` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `author` varchar(250) DEFAULT NULL,
  `email` varchar(250) DEFAULT NULL,
  `date` int(11) DEFAULT NULL,
  `url` varchar(250) DEFAULT NULL,
  `description` text,
  `priority` varchar(50) DEFAULT NULL,
  `status` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

Exploring the Zend Framework Models

In Zend Framework's MVC implementation, each table in the database has an associated class that manages it. In this CMS we refer to this class as the model, but note that a model can mean different things in a different context.

Learning About the Models

The model classes extend the Zend_Db_Table_Abstract class, which provides an object-oriented interface to the database table. It implements the Table Data Gateway pattern; the table data gateway encapsulates all the SQL for managing the underlying table. It provides a number of methods for common database management CRUD (create, read, update, delete) functions:

  • Create: You can create rows directly with Zend_Db_Table's insert() method, or you can create a new row, add the data to it, and save it.

  • Read: A number of methods exist for reading data, but the two most common are, first, building a select query using the Zend_Db_Select object and passing this to the Zend_Db_Table fetch methods and, second, fetching a row using its primary key with Zend_Db_Table's find() method.

  • Update: You can update rows using Zend_Db_Table's update() method, or you can make the changes directly to the row and then use the Zend_Db_Table_Row's save() method.

  • Delete: You can delete rows by passing a WHERE clause to the Zend_Db_Table's delete() method, or you can delete a row using the Zend_Db_Table_Row's delete() method.

All your application's model classes go in the application/models folder. The classes should use the camelCase naming convention and should use the Model_ namespace. This will enable the Zend_Loader autoloader to find and load your classes for you so you don't have to do this manually. To take advantage of the autoloader functionality, you will need to update the _initAutoload() method in the Bootstrap class, adding the Model_ namespace, similar to the change made for autoloading forms (see Listing 4-3).

Example 4.3. The Updated _initAutoload() Method in application/Bootstrap.php

protected function _initAutoload()
{
    // Add autoloader empty namespace
    $autoLoader = Zend_Loader_Autoloader::getInstance();
    $autoLoader->registerNamespace('CMS_'),
    $resourceLoader = new Zend_Loader_Autoloader_Resource(array(
        'basePath'      => APPLICATION_PATH,
        'namespace'     => '',
        'resourceTypes' => array(
            'form' => array(
                'path'      => 'forms/',
                'namespace' => 'Form_',
            ),
            'model' => array(
                'path'      => 'models/',
                'namespace' => 'Model_'
            ),
        ),
    ));
    // Return it so that it can be stored by the bootstrap
    return $autoLoader;
}

Creating the Bug Model

Create a new file in the application/models folder named Bug.php. Add a class to this file named Model_Bug, which extends Zend_Db_Table_Abstract. Next you need to define the model's table name; this is not completely necessary, because the framework will default to the class name in lowercase characters, but it adds a degree of flexibility to your class naming. You set the table name using the protected property $_name (see Listing 4-4).

Example 4.4. The Bug Model in application/models/Bug.php

<?php
class Model_Bug extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
}
?>

Working with Bugs

Now that you have the database, bugs table, and model set up, you are ready to start managing bug submissions.

Submitting a New Bug

The first thing you need to do to manage bugs (as is the case with most data) is insert the bugs into the database. There are several approaches to doing this; some people prefer to put the data management logic in the controller action, using the built-in Zend_Db_Table methods. I, on the other hand, prefer to create specific methods in the model classes for each of these actions. It takes an extra moment but makes managing the code much easier down the road.

Creating the createBug() Method

To get started, create a new method in the Bug model named createBug() (see Listing 4-5). Note that you must be careful when naming your methods; make sure you don't overload the core Zend_Db_Table methods!

As I mentioned earlier, there are two ways to create a new row in a table using Zend_Db_Table. I prefer to create a new row object, add the data to it, and then save it. This is a matter of preference, but I find it easier to read and work with than array notation.

Once you have created and saved the row, return the ID of the new row.

Example 4.5. The createBug() Method in application/models/Bug.php

public function createBug($name, $email,
                 $date, $url, $description, $priority, $status)
{
    // create a new row in the bugs table
$row = $this->createRow();

    // set the row data
    $row->author = $name;
    $row->email = $email;
    $dateObject = new Zend_Date($date);
    $row->date = $dateObject->get(Zend_Date::TIMESTAMP);
    $row->url = $url;
    $row->description = $description;
    $row->priority = $priority;
    $row->status = $status;

    // save the new row
    $row->save();

    // now fetch the id of the row you just created and return it
    $id = $this->_db->lastInsertId();
    return $id;
}

Updating the Bug Controller's Submit Action

Now that you have the database and model set up, you are ready to update the bug controller and implement the submit action. In the previous chapter, you created this action but just had it dump the submitted data out onto the page if the form passed its validation. Now you need to update this action and insert the bug into the database (see Listing 4-6).

First you create a new instance of the Bug model in the submit section. Then, once you have this instance, you call the createBug() method, passing it the values that were submitted with the form. If the createBug() method succeeds, it will return the primary key of the bug row that was just inserted. In this case, you should display a confirmation page (which you will create next).

Example 4.6. The Updated submitAction() in application/controllers/BugController.php

public function submitAction()
{
    $bugReportForm = new Form_BugReportForm();
    $bugReportForm->setAction('/bug/submit'),
    $bugReportForm->setMethod('post'),
    if($this->getRequest()->isPost()) {
        if($bugReportForm->isValid($_POST)) {
            $bugModel = new Model_Bug();
            // if the form is valid then create the new bug
            $result = $bugModel->createBug(
                $bugReportForm->getValue('author'),
                $bugReportForm->getValue('email'),
                $bugReportForm->getValue('date'),
                $bugReportForm->getValue('url'),
                $bugReportForm->getValue('description'),
                $bugReportForm->getValue('priority'),
                $bugReportForm->getValue('status')
);
            // if the createBug method returns a result
            // then the bug was successfully created
            if($result) {
                $this->_forward('confirm'),
            }
        }
    }
    $this->view->form = $bugReportForm;
}

Now you need to create the confirmation page. To create this page, you need to create an action as well as a view script. You can do this manually or using Zend_Tool, as shown in Listing 4-7.

Example 4.7. Creating the Thank-You Page with Zend_Tool

zf create action confirm bug

This command will create a new action in the bug controller named confirmAction() as well as a view script in application/views/scripts/bug/confirmation.phtml. This view script will render a notice with its location and the action that is rendering it. Open this script, and replace this with the thank-you note in Listing 4-8.

Example 4.8. The Confirmation View Script in application/views/scripts/bug/confirm.phtml

<h2>Thanks for submitting your bug report!</h2>
<p>We will review it shortly...</p>

Viewing All the Current Bugs

Now that you have created a method to enter bug reports, you need to create a method to manage them, which will query the database for all current bugs and create a list view. Each row in the list will represent one row in the bugs table and will have links to update the row as well as delete it.

Creating the fetchBugs() Method

First you need to create a method to fetch the current bugs. You could use the fetchAll() method directly, passing it an array of WHERE clauses, but I recommend using the Zend_Db_Select class. This class is used to create a select query programmatically. Then, once you build the query, you pass the select object to Zend_Db_Table's fetchAll() or fetchRow() method. Zend_Db parses the query, runs it, and builds the result object, which is a set of Zend_Db_Table_Row objects (appropriately called Zend_Db_Table_Rowset).

Create a new method in the Bug model named fetchBugs(), as shown in Listing 4-9. For the time being, you are just going to fetch all the bugs, so all you need to do is get the table's select object and then pass this to the fetchAll() method.

Note

The Zend_Db_Select object is an SQL API that is used to programmatically build queries. There are several advantages of using this API rather than writing SQL. Probably the most important is the fact that the Select object will adapt the SQL depending on the database adapter you are using, which makes the code more portable.

Example 4.9. The fetchBugs() Method in application/models/Bug.php

public function fetchBugs()
{
    $select = $this->select();
    return $this->fetchAll($select);
}

Adding the List Action to the Bug Controller

Now that you have a method to fetch all the bugs from the database, you are ready to start working with them. So, create a list action in the bug controller. Use the Zend_Tool line in Listing 4-10 to create the action and view.

Example 4.10. Creating the List Action with Zend_Tool

zf create action list bug

Now open the bug controller to update this action. For the time being, this will just create a new instance of the Bug model and pass the results of the fetchBugs() method to the view to render, as shown in Listing 4-11.

Example 4.11. The listAction() Method in application/controllers/BugController.php

public function listAction()
{
    $bugModel = new Model_Bug();
    $this->view->bugs = $bugModel->fetchBugs();
}

Creating the List View

To create the list view, open application/views/scripts/bug/list.phtml. This view script will render a table with all the current bugs. To render this table, it will use the partialLoop() view helper, which takes a data set and passes each item in the set to the partial script that you specify (see Listing 4-12).

Example 4.12. The List View Script in application/views/scripts/bug/list.phtml

<h2>Current Bugs</h2>

<table class='spreadsheet' cellspacing='0'>
    <tr>
        <th>&nbsp;</th>
        <th>Date</th>
        <th>Priority</th>
        <th>Status</th>
        <th>URL</th>
        <th>Submitter</th>
        <th>Description</th>
    </tr>
    <?php echo $this->partialLoop('bug/table-row.phtml', $this->bugs); ?>
</table>

Now you need to create the table-row.phtml view script to render the actual row, as shown in Listing 4-13.

Example 4.13. The Bug Table Row application/views/scripts/bug/table-row.phtml

<tr>
    <td><a href='/bug/edit/id/<?php echo $this->id;?>'>Edit</a> |
        <a href='/bug/delete/id/<?php echo $this->id;?>'>Delete</a></td>
    <td><?php echo date('m/d/y', $this->date); ?></td>
    <td><?php echo $this->priority; ?></td>
    <td><?php echo $this->status; ?></td>
    <td><?php echo $this->url; ?></td>
    <td><?php echo $this->author; ?></td>
    <td><?php echo $this->description; ?></td>
</tr>

Next enter a few sample bugs at http://localhost/bug/submit to test the functionality, and then go to http://localhost/bug/list. You should see a list of all your bugs as a simple table. This table is rather difficult to read, but a little CSS will fix that. Update the public/skins/blues/css/form.css file, adding the styles shown in Listing 4-14 for the spreadsheet table.

Example 4.14. The Style Declarations for the Spreadsheet Table in public/skins/blues/css/form.css

table.spreadsheet{
margin:10px 0;
border:#999 1px solid;
}

table.spreadsheet th{
background:#ccc;
font-weight:bold;
font-size:12px;
padding:5px 2px;
}

table.spreadsheet td{
border-top:#999 1px solid;
padding:5px 2px;
}

Now the bug list should be much easier to read, like Figure 4-1.

The styled bug list

Figure 4.1. The styled bug list

Filtering and Sorting the Bug Reports

The bug list is currently renders the bugs in the order they were received. In a real-world example, this would quickly become cumbersome, because you might have to search through hundreds or thousands of bug reports.

Updating the fetchBugs() Method

Next you will update the Bug model's fetchBugs() method to add the ability to filter the reports and sort by different criteria, as shown in Listing 4-15. You will pass the filters as an array, where the key is the bug field and value is the value that you are looking for.

Example 4.15. The Updated fetchBugs() Method in application/models/Bug.php

public function fetchBugs($filters = array(), $sortField = null, $limit = null,
    $page = 1)
{
    $select = $this->select();
    // add any filters which are set
    if(count($filters) > 0) {
        foreach ($filters as $field => $filter) {
            $select->where($field . ' = ?', $filter);
        }
    }
    // add the sort field is it is set
    if(null != $sortField) {
        $select->order($sortField);
    }
    return $this->fetchAll($select);
}

Creating the Form to Filter and Sort the Bugs

Now you need to create a form to filter the bug reports. This form needs to have select controls for each of the available sort criteria and filter fields. It also needs a text control for the filters. Create a new file named BugReportListToolsForm.php in application/forms. Then create the form class named Form_BugReportListToolsForm, adding controls for each of these fields, as shown in Listing 4-16.

Example 4.16. The List Tools Form in application/forms/BugReportListToolsForm.php

<?php
class Form_BugReportListToolsForm extends Zend_Form
{
    public function init()
    {
        $options = array(
            '0'                      => 'None',
            'priority'          => 'Priority',
            'status'                 => 'Status',
            'date'          => 'Date',
            'url'                    => 'URL',
            'author'           => 'Submitter'
        );

        $sort = $this->createElement('select', 'sort'),
$sort->setLabel('Sort Records:'),
        $sort->addMultiOptions($options);
        $this->addElement($sort);

        $filterField = $this->createElement('select', 'filter_field'),
        $filterField->setLabel('Filter Field:'),
        $filterField->addMultiOptions($options);
        $this->addElement($filterField);

        // create new element
        $filter = $this->createElement('text', 'filter'),
        // element options
        $filter->setLabel('Filter Value:'),
        $filter->setAttrib('size',40);
        // add the element to the form
        $this->addElement($filter);

        // add element: submit button
        $this->addElement('submit', 'submit', array('label' => 'Update List'));
    }
}
?>

Loading and Rendering the Filter Form

Now you need to update the listAction() method in the BugController, loading this form and passing it to the view script to render, as shown in Listing 4-17.

Example 4.17. The Updated listAction() Method in application/controllers/BugController.php

public function listAction()
{
    // fetch the current bugs
    $bugModels = new Model_Bug();
    $this->view->bugs = $bugModels->fetchBugs();
    // get the filter form
    $listToolsForm = new Form_BugReportListToolsForm();
    $listToolsForm->setAction('/bug/list'),
    $listToolsForm->setMethod('post'),
    $this->view->listToolsForm = $listToolsForm;
}

Now that you have the form created and loaded, you can add it to the list page. Open the list.phtml file, and render the filter/sort form between the headline and the list table, as shown in Listing 4-18.

Example 4.18. Adding the Filter/Sort Form to the List View in application/views/scripts/bug/list.phtml

<h2>Current Bugs</h2>
<?php echo $this->listToolsForm;?>

<table class='spreadsheet' cellspacing='0'>
    <tr>
        <th>&nbsp;</th>
        <th>Date</th>
        <th>Priority</th>
        <th>Status</th>
        <th>URL</th>
        <th>Submitter</th>
        <th>Description</th>
    </tr>
    <?php echo $this->partialLoop('bug/table-row.phtml', $this->bugs); ?>
</table>

Processing the Filters and Sort Criteria

This form posts back to BugController's listAction() method. Open this action. You will need to define the sort and filter variables and set them to default to null. Then check to see whether the request is a postback; if it is, then populate the form, and fetch the sort and filter criteria. Next you need to evaluate whether the filters and sort parameters have been set. If they have, then update the sort and/or filter variables. With this data linked up, you are ready to pass these values into the fetchBugs() method, as shown in Listing 4-19.

Example 4.19. The listAction() Method in application/controllers/BugController.php

public function listAction()
{
    // get the filter form
    $ListToolsForm = new Form_BugReportListToolsForm();
    // set the default values to null
    $sort = null;
    $filter = null;
    // if this request is a postback and is valid, then add the sort
    // filter criteria
    if($this->getRequest()->isPost()) {
        if($listToolsForm->isValid($_POST)) {
            $sortValue = $listToolsForm->getValue('sort'),
            if($sortValue != '0') {
                $sort = $sortValue;
            }
            $filterFieldValue = $listToolsForm->getValue('filter_field'),
            if($filterFieldValue != '0') {
                $filter[$filterFieldValue] = $listToolsForm->getValue('filter'),
            }
        }
    }
// fetch the current bugs
    $bugModel = new Model_Bug();
    $this->view->bugs = $bugModel->fetchBugs($filter, $sort);

    $listToolsForm->setAction('/bug/list'),
    $listToolsForm->setMethod('post'),
    $this->view->listToolsForm = $listToolsForm;
}

Limiting and Paginating Bug Reports Using Zend_Paginator

You can add limit and offset clauses to the Zend_Db_Select object very easily. You set the limit parameter, to which you can pass two arguments: the size of the result set and the offset (see Listing 4-20).

Example 4.20. Example of Adding LIMIT and OFFSET Clauses to a Select Object

$select = $this->select();
$limit = 10;
$offset = 20;
$select->limit($limit, $offset);

This is useful when you are programmatically building a query, but Zend Framework provides another tool to make the entire pagination process easier, Zend_Paginator.

The Zend_Paginator component enables you to paginate ranges of data from a variety of sources, such as an array, a DbSelect object, or an iterator. It handles these with adapters. In this case, you would use the DbTableSelect adapter. The DbTableSelect adapter adds the proper LIMIT and OFFSET clauses to the Zend_Db_Table_Select object to fetch the current page of results, reducing the memory consumption that accompanies fetching large result sets. It also dynamically fetches the count of the rows that the Zend_Db_Table_Select object will return. It uses this information to calculate the number of pages and results per page. You will need to do a few things to update your code to use Zend_Paginator, covered next.

Updating the fetchBugs() Method to Return a Zend_Paginator Adapter

The fetchBugs() method that you wrote earlier creates a Zend_Db_Table_Select object, runs the fetchAll() method, and then returns the results. Things work a little differently with the paginator; since Zend_Paginator runs its own queries, you need to update the fetchBugs() method to return an instance of the DbTableSelect adapter. One other thing—since the method is no longer returning bugs, you should rename the method to fetchPaginatorAdapter(). I always like to name a method so it describes what the method will do and what it will return, as shown in Listing 4-21.

Example 4.21. The Updated and Renamed fetchBugs() Method in application/models/Bug.php

public function fetchPaginatorAdapter($filters = array(), $sortField = null)
{
    $select = $this->select();
    // add any filters which are set
if(count($filters) > 0) {
        foreach ($filters as $field => $filter) {
            $select->where($field . ' = ?', $filter);
        }
    }
    // add the sort field is it is set
    if(null != $sortField) {
        $select->order($sortField);
    }
    // create a new instance of the paginator adapter and return it
    $adapter = new Zend_Paginator_Adapter_DbTableSelect($select);
    return $adapter;
}

Refactoring the Bug Controller listAction() to Load the Paginator

Now you need to refactor the BugController's listAction() one more time to fetch the paginator and pass it to the view. Note that as you develop more with the framework, you will see a pattern emerge where you cycle from the model to the controller to the view, working on all three components simultaneously to build and refactor functionality.

You first need to refactor how the controller is handling the request parameters in listAction(). This is because the sort and filter criteria are going to be appended to the page navigation links, so the controller needs to fetch these regardless of whether the request method is a POST.

Now you need to replace the fetchBugs() method call with fetchPaginatorAdapter(). Once you have this adapter, create a new instance of Zend_Paginator, passing the adapter to its constructor. Then you set the page size, which will be statically set to ten rows, and the page number, which you will set by passing the parameter page. Once the paginator is configured, pass it to the view to render, as shown in Listing 4-22.

Example 4.22. The Updated listAction() Method in application/controllers/BugController.php

public function listAction()
{
    // get the filter form
    $listToolsForm = new Form_BugReportListToolsForm();
    $listToolsForm->setAction('/bug/list'),
    $listToolsForm->setMethod('post'),
    $this->view->listToolsForm = $listToolsForm;

    // set the sort and filter criteria. you need to update this to use the request,
    // as these values can come in from the form post or a url parameter
    $sort = $this->_request->getParam('sort', null);
    $filterField = $this->_request->getParam('filter_field', null);
    $filterValue = $this->_request->getParam('filter'),

    if(!empty($filterField)) {
        $filter[$filterField] = $filterValue;
    }else{
        $filter = null;
    }
// now you need to manually set these controls values
    $listToolsForm->getElement('sort')->setValue($sort);
    $listToolsForm->getElement('filter_field')->setValue($filterField);
    $listToolsForm->getElement('filter')->setValue($filterValue);

    // fetch the bug paginator adapter
    $bugModels = new Model_Bug();
    $adapter = $bugModels->fetchPaginatorAdapter($filter, $sort);
    $paginator = new Zend_Paginator($adapter);
    // show 10 bugs per page
    $paginator->setItemCountPerPage(10);
    // get the page number that is passed in the request.
    //if none is set then default to page 1.
    $page = $this->_request->getParam('page', 1);
    $paginator->setCurrentPageNumber($page);
    // pass the paginator to the view to render
    $this->view->paginator = $paginator;

}

Rendering the Bug Reports Using the Paginator

Once you have the loaded paginator in the view, you need to add a pagination control to let the user navigate through the pages and then update the table to render the data from the paginator.

First you'll render the pagination control. The Zend Framework developers have gone out of their way to keep things as flexible as possible, so the paginator is not tied into any specific pagination control. You create a control and then set the paginator to use it.

To create a pagination control, you are going to need to create a new partial script. Since you want to be able to reuse this control, it makes sense to put it in a common view folder. Create a new folder named partials in application/views/scripts. Then add a new file to this folder named pagination-control.phtml.

The pagination control will need to do several things. It needs to create previous and next links, which should be disabled if you are at the beginning or end of the page set. It also needs to create a direct link to each page. Each of these links will use the url() helper, appending the page parameter and any request parameters to it, as shown in Listing 4-23.

Example 4.23. The Pagination Control Partial Script in application/views/scripts/partials/pagination-control.phtml

<?php if ($this->pageCount){
    // you need to add each of the request parameters to url
    $params = Zend_Controller_Front::getInstance()->getRequest()->getParams();
    // remove the system parameters
    unset($params['module']);
    unset($params['controller']);
    unset($params['action']);
?>
<div class="paginationControl">
<!-- Previous page link -->
<?php if (isset($this->previous)){ ?>
  <a href="<?php echo $this->url(array_merge(
      $params, array('page' => $this->previous))); ?>">
    &lt; Previous
  </a> |
<?php } else { ?>
  <span class="disabled">&lt; Previous</span> |
<?php } ?>

<!-- Numbered page links -->
<?php foreach ($this->pagesInRange as $page){ ?>
  <?php if ($page != $this->current){ ?>
    <a href="<?php echo $this->url(array_merge($params,
        array('page' => $page))); ?>">
        <?php echo $page; ?>
    </a> |
  <?php } else { ?>
    <?php echo $page; ?> |
  <?php }}?>

<!-- Next page link -->
<?php if (isset($this->next)){ ?>
  <a href="<?php echo $this->url(
      array_merge($params, array('page' => $this->next))); ?>">
    Next &gt;
  </a>
<?php } else { ?>
  <span class="disabled">Next &gt;</span>
<?php } ?>
</div>
<?php } ?>

Now you need to render the control. You render the pagination control using the Zend_View's paginationControl() helper, which takes three arguments: the paginator instance, the control type, and the partial script to render. Render the pagination control between the filter form and the results table, as shown in Listing 4-24.

Example 4.24. Rendering the Pagination Control in application/views/scripts/bug/list.phtml

<?php echo $this->paginationControl($this->paginator,
     'Sliding',
     'partials/pagination-control.phtml'), ?>

Now all you need to do is update the partialLoop() method that renders the table rows to use the paginator. Since the paginator is using the DbTableSelect adapter, the data is in the same format. You just pass the paginator to the partialLoop() helper in the place of the result set, as shown in Listing 4-25.

Example 4.25. The Updated partialLoop in application/views/scripts/bug/list.phtml

<?php echo $this->partialLoop('bug/table-row.phtml', $this->paginator); ?>

Once you have completed this, add a number of test bugs, and then check out http://localhost/bug/list. If everything worked correctly, you should see something like Figure 4-2.

The completed list view

Figure 4.2. The completed list view

Updating a Bug

When you created the bug list, you added links to edit and delete bugs. The edit link calls the editAction() method in the bug controller. You need to do a few things to implement this functionality. First, the bug report form does not have any way of identifying the current record, so you need to add a hidden ID field to it. Second, you need to create the edit action and view script to render and process the form.

Updating the Bug Report Form

To add an ID field to the bug report form, open the form, and add the hidden ID control to the beginning of the init() function, as shown in Listing 4-26.

Example 4.26. The ID Control in application/forms/BugReportForm.php

$id = $this->createElement('hidden', 'id'),
$this->addElement($id);

Creating the Edit Action

Next, to create the editAction() method in the bug controller, first create the action with Zend_Tool, as shown in Listing 4-27.

Example 4.27. Creating the Bug Controller Edit Action with Zend_Tool

zf create  action edit bug

The edit action is similar to the submit action, except it needs to open the selected item and then populate the form with its values, as shown in Listing 4-28. Note that Zend_Db_Table_Row has a helpful toArray() method, which makes populating the form with a database row very easy.

Example 4.28. The editAction() Method in application/controllers/BugController.php

public function editAction()
{
    $bugModel = new Model_Bug();
    $bugReportForm = new Form_BugReportForm();
    $bugReportForm->setAction('/bug/edit'),
    $bugReportForm->setMethod('post'),
    $id = $this->_request->getParam('id'),
    $bug = $bugModel->find($id)->current();
    $bugReportForm->populate($bug->toArray());
    //format the date field
    $frmBugReport->getElement('date')->setValue(date('m-d-Y', $bug->date));
    $this->view->bug = $bug;
    $this->view->form = $bugReportForm;
}

Creating the Edit View

The edit view script is similar to the submit view script. The only difference is the verbiage; the form handles rendering the different form actions, and so on. Update the edit.phtml file, as shown in Listing 4-29.

Example 4.29. The Edit View Script in application/views/scripts/bug/edit.phtml

<h2>Edit a bug report</h2>
<p>To update this bug report make any changes you need to in this form,
    and then click update.</p>
<?php
echo $this->form->render();
?>

Updating the Bug Record

Now you need to create a method in the Bug model to update an existing record. It will be virtually identical to the createBug() method except that you are going to pass the method an ID (the primary key), and it is going to find the row to update rather than creating a new one (see Listing 4-30).

Example 4.30. The updateBug() Method in application/models/Bug.php

public function updateBug($id, $name, $email, $date, $url,
    $description, $priority, $status)
{
    // find the row that matches the id
    $row = $this->find($id)->current();

    if($row) {
        // set the row data
        $row->author = $name;
        $row->email = $email;
        $d = new Zend_Date($date);
        $row->date = $d->get(Zend_Date::TIMESTAMP);
        $row->url = $url;
        $row->description = $description;
        $row->priority = $priority;
        $row->status = $status;

        // save the updated row
        $row->save();
    return true;
    } else {
        throw new Zend_Exception("Update function failed; could not find row!");
    }
}

With this method in place, you simply have to update the editAction() method in the bug controller to call this method on a postback (see Listing 4-31). This will function identically to the submitAction() method.

Example 4.31. The Updated editAction() Method in application/controllers/BugController.php

public function editAction()
{
    $bugModel = new Model_Bug();
    $bugReportForm = new Form_BugReportForm();
    $bugReportForm->setAction('/bug/edit'),
    $bugReportForm->setMethod('post'),
    if($this->getRequest()->isPost()) {
        if($bugReportForm->isValid($_POST)) {
            $bugModel = new Model_Bug();
            // if the form is valid then update the bug
            $result = $bugModel->updateBug(
                $bugReportForm->getValue('id'),
                $bugReportForm->getValue('author'),
                $bugReportForm->getValue('email'),
                $bugReportForm->getValue('date'),
                $bugReportForm->getValue('url'),
                $bugReportForm->getValue('description'),
                $bugReportForm->getValue('priority'),
                $bugReportForm->getValue('status')

            );
            return $this->_forward('list'),
        }
    } else {
        $id = $this->_request->getParam('id'),
        $bug = $bugModel->find($id)->current();
        $bugReportForm->populate($bug->toArray());
        //format the date field
        $bugReportForm->getElement('date')->setValue(date('m-d-Y', $bug->date));
    }
    $this->view->form = $bugReportForm;
}

Deleting a Bug

Finally, you need delete bugs. This is probably the easiest part of the CRUD functionality. You simply add a method to the model and an action to the controller. It does not need a view because it will redirect to the list once it has deleted the record.

Adding the Delete Method to the Bug Model

I always create a method in the model to delete records. For a simple example like this, it is not really necessary, but it is a good habit to get into. The reason I always create this method is when you delete items from a relational database, you often have to perform various validation and cleanup tasks. This method gives you a good place to keep them. You also wouldn't want to delete rows outside of the model class for encapsulation reasons.

The deleteBug() method first needs to find the row that is passed to it. If it finds this row, then it needs to delete it. Otherwise, it should throw an exception that the row was not found, as shown in Listing 4-32.

Example 4.32. The deleteBug() Method in application/models/Bug.php

public function deleteBug($id)
{
    // find the row that matches the id
    $row = $this->find($id)->current();
    if($row) {
        $row->delete();
        return true;
    } else {
        throw new Zend_Exception("Delete function failed; could not find row!");
    }
}

Creating the Bug Controller Delete Action

Now create the delete action in the bug controller using Zend_Tool (see Listing 4-33). Since this action does not require a view script, you can delete the script that is generated by the create action command.

Example 4.33. Creating the Bug Controller's Delete Action with Zend_Tool

zf create action delete bug

The delete action will be very straightforward. It simply needs to take the ID that was passed to it and call the Bug model's deleteBug() method. Once it deletes the record, it forwards back to the list view, as shown in Listing 4-34.

Example 4.34. The deleteAction() Method in application/controllers/BugController.php

public function deleteAction()
{
    $bugModel = new Model_Bug();
    $id = $this->_request->getParam('id'),
    $bugModel->deleteBug($id);
    return $this->_forward('list'),
}

Summary

In this chapter, you learned about the basics of data management with Zend Framework and added CRUD functionality to the bug form that you created in the previous chapter. This bug reporting tool served as a simple, real-life example of how Zend Framework handles data; it is not a necessary part of the CMS project, but the skills it introduced are.



[2] LAMP is Linux, Apache, MySQL, and Perl/PHP/Python. See http://en.wikipedia.org/wiki/LAMP_(software_bundle)

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

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