Estas en: Home > controlador

Entradas etiquetadas con controlador

Symfony: El controlador (III)

0

SEGURIDAD DE LA ACCIÓN

Con Symfony podemos controlar quién puede acceder a una acción concreta, utilizando para ello las herramientas de identificación de usuarios que el framework nos provee.

Una acción segura sólo podrá ser ejecutada por un usuario registrado y que tenga la acreditación para ello.

  • Las acciones seguras requieren que los usuarios estén autenticados.
  • Las credenciales son privilegios de seguridad agrupados bajo un nombre y que permiten organizar la seguridad en grupos.
  1. ver:
  2. is_secure: off # Todos los usuarios pueden ejecutar la acción "ver"
  3.  
  4. modificar:
  5. is_secure: on # La acción "modificar" es sólo para usuarios autenticados
  6.  
  7. borrar:
  8. is_secure: on # Sólo para usuarios autenticados
  9. credentials: admin # Con credencial "admin"
  10.  
  11. all:
  12. is_secure: off # off es el valor por defecto

 

Lo que sucede cuando un usuario trata de acceder una acción restringida depende de sus credenciales:

  • Si el usuario está autenticado y tiene las credenciales apropiadas, entonces la acción se ejecuta.
  • Si el usuario no está autenticado, es redireccionado a la acción de login.
  • Si el usuario está autenticado, pero no posee las credenciales apropiadas, será redirigido a la acción segura por defecto.

Las páginas login y secure son bastante simples, por lo que seguramente será necesario personalizarlas. Se puede configurar que acciones se ejecutan en caso de no disponer de suficientes privilegios en el archivo settings.yml de la aplicación cambiando el valor de las propiedades:

  1. all:
  2. .actions:
  3. login_module: default
  4. login_action: login
  5.  
  6. secure_module: default
  7. secure_action: secure

 

OTORGANDO ACCESO

Los métodos utilizados para permitir acceso a una acción segura se encuentran en el objeto sfUser. El estado identificado se establece con setAuthenticated() y se puede comprobar con isAuthenticated():

  1. <?php
  2.  
  3. class miCuentaActions extends sfActions
  4. {
  5. public function executeLogin($peticion)
  6. {
  7. if ($peticion->getParameter('login') == 'valor')
  8. {
  9. $this->getUser()->setAuthenticated(true);
  10. }
  11. }
  12.  
  13. public function executeLogout()
  14. {
  15. $this->getUser()->setAuthenticated(false);
  16. }
  17. }

Las credenciales son un poco más complejas de tratar, ya que se pueden verificar, agregar, quitar y borrar:

  1. <?php
  2.  
  3. class miCuentaActions extends sfActions
  4. {
  5. public function executeEjemploDeCredenciales()
  6. {
  7. $usuario = $this->getUser();
  8.  
  9. // Agrega una o más credenciales
  10. $usuario->addCredential('parametro');
  11. $usuario->addCredentials('parametro', 'valor');
  12.  
  13. // Verifica si el usuario tiene una credencial
  14. echo $usuario->hasCredential('parametro'); => true
  15.  
  16. // Verifica si un usuario tiene una de las credenciales
  17. echo $usuario->hasCredential(array('parametro', 'valor')); => true
  18.  
  19. // Verifica si el usuario tiene ambas credenciales
  20. echo $usuario->hasCredential(array('parametro', 'valor'), true); => true
  21.  
  22. // Quitar una credencial
  23. $usuario->removeCredential('parametro');
  24. echo $usuario->hasCredential('parametro'); => false
  25.  
  26. // Elimina todas las credenciales (útil en el proceso de logout)
  27. $usuario->clearCredentials();
  28. echo $usuario->hasCredential('valor'); => false
  29. }
  30. }

Las credenciales se pueden utilizar también para mostrar contenido autenticado en una plantilla:

  1. <?php
  2.  
  3. <ul>
  4. <li><?php echo link_to('seccion1', 'content/seccion1') ?></li>
  5. <li><?php echo link_to('seccion2', 'content/seccion2') ?></li>
  6. <?php if ($sf_user->hasCredential('seccion3')): ?>
  7. <li><?php echo link_to('seccion3', 'content/seccion3') ?></li>
  8. <?php endif; ?>
  9. </ul>

 

CREDENCIALES COMPLEJAS

a sintaxis YAML utilizada en el archivo security.yml permite restringir el acceso a usuarios que tienen una combinación de credenciales, usando asociaciones de tipo AND y OR. Con estas combinaciones, se pueden definir flujos de trabajo y sistemas de manejo de privilegios muy complejos — como por ejemplo, un sistema de gestión de contenidos (CMS) cuya parte de gestión sea accesible solo a usuarios con credencial admin, donde los artículos pueden ser editados solo por usuarios con credenciales de editor y publicados solo por aquellos que tienen credencial de publisher:

  1. editarArticulo:
  2. credentials: [ admin, editor ] # admin AND editor
  3.  
  4. publicarArticulo:
  5. credentials: [ admin, publisher ] # admin AND publisher
  6.  
  7. gestionUsuarios:
  8. credentials: [[ admin, superuser ]] # admin OR superuser

Cada vez que se añade un nuevo nivel de corchetes, la lógica cambia entre AND y OR. Así que se pueden crear combinaciones muy complejas de credenciales, como la siguiente:

  1. credentials: [[root, [supplier, [owner, quasiowner]], accounts]]
  2. # root OR (supplier AND (owner OR quasiowner)) OR accounts

 

CONFIGURACIÓN DEL MÓDULO

Algunas características de los módulos dependen de la configuración. Para modificarlas, se debe crear un archivo module.yml en el directorio config/ y se deben definir parámetros para cada entorno (o en la sección all: para todos los entornos).

  1. all: # Para todos los entornos
  2. enabled: true
  3. is_internal: false
  4. view_class: sfPHP
  5. partial_view_class: sf

El parámetro enabled permite desactivar todas las acciones en un módulo. En ese caso, todas las acciones se redireccionan a la acción module_disabled_module/module_disabled_action (tal y como se define en el archivo settings.yml).

El parámetro is_internal permite restringir la ejecución de todas las acciones de un módulo a llamadas internas. Esto es útil por ejemplo para acciones de envío de correos electrónicos que se deben llamar desde otras acciones para enviar mensajes de e-mail, pero que no se deben llamar desde el exterior.

El parámetro view_class define la clase de la vista. Debe heredar de sfView. Sobreescribir este valor permite utilizar otros sistemas de generación de vistas con otros motores de plantillas, como por ejemplo Smarty.

El parámetro partial_view_class define la clase de la vista que se emplea para los elementos parciales de este módulo. La clase indicada debe heredar de sfPartialView.

Symfony: El controlador (I)

0

Continuo dándole caña a Symfony. Ahora toca el controlador.

¿Qué hace el controlador? Pues lo siguiente:

  • El controlador frontal es el único punto de entrada a la aplicación. Carga la configuración y determina la acción a ejecutarse.
  • Las acciones contienen la lógica de la aplicación. Verifican la integridad de las peticiones y preparan los datos requeridos por la capa de presentación.
  • Los objetos request, response y session dan acceso a los parámetros de la petición, las cabeceras de las respuestas y a los datos persistentes del usuario. Se utilizan muy a menudo en la capa del controlador.
  • Los filtros son trozos de código ejecutados para cada petición, antes o después de una acción. Por ejemplo, los filtros de seguridad y validación son comúnmente utilizados en aplicaciones web. Puedes extender el framework creando tus propios filtros.

EL CONTROLADOR FRONTAL

http://localhost/index.php/mimodulo/miAccion

La URL de arriba es un ejemplo de que tarea realiza el controlador frontal. En este caso nuestro controlador frontal es el archivo index.php que se encargará de ejecutar miAccion de mimodulo y generar la página que mostrará al usuario.

