December 29th, 2011 Category: Curso Zend Framework
89 Comments »
En los anteriores capitulos habiamos usado el video como metodo de aprendizaje, hoy habiendo cubierto la parte inicial y basica de Zend Framework, creo que estamos preparados para dejar el video atras, y continuar los capitulos leyendo un poco.
En este capitulo vamos a ver como crear un sistema de login en nuestro proyecto. Nosotros actualmente en nuestro portal de noticias tenemos una pantalla donde publicamos nuestras noticias. Pero actualmente esa pantalla es accesible por cualquier persona.
El objetivo de este capitulo es crear una pantalla de login, y restringir el acceso a la administracion de las noticias solo a las personas que tengan usuario y contraseña.
Lo primero que vamos a hacer es crear un nuevo controller, UsuarioController.php
El codigo de este controller inicialmente sera asi.
class UsuarioController extends Zend_Controller_Action
{
public function init()
{
}
public function loginAction()
{
$form = new Application_Form_Login();
if( $this->getRequest()->isPost() ){
if( $form->isValid( $this->_getAllParams() )) {
// Do something
}
}
$this->view->form = $form;
}
}
Para este controller vamos a tener que crear una vista asi que creamos la carpeta en views/scripts/usuario y agregamos el archivo login.phtml, donde vamos a imprimir el formulario y mostrar un mensaje, el archivo quedaria asi
<div>
<h2>Ingresar al sistema</h2>
<?php echo $this->form ; ?>
</div>
Tambien vamos a necesitar un formulario para poder ingresar usuario y contraseña.
Creamos el archivo en forms/Login.php
Este formulario va a tener un input para el usuario otro para el contraseña, y el boton de submit, el formulario Login.php nos quedaria asi.
class Application_Form_Login extends Zend_Form
{
public function init()
{
$this->addElement(
'text', 'username', array(
'label' => 'Usuario:',
'required' => true
)
);
$this->addElement(
'password', 'password', array(
'label' => 'Contraseña:',
'required' => true
)
);
$this->addElement(
'submit', 'Ingresar', array()
);
}
}
Hasta ahora no vimos nada nuevo, es mas o menos el mismo mecanismo que usamos para crear un nuevo post. Si entramos en la url de nuestro proyecto /usuario/login vamos a ver el formulario de login como aparece en la siguiente imagen.

