Zend Form

Hoy vamos a ver como armar los formularios. Para eso vamos a usar la carpeta forms dentro de nuestras vistas, ahí vamos a crear una clase para cada formulario.

Muchas ponen los formularios en el controller y algún que otro loco en el modelo, y también en los helpers, nosotros vamos a crear una clase para que resuelva nuestro problema en la carpeta forms de la vista del modulo correspondiente.

Nuestra clase va a tener un solo método init() y va a extender de Zend_Form.

El ejemplo a continuación es el de login de usuarios, con 3 elementos, usuario, password, y el botón de submit.

  1.  
  2. <?php
  3. class forms_Authentication extends Zend_Form
  4. {
  5.     public function init ()
  6.     {
  7.         /**
  8.          * Identificador para nuestro formulario
  9.          */
  10.         $this->setName(‘authentication’);
  11.         /**
  12.          * Creamos un elemento de tipo Text
  13.          * - Seteamos el texto que queremos que represente a este campo
  14.          * - Agregamos un filtro, que hace que cuando enviamos el
  15.          *   fomrulario ponga todo el valor del campo en minuscula
  16.          * - Tambien especificamos que el campo es obligatorio.
  17.          * - Y agregamos el validador para preguntar si el campo es un campo valido
  18.          */
  19.         $username = new Zend_Form_Element_Text(‘username’);
  20.         $username->setLabel(‘Email’)
  21.             ->addFilter(‘StringToLower’)
  22.             ->setRequired(true)
  23.             ->addValidator(‘NotEmpty’, true);
  24.         /**
  25.          * Creamos el elemento tipo password.
  26.          * Aca vamos a generar el campo para la clave
  27.          */
  28.         $password = new Zend_Form_Element_Password(‘password’);
  29.         $password->setLabel(‘Clave’)
  30.             ->setRequired(true)
  31.             ->addValidator(‘NotEmpty’, true);
  32.         /**
  33.          * El boton para enviar el formulario
  34.          */
  35.         $submit = new Zend_Form_Element_Submit(’submit’);
  36.         $submit->setLabel(‘Entrar’);
  37.         /**
  38.          * Agregamos los elementos al formulario
  39.          */
  40.         $this->addElements(array($username , $password , $submit));
  41.     }
  42. }
  43.  
  44.  

Como se habrán dado cuenta nuestro archivo se va a llamar Authentication.php

Cada vez que se instancie y se imprima este objeto nos va a dibujar automaticamente nuestro formulario.

Ahora vamos a llamar a mostrar el formulario en nuestras paginas.

Vamos al controller

  1.  
  2.  
  3. <?php
  4. class Admin_IndexController extends Blogzf_Controller_Action
  5. {
  6.     public function indexAction ()
  7.     {
  8.         /**
  9.           * Instanciamos nuestro formulario
  10.           */
  11.         $form = new forms_Authentication();
  12.         if ($this->_request->isPost()) {
  13.             $credentials = $this->_request->getPost();
  14.             if ($form->isValid($credentials)) {
  15.                 $authAdapter = new Zend_Auth_Adapter_DbTable(
  16.                     Zend_Db_Table::getDefaultAdapter(),
  17.                         ‘users’,‘username’,‘password’, ‘MD5(?) AND status="ENABLED"’);
  18.  
  19.                 // Set the input credential values to authenticate against
  20.                 $authAdapter->setIdentity($credentials[‘username’]);
  21.                 $authAdapter->setCredential($credentials[‘password’]);
  22.                 $result = Zend_Auth::getInstance()->authenticate( $authAdapter );
  23.                 if ($result->isValid()) {
  24.                     $this->_redirect(‘/admin/dashboard/’);
  25.                 }
  26.                 $this->_flashMessenger->addError(‘usuario incorrecto’);
  27.                 $form->populate($credentials);
  28.             } else {
  29.                 $this->_flashMessenger->addError(‘Hay datos invalidos o vacios’);
  30.                 $form->populate($credentials);
  31.             }
  32.         }
  33.         $this->view->form = $form;
  34.     }
  35. }
  36.  
  37.  

