views:

355

answers:

3

Once you're OK with basic record form built after example from Tutorial, you realize you want more professionally designed Record Form. E.g. I don't want to duplicate record form for the same table in User and Admin areas.

1) Does anyone use some mechanism, possibly inheritance, to reduce duplication of almost similar admin and user forms? Is that burdensome or sometimes you better just do with copy-pasting?

2) Has anyone considered it to be a good idea to build some basic Record class

  • that can determine that among several record forms on this page, the current post is addressed specifically to this record form
  • that can distinguish between Edit or Delete buttons clicks in some organized fashion.

3) My current practice includes putting all form config code (decorators, validations, initial values) into constructor and form submit handling is put into a separate ProcessSubmit() method to free controller of needless code.

All the above addresses to some expected Record Form functionality and I wonder if there is any guideline, good sample app for such slightly more advanced record handling or people are still reinveting the wheel. Wondering how far you should go and where you should stop with such impovements...

A: 

I don't know what you mean with record form but as to the question of role specific form elements you can always add form elements in your controller depending on the role before you render the form.

if ($role == 'admin')
{
    $myFrom->addElement();
    //go on customize your element, add validators, filters, messages
}

$this->view->myForm = $myForm;
tharkun
why in controller? you can pass role to form class and then add elements depending on role
Irmantas
absolutely, sometimes I would sometimes I want to keep my form class more generic.
tharkun
+2  A: 

Couple of suggestions:

First of all - Use the init() function instead of constructors to add your elements when you are subclassing the form. The init() function happens after the parameters you pass to the class are set.

Second - Instead of subclassing your form - you can just set an "option" to enable the admin stuff:

class My_Record_Form extends Zend_Form {
    protected $_record = null;
    public function setRecord($record) {
      $this->_record = $record;
    }

    public function getRecord() {
      if ($this->_record === null || (!$this->_record instanceOf My_Record)) {
        throw new Exception("Record not set - or not the right type");
      }
      return $this->_record;
    }

    protected $_admin = false;
    public function setAdmin($admin) {
      $this->_admin = $admin;
    }

    public function getAdmin() { return $this->_admin; }

    public function init() {
      $record = $this->getRecord();

      $this->addElement(......);
      $this->addElement(......);
      $this->addElement(......);

      if ($this->getAdmin()) {
        $this->addElement(.....);
      }

      $this->setDefaults($record->toArray());
    }

    public function process(array $data) {
      if ($this->isValid($data)) {
        $record = $this->getRecord();
        if (isset($this->delete) && $this->delete->getValue()) {
          // delete button was clicked
          $record->delete();
          return true;
        }
        $record->setFromArray($this->getValues());
        $record->save();
        return true;
      }
    }
}

Then in your controller you can do something like:

$form = new My_Record_Form(array(
  'record'=>$record, 
  'admin'=>My_Auth::getInstance()->hasPermission($record, 'admin')
));

There is nothing "wrong" with making a My_Record_Admin_Form that handles the admin stuff as well - but I found this method keeps all the "record form" code in one single place, and a bit easier to maintain.

To answer section 2: The edit forms in my code are returned from a function of the model: $record->getEditForm() The controller code ends up looking a little like this:

  protected $_domain = null;
  protected function _getDomain($allowNew = false)
  {
    if ($this->_domain)
    {
      return $this->view->domain = $this->_domain;
    } else {
      $id = $this->_request->getParam('id');
      if (($id == 'new' || $id=='') && $allowNew)
      {
        MW_Auth::getInstance()->requirePrivilege($this->_table, 'create');
        $domain = $this->_table->createRow();
      } else {
        $domain = $this->_table->find($id)->current();
        if (!$domain) throw new MW_Controller_404Exception('Domain not found');      
      }
      return $this->view->domain = $this->_domain = $domain;
    }
  }

  public function editAction()
  {
    $domain = $this->_getDomain(true);

    MW_Auth::getInstance()->requirePrivilege($domain,'edit');
    $form = $domain->getEditForm();

    if ($this->_request->isPost() && $form->process($this->_request->getPost()))
    {
      if ($form->delete && $form->delete->getValue())
      {
        return $this->_redirect($this->view->url(array(
          'controller'=>'domain', 
          'action'=>'index',
        ), null, true));
      } else {
        return $this->_redirect($this->view->url(array(
          'controller'=>'domain', 
          'action'=>'view',
          'id'=>$form->getDomain()->id,
        ), null, true));        
      }
    }    
    $this->view->form = $form;
  }

So - the actual id of the record is passed in the URI /domain/edit/id/10 for instance. If you were to put multiple of these forms on a page - you should make sure to set the "action" attribute of the form to point to an action specific to that form.

gnarf
+1  A: 

I created a SimpleTable extends Zend_Db_Table and SimpleForm extends Zend_Db_Form classes. Both of these assume that your table has an auto-incrementing ID column.

SimpleTable has a saveForm(SimpleForm $form) function which uses the dynamic binding to match form element names to the columns of the record. I also included an overridable saveFormCustom($form) for any special handling.

The SimpleForm has an abstract setup() which must be overridden to setup the form. I use the init() to do the initial setup (such as adding the hidden ID field).

However, to be honest, I really don't like using the Zend_Form object, I feel like that should be handled in the View, not the Model or Controller.

jeef3