Validar Cgridview

Bunos días a todos.

Actualmente tengo un Cgridview alimentado con el DataProvider = $miModelo->search();, trabajando con consultas anidads (FK), y funcionando a la perfeccion.

Mi problema se da al utilizar el Filter, funciona de forma efectiva pero tengo campos que son solo números, entonces si ingreso otros caracteres a la busqueda, me produce un error en la consulta SQL. De que forma se podrá validar la información ingresada en los imputs superiores del CGridview?

Pense hacerlo en el controller, algo como esto:




$model=new Cliente('search');

		$model->unsetAttributes();  // clear any default values

		if(isset($_GET['Cliente'])){

				

			 $model->attributes=$_GET['Cliente'];

			 $valid=$model->validate();  

                    if($valid)

                   {      // Es porque DEBERIA salir todo Bien. 

                       $this->redirect('index');

                    }

                }



Ahora, una de las opciones pensada: una vez recibido los parametros por GET simplemente enviarlos a otro metodo que realice una validacion (siendo necesario validar todos los parametros de forma "manual") y si existe un dato que no corresponde, simplemente NO renderizar… Pero no encuentro que sea efectivo… Ademas me imagino YII debe tener mejores soluciones para este problema…

Espero haber expuesto de forma clara mi problema.

Toda sugerencia ayuda…

gracias.

Nunca he tenido ese problema con los filtros, creo que el error esta en la función search del model, y es porque haces la busqueda de forma manual o al menos eso creo.

Para evitar esos problemas puedes agregar las comillas simples al valor que vas a comparar por ejemplo

en vez de poner en el where “columna_numerica=$this->columna_numerica” por “columna_numerica=’$this->columna_numerica’”.

Si pones el codigo de la función search seria mas facil ayudarte.

Hola.

Intente lo que mencionas, pero lanza un excepcion YII, referente a la consulta, no deja ni cargar la vista.

Lo explico nuevamente, tengo unos campos que solo aceptan numero (celular1, telefono), en las rules del modelo esta restringido a solo numero, y el error que ocurre al utilizar los imputs de busqueda superior que trae el el CGridview (filer) con letras, es un error generado por la DB al utilizar un campos caracter, en donde debe ser entero, por eso mi necesidad de validar los inputs del grilla.

Como mencione antes, intente utilizar validate(), para que tomara las ruler del modelo pero no funciona…

la funcion search es:




	public function search()

	{

		$criteria=new CDbCriteria;

		$criteria->with = array('clientePersona');

		$criteria->compare('nombres',strtolower($this->nombre_search),true);  

		$criteria->compare('apellidos', strtolower($this->apellido_search),true);

		$criteria->compare('t.rut',$this->rut,true);

		$criteria->compare('tipo',$this->tipo,true);

		$criteria->compare('celular1',$this->celular1);

		$criteria->compare('celular2',$this->celular2);

		$criteria->compare('telefono',$this->telefono);

		$criteria->compare('email',$this->email,true); 

		$criteria->compare('comentario',$this->comentario,true);




		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

			'pagination'=>array('pageSize'=>6)

		));

	}



OJO, el filter si funciona, si yo ingreso numero telefonico en el imput correspondiente todo bien, el problema es si ingreso caracteres, eso lo que pretendo evitar.

Hasta ahora, lo único funcional que se me ocurrió es:

En el controlador




	public function actionAdmin($id = null)

	{

		$model=new Cliente('search');

		$model->unsetAttributes();  

		

		//$model->scenario = 'search';

		if(isset($_GET['Cliente'])){

			

			$model->attributes=$_GET ['Cliente'];


			//si no pasa validacion

			if(!$model->validarGridView())

			{ 

				$model->unsetAttributes();     

				$this->redirect('admin');

			}

		}

			

			

		$this->render('admin',array(

			'model'=>$model,

			'tipoCliente'=>$id

		));

	}



y luego en el modelo, definí una función para validar




	public function validarGridView(){

		

			if($this->celular1 != '' || $this->celular2 != '' || $this->telefono != '')

			{

				//si no son numeros, validar retorna falso

				if(!is_numeric($this->celular1) || !is_numeric($this->celular2) || !is_numeric($this->telefono)){

					return false;

				}

			}

		

			return true;

	}



Es una solución "parche" porque imagino que YII tiene algo mas automatizado o no?

Saludos.

respecto a:

"…Es una solución "parche" porque imagino que YII tiene algo mas automatizado o no?.."

SI.

Solucion1: usa los rules() y una funcion de validacion especializada segun escenario. al llamar a validate() se validara con este metodo provisto aca




	// dentro de Cliente.php


	public function rules(){

      	return array(

          	array('celular1, celular2, telefono', 'validacionSearch', 'on'=>'search'),

      	);

	}


	public function validacionSearch($attrs, $params) {

           	$valor = trim($this[$attr]);

            	if($valor == "")

                	$this->addError($attr, "debe proveer un valor");

            	if(!is_numeric($valor))

                 	$this->addError($attr, "debe proveer un valor numerico");

	}



Solucion2:

Usar el modelado y el subclassing para hacer un override del metodo validate(), agregando tu personalización expuesta en validarGridView() pero usando el atributo $this->scenario.




	// dentro de Cliente.php

	public function validate($attributes = null, $clearErrors = true ) {

          $ok = parent::validate($attributes, $clearErrors);

       	if(!$ok)

         	return false;  // algun error detectado en tus rules()

        	if($this->scenario == 'search'){

          	if($this->celular1 != '' || $this->celular2 != '' || $this->telefono != '')

            	if(!is_numeric($this->celular1) || !is_numeric($this->celular2) || !is_numeric($this->telefono)){

                 	// agrega un error por cada campo fallido.

                 	$this->addError("celular1", "debe existir y ser numerico estricto");

                 	return false;

             	}

           	return true;

        	}else

        	return true;

	}



respecto a:

"…Es una solución "parche" porque imagino que YII tiene algo mas automatizado o no?.."

SI.

Solucion1: usa los rules() y una funcion de validacion especializada segun escenario. al llamar a validate() se validara con este metodo provisto aca




	// dentro de Cliente.php


	public function rules(){

      	return array(

          	array('celular1, celular2, telefono', 'validacionSearch', 'on'=>'search'),

      	);

	}


	public function validacionSearch($attr, $params) {

           	$valor = trim($this[$attr]);

            	if($valor == "")

                	$this->addError($attr, "debe proveer un valor");

            	if(!is_numeric($valor))

                 	$this->addError($attr, "debe proveer un valor numerico");

	}



Solucion2:

Usar el modelado y el subclassing para hacer un override del metodo validate(), agregando tu personalización expuesta en validarGridView() pero usando el atributo $this->scenario.




	// dentro de Cliente.php

	public function validate($attributes = null, $clearErrors = true ) {

          $ok = parent::validate($attributes, $clearErrors);

       	if(!$ok)

         	return false;  // algun error detectado en tus rules()

        	if($this->scenario == 'search'){

          	if($this->celular1 != '' || $this->celular2 != '' || $this->telefono != '')

            	if(!is_numeric($this->celular1) || !is_numeric($this->celular2) || !is_numeric($this->telefono)){

                 	// agrega un error por cada campo fallido.

                 	$this->addError("celular1", "debe existir y ser numerico estricto");

                 	return false;

             	}

           	return true;

        	}else

        	return true;

	}



No veo nada malo en la función search, lo raro es que mysql que yo sepa no da ningun error con los tipos de datos en la clausula where, puedes hacer “SELECT * FROM tabla WHERE columna_entera=‘aaa’” y no te va a marcar error, pero si haces “SELECT * FROM tabla WHERE columna_entera=aaa” si te marca error por que interpreta “aaa” como una columna y no la encuentra en la tabla, pero no marca ningun error de tipos de datos.

