Select multipli (o in cascata) in CakePHP con JQuery e AJAX

E’ un classico problema per i form da utilizzare nelle web application: selezionare in una select un elemento e popolare una seconda select con elementi ottenuti dalla scelta della prima.
Il tipico esempio è quello della scelta del Comune: per facilitare la vita all’utente gli si chiede di selezionare la provincia in una prima select per poi scegliere nella seconda tra i comuni della sola provincia selezionata.
Tra i vari modi di ottenere delle select in cascata, ho scelto di sfruttare JQuery, ma soprattutto gli oggetti messi a disposizione da CakePHP nella versione 1.3 per accedervi facilmente.

Installazione di JQuery

Si potrebbe scaricare la libreria sul proprio server e richiamarla da lì, ma preferisco richiamarle dal repository di Google.
[php]echo $this->Html->script(array(‘https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js’));[/php]
per inserirla nel layout, oppure

$this->Html->script(array('https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js'),false);

da utilizzare occasionalmente nella view.

Nel layout

Si deve aggiungere un comando per la scrittura della cache degli script prima del tag di chiusura

echo $this->Js->writeBuffer(); // Write cached scripts

Nel controller

Immaginiamo di dover scegliere la città per un utente, quindi nel controller Users richiamiamo l’heleper

var $helpers = array('Js' => array('Jquery'));

Popoliamo la prima select con i nomi delle province (districts)

	function add(){
		$this->set('districts',$this->User->City->District->find('list'));
	}

Per ottenere l’elenco delle città in base alla provincia selezionata si deve creare un metodo del controller Cities

function getCities(){
		$this->layout="ajax";
		$district_id = $this->data['City']['district_id'];
		$cities = $this->City->find('list',array('conditions'=>array('City.district_id'=>$district_id)));
		$this->set('cities',$cities);
}

Nella view Cities/getCities

foreach($cities as $k=>$v){

	echo "


";

}

Nella view Users/add

echo $this->Form->create();
echo $this->Form->input('district_id',array('empty'=>true));
echo $this->Form->input('city_id',array('type'=>'select'));
echo $this->Form->end('Invia');

A questo punto bisogna inserire il codice JQuery che attiva il meccanismo, ma lo facciamo utilizzando i metodi del component JS.
Inserire prima del form il seguente codice:

$this->Js->get('#UserDistrictId')->event('change',
				$this->Js->request(array('controller'=>'cities','action' => 'getCities'),
						array(
							'method' => 'POST',
							'type'=>'json',
							'async' => true,
							'update' => '#UserCityId',
							'dataExpression'=>true,
							'data'=>$this->Js->serializeForm(array('isForm' => true, 'inline' => true))
						)
					)
			);

Fare attenzione al nome delle select interessate, utilizzando la notazione CSS per la loro identificazione ‘#nomecampo’ e la convenzione di CakePHP.

5 comments for “Select multipli (o in cascata) in CakePHP con JQuery e AJAX

  1. 1 marzo 2012 at 22:37

    Ciao e scusa se ti rispondo con ritardo.
    Il tag è quello di chiusura del body ovvero . Di solito gli script si mettono qui per velocizzare il caricamento della pagina HTML.

    La relazione che ho utilizzato è la classica:

    Region hasMany District
    District hasMany City
    City hasMany User

    Quindi nella tabella dell’User avremo il campo city_id che identifica univocamente il comune che ci interessa.

  2. Andrea
    23 febbraio 2012 at 12:35

    Ciao! Solo due informazioni… “Si deve aggiungere un comando per la scrittura della cache degli script prima del tag di chiusura”. Quale tag di chiusura? head o body o un altro?
    E poi, come sono gestite le tabelle del database, e relative relazioni? C’è una tabella per le città, una per le province, e quella degli utenti a quale si lega nel model?
    Grazie!

  3. Andrea
    12 gennaio 2011 at 18:34

    Io sto cercando di fare funzionare la stessa cosa però su 1.2 ma di sicuro mi perdo in qualche passaggio.
    Allora in cities_controller.php

    function getCities() {

    $district_id = $this->data['City']['district_id'];
    debug($district_id);
    $cities = $this->City->find('list',array
    ('conditions' => array('City.district_id'=>$district_id)));
    $this->set('cities',$cities);

    }

    Mi creo la relativa view getCities.ctp

    $v){
    echo "$v";
    }
    ?>

    Nel controller in cui ho la funzione add, ovvero courses_controller.php inserisco


    function add() {
    if(!empty($this->data)) {

    if($this->Course->save($this->data))
    {
    $this->Session->setFlash(__('Corso salvato',true));
    $this->redirect(array('action' => 'index'));

    }
    else
    {
    $this->Session->setFlash('Errore');
    }

    }

    $this->set('districts',$this->Course->City->District->find('list'));
    }

    La relativa add.ctp del controller

    echo $form->create('Course');?>
    echo $form->input('district_id',array('empty'=>true));
    echo $form->input('city_id',array('type'=>'select'));
    echo $form->end('Crea Corso');

    echo $ajax->observeField('CourseDistrictId',
    array(
    'url' => array('controller'=>'cities','action'=> 'getcities'),
    'update' => 'CourseCityId',
    'frequency' => 0.2,

    )
    );
    $this->render('/cities/getCities');


    Ovviamente in default.ctp ho caricato

    echo $javascript->link('prototype',false);
    echo $javascript->link('scriptaculous',false);

    e in app_controller.php ho

    var $helpers = array('Html', 'Form', 'Ajax', 'Javascript', 'Session');

    Ho controllato il codice sorgente della pagina add.ctp renderizzato e ho

    District

    Ho letto il tuo post http://www.luizz.it/234/cakephp/controllare-lo-stato-di-un-campo-con-ajax-e-cakephp
    e credo di aver fatto le cose in modo corretto.
    Grazie in anticipo per l’aiuto.

  4. 11 gennaio 2011 at 10:44

    Bene, sei il benvenuto su questo sito !

  5. 11 gennaio 2011 at 9:49

    Grazie mille per la condivisione, è una cosa che probabilmente mi servirà tra un po’ ed ero pronto a perderci qualche ora.. adesso invece so da dove partire, con la pappa pronta :)

    Se al momento buono avrò qualche cosa di significativo da aggiungere risponderò qui

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *