Utilizar cakephp sin base de datos

Hay ocasiones en las que se puede dar que nos enfrentemos a un proyecto que no necesita tener una base de datos asociada, bien porque es un proyecto sencillo, bien porque no requiere tener datos almacenados o simplemente porque el servidor en el que vamos a alojar la aplicación no dispone de una base de datos.
Esta razón no deberá ser un impedimento para seguir utilizando cakephp, con unos pequeños cambios podremos seguir usándolo normalmente.

En primer lugar tendremos que crear un nuevo datasource, estos ficheros van almacenados en el directorio “/app/models/datasources/dbo/”, será un fichero php cuyo nombre comience por dbo_, en nuestro caso lo llamaremos “dbo_mi_dbo.php”.

Tendrá el siguiente contenido:

class DboMiDbo extends DboSource
{
	function connect()
	{
		$this->connected = true;
		return $this->connected;
	}
	function disconnect()
	{
		$this->connected = false;
		return !$this->connected;
	}
}

Después de esto tendremos que cambiar el driver de la base de datos utilizada por nuestro proyecto cakephp, por defecto mysql. Para esto, en el archivo database.php que se encuentra en “/app/config/”, modificamos la variable $default, debería quedar así:

var $default = array(
		'driver' => 'mi_dbo',
		'persistent' => false,
		'host' => '',
		'port' => '',
		'login' => '',
		'password' => '',
		'database' => '',
		'schema' => '',
		'prefix' => '',
		'encoding' => ''
	);

Por último, en todos los modelos que creemos, tendremos que añadir la línea “var $useTable = false;“, para indicarle que ese modelo no va a tener una tabla asociada.

A partir de aquí podremos utilizar toda la potencia de cakephp sin preocuparnos de tener una base de datos instalada.

Validación en CakePHP 1.2 (III): Internacionalización de mensajes en el modelo

Continuando con los posts anteriores relativos a la validación y relacionado con el post en el que se trata la localización e internacionalización de nuestras aplicaciones, explicaremos en éste cómo realizar la internacionalización de los mensajes de error. Tenemos dos opciones para implementar este funcionamiento:

  1. Utilizar la función error del FormHelper (realizando la llamada desde la vista) o la función invalidate del modelo (si realizamos la validación manual desde el controlador).
  2. Utilizar el propio modelo como fuente de estos mensajes de error.

En el primero de los casos utilizaríamos el siguiente código para la vista y el controlador respectivamente:

$form->error('campo', __('MENSAJE_ERROR', true));
$this->Modelo->invalidate('campo', __('MENSAJE_ERROR', true));

y de esta forma buscaría esa clave del mensaje en el fichero de idioma.

La ventaja de hacerlo desde el modelo y no desde las vistas es que nos permite unificar los mensajes de error en cada campo y evitar la repetición de este código cuando estemos en formularios distintos que incluyan los mismos campos. El inconveniente es que en el modelo no se permite la utilización de la función __() de CakePHP.

Para solucionar este problema será necesario modificar la clase AppModel de la que extienden todos los modelos y que a su vez extiende de Model para redefinir la función invalidate (podemos ver la definición original en el api de CakePHP). Así, el fichero cake/libs/model/app_model.php quedaría como se muestra a continuación:

<?php
class AppModel extends Model {
   function invalidate($field, $value = true) {
      if (!is_array($this->validationErrors)) {
         $this->validationErrors = array();
      }
      $this->validationErrors[$field] = __($value, true);
   }
}
?>

Con este simple cambio, tenemos los mensajes de error del modelo internacionalizados.

Consultas complejas con cakephp

El framework cakephp tiene una definición de los modelos muy potente, lo que hace que sea muy fácil acceder a los datos de la base de datos, realizando consultas todo lo complejas que se desee.
En esta entrada hablaremos de todas las posiblidades, que tal vez no conozcáis, de establecer las condiciones de una consulta en cakephp.

Consulta sencilla

Las consultas más simples, que afectan a una única columna son fáciles de realizar, se resuelven con un find y la condición pasada como un parámetro en la llamada a este método.

De esta manera:

$this->Modelo->find('all',array('conditions'=>'Modelo.columna = valor'))

se generará un select con una condición

where Modelo.columna = valor

Consulta con varias condiciones separadas por AND

Surge la duda de ¿qué sucede si deseamos realizar una consulta con varias condiciones?, se podría hacer poniendo como segundo parámetro del array conditions una cadena con todas las condiciones concatenadas con AND entre ellas, pero cakephp ya pensó en eso y hay una manera más sencilla y más limpia de hacerlo, que es pasar un array con todas las condiciones que deseamos que cumpla la consulta, con el formato Modelo.columna => valor :

De esta manera:

$this->Modelo->find('all',array('conditions'=>array('Modelo.columna' => 'valor','Modelo.columna2' => 'valor2')))

Esta llamada generará una consulta con una condición

where Modelo.columna = valor1 AND Modelo.columna2 = valor2

Consulta utilizando <>, <=, >=, > o <

Si en lugar de querer que todas las columnas coincidan con los valores que se les pasan en el array, queremos que alguna columna sea distinta del valor, la condición debería tener este formato

$this->Modelo->find('all',array('conditions'=>array('Modelo.columna <>' => 'valor','Modelo.columna2' => 'valor2')))

Esta llamada generará una consulta con una condición

where Modelo.columna <> valor1 AND Modelo.columna2 = valor2

Este formato también permitiría además de utilizar el distinto de (<>), utlizar mayor que (>) ,mayor o igual que (>=), menor que (<) o menor o igual que (<=).

Consulta utilizando OR

Una nueva duda surge cuando se nos plantea, ¿qué pasa si en lugar de realizar una consulta con condiciones separadas con AND queremos crear una consulta con las condiciones separada por OR?, pues cakephp también permite hacerlo de manera muy sencilla, creando un find con el siguiente formato:

$this->Modelo->find('all',array('conditions'=>array('OR'=>array('Modelo.columna' => 'valor','Modelo.columna2' => 'valor2'))))

Esto creará una consulta con una condición

where Modelo.columna = valor OR Modelo.columna2 => valor2

Esta opción se puede utilizar con cualquiera de los operadores booleanos AND, OR, NOT, XOR.

Cakephp también permite otras opciones más complejas:

Consulta utilizando IN

¿Qué sucede si queremos utilizar una consulta en la que queramos que una columna coincida con una serie de valores?. Para esto, lo más sencillo sería utilizar la sentencia sql in.
Instintivamente la primera idea que podemos tener para crear una consulta de este tipo es generar una cadena con todos los valores que deseamos pasarle a la consulta y esto concatenarlo con Modelo.columna IN.
Cakephp ya había pensado en esto y ha hecho que sea mucho más sencillo que todo esto, para realizar una consulta con un IN como condición habrá que pasarle un array con los valores que queramos que se compruebe la coincidencia, de este modo:

$valores = array(1,2,3,4,5,6);
$this->Modelo->find('all',array('conditions'=>array('Modelo.columna' => $valores,'Modelo.columna2' => 'valor2')))
where Modelo.columna in (1,2,3,4,5,6) AND Modelo.columna2 => valor2

Consulta utilizando NOT IN

Si en lugar de que se compruebe la coincidencia con ciertos valores, se compruebe la no coincidencia (NOT IN), haremos la llamada de esta manera:

$valores = array(1,2,3,4,5,6);
$this->Modelo->find('all',array('conditions'=>array('NOT'=>array('Modelo.columna' => $valores),'Modelo.columna2' => 'valor2')))
where Modelo.columna in (1,2,3,4,5,6) AND Modelo.columna2 => valor2

Consulta utilizando BETWEEN

Podría darse la posibilidad de tener que comprobar si el valor de un campo está contenido entre dos valores dados (BETWEEN), para esto igual que para la expresión in se nos ocurriría crear una cadena concatenando los valores y la sentencia sql, pero cakephp permite una forma mucho más sencilla de crearlo:

$this->Modelo->find('all',array('conditions'=>array(array('Modelo.columna BETWEEN ? AND ?' => array(1,8)))))

Esta llamada al find crearía una consulta sql con la siguiente condición:

where Modelo.columna between 1 AND 8

Con todas estas opciones se podrían crear consultas tan complejas como deseemos sin tener que preocuparnos de generar cadenas con el código sql para concatenar a las condiciones, de una manera muy sencilla e intuitiva.

Plantéanos tus dudas.

El objetivo principal de este blog era, aparte de compartir nuestros conocimientos de cakephp, intentar ayudar a todos los que se les presenta alguna dificultad que no son capaces de resolver.

Hace tiempo que no escribimos ninguna entrada en el blog, casi nos dedicamos únicamente a resolver posibles dudas sobre las entradas ya escritas, esto es debido además de porque es una época en la que tenemos varios proyectos de desarrollo web activos, por la dificultad de encontrar algún tema interesante para vosotros que nos visitáis.

Por eso, desde aquí os invito a que nos plateéis vuestras dudas e ideas para nuevos artículos, ¡qué mejor para escribir un blog para nuestros visitantes que que sean ellos quienes nos indican los temas que más les interesan!

Validación en CakePHP 1.2 (II)

Continuando con el post Validación en CakePHP 1.2, explicaremos en esta ocasión cómo validar formatos de campos permitiendo que estén vacíos y cómo realizar una validación personalizada, introduciendo manualmente el código de validación.

Validaciones con campos vacíos

Hasta hoy, no habíamos encontrado la forma de validar formatos de campos, permitiendo que se encuentren vacíos, directamente en el modelo. Lo que hacíamos era hacer este tipo de comprobaciones manualmente, rompiendo un poco la flexibilidad que nos ofrece CakePHP para hacer las validaciones directamente en el modelo y sin volver a preocuparnos de ellas.

Ahora ya descubrimos la manera de hacerlo. Es una cosa muy simple que habíamos pasado por alto. Simplemente tenemos que pasar un parámetro a la función de validación, de la manera siguiente:

var $validate = array(
   'email' => array(
   'valid' => array( 'rule' => array('email'),
                             'allowEmpty' => true,
                             'message' => 'Formato email inválido')
                             )
              );

Validación a medida

En el modelo introduciremos el siguiente código:

var $validate = array(
    'campo' => array(
                  'valid' => array( 'rule' => 'reglaPropia',
                              'param1'=>'valor',
                              'message' => 'Mensaje de salida')
                              )
                    );

function reglaPropia($value, $params)
{
   $valid = false;
   if (!condicion){
      // Asignamos el valor a $valid en función de la condición que queramos establecer
   }
   return $valid;
}

A la función creada le hemos pasado dos argumentos, aunque el segundo podríamos no ponerlo si no es necesario. Ambos serán arrays, de manera que si queremos acceder al valor del campo a validar tendremos que leer $value['campo'] y para acceder a los parámetros $params['param1'] , pudiendo introducir tantos parámetros como sea necesario.

En esta función podemos utilizar también las funciones de validación de CakePHP. Por ejemplo:

$valid = Validation::custom($value['campo'], $params['param1']);

Nueva versión de Cakephp 1.2

Hace unos días CakePHP lanzaba la tercera candidata de la beta de la versión 1.2 de su framework.

Todavía no sabemos si aporta algo nuevo. Como en versiones anteriores corrige bugs y por lo que se puede ver en la noticia del lanzamiento aumenta hasta 10 veces la velocidad de la anterior candidata.

Como siempre, iremos comentando las nuevas características e impresiones que tengamos en el desarrollo páginas web, con esta nueva versión candidata.

Conexiones HTTP seguras (https) en CakePHP 1.2

Para el desarrollo de conexiones seguras de nuestra página web, vamos a utilizar un método que hemos visto en el weblog de Tanveer. Lo normal es que necesitemos que algunas de las páginas de la web sean seguras, como pueden ser formularios de contratación de servicios, y otras no lo sean, por ejemplo páginas simples de información de dichos servicios. Por lo que desarrollaremos un sistema que nos permita hacer el cambio entre unas y otras.

Utilizamos para implementar este mecanismo el componente de la Bakery de CakePHP para forzar conexiones seguras, y lo utilizaremos de la misma manera que se explica en este artículo, pero añadiéndole la funcionalidad inversa. Lo explicaremos paso a paso:

- Añadimos el componente ssl.php, en la carpeta app/controllers/components/ con el siguiente contenido:

<?php
class SslComponent extends Object {

   var $components = array('RequestHandler');

   var $Controller = null;

   function initialize(&$Controller) {
      $this->Controller = $Controller;
   }

   function force() {
      if(!$this->RequestHandler->isSSL()) {
        $this->Controller->redirect('https://'.$this->__url());
      }
   }

   function unforce() {
      if($this->RequestHandler->isSSL()) {
         $this->Controller->redirect('http://'.$this->__urll());
      }
   }

   function __url() {
      $port = env('SERVER_PORT') == 80 ? '' : ':'.env('SERVER_PORT');
      return env('SERVER_NAME').$port.env('REQUEST_URI');
   }

   function __urll() {
      $port = env('SERVER_PORT') == 443 ? '' : ':'.env('SERVER_PORT');
      return env('SERVER_NAME').$port.env('REQUEST_URI');
   }

}
?>

- Añadimos en el app_controller (cake/libs/controlller/app_controller.php) la redirección a conexión segura para aquellas acciones que nos interese. Ésta es otra diferencia con el artículo de la Bakery, ya que en ésta se hace esa redirección desde cada acción de cada controlador.

<?php
class AppController extends Controller {

   var $components = array( 'Ssl' );

   public function beforeRender(){
      $action = array( 'action1', 'action2' );
      if( in_array( $this->params['action'] , $action ) ){
         $this->Ssl->force();
      }else{
         $this->Ssl->unforce();
      }
   }

}
?>

Podríamos cambiar en este punto la condición por la que nos interese para nuestro caso, por ejemplo, que sean controladores (todas las acciones de ese controlador) los que necesiten añadir este tipo de conexión.

Crear pdfs en cakephp utilizando TCPDF

En nuestra experiencia en desarrollo web, se nos ha planteado proyectos que tenían como uno de sus requisitos la creación de un archivo pdf generado automáticamente, por ejemplo una factura de compra o un informe de ventas.

En un principio, comenzamos utilizando fpdf, una clase en php, totalmente gratuíta, que permite crear automáticamente archivos pdf y que además tenía un helper para cakephp 1.1.

Esta clase funcionaba increíblemente bien, pero como todos los proyectos que no se actualizan regularmente (hasta hace poco llevaban más de 4 años sin realizar cambios), se quedan desfasados.

Tenía un gran número de desventajas como que no aceptaba codificación UTF-8, o lo complejo que resultaba su uso.

De este proyecto surgió en 2002 una rama, TCPDF. Este proyecto ,también libre y gratuíto, en un principio solucionaba carencias del fpdf, pero finalmente se ha convertido en una versión mucho más potente, con gran cantidad de posibilidades como por ejemplo el soporte de UTF-8 o idiomas de derecha-izquierda, permite la utilización de códigos html e incluye una lista bastante amplia de fuentes libres, que podremos utilizar en nuestro pdf.

Para incluirlo en uno de nuestros proyectos y empezar a utilizarlo hay que llevar a cabo una serie de pasos bastante sencillos (añadir nuevas funcionalidades a un proyecto cakePHP se ha facilitado en la versión 1.2), en primer lugar bajar la última versión de TCPDF, descomprimirla en el directorio /app/vendors de nuestra aplicación.

El resultado debería ser /app/vendors/tcpdf/ conteniendo al menos, tcpdf.php y los directorios tcpdf/config y tcpdf/fonts

En segundo lugar tendremos que crear un layout para los pdf’s en /app/views/layouts/pdf.ctp, con este contenido

header("Content-type: application/pdf");
echo $content_for_layout;

En el controlador que queramos crear los pdf’s, crearemos una función para ello

function pdf()
{
      Configure::write('debug',0);
      $this->layout = 'pdf'; //this will use the pdf.ctp layout
      // Operaciones que deseamos realizar y variables que pasaremos a la vista.
      $this->render();
}

Por último una vista para esta función, con el siguiente contenido:

App::import('Vendor','tcpdf');
$tcpdf = new TCPDF();
$textfont = 'freesans';
$tcpdf->SetCreator(PDF_CREATOR);
$tcpdf->SetAuthor("autor");
$tcpdf->SetTitle("Título");
$tcpdf->SetSubject("Tutorial TCPDF en cakePHP");
$tcpdf->SetKeywords("TCPDF, PDF, cakePHP, ejemplo");
$tcpdf->setPrintHeader(false);
$tcpdf->setPrintFooter(false);
$tcpdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$tcpdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
$tcpdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
$tcpdf->setLanguageArray($l);
$tcpdf->AliasNbPages();
$tcpdf->AddPage();
$tcpdf->SetFont("freesans", "BI", 20);
$tcpdf->Cell(0,10,"Hola mundo",1,1,'C');
$tcpdf->Output("ejemplo.pdf", "I");

En este ejemplo se creará un pdf muy sencillo con un cuadrado con el texto hola mundo dentro.
Las posibilidades de TCPDF son muchas, para comprobar lo que es capaz de hacer, es recomendable ojear los ejemplos contenidos en la página y la documentación de la clase, que explica el funcionamiento de todos sus métodos.

En nuestra página de pruebas de cakephp hay un ejemplo sencillo de funcionamiento.

