Zend_Dom_Query Bug Found – assertQueryCount Doubles

This is wrong. There is no bug. The problem was caused by calling a parent test method within the child, so this created TWO requests to the server, which means, the web content was doubled.

So while writing some test cases for one of my applications I encountered a problem while trying to count the number of

So when I would run

$this->assertQueryCount("select#myselectoption", 2);

it would fail. I got around this by just multiplying the expected value by two and submitted a bug report to Zend (http://framework.zend.com/issues/browse/ZF-9867). Hopefully this is actually a bug and not some mistake on my part 😉

Setting Up FirePHP with Zend and PHPUnitTest

So FirePHP is a pretty awesome add on for Firefox. Basically I can just send all my error_log(), print, echo etc statements to FireBug’s console window instead of the application output or error log. To set it up, just do the following:

1) Download FireBug and FirePHP for Firefox.

2) In your bootstrap add the following function (if you’re not using functions, just add the code within somewhere before you initialize your front controller.)


protected function _initLog()
{
// If you don't have this, 99% chance your unit tests are going to explode and be wrong. I haven't investigated thoroughly yet.
date_default_timezone_set('GMT');

$writer = new Zend_Log_Writer_Firebug();
$logger = new Zend_Log($writer);

$profiler = new Zend_Db_Profiler_Firebug('All Database Queries:');
$profiler->setEnabled(true);

// Check if development env for FirePHP logging (not testing or production)
if(APPLICATION_ENVIRONMENT != 'development'){
$writer->setEnabled(false);
$profiler->setEnabled(false);
}

Zend_Registry::set('logger', $logger);
Zend_Registry::set('profiler', $profiler);
}

3) To start logging, just call the following in your application code

Zend_Registry::get('logger')->log('Hello FirePHP', Zend_Log::INFO);

Step 3 gets skipped in the production and testing environments.

Write / Read Custom Data – Zend_Session_SaveHandler_DbTable

I couldn’t find this anywhere so I’ll post it here. When storing information with sessions you sometimes might want to keep a user id, real user name, etc as part of the session. To accomplish this using Zend and a session database table:

1) Set up the database: http://framework.zend.com/manual/1.10/en/zend.session.savehandler.dbtable.html

2) To save custom data:

// Create the table adapter and then authenticate the login.
$adapter = $this->getAuthAdapter($db, $this->view->form->getValues());
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);

// If a successful login, store custom session data into the session table's datacolumn.
if($result->isValid()) {
$results = $adapter->getResultRowObject(array('id', 'username', 'firstname', 'lastname'));
$mysession = new Zend_Session_Namespace('mysession');
$mysession->id = $results->id;
$mysession->username = $results->username;
$mysession->firstname = $results->firstname ;
$mysession->lastname = $results->lastname ;
}

3) To get the custom data:

$mysession = new Zend_Session_Namespace('mysession');
echo $mysession ->id;
echo $mysession ->username;
echo $mysession ->firstname;
echo $mysession ->lastname;

Creating A Dynamic Grouped Form Element

So a common thing that I’ve always done in my forms is create a group of inputs that can be dynamically created again (through JS). For example I would allow users to add N number of contacts. To add a new one the user would click on “add” and JS would do its DOM creation and add all the new elements with name=”myname[]”. I wanted to do this with Zend and still have the JS generated content reshow up on a form submission. I’ve searched forever on how to do this, so hopefully this helps someone. (It’s probably not the best way, but it does work.)

These two tutorial helped greatly:

http://weierophinney.net/matthew/archives/212-The-simplest-Zend_Form-decorator.html (custom elements)
http://www.jeremykendall.net/2009/01/19/dynamically-adding-elements-to-zend-form/ (ajax)

Form HTML to reproduce:





The Person Element Class:

addPrefixPath(
'My_Form_Decorator',
'My/Form/Decorator',
'decorator'
);

parent::__construct($spec, $options);
$this->_fnameID = parent::getID().'FirstName';
$this->_lnameID = parent::getID().'LastName';
$this->_emailID = parent::getID().'Email';
}

public function loadDefaultDecorators()
{
if ($this->loadDefaultDecoratorsIsDisabled()) {
return;
}
$decorators = $this->getDecorators();
if (empty($decorators)) {
$this->addDecorator($this->getDecoratorName())
->addDecorator('Errors')
->addDecorator('Description', array(
'tag' => 'p',
'class' => 'description')
)
->addDecorator('HtmlTag', array(
'tag' => 'ul',
'id' => $this->getName())
);
}
}

protected function getDecoratorName(){
return 'Person';
}

public function getLabel($type){

if(isset($type)){
switch($type){
case 'firstName':
return $this->getFirstNameLabel();
case 'lastName':
return $this->getLastNameLabel();
case 'email':
return $this->getEmailLabel();
}
}
else {
return parent::getID();
// throw new Exception('Please choose a label.');
}

}

public function getID($type){

if(isset($type)){
switch($type){
case 'firstName':
return $this->getFirstNameID();
case 'lastName':
return $this->getLastNameID();
case 'email':
return $this->getEmailID();
}
}
else {
return parent::getID();
// throw new Exception('Please choose a label.');
}

}

public function getFirstNameID(){
return $this->_fnameID;
}

public function getFirstNameLabel(){
return $this->_fnameLabel;
}

public function setFirstName($value){
$this->_fname = $value;
return $this;
}

public function getFirstName(){
return $this->_fname;
}

public function getLastNameID(){
return $this->_lnameID;
}

public function getLastNameLabel(){
return $this->_lnameLabel;
}

public function setLastName($value){
$this->_lname = $value;
return $this;
}

public function getLastName(){
return $this->_lname;
}

public function getEmailID(){
return $this->_emailID;
}

public function getEmailLabel(){
return $this->_emailLabel;
}

public function setEmail($value){
$this->_email = $value;
return $this;
}

public function getEmail(){
return $this->_email;
}

public function setValue($values){
if(isset($values['firstName']) && isset($values['lastName']) && isset($values['emailName'])){
$this->setFirstName($values['firstName']);
$this->setLastName($values['lastName']);
$this->setEmail($values['email']);
}
else {
// What till validation, could add it here?
; //throw new Exception('Invaild Person, please provide a first name, last name and email.');
}

return $this;
}

public function getValue(){
return $this->getFirstName().' '.$this->getLastName().', '.$this->getEmail();
}

}

The People Decorator class:

%s';

public function render($content){
$el = $this->getElement();
if(!$el instanceof My_Form_Element_Person){
return $content;
}

$view = $el->getView();
if(!$view instanceof Zend_View_Interface){
return $content;
}
$markup = $this->wrap($this->renderFirstName($el).$this->renderLastName($el).$this->renderEmailName($el));

switch ($this->getPlacement()) {
case self::PREPEND:
return $markup . $this->getSeparator() . $content;
case self::APPEND:
default:
return $content . $this->getSeparator() . $markup;
}
}

protected function renderFirstName($el){
return $this->renderEl('firstName', $el->getFirstName(), $el);
}

protected function renderLastName($el){
return $this->renderEl('lastName', $el->getLastName(), $el);
}

protected function renderEmailName($el){
return $this->renderEl('email', $el->getEmail(), $el);
}

protected function renderEl($type, $value, $el){
$name = $el->getFullyQualifiedName();
return sprintf($this->_format, $el->getID($type), $el->getLabel($type), $el->getID($type), $name.'['.$type.']', $value);
}

protected function wrap($markup) { return '

  • '.$markup.'
  • '; }
    }

    Here is the DynamicController that handles the AJAX JS creation:

    disableLayout();
    $ajaxContext = $this->_helper->getHelper('AjaxContext');
    $ajaxContext->addActionContext('newfield', 'html')->initContext();

    $id = $this->_getParam('id', null);

    $element = new My_Form_Element_Person('person');

    // What is returned thru ajax
    $this->view->field = $element->__toString();
    }
    }

    My JS script (I use prototype) You can see how to do it with JQuery from on of the above tutorials:

    Event.observe('addElement', 'click',
    function() {
    ajaxAddField();
    }
    );
    // Get value of id - integer appended to dynamic form field names and ids
    var id = $("id").getValue();
    // Retrieve new element's html from controller
    function ajaxAddField() {

    // Update the database based on the Primary key with the new value.
    new Ajax.Request('/apply/index.php/dynamic/newfield',
    {
    method: 'post',
    parameters: {'id':'id='+id},
    onSuccess: function(el){
    $("addElement-label").insert(el.responseText);
    // Increment and store id
    $("id").val(++id);
    }
    });

    }

    The prevalidation method (in your Form class) that captures the dynamic content on submit:

    public function preValidation(array $data) {
    // Search $data for dynamically added fields using findFields callback
    $fnames = $data['person']['firstName'];
    $lnames = $data['person']['lastName'];
    $emails = $data['person']['email'];

    // For each person
    for($i=0;$isetFirstName($fnames[$i]);
    $el->setLastName($lnames[$i]);
    $el->setEmail($emails[$i]);
    $this->persons[] = $el;
    }
    }
    // Used to redisplay the new content.
    public function getPersons(){

    $els = $this->persons;
    $xhtml = '';
    foreach($els as $el => $e){
    $xhtml .= $e->__toString(); // Add HTML
    }
    return $xhtml;
    }

    In my main view I display all of the people by:

    form->getPersons() ?>

    For the newField action you need a viewscript with:

    field ?>

    That’s all of the pieces. If I get time better I’ll try to explain how to put them together. Please read this tutorial first, it will help you set up the AJAX stuff. Then just add a button element and a container and everything should work.

    What do you think??

    Zend_Form – Submitting Without MVC

    There was a time when I didn’t use MVC…


    class ContactForm extends Zend_Form {
    public function __construct($options = null) {
    parent::__construct($options);

    $this->setAction("test.php");
    $this->setMethod("post");

    $contact_name = new Zend_Form_Element_Text('contact_name');
    $contact_name->setLabel('Contact Name: ')
    ->setRequired(true)
    ->addValidator('NotEmpty');

    $submit = new Zend_Form_Element_Submit('submit');
    $submit->setLabel('Submit');

    $this->addElements(array($contact_name, $submit));
    }

    public function setView(Zend_View_Interface $view) {

    parent::setView($view);
    // Render each form element.
    foreach ($this as $item) {
    $item->setView($view);
    }
    return $this;
    }
    }
    session_start();
    if (isset($_POST['submit'])) {
    // Get the form object.
    $form = unserialize($_SESSION['myForm']);
    if ($form->isValid($_POST)) {
    echo 'success';
    }
    // Not valid, so populate the form with the sent values.
    else {
    $form->populate($formData);
    }
    }
    else {
    // Need to keep a handle on the form (I think Zend does this internally if you use the MVC approach)
    $form = new ContactForm();
    $_SESSION['myForm'] = serialize($form);
    }
    // Display your form.
    echo $form->render(new Zend_View());