En esta entrada vamos a explicar como asociar dos selects mediante ajax y al modificar el elemento seleccionado en uno de ellos cambie el contenido del otro.
Podemos ver el ejemplo de funcionamiento aquí.
Un ejemplo muy claro para esta situación sería dos selects, uno con provincias y el otro con localidades, lo que queremos es que al cambiar de provincia varíe la lista de localidades y muestre las que están en la provincia seleccionada.
Lo haremos utilizando el ajaxHelper, para que no sea necesario la recarga de la página y quede más atractivo.
Lo primero que haremos será definir los modelos de provincias ,localidades y un tercer modelo en el que usaremos los selects, por ejemplo podría ser un modelo de cliente, en el que al insertar un nuevo cliente tendríamos que elegir la provincia y localidad a la que pertenece.
Modelos
Modelo de provincia.
/app/models/provincia.php
class Provincia extends AppModel
{
var $name = 'Provincia';
}
Modelo de localidad.
/app/models/localidade.php
class Localidade extends AppModel
{
var $name = 'localidade';
}
Modelo de cliente.
/app/models/cliente.php
class Cliente extends AppModel
{
var $name = 'Cliente';
}
Controladores
Después de esto tendremos que definir los controladores.
Controlador de localidad.
/app/controllers/localidades_controller.php
class LocalidadesController extends AppController
{
var $name = 'Localidades';
}
Controlador de provincia.
/app/controllers/provincias_controller.php
class ProvinciasController extends AppController
{
var $name = 'Provincias';
}
Y por últino el controlador de clientes que será el que implementará la funcionalidad.
Controlador de cliente.
/app/controllers/clientes_controller.php
class ClientesController extends AppController
{
var $name = 'Clientes';
var $helpers = array('Ajax');
var $uses = array('Cliente','Provincia','Localidade');
function insertar()
{
$listadoProvincias = $this->Provincia->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
$this->set('provincias', Set::combine($listadoProvincias, "{n}.Provincia.id","{n}.Provincia.nombre"));
$primera_provincia = $this->Provincia->find(null,null,'nombre ASC');
$listadoLocalidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC','conditions'=>'Localidade.provincia_id='.$primera_provincia['Provincia']['id']));
$this->set('localidades', Set::combine($listadoLocalidades, "{n}.Localidade.id","{n}.Localidade.nombre"));
// RESTO DE LA FUNCIONALIDAD DE INSERCIÓN DE CLIENTES
}
function update_select()
{
if (!empty($this->data['Localidade']['provincia_id']))
{
$provincia_id = $this->data['Localidade']['provincia_id'];
$localidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC','conditions'=>array('provincia_id'=>$provincia_id)));
}
else
{
$localidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
}
$this->set('options', Set::combine($localidades, "{n}.Localidade.id","{n}.Localidade.nombre"));
$this->render('/elements/update_select', 'ajax');
}
}
Por un lado tenemos la función insertar, que será una función genérica de inserción de clientes. La parte que a nosotros nos interesa es en la que se inicializan las variables que después utilizaremos en los selects, estas deberán ser arrays en los que cada elemento tenda un identificador y su valor, para que el select pueda utilizarlos en la vista.
En la versión 1.1 de cakephp, esto se podía hacer mediante la función generateList del modelo, función que se ha eliminado en la 1.2.
Por ello ahora es necesario hacerlo en dos pasos, por un lado realizar la búsqueda con un findAll y después utilizar la función combine de la clase Set que genera un array con la estructura deseada, que será el que le pasemos a la vista.
$listadoProvincias = $this->Provincia->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
$this->set('provincias', Set::combine($listadoProvincias, "{n}.Provincia.id","{n}.Provincia.nombre"));
La segunda función update_select, es a la que llamará el ajax para actualizar el listado de localidades a partir de un identificador de provincia.
Como se puede ver en el código, la función coge el identificador de provincia del select y envía a la vista los arrays actualizados de provincias y localidades.
Vistas
Localidades y provincias no tendrán vistas asociadas, ya que no hay ninguna operación en el controlador.
En clientes tendremos que crear la vista para la operación insertar del controlador, esta podría ser algo como esto:
/app/views/clientes/insertar.ctp
echo $form->create('Inscripcione',array('action'=>'insertar'));
echo $form->inputs(array('legend'=>'Actualizar Provincias',
'Localidade.provincia_id' => array('label'=> 'Provincia','showEmpty'=>'false','id'=>'provincias'),
'Alumno.localidade_id' => array('label'=> 'Localidad','showEmpty'=>'false','id'=>'localidades'),
));
echo $form->end();
$options = array('url' => 'update_select','update' => 'localidades');
echo $ajax->observeField('provincias',$options);
La vista será muy sencilla, un formulario con dos selects, uno de localidades y otro de provincias y una llamada al ajaxHelper en la que se indica que cada vez que se modifique el valor del select provincias, se llame a la función update_select y actualice el select de localidades.
Este select se actualizará con los valores devueltos por la vista de update_select, que lo único que hace es imprimir todos los nombres de localidades devueltos por la función del controlador.
/app/views/elements/update_select.ctp
if(!empty($options)) {
foreach($options as $k => $v) {
echo "<option value='$k'>$v</option>";
}
}
En este enlace se puede ver un ejemplo de funcionamiento.