EL TRABAJO DEL CONTROLADOR EN DETALLE

Estas son las tareas que ejecuta el controlador antes de que se muestre la página al usuario:

  1. Carga la clase de configuración del proyecto y las librerías de Symfony.
  2. Crea la configuración de la aplicación y el contexto de Symfony.
  3. Carga e inicializa las clases del núcleo del framework.
  4. Carga la configuración.
  5. Decodifica la URL de la petición para determinar la acción a ejecutar y los parámetros de la petición.
  6. Si la acción no existe, redireccionará a la acción del error 404.
  7. Activa los filtros (por ejemplo, si la petición necesita autenticación).
  8. Ejecuta los filtros, primera pasada.
  9. Ejecuta la acción y produce la vista.
  10. Ejecuta los filtros, segunda pasada.
  11. Muestra la respuesta.

index.php, EL CONTROLADOR FRONTAL POR DEFECTO

Este archivo se encuentra en la carpeta /web del proyecto y contiene el siguiente código:

  1. <?php
  2.  
  3. require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
  4.  
  5. $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
  6. sfContext::createInstance($configuration)-&gt;dispatch();

Lo que hace es simple, carga el archivo de la clase de configuración, llama a la clase y despacha la petición usando el método dispatch() de la clase sfContext.

Podemos crear otros controladores frontales simplemente copiando el archivo y modificando el segundo parámetro del método getApplicationConfiguration().

Para ello copiaremos el código del archivo index.php a frontend_staging.php y sustituiremos el segundo parámetro del método getApplicationConfiguration() que en vez de ser prod (de producción), será staging. Utilizaremos este controlador para que el cliente pueda probar la aplicación antes de ponerla en producción.

Una vez hecho esto modificaremos el archivo app.yml en la carpeta /config de la aplicación (en este caso sería /frontend/config) como sigue:

  1. staging:
  2. mail:
  3. webmaster: falso@misitio.com
  4. contacto: falso@misitio.com
  5. all:
  6. mail:
  7. webmaster: webmaster@misitio.com
  8. contacto: contacto@mysite.com

Ahora solo tienes que escribir la siguiente URL para ver como se comporta el nuevo controlador frontal:

http://localhost/frontend_staging.php/mimodulo/index

LAS ACCIONES

  1. <?php
  2.  
  3. class mimoduloActions extends sfActions
  4. {
  5. public function executeIndex()
  6. {
  7. // ...
  8. }
  9. }

Las acciones son métodos de una clase que hereda de sfActions y se encuentran agrupadas por módulos. Esta clase se encuentra en el archivo actions.class.php en la carpeta /actions del módulo.

Para añadir más acciones sólo es necesario añadir más métodos execute al objeto sfActions:

  1. <?php
  2.  
  3. class mimoduloActions extends sfActions
  4. {
  5. public function executeIndex()
  6. {
  7. // ...
  8. }
  9.  
  10. public function executeListar()
  11. {
  12. // ...
  13. }
  14. }

Y para ver en el navegador estas acciones sólo habría que escribir:

Para executeIndex() -> http://localhost/frontend_dev.php/mimodulo/index
Para executeListar() -> http://localhost/frontend_dev.php/mimodulo/listar

 

SEPARANDO LAS ACCIONES EN ARCHIVOS DIFERENTES

Para crear acciones de un mismo módulo en archivos diferentes se debe crear una clase que extienda sfAction (en vez de sfActions que utilizabamos anteriormente), en un archivo llamado nombreAccionAction.class.php y el nombre del método en cada archivo será, simplemente, execute.

  1. <?php
  2.  
  3. class indexAction extends sfAction
  4. {
  5. public function execute($peticion)
  6. {
  7. // ...
  8. }
  9. }
  1. <?php
  2.  
  3. class listarAction extends sfAction
  4. {
  5. public function execute($peticion)
  6. {
  7. // ...
  8. }
  9. }

 