Actualización (9/2/2009) :

Errores frecuentes:

“TCPDF error: Some data has already been output to browser, can’t send PDF file”: Este error se produce cuando cakePHP envía algún dato antes de crear el pdf y enviarlo, esto puede ocurrir por una serie de razones que habrá que comprobar:

  • Realizar un debug o un echo, en la vista o controlador.Esto crea salida de datos por lo que se genera el error, para eso añadimos en la primera línea de la función del controlador Configure::write(’debug’,0); , función que lo que hace es cambiar el nivel de debug a modo producción, donde no se muestra ningún mensaje, ni de error ni warnings.
  • Existencia de caracteres de salto de línea o espacios después del símbolo de fín de archivo php, ?>. Para esto hay que comprobar todos los ficheros de nuestro proyecto y borrar todos los caracteres o saltos de línea que haya al final del fichero. Esta suele ser la razón más habitual para este error por lo que debemos comprobar todos los ficheros antes de tirar la toalla con tcpdf.

Integración de FCKeditor con CakePHP 1.2

En el desarrollo de páginas web muchas veces tenemos la necesidad de introducir un editor de texto, sobre todo cuando es un usuario que no tiene conocimientos de html y css el que tiene que editar un campo o valor que será visible en una página web.

Para ello utilizaremos el editor FCKeditor. Es un software gratuito, aunque también existe versión comercial, y que permite manipular el texto como si se tratase de MS Word o OpenOffice Writer, conocidos por la mayoría de los usuarios.

Haremos la integración de la siguiente manera:

1. Descargamos de la página web de FCKEditor el editor, lo descomprimimos y copiamos su contenido en /app/webroot/js. No es necesario copiar la carpeta samples, si no queremos. De esta manera hemos copiado en /app/webroot/js varios ficheros y una carpeta llamada editor.

2. Copiamos en /app/views/helpers el FckHelper y lo importamos en el controlador en el que queramos que se vea el editor. Necesitamos también importar el helper Ajax.

- FCKHelper (/app/views/helpers/fck.php):

class FckHelper extends HtmlHelper {

var $Width = 500;
var $Height = 300;

function load($id, $width=null, $height=null, $toolbar = 'Default') {
$did = Inflector::camelize(str_replace('/', '_', $id));
if($width){ $this->Width = $width; }
if($height){ $this->Height = $height; }
$js = $this->webroot.'js/';
return<<<FCK_CODE
<script type="text/javascript">
fckLoader_$did = function () {
var bFCKeditor_$did = new FCKeditor('$did');
bFCKeditor_$did.BasePath = '$js';
bFCKeditor_$did.ToolbarSet = '$toolbar';
bFCKeditor_$did.Width = $this->Width;
bFCKeditor_$did.Height = $this->Height;
bFCKeditor_$did.ReplaceTextarea();
}
fckLoader_$did();
</script>
FCK_CODE;
}
function fileBrowserInput($fieldName, $htmlAttributes = array(), $return = false) {
$output = $this->input($fieldName, $htmlAttributes, $return);
if (!isset($htmlAttributes['id'])) {
$htmlAttributes['id'] = $this->model . Inflector::camelize($this->field);
}
$output .= '<script type="text/javascript">';
$output .= "//<![CDATA[\n";
$output .= "function openFileBrowser(id){\n";
$output .= "var fck = new FCKeditor(id);\n";
$output .= "fck.BasePath = '".$this->webroot."js/'\n";
$output .= "var url = fck.BasePath + 'editor/filemanager/browser/default/browser.html?Type=Image&amp;Connector=connectors/php/connector.php';\n";
$output .= "var sOptions = 'toolbar=no,status=no,resizable=yes,dependent=yes,scrollbars=yes';\n";
$output .= "sOptions += ',width=640';\n";
$output .= "sOptions += ',height=480';\n";
$output .= "window.SetUrl = function(fileUrl){\n";
$output .= "\$(id).value = fileUrl;\n";
$output .= "}\n";
$output .= "var oWindow = window.open( url, 'FCKBrowseWindow', sOptions ) ;\n";
$output .= "}\n";
$output .= "//]]>\n";
$output .= '</script>';
$output .= '<a href="#" onclick="openFileBrowser(\''.$htmlAttributes['id'].'\'); return false;">select an image...</a>';
return $output;
}
}

- En el controlador:

var $helpers = array('Fck', 'Ajax');

3. Y en la vista, lo asociamos con el input que queramos a través de su identificador, enlazando previamente el fichero javascript correspondiente:

echo $javascript->link('fckeditor');
echo '<h1>Integración de FCKeditor con CakePHP 1.2</h1>';
echo $form->create('Cliente',array('action'=>''));
	echo $form->input('contenido',array('type'=>'textarea', 'label'=>'', 'id' => 'Contenido'));
	echo $fck->load('Contenido', 600, 300);
echo $form->end();

Ya tenemos nuestro editor, que podemos ver en este ejemplo.

Autocompletar ajax

En la entrada semanal de nuestro blog relacionado con cakephp, vamos a hablar sobre autocompletar los campos mediante ajax, una opción que puede simplificar bastante la inserción de datos por parte de los usuarios y que a su vez resulta bastante elegante.

El funcionamiento es sencillo, al introducir una letra en un campo de entrada se buscarán en la base de datos las coincidencias con el texto que hemos insertado, mostrándolas como una lista y dándonos la opción de elegir entre todos los resultados.

Esta opción se usa habitualmente con los listados de paises, en nuestro ejemplo utilizaremos, como en el ejemplo de actualizar un select con ajax, las provincias españolas.

En este enlace se puede comprobar el funcionamiento.

Añadirlo en nuestro proyecto es realmente sencillo, supongamos que tenemos un modelo, cuyo controlador tiene una operación y una vista asociada, que será donde queremos utilizarlo.

Para ello en la vista, añadimos este trozo de código dentro de un formulario:

 echo $form->label('provincias','Provincias');
 echo $ajax->autoComplete('nombre','autocomplete');

Añadimos un label que tendrá el texto Provincias y un campo de entrada autocomplete, este recibe como parámetros, el nombre del campo y como segundo parámetro la url donde se encuentra la función a la que se va a llamar cuando se introduzca una letra, en nuestro caso, la vamos a añadir en el mismo controlador que estamos utilizando, si no fuese así la dirección sería ‘/nombre_del_controlador/función’.

En segundo lugar, creamos la función autocomplete, dentro del controlador utilizado añadimos el siguiente código:

  function autocomplete()
  {
 	$str = trim($this->data['Provincia']['nombre']);
	$str = str_replace("á","a",$str);
	$str = str_replace("é","e",$str);
	$str = str_replace("í","i",$str);
	$str = str_replace("ó","o",$str);
	$str = str_replace("ú","u",$str);
	$str = str_replace("Á","A",$str);
	$str = str_replace("É","E",$str);
	$str = str_replace("Í","I",$str);
	$str = str_replace("Ó","O",$str);
	$str = str_replace("Ú","U",$str);
	$str = str_replace("Ñ","ñ",$str);
	$condicion = "REPLACE (REPLACE( REPLACE( REPLACE( REPLACE( REPLACE ( Provincia.nombre,'á','a'), 'é','e'),'í','i'),'ó','o'),'ú','u'),'Ñ','ñ') LIKE "$str%"";
	$this->set('provincias', Set::combine($this->Provincia->findAll($condicion,array('Provincia.nombre','Provincia.id'),'Provincia.nombre'), "{n}.Provincia.id","{n}.Provincia.nombre"));
	$this->render('autocomplete','ajax');
 }

La función es muy sencilla, realiza la búsqueda en la base de datos de todas las provincias cuyo nombre coincida con el texto introducido, pasado en $this->data con el nombre que se indica en el primer parámetro de la función autocomplete utilizada en la vista, para eso se añade en la condición la opción LIKE cadena% de sql.

Todas las opciones que añadimos anteriormente sirven para ignorar los acentos, tanto en la cadena que introducimos como en los nombres de las provincias almacenadas en la base de datos, con esto al realizar la búsqueda funcionará aunque cometamos fallos ortográficos.

Crearemos una vista para esta función, con una única línea de código, que crea una lista con todas las entradas del array.

 echo $html->nestedList($provincias);

Y por último añadiremos al css que estemos utilizando, dos clases que modificarán el aspecto del listado que hemos creado:

  div.auto_complete {
 	position :absolute;
 	width :250px;
 	background-color :white;
 	border :1px solid #888;
 	margin :0px;
 	padding :0px;
  }

 div.auto_complete ul{
	list-style: none;
	margin: 0px;
 }

 li.selected{
	background-color: #ffb;
 }

Con esto ya tendremos un campo de texto en el que al añadir texto se generará un listado con todas las entradas de la base de datos que coincidan con el texto introducido.