Basic CRUD operations with Zend Framework

Hi everyone,

Here I am again. Yesterday a friend asked me how to create the basics CRUD (Create, Release, Update, Delete) operations using Zend Framework. As everybody now, a basic CRUD it’s a good way to understand some framework’s resources to a new ZF developer. As he is a newbie with Zend Framework, the example will be simpler, but complete.

I think this example resumes a lot of things of a basic usage of Zend Framework, like controllers, models – not exactly ;-), views and the magic Zend_Form, with form validation, filters and more.

Our application context will be a simple Blog using MySQL as database. Why blog again? Because it’s simple to understand, just it. Our blog application will have just the posts table, to show only the CRUD funcionallity. Comments and files can be reason to a future post.

We won’t worry about CSS or visual effects, even less Javascript or Ajax . It’s a simple post to explain a basic operation with the framework. Another resources will be subject to a new post. Let’s go!

Creating the Blog project

We’ll create the new blog project using the zf command line. For details using the command line see Using Zend_Tool On The Command Line.

zf create project blog

Download Zend Framework and copy the ZendFramework-1.11.8-minimal/library/Zend folder to your library folder, inside the Blog project.

You can share a Zend Framework folder just adding a new include_path inside index.php.

Configuring the database on the Bootstrap.php file

I don’t like much configuration files. So, I prefer to setup the database on my Bootstrap.php file. Let’s go.

PS: More about Bootstrap.php file.

<?php

/**
* application/Bootstrap.php  
*/  
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap  
{  

    protected function _initDatabase()  
    {  
        $adapter = Zend_Db::factory('pdo\_mysql', array(  
            'host' => 'localhost',  
            'username' => 'root',  
            'password' => '',  
            'dbname' => 'blog',  
            'charset' => 'utf8'

            /**  
            * Some options  
            * 'port' => '3307',  
            *'unix_socket' => '/tmp/mysql2.sock'  
            */  
        ));  
        Zend_Db_Table_Abstract::setDefaultAdapter($adapter); // setting up the db adapter to DbTable  
    }  

}

Creating the Post DbTable (like a model)

What is DbTables and how it works?

junior-mb:basic_crud juninhogr$ zf create db-table posts  
Please provide a value for $actualTableName  
zf> posts  
Note: The canonical model name that is used with other providers is "Posts"; not "posts" as supplied  
Creating a DbTable at /Library/WebServer/Documents/blog_posts/basic_crud/application/models/DbTable/Posts.php  
Updating project profile '/Library/WebServer/Documents/blog_posts/basic_crud/.zfproject.xml'

Setting up AutoLoader and some IncludePaths

To load DbTable (models) easier, let’s configure the ZF AutoLoader and add the models folder to the PHP include_path.

<?php

/**
 * application/Bootstrap.php  
 */  

class Bootstrap extends Zend\_Application\_Bootstrap_Bootstrap  
{  
    public function _initAutoloader()  
    {  
        $loader = Zend\_Loader\_Autoloader::getInstance();  
        $loader->setFallbackAutoloader(true);  
    }

    protected function _initDatabase()  
    {  
        // ...  
    }  
}

Take a look at 12 and 17 lines.

<?php

/**
 * public/index.php  
 */

// Define path to application directory  
defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment  
defined('APPLICATION_ENV') || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'development')); // change to development now

// Ensure library/ is on include_path  
set_include_path(implode(PATH_SEPARATOR, array(  
    realpath(APPLICATION_PATH . '/../library'),  
    realpath(APPLICATION_PATH . '/models/DbTable'), // we will just call 'new Posts()'  
    get_include_path(),  
)));

/** Zend_Application */  
require_once 'Zend/Application.php';

// Create application, bootstrap, and run  
$application = new Zend_Application(  
    APPLICATION_ENV,  
    APPLICATION_PATH . '/configs/application.ini'  
);  

$application->bootstrap()->run();

Let’s change our Post class name to just Posts, at line 5.

<?php

/**
 * application/models/DbTable/Posts.php  
 */  

class Posts extends Zend_Db_Table_Abstract  
{  
    protected $_name = 'posts';  
    /**  
    * Our posts' primary key is just 'id'.  
    * If you have another name to that field just use this line below:  
    * protected $_primary = 'post_id';  
    */

}

SQL Dump