En nuestro indexAction vemos que lo primero que se hace es instanciar la clase forms_Authentication que creamos anteriormente. Seguidamente tenemos una linea que pregunta si hay datos enviados via post, en nuestro caso esta condicion va a ser falsa porque todavia no enviamos ningun formulario. Asi que vamos a explicar lo que hacemos en caso de que no se haya enviado el formulario aun.

  1. $this->view->form = $form;

Aca estamos pasando a la vista nuestro objeto Form. Para dibujar nuestro formulario desde el controller esto es lo unico que tenemos que hacer.

Ahora pasemos a la vista. Y veamos como queda de complejo nuestro index/index.phtml

  1. <?=$this->form?>

Esto solo y nos deberia mostrar lo siguiente.

login

Ahora que ya tenemos nuestro formulario vamos a completar datos y enviarlo. Completamos con cualquier dato. Pero para probar vamos a completar el usuario y no la clave. Para aprender a usar los Validators.

Si hacemos esto nos deberia aparecer el formulario resaltando el problema por el cual no proceso el formulario.

login-error

Y como hicimos esto?

Muy facil, cuando creamos el formulario, no especificamos a donde queremos que envie los datos, entonces los envia a si mismo.

Esto quiere decir que enviamos un post a indexAction, ahora el condicional if ($this->_request->isPost()) nos va a dar verdadero.

Lo siguiente que preguntamos es si los datos de nuestro formulario son validos, en el caso que lo sean vamos a seguir con el logueo de usuario. En caso contrario vamos a volver a mostrar el formulario con los datos que envio el usuario, y agregano los errores correspondientes, esto lo hacemos llamando al metodo populate de Zend_Form, que solo se encarga de completar los datos que vinieron por post en el elemento correspondiente, y los errores. Lamentablemente los errores estan en ingles, y por ahora vamos a dejarlo asi. Tambien agregamos un error con el flashHelper de Zsamer.

Ahora probemos completar todos los datos, si corresponden a los de la base de datos te va a loguear, y redireccionar a tu dashboard.

Zend Auth.

Una de las cosas que mas me sorprendio cuando empeze con Zend Framework, fue la cantidad de tutoriales sobre este componente. La verdad que no tiene mucha mistica, pero el uso que le damos en blogzf es bastante basico y comun,

Primero instanciamos la clase Zend_Auth_Adapter_DbTable, y le pasamos los datos de nuestra tabla donde guardamos los datos de los usuarios, seteamos la columna donde esta el identificador, y la columna donde esta la clave (credencial).

Ya con la instancia seteamos los datos que vinieron con el formulario.

Y si Zend_Auth::getInstance()->authenticate( $authAdapter )->isValid() nos da true, estamos logueados.

Facil?, si es Zend.

Ahora necesitamos preguntar en cada modulo que lo requiera si el usuario ya esta logueado, en caso que no lo este vamos a redireccionarlo a la pantalla de login. En nuestro sistema lo vamos a hacer desde el plugin de Blogzf_Controller_Plugin_Backoffice.

  1.  
  2.  
  3. $auth = Zend_Auth::getInstance();
  4. if ( $request->module == ‘admin’ ) {
  5.     if( !$auth->hasIdentity() && $request->controller != ‘index’ ) {
  6.         header(’Location: /admin/’);
  7.     } elseif ( $auth->hasIdentity()  && $request->controller == ‘index’ ) {
  8.         header(’Location: /admin/dashboard/’);
  9.     }
  10. }
  11.  
  12.  

Con esto es suficiente.