OBTENIENDO INFORMACIÓN DE LAS ACCIONES

sfActions proporciona acceso a sfContext::createInstance() a través de getContext() que devuelve un objeto que guarda una referencia de todos los objetos del núcleo de symfony relacionados con la petición dada:

  1. <?php
  2.  
  3. class mimoduloActions extends sfActions
  4. {
  5. public function executeIndex($peticion)
  6. {
  7. // Obteniendo parametros de la petición
  8. $password = $peticion->getParameter('password');
  9.  
  10. // Obteniendo información del controlador
  11. $nombreModulo = $this->getModuleName();
  12. $nombreAccion = $this->getActionName();
  13.  
  14. // Obteniendo objetos del núcleo del framework
  15. $sesionUsuario = $this->getUser();
  16. $respuesta = $this->getResponse();
  17. $controlador = $this->getController();
  18. $contexto = $this->getContext();
  19.  
  20. // Creando variables de la acción para pasar información a la plantilla
  21. $this->setVar('parametro', 'valor');
  22. $this->parametro = 'valor'; // Versión corta.
  23. }
  24. }
  • sfController: El objeto controlador (->getController())
  • sfRequest: El objeto de la petición (->getRequest())
  • sfResponse: El objeto de la respuesta (->getResponse())
  • sfUser: El objeto de la sesión del usuario (->getUser())
  • sfDatabaseConnection: La conexión a la base de datos (->getDatabaseConnection())
  • sfLogger: El objeto para los logs (->getLogger())
  • sfI18N: El objeto de internacionalización (->getI18N())

Se puede llamar al método sfContext::getInstance() desde cualquier parte del código.

 

TERMINACIÓN DE LAS ACCIONES

Normalmente cuando finalizamos un método o función y queremos devolver algún dato utilizamos la palabra return seguida de alguna variable u objeto. En Symfony es más o menos igual, me explico:

Symfony, una vez a procesado la acción, enviará los datos a la plantilla para que el usuario pueda visualizarlo. Por defecto, Symfony siempre utiliza return sfView::SUCCESS para buscar la plantilla que debe mostrar los datos:

  1. <?php
  2.  
  3. public function executeIndex()
  4. {
  5. return sfView::SUCCESS;
  6. }
  7.  
  8. public function executeListar()
  9. {
  10. }

Al ser sfView::SUCCESS la vista por defecto, si no se indica en la acción, Symfony buscará una plantilla llamada nombreacciónSuccess.php.

En el caso de que quisieramos mostrar al usuario otra plantilla en caso de error haríamos lo siguiente:

  1. <?php
  2.  
  3. return sfView::ERROR;

Esto buscará una plantilla llamada nombreacciónError.php. Si queremos mostrar una vista personalizada:

  1. <?php
  2.  
  3. return 'MiResultado';

En ete caso, Symfony buscará una plantilla llamada nombreacciónMiResultado.php (ojo a las mayusculas ya que Symfony es case sensitive)

Si no se quiere utilizar ninguna vista:

  1. <?php
  2.  
  3. return sfView::NONE;

En el caso de que la acción vaya a ser utilizada por Ajax, podemos devolver los datos sin necesidad de pasar por la vista con el método renderText():

  1. <?php
  2.  
  3. public function executeIndex()
  4. {
  5. $this->getResponse()->setContent("<html><body>¡Hola Mundo!</body></html>");
  6.  
  7. return sfView::NONE;
  8. }
  9.  
  10. // Es equivalente a
  11. public function executeIndex()
  12. {
  13. return $this->renderText("<html><body>¡Hola Mundo!</body></html>");
  14. }

En algunos casos se necesita enviar sólo las cabeceras de la petición, como en el caso de J-SON:

  1. <?php
  2.  
  3. public function executeActualizar()
  4. {
  5. $salida = '<"titulo","Mi carta sencilla"],["nombre","Sr. Pérez">';
  6. $this->getResponse()->setHttpHeader("X-JSON", '('.$salida.')');
  7.  
  8. return sfView::HEADER_ONLY;
  9. }

