Archivio mensile:Settembre 2010

Zend Form, jQuery and autocomplete

When a component in a Framework is inflexible and difficult to maintain I believe is time to change way. ZendX_JQuery is powerful but not in the jQuery UI Autocomplete. After a thorough discussion about Autocomplete now we are at a dead end and we can only wait a new release. I prefer to change my way and go ahead.

This is my different solution for Autocomplete UI in Zend Form (without ZendX_JQuery).

First of all I extended the Zend_Form with a general class My_Form where I can set all defaults values of decorators, filters, validators, etc. Then I added my createMyAutoComplete method:

class My_Form extends Zend_Form
{
    // autcomplete extension of idfield
    private $_acExtension = "_ac";
 
    /*
     *  Create My AutoComplete Elements
     */
        public function createMyAutoComplete($id, $source, $params=array())
        {
            $idAc = $id . $this->_acExtension; // support element id for autocomplete
            // Create autocomplete element
            $this->addElement('text', $idAc, $params);
            // Create autocomplete hidden element (for id field)
            $this->addElement('hidden', $id, array('decorators' => $this->_noElementDecorator));
 
            // Add my custom Javascript functions to onLoad
            $view = $this->getView();
            $view->jQuery()->addOnLoad("MyAutocomplete.initialize('".$id."', '".$source."', '".$this->_acExtension."');");
 
            // return id of element Autocomplete to manage in group, add params, ecc...
            return $idAc;
        }

Above you can see the createMyAutoComplete method to generate an autocomplete field and its support field id. The parameters are:

  • $id = the id of the element
  • $source = the source where to read data for autocomplete
  • $params = like the params of the Zend_Form (see below)

As we seen the method creates two elements, for example, if we set $id=”idcity” we will have:

  • idcity, hidden field (our real id)
  • idcity_ac, text field (support field, only for autocomplete feature)

Remember to enable jQuery in the view otherwise the $view->jQuery->addOnLoad() above does not work!

Then you can call this method in the Forms:

class Application_Form_User_City extends My_Form
{
	public function init()
	{
	         // Set the method for the display form to POST
	         $this->setAttrib('id', 'user_city_form')
				->setAttrib('class', 'f1n120');
 
	        // element: Description
	        $this->addElement('text', 'description', array(
	            'label'      => $this->translate('Description'),
	            'attribs'      => array('size' => 35),
	            'required'   => true,
	            'filters'    => array('StringTrim'),
	            'validators' => array('NotEmpty'),
	        ));
 
	       // element: City
               $idAc = $this->createMyAutoComplete( "idcity",
                                         "/utility/search.city",
                                         // set params of element
                                         array(
                                             'label'    => $this->translate('City'),
                                             'attribs'    => array('size' => 25),
                                             'required' => true,
                                             'ignore'   => true, // to ignore when submit
                                             'filters'    => array('StringTrim'),
                                             'validators' => array('NotEmpty'),
                                         )
                );
	}
}

So we can create quickly an AutoComplete element and its hidden field.
Note: Add ignore attrib not submit this field. So we’ll submit directly the id.

To finish I created a custom Javascript to manage the autocomplete element.
Include this one in your Javascript file:

    MyAutocomplete = {
        initialize: function(idelement, mysource, acext)
        {
            eac = '#' + idelement + acext;
            eac_id = '#' + idelement;
 
            $(eac).autocomplete({
                minLength: 0,
                source: mysource,
                focus: function(event, ui) {
                    $(eac).val(ui.item.label);
                    return false;
                },
                select: function(event, ui) {
                    $(eac).val(ui.item.label);
                    $(eac_id).val(ui.item.value);
                    return false;
                }
            })
            .data( "autocomplete" )._renderItem = function( ul, item ) {
                return $( "<li></li>" )
                    .data( "item.autocomplete", item )
                    .append( "<a>" + item.label + "</a>" )
                    .appendTo( ul );
            };
        }
    }

The ajax response from the source must be an array with two fields: label and value.
This js function prepare data for every single item (_renderItem) and set the the value on the hidden field (eac_id). Need 3 parameters:

  • idelement – the real id
  • source – where to read data
  • acext – the extension of the autocomplete field

This parameters are set automatically in the above My_Form class.
Try it!

Cerca errori di sintassi in un progetto PHP

Ho trovato questo simpatico snippet per verificare eventuali errori di sintassi del codice PHP.
Lanciate questo comando da riga di comando posizionandovi nella root del vostro progetto:

find . \( -name "*.php" -o -name "*.phtml" \) -exec php -l {} \;

[via hackix.com]

Ottimizzazione URL (SEO) con Zend Framework

Rimettendo mano sul framework oggi mi sono scontrato di nuovo con un problema che da tempo volevo risolvere. Come sappiamo gli url con cui chiamiamo i vari Controller di Zend Framework vengono richiamati con una codifica ben precisa:

http://www.m4ss.net/controller/action

In questo modo quindi chiamiamo tutti i Controller e le Action che desideriamo invocare. A questo indirizzo possiamo poi aggiungere i parametri in questo modo:

http://www.m4ss.net/controller/action/params1/valore1/params2/valore2

In pratica accodiamo il nome del parametro e a seguire il suo valore.

Ciò che mi premeva risolvere da tempo era il poter accorciare questo indirizzo quando chiamo il contenuto di un sito. Ipotizziamo di avere un sito semplice che richiama i vari contenuti in base ad un codice ottimizzato per il SEO. Un esempio di codice potrebbe essere:

Tale codice è presente in un campo di database e, tramite query, estraggo il contenuto richiesto. Nel mio sito avrò un Controller che si occupa di estrarre i contenuti in base a tale codice. Se utilizzassimo la route normale avremmo un indirizzo del tipo:

http://www.m4ss.net/content/get/code/zend-framework-teoria-di-base-front-controller-action

/content/get/code si ripete per ogni contenuto, allo stesso modo, senza alcuna differenza. Mi premeva accorciarlo un po’!

Ho risolto in questo modo, il mio Controller Content è così composto:

class ContentController extends Zend_Controller_Action
{
private $_myCode;
public function init()
{
// get my code
$this->_myCode = $this->getRequest()->getActionName();
// change action -> redirect always to index
$this->getRequest()->setActionName('index');
}
public function indexAction()
{
// get parameters
$cModel = new Model_ContentLang();
$myContent = $cModel->getByCode($this->_myCode);
$this->view->myC = $myContent;
}
}

In pratica intercetto l’action nella funzione init() di inizializzazione del Controller. Aggancio l’action facendolo diventare il mio codice e reimposto l’action inviando il tutto sempre ad index. In index poi prelevo il contenuto dal database in base al codice e lo invio alla View.

In questo modo l’url può essere semplificato in questo modo:

http://www.m4ss.net/content/zend-framework-teoria-di-base-front-controller-action