First we created the database named ‘blog’. The SQL code below is the result of a Sequel Proexportation feature.

# Dump of table posts  
# ------------------------------------------------------------

CREATE TABLE `posts` (  
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,  
    `title` varchar(120) DEFAULT NULL,  
    `category` varchar(100) DEFAULT NULL,  
    `body` text,  
    `created` datetime DEFAULT NULL,  
    `updated` datetime DEFAULT NULL,  
    PRIMARY KEY (`id`)  
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Getting the Post form using Zend_Form

Let’s create a Zend_Form to represent our Posts model.

<?php

/**
 * controllers/PostsController.php  
 */  

class PostsController extends Zend_Controller_Action  
{  
    public function getForm()  
    {  
        $title = new Zend_Form_Element_Text('title');  
        $title->setLabel('Title')  
            ->setDescription('Just put the post title here')  
            ->setRequired(true) // required field  
            ->addValidator('StringLength', false, array(10, 120)) // min 10 max 120  
            ->addFilters(array('StringTrim'));

        $category = new Zend_Form_Element_Select('category');  
        $category->setLabel('Category')  
            ->setDescription('Select the post category')  
            ->setRequired(true)  
            ->setMultiOptions(array(  
                '' => ':: Select a category',  
                'php' => 'PHP',  
                'database' => 'Database',  
                'zf' => 'Zend Framework'  
                // ... more categories if you want  
            ))  
            ->addFilters(array('StringToLower', 'StringTrim')); // force to lowercase and trim

        $body = new Zend_Form_Element_Textarea('body');  
        $body->setLabel('Post')  
            ->setRequired(true)  
            ->setDescription('Your text')
            ->addFilters(array('HtmlEntities')); // remove HTML tags

        $submit = new Zend_Form_Element_Submit('submit');  
        $submit->setLabel('Post to Blog') // the button's value  
            ->setIgnore(true); // very usefull -> it will be ignored before insertion

        $form = new Zend_Form();  
        $form->addElements(array($title, $category, $body, $submit));  
            // ->setAction('') // you can set your action. We will let blank, to send the request to the same action

        return $form; // return the form  
    }  
}

Starting to create the Add feature

Let’s first show the form in view file.

<?php 

/**
 * controllers/PostsController.php  
 */  

class PostsController extends Zend_Controller_Action  
{  
    public function addAction()  
    {  
        $form = $this->getForm(); // getting the post form  
        $this->view->form = $form; // assigning the form to view  
    }

    // ... public function getForm()  
}  

<!-- the views/scripts/posts/add.phtml file -->
<?php echo $this->form ?>

Let’s create the add action and redirect to index action, our posts list, after insert. After do this, add some posts into your own application to test the results. In your browser go to something like this: http://localhost/blog_app_path/public/posts/add.

<?php

/**
 * controllers/PostsController.php  
 */  

class PostsController extends Zend_Controller_Action  
{  
    public function init() // called always before actions  
    {  
        $this->Posts = new Posts(); // DbTable  
    }

    public function addAction()  
    {  
        $form = $this->getForm(); // getting the post form

        if ($this->getRequest()->isPost()) { //is it a post request ?  
            $postData = $this->getRequest()->getPost(); // getting the $_POST data  
            if ($form->isValid($postData)) {  
                $formData = $form->getValues(); // data filtered  
                // created and updated fields  
                $formData += array('created' => date('Y-m-d H:i:s'), 'updated' => date('Y-m-d H:i:s'));  
                $this->Posts->insert($formData); // database insertion  
            }  
            else $form->populate($postData); // show errors and populate form with $postData  
        }

        $this->view->form = $form; // assigning the form to view  
    }

    public function indexAction()  
    {  
        // get all posts - the newer first  
        $this->view->posts = $this->Posts->fetchAll(null, 'created desc');  
    }

    public function getForm()  
    {  
        // function logic  
    }  
}

The posts/index.phtml file, showing the posts list.

<!-- application/views/scripts/posts/index.phtml -->

<h1>Posts</h1>

<table>
    <tr>
        <th>Title</th>
        <th>Created</th>
        <th></th>
    </tr>

    <?php foreach ($this->posts as $key => $post): ?>

    <tr>
        <td>
            <a href="<?php echo $this->baseUrl('/posts/show/id/' . $post->id) ?>">
                <?php echo $post->title ?>
            </a>
        </td>

        <td>
            <?php echo $post->created ?>
        </td>

        <td>
            <a href="<?php echo $this->baseUrl('/posts/edit/id/' . $post->id) ?>">Edit</a>
            <a href="<?php echo $this->baseUrl('/posts/del/id/' . $post->id) ?>">Delete</a>
        </td>
    </tr>

    <?php endforeach ?>

</table>

Release, Update, Delete features

Now let’s create the actions show, edit, and del

<?php

/**
 * controllers/PostsController.php  
 */  

class PostsController extends Zend_Controller_Action  
{  
    // init(), addAction(), indexAction() 

    public function showAction()  
    {  
        $id = $this->getRequest()->getParam('id');  
        if ($id > 0) {  
            $post = $this->Posts->find($id)->current(); // or $this->Posts->fetchRow("id = $id");  
            $this->view->post = $post;  
        }  
        else $this->view->message = 'The post ID does not exist';  
    }

    public function editAction()  
    {  
        $form = $this->getForm();  
        $id = $this->getRequest()->getParam('id');

        if ($id > 0) {  
            if ($this->getRequest()->isPost()) { // update form submit  
                $postData = $this->getRequest()->getPost();  
                if ($form->isValid($postData)) {  
                    $formData = $form->getValues();  
                    $formData += array('updated' => date('Y-m-d H:i:s'));  
                    $this->Posts->update($formData, "id = $id"); // update  
                    $this->_redirect('/posts/index');  
                }  
                else $form->populate($postData);  
            }  
            else {  
                $post = $this->Posts->find($id)->current();  
                $form->populate($post->toArray()); // populate method parameter has to be an array

                // add the id hidden field in the form  
                $hidden = new Zend_Form_Element_Hidden('id');  
                $hidden->setValue($id);

                $form->addElement($hidden);  
            }  
        }  
        else $this->view->message = 'The post ID does not exist';

        $this->view->form = $form;  
    }

    public function delAction()  
    {  
        $id = $this->getRequest()->getParam('id');  
        if ($id > 0) {  
            // option 1  
            /*$post = $this->Posts->find($id)->current();  
            $post->delete();*/

            // option 2  
            $this->Posts->delete("id = $id");
            $this->_redirect('/posts/index');  
        }  
    }

    // getForm()

}

… and the views show.phtml and edit.phtml. The delAction redirects to indexAction after insert, so it’s not necessary to create a view file to it.

/ application/views/scripts/posts/show.phtml

<h1>Show Post</h1>

<?php if ($this->post): ?>

    <table>
        <tr>
            <th>ID</th>
            <td><?php echo $this->post->id ?></td>

            <th>Title</th>
            <td><?php echo $this->post->title ?></td>

            <th>Body</th>
            <td><?php echo $this->post->body ?></td>

            <th>Post Date</th>
            <td><?php echo $this->post->created ?></td>

            <th>Updated Date</th>
            <td><?php echo $this->post->updated ?></td>
        </tr>
    </table>

<?php else: ?>

    <?php echo $this->message ?>

<?php endif ?>


// application/views/scripts/posts/edit.phtml


<h1>Editing Post</h1>

<?php if ($this->message): ?>

    <?php echo $this->message ?>

<?php endif ?>


<?php echo $this->form ?>

After that, test your posts CRUD and customize it according your needs.

Well, that’s all. Use Zend Framework it’s not hard and you have only understand the framework’s concepts. After that, search and study some components you like in the documentation and make miracles with this powerfull framework.

That is just an example what you can do with ZF. You can create a BaseController class and make this CRUD functionality extensible to a lot of DbTables and Controllers, using the OOP.

To get this application zipped file click here.

Thanks a lot! See you…

Published by

Junior Grossi

senior software engineer & stutterer conference speaker. happy husband & dad. maintains Corcel PHP, elePHPant.me and PHPMG. Engineering Manager @ Paddle

33 thoughts on “Basic CRUD operations with Zend Framework”

  1. Really, awesome tutorial to learn the ZF, I am enjoying with this, you can add one more thing to validate the id exists in the database or not, If some one going to edit the post and change the id in the URL, that is not present.
    Thanks for great tutorials.

    1. Hi Intizar!

      Sure. I will improve that post with the id validation.

      Thanks for the comment and welcome to the blog.

    1. Hi Kaius!

      Yes you can. Thia post is about CRUD operations you can or not use modules for that. The usage of modules will change only you URL but the thought won’t change.

      Thanks for the comment and you’re welcome.

      Regards.

  2. Hi,

    Like your post very much. I am new in ZF ,after searching more on google , finally this tutorial helped me to understand zend .

    Thanks

    1. Hi Seema. Thanks for the comments. I wish this blog help you learn more about this great framework. Thanks.

  3. hello, I’m at views/scripts/posts/add.phtml but going to http://localhost//public/posts/add – Zend generated an error “Zend_Controller_Exception: script ‘layout.phtml’ not found in path”.

    I’d already changed the values in application.ini to resources.layout.layoutPath = APPLICATION_PATH “/views/scripts”. Is this has something to do with the error above? TIA

    1. Hi Cxian…
      You are with layout enabled, so you have to create your layout.phtml file (where you’ll set header and footer elements) and point your ‘content’ that will be your add.phtml.
      Thanks for the comment.

  4. We are a group of volunteers and starting a new scheme in our community. Your web site offered us with valuable information to paintings on. You’ve performed a formidable task and our whole neighborhood will probably be grateful to you.

  5. Hello,
    I am new at zf. looking for help in making your app work above… i the a url error says:
    The requested URL /BCL_PHP_1/public/owners/show/id/1 was not found on this server.
    (i replaces your posts with owners) what am i missing so i dont point to my public folder here?
    can you help?
    thanks!
    Rose

    1. Hello Rose,
      Is your Apache’s mod_rewrite enabled? Check that and your .htaccess file too. Because the error message I think may be that.
      Thanks.

  6. hi,
    Thanks a lot..after searching lot on Google finally this tutorial helped me to understand zend better.

    I understand the whole tutorial but didn’t got why
    this else part is mentioned in editAction for what purpose, i am not able to get it..

    ***
    else {
    $post = $this->Posts->find($id)->current();
    $form->populate($post->toArray()); // populate method parameter has to be an array

    // add the id hidden field in the form
    $hidden = new Zend_Form_Element_Hidden(‘id’);
    $hidden->setValue($id);

    $form->addElement($hidden);
    }
    ***

    Please tell me why you have used this else part for what purpose.. i am really confused..

    1. Hi Pratik,

      First, thanks for the comment. I am sure Zend is one of the most complete PHP Frameworks… And welcome…

      About your question…

      – editAction line 36: here I have my post ID, right? I get the row from database and store it in $post. After, I populate the form with an array (my post object as an array).

      – editAction line 40: here I added one more element in the post form. I create an hidden element with the post ID as value and added the element on line 43.

      Thanks all. If more questions ask me. Welcome!

      []s Junior Grossi

    2. Oh sorry. I forgot the “else” part…

      On line 25 I make a verification if the request is post. If it is post request, the user is updating the post data, after fill the form fields, right? ELSE, or, if the request is differente from post (the user clicked on the edit link, and we have to show him the form with the post data), we have to populate the form with the data and add a hidden element with the ID of the element we want to update…

      That’s it!

      []s Junior Grossi

  7. Great article, thank you!

    I discovered that
    $dbTable->insert($data)
    fails if data array does not match the db table structure, and tried
    $dbTable->createRow($data)->save() instead
    It safely ignored non-existing columns.

    1. Hi Gregory!

      Thanks for your comment and welcome to my blog. Zend Framework is powerfull and every day we discovery a new feature hehe.

      Thanks a lot!

  8. Like your post very much but is there any particular reason why you performed the actual CRUD operation in the controller instead of Model (Posts class)? The controller would have looked less crowded if you would have included the CRUD functions in the Posts class.

    New to Zend, so i may be wrong.

    Thanks again for the great article.

    1. Hi Sumit,

      First, thanks so much for your reply. About the CRUD operations on the Controller (or Model), it’s just for explain how Zend_Controller and Zend_Form can work together. As I said on the post, it’s better Zend_Form be in a separated class, not in the controller. I agree that CRUD operations must be on the Model, but I put them in Controller just for easy understanding.

      Once again. Thanks for the reply!

      =)

  9. Somebody essentially help to make seriously posts I’d state. That is the first time I frequented your web page and up to now? I amazed with the analysis you made to create this particular submit incredible. Great job!

Leave a Reply to Junior Grossi Cancel reply

Your email address will not be published. Required fields are marked *