Programming and Technology
RSS icon Home icon
  • Modular Zend Framework Application Skeleton (Part 2: CRUD)

    Posted on March 19th, 2008 brandon 9 comments

    This article has been superseded by an updated article with new examples

    This is the second part of a series of articles designed to introduce people to smart ways to develop using the Zend Framework. In the previous article, I went over the folder structure for a skeleton application that can be used and extended to create complex and flexible web applications. Now I will go over how I handle the most tedious part of designing web applications: CRUD (Create,Read,Update,Delete). All database driven applications required these operations in order to do anything useful and will generally make up the bulk of the code in any enterprise level web application.

    Naturally the place to start is at the database itself. Regardless of the type of database you use, you should always stick to some sort of convention. I’ve found the the Ruby on Rails framework has conceived of a sane convention within their ActiveRecord library, so I’ve adapted it for my own use here. Here are some general rules of thumb.

    1. Always define a column called ‘id’ - First of all this is required by Zend_Db_Table which is what I use as my ORM (Object Relational Mapper). Additionally, it serves as a great primary key and quick and dirty handle for retrieving records of almost any type. They make great foreign keys too which leads me to my next rule of thumb.

    2. Make foreign keys easy to spot, since I tend to use the id column from another table as my foreign key, I always suffix that column with ‘_id’. So if I have a table called ‘profiles’ which has a one to one relationship with another table called ‘users’, I would define a column called ‘user_id’ in ‘profiles’ that implies that it is a foreign key to ‘id’ in ‘users’.

    I should note that I tend to create an enhanced base class for my table models that extends Zend_Db_Table with some additional functionality or convenience functions that are not provided by the framework.

    Zend/Db/Table2.php:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/basetable.php could not be found]

    In this base class, I define two convenience functions get() and getCount() which I will use later in my controllers. The purpose of get() is to provide functionality similar to the way Ruby on Rails provides find() on a table model. This function simply takes an value that is used to retrieve a row by its primary key. getCount() simply returns the number of records that match the where criteria you pass it.

    Next, you want to write your models. You will have one model per table. Here is what a typical model would look like for me:

    Users.php:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/users.php could not be found]

    q

    Obviously this pretty simplistic and you will likely have much more to your models as well as some table relationships which aren’t shown here but this is meant to give you an idea for the purposes of CRUD operations. In this file I define two classes (User and Users): User represents a row or record while Users represents the table. You will also see that I have a create function defined in the table model and an update function defined in the row model. Both functions take raw post variables and assigns the submitted fields to corresponding columns in the table/row.

    The controller should look something like this:

    UserController.php:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/controller.php could not be found]

    In this controller there are 4 actions defined, 1 per CRUD operation. I write the actions in such a way that they can be reused for either GET or POST operations. For GET operations, the entire function will be executed which will cause the controller to execute the view for that action. For post operations, it will perform some of the function and then redirect to some arbitrary url rather than execute its own view. This method saves you from having to write 2 actions per CRUD operation, but again its a matter of preference, just update your templates to post back to the appropriate action.

    Let me break it down.

    Create:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/create.php could not be found]

    First the table model object is instantiated. We create an empty record using createRow() first for use by the template, this allows us to reuse the same template for both create and update operations (I will explain below). In a GET request, we simply assign this empty user record to a template variable which is used to construct a blank form. In a POST request, the values submitted in the variable array ‘user’ are passed to the create function of the Users object. This function should return a valid user row object.

    Both the GET and POST url are the same:
    http://(hostname)/user/create

    Edit:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/edit.php could not be found]

    Here instead of instantiating a blank record, we retrieve a user record using the id passed in as a GET variable. For GET requests we pass it to the template, for post requests, we simply update the record with the new submitted values and redirect away.

    The GET url looks something like:
    http://(hostname)/user/edit/id/(id)
    The POST url will simply be:
    http://(hostname)/user/edit (The id will be a hidden input in the form) but it can always be part of the url as well.

    Delete:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/delete.php could not be found]

    The delete action is almost identical to the edit action except that instead of updating the record, we delete it.

    View:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/view.php could not be found]

    The view action simply assigns the record retrieved by id to the template.

    This structure can be copied across all of your controllers with modifications wherever they are necessary. Since most of the logic is in your controllers, most of your controllers end up being pretty similar. If you were feeling particularly limber, you could probably share the functionality for multiple models in a single controller. I won’t cover that here.

    In a later article, I will demonstrate how to create scaffolding scripts that eliminate alot of the work in writing this kind of code.

    Moving on to the views, as expected your files would look something like this:

    • views/
      • templates/
        • user/
          • create.html
          • edit.html
          • delete.html
          • view.html
          • _user.html

    You may have noticed there is an extra template which does not correspond to any actions (_user.html). The purpose of this file is to contain what are called template macros. Macros are a feature of phptal which allows you to define blocks of html which can be reused in multiple templates. In this situation I define macros that will be shared by all user-controller related templates. Generally I have a convention where I place an underscore at the beginning of template files which contain macro ‘libraries’. I won’t go into much detail about how the macros work as you can read for yourself here.

    _user.html:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/_user.html could not be found]

    Here we define 3 macros, fields, css, and js. Fields will contain the inputs that go into the forms for Create and Edit. It makes sense to share these fields since they are identical on both forms. Using the tal:attributes, we are able to assign the value from the methods on the user object. When we are on the create form, user will be an empty record meaning all of the fields will be blank so we will be presented with a blank form. When we are on the edit form, user will be a record we retrieve from the database, so the users attributes will be prepopulated in the form inputs. We define macros for css and js so that the two forms can share any common styles or javascript.

    create.html:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/create.html could not be found]

    This is a full template for the create action that pulls in shared macros from _user.html.

    edit.html:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/edit.html could not be found]

    This is similar to create except that it references a different url in the form and includes a hidden value to remember the user id

    delete.html:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/delete.html could not be found]

    Delete is similar to edit except that we don’t need to show the fields. Instead we display the user’s username in a confirmation question.

    view.html:

    [The requested file http://misc.realmofzod.com/code/modular_zend_framework2/view.html could not be found]

    View is very simple. It simply shows the user attributes in some formatted fashion.

    This are obviously greatly simplified but you can see how alot of duplicated effort can be eliminated by sharing certain elements between templates. Also by using conventions you can avoid alot of conditional logic. For example, because we assign input names to arrays that correspond with the model they are for, you can hand off large groups of variables to various models without the controller ever needing to know what variables are actually in there. This also allows you to easily distinguish groups of inputs that are intended for different models while being able to mix them all on one form.

    Once you have a convention thats comfortable for you, you can make your life easier by creating scaffolding code that automates generating much of this code. I will go into that in a later article.

     

    9 responses to “Modular Zend Framework Application Skeleton (Part 2: CRUD)”

    1. How could we set form dynamically?

      In models/CategoryForm.php
      I have apply following code

      public function __construct($options = null)
      {
      $this->addElementPrefixPath(’App’, ‘App/’);
      parent::__construct($options);

      $this->setName(’category’);
      $this->setAttrib(’enctype’, ‘multipart/form-data’);

      Now how could we put code in phtml file so it would set properly.

    2. I’m not entirely sure what your trying to do. Can you provide more details on your problem and your implementation?

    3. My question is how could we pass form name and enctype dynamically? For more clarity below 2 lines I have added in model file.

      PHP Code:
      $this->setName(’category’);
      $this->setAttrib(’enctype’, ‘multipart/form-data’);

      Now I want to create .phtml file(view). Here I don’t want to use form; ?> Instead of I’m creating phtml file as per my format. How could I get form name and enctype in that form.

      <form id=”???” enctype=”???” action=”url(array(’action’=>’edit’)); ?>” method=”post” >

    4. Ok well if you are using the phptal view plugin it would look something like this:

      <form tal:attributes=”id form/id;name form/name;enctype form/enctype;action php:url(array(’action’ => ‘edit’))” method=”post”>

      If you are using standard phtml it would look like this:

      <form id=”<?php echo $this->form->id; ?>” name=”<?php echo $this->form->name; ?>” enctype=”<?php echo $this->form->enctype; ?>” action=”<?php echo this->url(array(’action’ => ‘id’)); ?>” method=”post”>

      This is minus properly escaping the variables which is good practice, and given that I avoid standard phtml I cannot confirm that this is 100% accurate but according to the zend manual it should work.

      Both examples assume that your form object has those fields and has been assigned properly in the controller via: $this->view->form = ….

      Hope that helps.

    5. Hi brandon,

      Thanks for your reply.

      I have found the way to get name and enctype of the form. you can get these details with help of getAttrib method.
      $this->form->getAttrib(’name’)
      $this->form->getAttrib(’enctype’)

      Cheers :)

    6. what happened to all the examples

    7. [...] http://blog.realmofzod.com/2008/03/19/modular-zend-framework-application-skeleton-part-2-crud/ [...]

    8. I can not see your example files, only “[The requested file http://misc.realmofzod.com/code/modular_zend_framework2/_user.html could not be found]“. Can you please look at this?

      Thanks,
      regards

    9. The examples were lost in a server migration. I will not be restoring them because I consider them obsolete. Please refer to the latest article in the series for the latest examples: http://blog.realmofzod.com/2009/04/08/modular-zend-framework-skeleton-2009/

    Leave a reply