28 Responses to “Creando un blog con Zend Framework - Parte 5”

  1. Gabriel Says:

    Hola Pablo, muy buen aporte, siempre me doy una vuelta por tu blog. Me han quedado la siguientes dudas (estoy aprendiendo de a poquito).
    - Con que linea estas instanciando el objeto del formulario en el controller? no logro distinguir donde esta el “new objeto”,

    - Como te quedaria la primera linea de la clase del formulario, asi?
    class Authenciador.php extends Zend_Form.

    - otra cosita, como configuras el Zend para informarle, que la carpeta forms de las vistas, tambien va a tener clases?

    desde ya
    Muchas gracias.

  2. Pablo Morales Says:

    Hola Gabriel.

    La primer respuesta, es facil $form = new forms_Authentication(); esta en la linea 11 del controller.

    La primera y la segunda estan relacionadas. Al yo incluir mis vistas en view, Zend ya lo toma dentro del path, asi que con solo decirle forms_Authentication(), uno de los lugares donde va a buscar el archivo es en application/blog/view/, pero tambien va a buscar en library/, y en la de los modelos ;)

  3. Gabriel Says:

    Gracias por responder, pero hay algo que no me sale, cree una clase cual cabecera es forms_Editarpagina extends Zend_Form. Lo grabe en una carpeta a la cual cree dentro de mi carpeta views llamada forms (tratando de seguir tus pasos). ahora bien, cuando instancio un objeto de esa clase, mi ide ZEND Studio for Eclipse, lo reconoce, pero cuando lo intento ejecutar con el explorer, me dice que no la encuentra. es decir, esa buscando esa clase en my carpeta LIBRARY pero no en mi carpeta forms dentro de la views. ahora bien, como configuraste eso? y otra cosita, mi arbol de direcotorio es el siguiente:

    application
    - modules
    — Administracion
    — Controllers
    — Views
    — Models
    — FrotnEnd
    — Controllers
    — Views
    — Models

    Es decir tengo un views para cada uno de los dos modulos, ahora, como utilizar el formulario? es decir, a que carpeta forms va a apuntar cuando lo instancie? pienso que si estoy dentro del controlador administracion solo puede instanciar a los forms que este dentro de su propio view, y no del view del otro modulo. Esto es asi?

    Desde ya muchisimas gracias!

  4. Pablo Morales Says:

    Desde el predispatch haces $this->setScriptPath(’aplication/modules/Administracion/views’), y si tenes que agregar mas en vez del anterior haces

    $this->addScriptPath(’aplication/modules/administracion/views’);
    $this->addScriptPath(’aplication/modules/FrotnEnd/views’);

  5. Martin Says:

    Hola Pablo, excelente lo que he leido de tu blog. Estoy empezando con zf y tengo una pregunta sobre formularios. Todos los ejemplos que veo presentan los controles (text, select, etc) con echo $this->form, lo que los muestra como una lista para abajo. De qué forma es posible aplicar un diseño sobre los controles?, va por el lado de css o existe una manera de desplegarlos según uno quiera.
    Gracias por tu atención, saludos.

  6. Pablo Morales Says:

    Hola Martin.

    La forma que muestro yo los formularios usan los tags predeterminados. Pero si vos queres poder establecer tus propios tags, usando los decorators. Y para los css solo con agregar los css de forma correcta ya tendria que tomarte tu estilo, tambien podes decirle a un objeto que clase, o style usar. ;)

    Aca podes ver los decorators
    http://framework.zend.com/manual/en/zend.form.elements.html#zend.form.elements.decorators

    Mas info en la documentacion

  7. Gabriel Says:

    Hola Pablo, gracias por responder, intenté de implementar/ corregir lo ultimo que me comentaste en mi proyecto, y puse la siguiente linea:
    ” $this->view->setScriptPath(’aplication/modules/administracion/views’)” en el predistpach de mi controlador que instancia al formulario, pero me sigue diciendo el mismo error, que no encuentra la clase del formulario.

    Qué hice mal?

    PD: puse $this->view porque si ponia $this solamente, no podia encontrar el metodo setScriptPath
    Desde ya, muchas gracias.

  8. Gabriel Says:

    Segui intentando, y lo solucione, poniendolo en el bootstrap, agregando la siguiente linea:

    PATH_SEPARATOR . ‘..aplication/modules/administracion/views’

    pero nose si es lo correcto, que opinas? de otra forma no pude hacerlo andar :-(

    un abrazo.
    gracias.

  9. Martin Says:

    Gracias por la respuesta, es lo que necesito, saludos.

  10. Pablo Morales Says:

    @Martin: Perfecto!

    @Gabriel: Mira ahora que veo mi bootstrap yo lo tengo igual que vos. No me parece que sea la mejor solucion, pero todavia no le encontre nada mejor que eso, habra que investigar un poco mas. Gracias por aportar la solucion.

  11. Gabriel Says:

    Buenisimo pablo, si llego a encontrar algo mejor, lo aporto en algun comentario, si lo llegas a encontrar por favor agregalo a tu blog, ya que me paso periodicamente.!! = );
    Un abrazo.

  12. Gabi Says:

    Hola Pablo.

    En primer quiero felicitarte por el blog, tienes cosas muy interesantes. Llevo unos 3-4 días mirándome un poco el Zend Framework y aunque sigo con muchas dudas… Tú blog me ha ayudado mucho.

    Quería preguntarte acerca de los helpers y plugins. No se que concepto tienen respecto a Zend Framework. Por otro lado me ha quedado claro que Zend View maneja la parte visible, con respecto a que se encarga de procesar los archivos phtml. Por lo tanto todas las variables deben cargarse ya sea mediando $view -> assign(Mixed content[,mixed value]) donde $view hace una llamada previa a new Zend_View

    Y bueno de momento creo que con haber pillado eso es suficiente… Pero la autentificación, es decir el checkeo de un usuario esta logueado y para según eso actuar de una forma u otra… ¿Cómo debo hacerlo? ¿Mediante algún helper, plugin o similar?

    Un Saludo y muchas gracia spor tus esfuerzos.

  13. Pablo Morales Says:

    Hola Gabi, gracias por los elogios.

    Por un lado tenes dos tipos de Helper, los de la vista y los del controller, son para que puedas extender funcionalidades, nada del otro mundo, la forma que lo podes usar con ZF, es bastante comoda.

    Con respecto al login, en este proceso yo estoy chequeando si el usuario esta logueado desde el plugin del backend.

    Pero vos podes hacerlo en el predispatch de los controller que necesites autenticar.
    Y lo haces de esta manera

    $auth = Zend_Auth::getInstance();
    if( $auth->hasIdentity() ) {
    header(’Location: /admin/dashboard/’);
    }else {
    header(’Location: /admin/’);
    }

  14. paolo Says:

    holas pablo, como veras sigo con tus tuturiales que sigen estando muy buenos, mira volvi a tener otro error esta vez es cuando me estoy logenado me da este error

    La página no está redirigiendo adecuadamente

    Firefox ha detectado que el servidor está redirigiendo la solicitud a esta dirección de una manera en la que nunca terminará.

    * Este problema a veces está causado por desactivar o rechazar la recepción de cookies.

    pero en mi navegador estan habilitadas las cookies

    y salio el error cuando aumente estas lineas en el

    predispach

    if (UserModel::isLoggedIn()) {
    $this->view->loggedIn = true;
    $this->view->user = UserModel::getIdentity();
    }else {
    header(’Location: /index/index/’);
    }

    como podras ver mescle un poco de codigo del otro tuturial de blog en el capitulo 4 o 5 en logearse para dejar un comentario
    por fis si podrias darme una manito te lo agradece mucho una ves mas

  15. paolo Says:

    ups me confundi era mescla con el tuturial del
    Sistema de Login

  16. paolo Says:

    Holas otra ves, otra duda por fis mira logro hacer que el usuario se logee dentro el sistema pero cuando le doy a logout no mata la session o cookies que creo que es lo que se crea y si va a la pagina de login pero se sigen viendo los menus de usuario cual crees que sea i error?

  17. paolo Says:

    ah otra cosa no me manda los valores de usuario para poder mostralos en la vista

  18. paolo Says:

    hola pablo otra ves yo, que crees otro error que no se de donde viene resulta que cuando me deslogeo no ocurre tal cosa, que crees que este pasando?
    por fis ayudame
    aqui te va mi codigo

    [code]
    view->baseUrl = $this->_request->getBaseUrl();
    $this->user = new PersonaModel();
    }

    public function getForm() {
    return new LoginForm ( array (’action’ => ‘/index/login/’, ‘method’ => ‘post’ ) );
    }

    public function preDispatch() {

    if (PersonaModel::isLoggedIn()) {
    $this->view->loggedIn = true;
    $this->view->person = PersonaModel::getIdentity();
    }
    }

    public function indexAction() {
    $this->view->headTile = “Index Page”;
    $this->view->form = $this->getForm ();
    }

    public function loginAction() {
    $form = new LoginForm();
    if ($this->_request->isPost()) {
    $credentials = $this->_request->getPost();
    if ($form->isValid($credentials)) {
    try{
    $this->user->setMessage(’El nombre de Usuario y Password no coinciden.’, PersonaModel::NOT_IDENTITY);
    $this->user->setMessage(’La contraseña ingresada es incorrecta. Inténtelo de nuevo.’, PersonaModel::INVALID_CREDENTIAL);
    $this->user->setMessage(’Los campos de Usuario y Password no pueden dejarse en blanco.’, PersonaModel::INVALID_LOGIN);
    $this->user->login($credentials['username'], $credentials['password']);
    $this->_redirect(’index/login’);
    } catch(Exception $e){
    echo $responseLogin = $e->getMessage();
    $this->_redirect(’index’);
    }
    $this->view->responseLogin = $responseLogin;
    }
    }

    }

    public function logoutAction() {
    $this->user->logout();
    $this->loggedIn=false;
    $this->_redirect(’/index’);

    }
    }

    [/code]

    este es el PersonModel

    [code]
    “Not existent identity. A record with the supplied identity could not be found.”,
    self::INVALID_CREDENTIAL => “Invalid credential. Supplied credential is invalid.”,
    self::INVALID_USER => “Invalid User. Supplied credential is invalid”,
    self::INVALID_LOGIN => “Invalid Login. Fields are empty” );

    /**
    * @param string $messageString
    * @param string $messageKey OPTIONAL
    * @return UserModel
    * @throws Exception
    */
    public function setMessage($messageString, $messageKey = null)
    {
    if ($messageKey === null) {
    $keys = array_keys($this->_messages);
    $messageKey = current($keys);
    }
    if (!isset($this->_messages[$messageKey])) {
    throw new Exception(”No message exists for key ‘$messageKey’”);
    }
    $this->_messages[$messageKey] = $messageString;
    return $this;
    }

    /**
    * @param array $messages
    * @return UserModel
    */
    public function setMessages(array $messages)
    {
    foreach ($messages as $key => $message) {
    $this->setMessage($message, $key);
    }
    return $this;
    }

    public function login($nick, $password)
    {
    if (! empty ( $nick ) && ! empty ( $password )) {

    Zend_Loader::loadClass ( ‘Zend_Auth_Adapter_DbTable’ );

    $autAdapter = new Zend_Auth_Adapter_DbTable ( self::getAdapter () );

    $autAdapter->setTableName ( ‘persona’ );
    $autAdapter->setIdentityColumn ( ‘identificador’ );
    $autAdapter->setCredentialColumn ( ‘contrasenia’ );

    $autAdapter->setIdentity ( $nick );
    $autAdapter->setCredential ( md5 ( $password ) );
    $autAdapter->setCredentialTreatment(” AND estado=’Activo’”);
    $auth = Zend_Auth::getInstance ();
    $result = $auth->authenticate ( $autAdapter );

    switch ($result->getCode ())
    {
    case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND :
    throw new Exception ( $this->_messages [self::NOT_IDENTITY] );
    break;

    case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID :
    throw new Exception ( $this->_messages [self::INVALID_CREDENTIAL] );
    break;

    case Zend_Auth_Result::SUCCESS :
    if ($result->isValid ()) {
    $storage = new Zend_Auth_Storage_Session();
    $storage->write($autAdapter->getResultRowObject ());
    // $data = $autAdapter->getResultRowObject ();
    // $auth->getStorage ()->write ( $data );
    } else {
    throw new Exception ( $this->_messages [self::INVALID_USER] );
    }
    break;

    default :
    throw new Exception ( $this->_messages [self::INVALID_LOGIN] );
    break;
    }

    } else {
    throw new Exception ( $this->_messages [self::INVALID_LOGIN] );
    }
    return $this;
    }

    public static function logout()
    {
    // Zend_Auth::getInstance()->clearIdentity();
    // Zend_Auth_Storage_Session::clear();
    // Zend_Session::expireSessionCookie();
    $storage = new Zend_Auth_Storage_Session();
    $storage->clear();
    return $this;
    }

    public static function getIdentity()
    {
    $auth = Zend_Auth::getInstance();

    if ($auth->hasIdentity()) {
    return $auth->getIdentity();
    }
    return null;
    }

    public static function isLoggedIn()
    {
    return Zend_Auth::getInstance()->hasIdentity();
    }
    }

    [/code]

    las lineas comentadas son las que ya cambie sin exito
    plis ojala puedas ayudarme

  19. Pablo Morales Says:

    Hola Paolo, para desloguearte es asi

    Zend_Auth::getInstance()->clearIdentity();

    Saludos.

  20. Pablo Morales Says:

    Chequea que estes llamando bien a ese metodo Imprimeindo algo para saber si esta llamando bien a ese metodo

  21. paolo Says:

    una ves mas muchas gracias. tenias razon la accion a la que iva no era la correcta

  22. paolo Says:

    otro favor en el metodo predispach validamos que el usurio este logeado verdad que pasa si no lo esta como redirecciono a otro lugar?

  23. Pablo Morales Says:

    dentro de un controller podes hacer uso del metodo $this->redirect( ‘url donde quieras redireccionar’ )

  24. paolo Says:

    holas pablo una cosita mas mira yo le mando un array de esta manera

    $this->view->assign(’roles’,array(’admin’,’segundo’));

    despues del logeo y recupero de la siguiente manera en la vista pero no sale nada mas bien un error

    foreach ($this->roles as $rol) :
    echo $i++;
    $this->escape($rol->name);
    endforeach;

    y el error es

    Warning: Invalid argument supplied for foreach() in D:\MobiusWS\Quantum\application\default\views\scripts\index\Login.phtml on line 4

    que puede estar fallando envio bien los datos?
    tambien intente mandarlo de la siguiente manera

    $this->view->roles = array(’admin’,’segundo’);

    pero tampoco me llega nada

    y

  25. paolo Says:

    holas pablo, una ayudita con este viewhwlper pasa que estoy queriendo que mis formularios se muestren en una tabla y asi sale pero todos los testos me los pone en una linea u y no en filas

    user:______ pass:_______ nomb:_____________

    y asi
    yo quise que se vieran

    user:_________
    pas:________
    nom:________

    y lo hice de la siguiente manera :

    class UserForm extends Zend_Form {

    public $elementDecorators = array(
    ‘ViewHelper’,
    ‘Errors’,
    array(array(’data’ => ‘HtmlTag’), array(’tag’ => ‘td’, ‘class’ => ‘element’)),
    array(’Label’, array(’tag’ => ‘td’),
    array(array(’row’ => ‘HtmlTag’), array(’tag’ => ‘tr’))),
    );

    public $buttonDecorators = array(
    ‘ViewHelper’,
    array(array(’data’ => ‘HtmlTag’), array(’tag’ => ‘td’, ‘class’ => ‘element’)),
    // array(array(’label’ => ‘HtmlTag’), array(’tag’ => ‘td’, ‘placement’ => ‘prepend’)),
    array(array(’row’ => ‘HtmlTag’), array(’tag’ => ‘tr’)),
    );

    public function init()
    {
    $this->addElement(’text’, ‘username’, array(
    ‘decorators’ => $this->elementDecorators,
    ‘label’ => ‘Username:’)
    );
    $this->addElement(’password’, ‘firstname’, array(
    ‘decorators’ => $this->elementDecorators,
    ‘label’ => ‘First Name:’)
    );
    $this->addElement(’text’, ‘lastname’, array(
    ‘decorators’ => $this->elementDecorators,
    ‘label’ => ‘Last Name:’ ) );
    $this->addElement(’submit’, ’save’, array(
    ‘decorators’ => $this->buttonDecorators,
    ‘label’ => ‘Save’)
    );
    }

    public function loadDefaultDecorators()
    {
    $this->setDecorators(array(
    ‘FormElements’,
    array(’HtmlTag’, array(’tag’ => ‘table’)),
    ‘Form’,
    ));
    }

    }

    pero me sale todo en una linea alguna idea del porque?
    gracias de antemano.

  26. walter Says:

    Hola Pablo, muy bueno tu sitio, tengo una duda con el metodo populate().

    Como puedo hacer para traer los datos del action de validacion, al action del form (en caso de que los datos no sean correctos),
    se puede hacer con populate?

  27. Pablo Morales Says:

    @walter:

    Los datos del formualrio lso tenes en $form->getValue(’elemento’);

    y para popular todo haces $form->populate($form->getValues());

    Saludos.

Leave a Reply