Ahora que tenemos el codigo vamos a crear una tabla donde se van a guardar todos los usuarios que van a tener permisos al panel de administracion de nuestro proyecto. El sql para crear la tabla es el siguiente.
CREATE TABLE IF NOT EXISTS `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(40) NOT NULL,
`password` char(40) NOT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
INSERT INTO `users` (`username`, `password`) VALUES
('pablo', '7c4a8d09ca3762af61e59520943dc26494f8941b');
Nuestra tabla de usuario es bastante sencilla, por ahora vamos a cumplir con los requerimientos minimos. El hash extraño que se ve es la contraseña “123456″ encriptada con la funcion sha1()
Ahora que ya tenemos un usuario (pablo) y una contraseña (123456), nos falta agregar la logica para que se valide si el usuario existe o no.
Zend Framework nos brinda el componente Zend_Auth el cual a partir de sus Adapters, nos permite validar un usuario y loguearlo en el sistema. La forma de usarlo es bastante simple, Zend Auth entiende identidad y credencial, donde la identidad es el nombre de usuario o mail que vamos a usar para identificar a un usuario dentro del sistema, y la credencia es la contraseña.
Nosotros vamos a usar Zend_Db_Adapter_DbTable el cual se encarga de validar el usuario y contraseña en nuestra base de datos.
El codigo cuando usemos Zend_Auth deberia ser el siguiente.
$authAdapter = new Zend_Auth_Adapter_DbTable();
$authAdapter
->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password');
$authAdapter
->setIdentity('pablo')
->setCredential(sha1('123456'));
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if( $result->isValid() ){
echo "Estamos Logueado!";
}else{
echo "Datos incorrecto";
}
Ahora este codigo vamos a adaptarlo a nuestro controller. Nos quedaria asi
class UsuarioController extends Zend_Controller_Action
{
public function init()
{
}
public function loginAction()
{
$form = new Application_Form_Login();
if( $this->getRequest()->isPost() ){
if( $form->isValid( $this->_getAllParams() )) {
$authAdapter = new Zend_Auth_Adapter_DbTable();
$authAdapter
->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password');
$authAdapter
->setIdentity($form->getValue('username'))
->setCredential(sha1($form->getValue('password')));
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if( $result->isValid() ){
return $this->_redirect('/posts/listar');
}else{
$form->username->addErrorMessage('Datos Incorrectos');
}
}
}
$this->view->form = $form;
}
}
Con este codigo cada vez que nos identifiquemos correctamente nos va a redirigir a /posts/listar donde vamos a mostrar todas las noticias.
Ahora los que nos faltaria agregar es que en cada lugar donde nosotros queramos restringir el acceso, agregar el chequeo si el usuario esta logueado o no, sino esta logueado vamos a redireccionarlo a la pantalla de login.
En cada action que querramos validar esto vamos a agregar el siguiente codigo.
$auth = Zend_Auth::getInstance();
if (! $auth->hasIdentity()) {
return $this->_redirect('/usuarios/login');
}
Notese que en el caso de PostsController, tenemos metodos que son de acceso publico y acceso privado, si llegado el caso tenemos un Controller donde necesitamos que todos los metodos sean de alcance privado, las lineas de arriba podemos escribirlas en el metodo init() de nuestro controller, ese metodo init se va a llamar antes de ejecutar un action de ese controller, y de esta forma evitamos repetir codigo.
Hay otras practicas para mejorar esto pero eso podemos verlo mas adelante.
Ahora que restringimos el acceso, vamos a necesitar la posiblidad de desloguearnos del sistema, para eso vamos agregar un link en nuestro layout.phtml en la parte del menu, que nos redireccione a /usuarios/logout y este es el codigo de nuestro action para desloguarnos.
public function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
return $this->_redirect('/usuario/login');
}
Bastante simple, no
, asi es todo con Zend Framework.
Ahora, vamos a dejarlo mas bonito a todo esto, y vamos validar en la vista si el usuario esta logueado, voy a mostrar un link a logout, si esta deslogueado voy a mostrar un link a la pantalla de login. El codigo a agregar en nuestro layout seria el siguiente.
<?php
$auth = Zend_Auth::getInstance();
if ( $auth->hasIdentity()) :
?>
<li>Usuario:<?php echo $auth->getIdentity() ?></li>
<li><a href="/usuario/logout">Logout</a>
<?php else: ?>
<li><a href="/usuario/login/">Login</a>
<?php endif;?>
Y con esto ya somos capaces de agregar un sistema de login en nuestros proyectos usando Zend Framework.
Ver todos los capitulos del curso

Comentar
89 Responses
Tambien recorda, que no es una buena practica tener metodos que no sean nombreAction() en los controllers, y menos privates, de ultima llama a los modelos.
private function getAuthAdapter()
{
...
}
dentro del controller, es mejor inyectarlo desde un modelo?? o sea esto esuna funcion particular para partir el codigo.
Cuando abris un controller la idea es que lo unico que veas es metodos Action, no funcionalidades extras. Para eso tenes las librerias y los modelos. Los controllers tienen que ser lo mas escueto posible.
Es un intento de ZF de tener su propio ORM, no me parece mal, salvo lo de getter y setter, creo que el Data Mapper es algo perfecto para usar.
El tema de getter y setter es una pelotudes que se implementa copiando a la gente de Java, totalmente inutil, y totalmente inecesario.
Tenemos magi method, o mejor es usar un toArray, o setFromArray o cosas parecidas. Yo tendria dos capas.
El DbTable que sea como el modelo de hoy en Dia, extendiendo de Zend_Db_Table, y un dataMapper no por cada tabla sino cada vez que lo amerite
Que diferencia hay entre colocar la validacion:
$auth = Zend_Auth::getInstance();
if (! $auth->hasIdentity()) {
return $this->_redirect('/usuarios/login');
}
en preDispatch() y en init() ??
No es mas seguro que este en preDispatch() ?.
Saludos
El metodo init lo ejecuta antes de ejecutar un action, deberia comportarse igual.
Pero podes agregarlo en el PreDispatch tambien, o de alguna forma mas personalizada en el Bootstrap, pero ahi tendrias que trabajar un poquito mas con la logica.
if( $result->isValid() ){
return $this->_redirect('/posts/listar');
}else{
$form->username->addErrorMessage('Datos Incorrectos');
}
No me muestra el error si coloco datos incorrectos, pero cambie la funcion addErrorMessage a addError y si funciona..
Saludos
me despido de usted deseandole exito en todo y ya le considero como un amigo mas porque me ha facilitado sus conocimientos para poder realizar mi tesis,de verdad gracias por su ayuda desinteresada. bueno le deso un Feliz Año Nuevo.
@fredyx007 Gracias, un gusto haber servido. LO de Zend_PDF podemos verlo mas adelante. La verdad es que nunca lo use.
Feliz año nuevo! y gracias!
Una vez mas te agradezco por este curso.
Feliz año nuevo!!! :)
Manito sigo guerriando con la vaina de la edicion de datos, listo me cargan los datos al campo de l form, pero cuando voy a actualizar con algun campo vacion, me genera erro no hace nada,s e supone que el form debe ser validado antes, que tendre mal mano.
@francisco cuando hablo de data mapper me refiero a esto http://martinfowler.com/eaaCatalog/dataMapper.html
El ejemplo que usan el quickstart de la pagina de Zend Framework, te muestra como usar el mapper, y los dbtable.
De antemano Gracios
Por lo que entiendo, se te da acceso a una url especifica en un puerto especifico. Vos deberias usar aglun reproductor flash seguramente para hacer streeming en esa url especifica.
Y claramente podrias embeberla en cualqueir vista del proyecto.
Esto tocando de oido, pero supongo que no es mucho mas que eso.
http://tecnoculto.com/wp-content/uploads/etapas_del_programador-26-06-2008.jpg
Pablo muchas gracias por este tutorial, sigue con tus excelentes contribuciones.
no se
Quisiera que porfavor me ayudaras con un tutorial o clase usando zend_acl y zend_navegation.He encontrado poca informacion acerca de esos componentes zend y ademas están en inglés.
Saludos..
@Alfredo Voy a tratar de agregar lo de Zend Acl para los proximos capitulos
@Nalleli No entendi el problema.
@Jonathan, gracias por el feedback, como le decia a Alfredo en los proximos voy a ver de incluir el tema de ACL
//Esta funcion ponela en el bootstrap..modifica algunos valores por los nombres que les hayas puesto a tus variables de autenticacion..
public function profileLink()
{
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$username = $auth->getStorage()->read('user');
return 'Bienvenido, ' . $username . ' | Logout';
}
return 'Login';
}
//Y despues yo en el layout hice lo siguiente..
Espero que te sirva, saludos!
https://code.google.com/p/midocu/downloads/list
@Nalleli, podes traerlo haciendo una busqueda en el modelo con el username
Podrías darme un ejemplo??
tengo muchas dudas sobre como ponerlo :(....
Gracias por el curso ...
Para cuando el siguiente capitulo ?
Este finde por ahi lanzo otro video.
pd: No me spamees el blog!!
pero me gustaria como guardar los datos de una sesion en una cookie, ya que estoy haciendo un carrito de compras en zend, pero los datos los esta gurdando en una base de datos, pero la recomendacion que me dieron fue que gurdara la informacion en una cookie.
Si puedes redponderme ha esta pregunta estaria muy agradecido
http://framework.zend.com/manual/en/zend.http.cookies.html
Class Application_Model_LibraryAcl extends Zend_Acl {
public function __construct() {
$this->add(new Zend_Acl_Resource('cotizaciones'));
$this->add(new Zend_Acl_Resource('listar'), 'cotizaciones');
$this->addRole(new Zend_Acl_Role('user'));
$this->addRole(new Zend_Acl_Role('admin'),'user');
$this->allow('user', 'cotizaciones', 'listar');
$this->allow('admin', 'cotizaciones', 'index');
$this->allow('admin', 'cotizaciones', 'edit');
$this->allow('admin', 'cotizaciones', 'delete');
}
}
Perdonen :(
y OTRA ESTE ES EL PROYECTO DE bootstrap twitter NO?? SI ES UN CMS COMO LO ADAPTASTES.
MUCHAS MUCHAS GRACIAS DE ANTE MANO. ESPERO TU RECOMENDACI'ON
de nuevo gracias y saludos cordiales
$role=$auth->getIdentity()->role;
siendo role la columna correspondiente de la base de datos obtenida con getStorage
me salte una notice
trying to ger property of non-object
linea $role=$auth->getIdentity()->role;
La aplicación funciona bien pero no consigo quitar el aviso.
Gracias Un saludo
Tengo los modulos de default y backoffice. En el default tengo la pagina web y en el backoffice la parte administrativa.
uso zend_Acl y Zend_Auth para autentificarme.
La ayuda que pido es que quiero insertar el registro de la session en la bd solo cuando me autentifique y estando con el role->guest
gracias.
en el bootstrap tengo
protected function _initSessionSaveHandler()
{
$this->bootstrap("db");
$config = array(
'name' => 'session', //table name as per Zend_Db_Table
'primary' => 'id', //the sessionID given by php
'modifiedColumn' => 'modified', //time the session should expire
'dataColumn' => 'data', //serialized data
'lifetimeColumn' => 'lifetime', //end of life for a specific record
'lifetime' => 10*60, //parametrizar tiempo de session,que corre con la inactividad
'overrideLifetime' => false //no se modifica el tiempo de vida de la session
);
$config['options'] = array (
'cookie_httponly' => '1',
'cookie_lifetime' => '86400',
'name' => 'WCBSESSID'
);
//create your Zend_Session_SaveHandler_DbTable and
//set the save handler for Zend_Session
Zend_Session::setSaveHandler(new Zend_Session_SaveHandler_DbTable($config));
Zend_Session::setOptions($config['options']);
Zend_Session::start();
}
La ayuda que pido es que quiero insertar el registro de la session en la bd solo cuando me autentifique y "NO" con el role->guest
gracias.
Muchas gracias.
Tengo una tabla USUARIOS que se relaciona con una tabla PERSONAS (la primera tiene el id de la segunda), por lo cual para crear un usuario debo primero crear la persona.
Debo crear un controlador para el objeto persona que llame a un modelo que tome los datos del formulario, lo inserte en la bd y me devuelva el id, y luego se los pase al controlador del objeto usuario para que este pueda almacenar al usuario?
Desde ya muchas gracias y realmente me han sido de mucha utilidad tus videos
Estaba interesado en que mi aplicación se identificara contra Google, mirando un poco he visto que se puede crear tu propio Adapter implementando la clase Zend_Auth_Adapter_Interface. Para eso se crea un fichero php en la carpeta /application/library con el nombre de clase que quieras, en mi caso Google_Auth_Adapter.php. He resuelto la identificación de google así por si a alguien le puede venir bien:
[code]
_username=$username;
$this->_password=$password;
}
public function authenticate()
{
$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
// Create an authenticated HTTP client
try{
$client = Zend_Gdata_ClientLogin::getHttpClient($this->_username, $this->_password, $service);
} catch (Zend_Gdata_App_CaptchaRequiredException $cre) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,$this->_username);
} catch (Zend_Gdata_App_AuthException $ae) {
return new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID,$this->_username);
}
return new Zend_Auth_Result(Zend_Auth_Result::SUCCESS,$this->_username);
}
}
?>
[/code]
Una vez creado en el controler del login - UsuarioController.php - hay que hacer include del archivo al principio:
[code]
include APPLICATION_PATH.'/library/Google_Auth_Adapter.php';
[/code]
y luego crear el adapter:
[code]
$auth = Zend_Auth::getInstance();
$result= $auth->authenticate(new Google_Auth_Adapter($user,$pass));
[/code]
Y parece que funciona.
Quisiera hacerte una consulta, quizas sabes como puedo recuperar los datos de Zend framework del servidor e interpretarlos corriendo Ext JS en una aplicacion cliente... Quizas me puedas ayudar...
Te lo agradezco de antemano...
Muchas gracias por los tutos , que gracias a ellos aprendi ZF.
Quisiera porfavor , una orientación en los mensajes de errores.
redireccionar y mostrar un mensaje como una alerta y que se cierre de manera automatica.
Saludos!
Sucede que en muchos casos deseo hacer validadores personalizados o programados en zend framework,s como por ejemplo que el precio a ingresar no sea superior al que se debe o menor a lo que no debe meter, por ejemplo, y la idea es que esos validadores, desde mi punto de vista, pertenecen a la interacción de datos, y por ende deseo guardarlos en una carpeta dentro de models, llamada validadores, y se que no basta con decirle por ejemeplo Application_Model_Validadores_NombreValidador para que sea llamado como clase, creo que debo incluir la libreria que estoy haciendo algo asi, me puedes colaborar porfavor.
Gracias de antemano
Como vas, esperoq ue bien.
1. Como hacer que el logueo expire despues de un tiempo?
2. Tiene algo de Zend_acl complejo?
Gracias
bueno antes me tiraba este error:
Catchable fatal error: Argument 1 passed to Zend_Auth_Adapter_DbTable::__construct()
must be an instance of Zend_Db_Adapter_Abstract, none given,
called in C:\xampp\htdocs\news123\application\controllers\UsuarioController.php
on line 19 and defined in C:\xampp\php\zend\library\Zend\Auth\Adapter\DbTable.php on line 128
y la linea 19 era esta:
$authAdapter = new Zend_Auth_Adapter_DbTable();
y luego de buscar una solución encontré que $authAdapter se instanciaba con al menos 1 parametro que era del tipo Zend_Adapter_Abstract y obtuve esta solución.
if ($form->isValid($this->_getAllParams())) {
$dbAdapter = new Zend_Db_Adapter_Mysqli(
array(
'dbname' => 'news123',
'username' => 'root',
'password' => '',
'host' => 'localhost'
)
);
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter
->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password');
.
.
.
y sólo así me funcionó, me gustaría que me explicaras porque me paso eso?.
PD: gracias por los video tutos :)
Siguiendo tus tutoriales estoy aprendiendo mucho.
Con respecto a este tema, aunque usemos SHA1 en setCredential, de todos modos la password viaja en claro desde el cliente hasta el servidor y algun curioso podria interceptarla husmeando en la red.
Como podemos hacer para que viaje encriptada ? (supongo que utilizando ssh pero no se como hacerlo).
Saludos
Me explico, que cuando salgo de navegador no se cierre la sesión y cuando abro el navegador que ya este la sesión. He visto algunas páginas que tiene opción no serrar sesión, como podría hacer eso con zend framework
gracias
Una cosa distinta es que ud tenga el navegador abierto y un pestaña donde este la aplciacion de zend y al cerrar la pestaña y ud volver a abrir una neuva pestaña e ingresar de enuvo a la aplicacion se ve la sesion activa.
resources.db.adapter = pdo_pgsql
resources.db.params.host = localhost
resources.db.params.username = postgres
resources.db.params.password = infor
resources.db.params.dbname = prueba
resources.db.isDefaultTableAdapter = true
Se agradece el esfuerzo




Me sruge una duda, por que el mismo codigo no funciona en local, pero se sube al servidor y si funciona, logicamente con las respectivas configuraciones del config.ini
Quisiera hacerte una sugerencia, pero como vos dices, es cuestion de mejorar el aplicativo.
Crear en el controller del login o usuarios como sea una funcion tal cual
y si son validos los datos del form