No necesitas validar los datos para filtrar, si el usuario pone letras en un campo donde deben ser numeros pues simplemente no va a recibir datos.

¿Cual es el error que marca mysql?

Buenos días.

a18327, si en tu consulta SQL no genera error, es porque como fue definida la estructura de tu tabla, seguramente es del tipo varchar, en mi situacion tengo una columna de la tabla definida como entera, por lo tanto cualquier consulta SQL que ejecute haciendo referencia a esa columan, debo pasarle un entero, de lo contrario generar error por pasar otro tipo de datos, esto es independiente de cualquier lenguaje utilizado, ya que es a nivel base datos, es mas si realizas la consulta directa en la base datos, se genera el mismo error. Yo utilizo postgres como DB, pero no creo que mysql se comporte diferente.

bluyell: Efectivamente, mi primer post mencione la utilización de las RULES, pero el aplicar el validate, siempre me devuelve true estaba utilizando la rules:




  array('celular1, celular2, telefono', 'numerical', 'integerOnly'=>true, 'on'=>'search'),



luego en mi controller tenia:




$model=new Cliente('search');

                $model->unsetAttributes();  // clear any default values

                if(isset($_GET['Cliente'])){

                                

                         $model->attributes=$_GET['Cliente'];

                         $valid=$model->validate();  

                    if($valid)

                   {      // Es porque DEBERIA salir todo Bien. 

                       $this->redirect('index');

                    }

                }



y como mencione antes, validate simpre devolvia true, aunque yo pasara cualquier tipo de caracter, es como que no considerara la rule IntegerOnly antes definida.

Por lo que veo en tus soluciones, es similar a lo que habia echo, pero al verdadero estilo YII jajajaja, voy a definir una Ruler nueva, aciendo referencia al metodo validateCgridview que habia definido anteriormente, bueno aplicando las modificaciones correspondiente para que permita utilizarla. Osea aplicar la solucion 1.

respecto a la solucion 2, tienes toda la razon, podria haber sobrescrito validate, pero haciendo referencia al escenario de busqueda, no se como no lo habia pensando ajajjaja :lol:

Muchas gracias…

voy a realizar los cambios de inmmediato y les comento como me va… :)

Saludos nuevamente.

Implemente lo comentado, y sucede lo mismo, validate siguie devolviendo TRUE, siendo que deberia devolver lo contrario cuando ingresos letra en el campo de numeros… Como dije antes, es como que no quiere tomar la rules… Paso a detallar los codigos:

MODELO:




public function rules()

	{

		return array(

			array('rut, tipo, celular1', 'required'),

			array('celular1, celular2, telefono', 'numerical', 'integerOnly'=>true),

			array('rut', 'length', 'max'=>12),

			array('tipo', 'length', 'max'=>20),

			array('email', 'length', 'max'=>60),

			array('comentario', 'length', 'max'=>300),

			array('rut, tipo, celular1, celular2, telefono, email, comentario, nombre_search, apellido_search, razon_search,contacto_search', 'safe', 'on'=>'search'),

			array('celular1, celular2, telefono', 'validaGridView', 'on'=>'search'),

			

		);

	}




	public function validaGridView($attr, $params){

		

			$valor = trim($this[$attr]);

			if(!is_numeric($valor)){

				$this->addError($attr, "Debe ingresar solo numeros");

			}

			/*no se valida vacio ($valor==""), ya que es necesario para que el CGridview vuelva

			 a su estado inicial (con todos los registros) despues de una busqueda por filter*/

	 

	}



CONTROLADOR:




	public function actionAdmin($id = null)

	{

		$model=new Cliente('search');

		$model->unsetAttributes();  // clear any default values

		

		if(isset($_GET['Cliente'])){

			$model->attributes=$_GET ['Cliente'];


            if(!$model->validate())

            {    

				$this->redirect('NO pasa validacion');

            }else

			{	

				$this->redirect('INFO VALIDADO');

			}

			

		}

			

			

		$this->render('admin',array(

			'model'=>$model,

			'tipoCliente'=>$id

		));

	}



y por si acaso la vista:




$this->widget('zii.widgets.grid.CGridView', array(

	'id'=>'cliente-grid',

	'summaryText'=>false,

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'cssFile' => Yii::app()->baseUrl . '/css/gridView.css',

	'ajaxUpdate'=>true,

	'columns'=>array(

		'rut', 

		array(

			'name'=>'nombre_search',

			'value'=>'ucwords($data->clientePersona->nombres)',

		),

		array(

			'name'=>'apellido_search',

			'value'=>'ucwords($data->clientePersona->apellidos)',

		),

		array(

			'name'=>'celular1',

			'header'=>'Celular1',

			'value'=>'$data->celular1',

			'filter' => CHtml::activeTextField($model, 'celular1')

		),

		'celular2',

		'telefono',

		'email',

		array(//FALTA terminar.....

			'header'=>'Opciones',

			'class'=>'CButtonColumn',

			'template'=>'{delete}{update}{accion_nueva}', // botones a mostrar

            'updateButtonUrl'=>'Yii::app()->createUrl("/nombre_modelo/update?id=$data->rut" )', // url de la acción 'update'

            'deleteButtonUrl'=>'Yii::app()->createUrl("/nombre_modelo/delete?id=$data->rut" )', // url de la acción 'delete'

            'deleteConfirmation'=>'Seguro que quiere eliminar el elemento?', // mensaje de confirmación de borrado

            'afterDelete'=>'$.fn.yiiGridView.update("nombre-grid");', // actualiza el grid después de borrar

			'buttons'=>array(

					'accion_nueva' => array( //botón para la acción nueva

								'label'=>'<span class="iconfa-pencil"></span> NUEVO', // titulo del enlace del botón nuevo

								'url'=>'Yii::app()->createUrl("/nombre_modelo/accion_nueva?id=$data->rut" )', //url de la acción nueva

								),

					),

			

			

		),

	),

));

Resultados:

Hasta el momento la "solucion parche", es la única que presenta efectividad…

Estraño que validate no considere la regla definida… alguna idea?

Intento replicar tu problema, pero a mi me funciona solo con el numerical integerOnly; has una prueba con este codigo para ver los atributos del model


if(!$model->validate()){

    $a=print_r($model->attributes,true);

    throw new CHttpException(0,$a);

}else{

    $e=print_r($model->errors,true);

    throw new CHttpException(0,$e);

}

Estimado a18327, probé lo comentado… al utilizar letras, me devuelve el modelo completo con sus atributos, y al ingresar números ocurre lo mismo. Pero veo que ambos cometemos un grave error. En nuestras condiciones IF, incluimos negación, que al final y al acabo es lo mismo ya que tiene un else, entonces si no es una sera el otro, pero el problema empieza en esa parte…

Estuve mirando la documentación y modelo->validate() Solo devuelve un String con un JSON, por lo tanto el resultado que entrega es: si existe errores string JSON, si esta todo OK 1 (true)… Por lo tanto por eso siempre se cumplía la primera parte del if jajajajaj fail… :lol:

Ya, pero eso no es todo, una vez aclarado ese percance, el problema persistió, y se da con las rules, actualmente utilizo un scenario (search), por lo tanto las validaciones que solo necesito están declaradas bajo ese escenario, PEROOOO, y aquí esta el problema, al validar el modelo, validate utiliza las reglas del scenario + las reglas por defecto del modelo, y hay esta el problema ya que existe una regla required para varios otros campos.

Si se utiliza, escenarios no debería solo utilizar las reglas del scenario?

Ahora, un poco de código y menos bla bla:

CONTROLER:




	public function actionAdmin($id = null)

	{

		$model=new Cliente('search');

		$model->unsetAttributes(); 


		if(isset($_GET['Cliente'])){ 

			$model->attributes=$_GET ['Cliente'];			

			$model->scenario = "search"; //aunque ya fue indicado


                        if($model->validate()){ //todo bien....

				$a=print_r($model->attributes,true);

				throw new CHttpException(0,$a);

			}else{ //no paso validacion..

				$e=print_r($model->errors,true);

				throw new CHttpException(0,$e);

			}

			

		}			

		$this->render('admin',array(

			'model'=>$model,

			'tipoCliente'=>$id

		));

	}



MODELO:




	public function rules()

	{

		return array(

			array('celular1', 'numerical', 'integerOnly'=>true, 'on'=>'search'),

			#array('rut, tipo, celular1', 'required'),

			#array('celular1, celular2, telefono', 'numerical', 'integerOnly'=>true),

			array('rut', 'length', 'max'=>12),

			array('tipo', 'length', 'max'=>20),

			array('email', 'length', 'max'=>60),

			array('comentario', 'length', 'max'=>300),		

		);

	}



VISTA:

Como pueden ver, funciona bien :rolleyes: , pero se debe a que están comentada las otras rules.

pregunto nuevamente, Si yo creo un nuevo escenario (este caso search) NO deberia tomar las otras reglas? o a las que no se especifica un scanerio pasan por defecto para todos los otros escenarios creados? <_<

Que curioso, yo no he tenido problemas con esto, aunque en BBDD siempre guardo los teléfonos como char.

Respecto a las rules enlace:

  • una regla por defecto se aplica a todos los escenarios

  • una regla con ‘on’=>‘escenario1,…,escenarion’ -> se aplica a los escenarios seleccionados

  • una regla con ‘except’=>‘escenario1,…,escenarion’ -> se aplica a todos los escenarios excepto los seleccionados. (desde 1.11)

Si localizas el problema coméntalo porque estos "problemillas" hacen perder muchas horas, suerte.

Hola.

Si efectivamente, en el post anterior comente donde se ubica el problema, y prácticamente ya tenemos la solución, por lo general les envió código he imágenes para que quede todo documentado. ;)

Como dices tu (rahif), las reglas por defecto se aplican a todos los scenarios, y hay esta el problema. Mi modelo contiene reglas que no permite el correcto funcionamiento a los filter del CGridview, al no producirse de forma efectiva la validacion del modelo, y al utilizar el filter (celular1) con letras en campos de solo numero, me marcaba un error desde la DB por utilizar un valor no numero… Obiamente este error se evita si la validacion del modelo fuera correcta.

Por lo tanto: la solución la acabas de comentar, aplicar una excepción a las reglas por defecto, las cuales no están permitiendo la correcta validación. Por lo tanto tenemos:

MODELO:




	public function rules()

	{


		return array(

                        /* cambios aqui...*/

			array('celular1', 'numerical', 'integerOnly'=>true, 'on'=>'search'),

			array('rut, tipo, celular1', 'required', 'except'=>'search'),

			array('celular1, celular2, telefono', 'numerical', 'integerOnly'=>true,'except'=>'search'),

                        /*fin cambios...*/


			array('rut', 'length', 'max'=>12),

			array('tipo', 'length', 'max'=>20),

			array('email', 'length', 'max'=>60),

			array('comentario', 'length', 'max'=>300),

			array('rut, celular1, nombre_search, apellido_search, razon_search,contacto_search,celularContact_search,direccion_search,', 'safe', 'on'=>'search'),

			

		);

	}



CONTROLLER:




	public function actionAdmin($id = null)

	{

		$model=new Cliente('search');

		$model->unsetAttributes();  


		if(isset($_GET['Cliente'])){ 

			$model->attributes=$_GET ['Cliente'];

			

			if($model->validate()){ 

				//todo bien, validado OK

			}else{ 

                               //alguna validacion no corresponde

				$this->redirect('admin');

			}

			

		}	




		$this->render('admin',array(

			'model'=>$model,

			'tipoCliente'=>$id

		));

	}



Esta probado y funcionando impecable, gracias a todos. :D

Pues felicidades por conseguir resolverlo y agradecer el que publiques la solución.