Si se quiere utilizar una plantilla específica, se debe prescindir de la sentencia return y utilizar el método setTemplate():

  1. <?php
  2.  
  3. $this->setTemplate('miPlantillaPersonalizada');

 

SALTANDO A OTRA ACCIÓN

Hay dos formas de saltar a otra acción:

  1. <?php
  2.  
  3. //Si la acción debe continuar en otro módulo
  4. $this->forward('otroModulo', 'index');
  5.  
  6. //Si la acción debe redireccionar a otro módulo o una web externa
  7. $this->redirect('otroModulo/index');
  8. $this->redirect('http://www.google.com/');

En el caso de que quisieramos redireccionar para mostrar un error 404:

  1. <?php
  2.  
  3. public function executeVer($peticion)
  4. {
  5. $articulo = ArticuloPeer::retrieveByPK($peticion->getParameter('id'));
  6. if (!$articulo)
  7. {
  8. $this->forward404();
  9. }
  10. }

Si estás buscando la acción y la plantilla del error 404, las puedes encontrar en el directorio $sf_symfony_lib_dir/controller/default/. Se puede personalizar esta página agregado un módulo default a la aplicación, sobrescribiendo el del framework, y definiendo una acción error404 y una plantilla error404Success dentro del nuevo módulo. Otro método alternativo es el de establecer las constantes error_404_module y error_404_action en el archivo settings.yml para utilizar una acción existente.

La clase sfActions tiene algunos métodos más, llamados forwardIf(), forwardUnless(), forward404If(), forward404Unless(), redirectIf() y redirectUnless(). Estos métodos simplemente requieren un parámetro que representa la condición cuyo resultado se emplea para ejecutar el método. El método se ejecuta si el resultado de la condición es true y el método es de tipo xxxIf() o si el resultado de la condición es false y el método es de tipo xxxUnless():

  1. <?php
  2.  
  3. // Esta acción es equivalente a la mostrada en el Listado 6-11
  4. public function executeVer($peticion)
  5. {
  6. $articulo = ArticuloPeer::retrieveByPK($peticion->getParameter('id'));
  7. $this->forward404If(!$articulo);
  8. }
  9.  
  10. // Esta acción también es equivalente
  11. public function executeVer()
  12. {
  13. $articulo = ArticuloPeer::retrieveByPK($peticion->getParameter('id'));
  14. $this->forward404Unless($articulo);
  15. }

 

REPITIENDO CÓDIGO PARA VARIAS ACCIONES DE UN MÓDULO

Si por alguna razón nuestras acciones siempre van a ejecutar el mismo código al inicio o al final del método, podemos evitar duplicar código creando los métodos postExecute() y preExecute(), pero además podemos añadir al archivo de la acción (con sfAction) o acciones (con sfActions) nuestros propios métodos siempre que no empiecen con execute y no sean métodos públicos (utilizando para ello las sentencias protected o private).

  1. <?php
  2.  
  3. class mimoduloActions extends sfActions
  4. {
  5. public function preExecute()
  6. {
  7. // El código insertado aquí se ejecuta al principio de cada llamada a una acción
  8. // ...
  9. }
  10.  
  11. public function executeIndex($peticion)
  12. {
  13. // ...
  14. }
  15.  
  16. public function executeListar($peticion)
  17. {
  18. // ...
  19. $this->miPropioMetodo(); // Se puede acceder a cualquier método de la clase acción
  20. }
  21.  
  22. public function postExecute()
  23. {
  24. // El código insertado aquí se ejecuta al final de cada llamada a la acción
  25. ...
  26. }
  27.  
  28. protected function miPropioMetodo()
  29. {
  30. // Se pueden crear métodos propios, siempre que su nombre no comience por "execute"
  31. // En ese case, es mejor declarar los métodos como protected o private
  32. // ...
  33. }
  34. }
Ir arriba