Este tutorial es una rápida introducción técnica de Symfony 1.3 y 1.4. Está pensada para los programadores que ya conocen Symfony 1.2 y quieren aprender lo más rápido posible todas las novedades de Symfony 1.3 y 1.4.
En primer lugar, Symfony 1.3 es compatible con PHP 5.2.4 o superior. Si quieres actualizar un proyecto desarrollado con Symfony 1.2, puedes leer la guía de actualización donde se explica detalladamente cómo actualizar tus proyectos de forma segura a Symfony 1.3.
Mailer
Symfony 1.3/1.4 incluyen un nuevo mailer basado en la librería SwiftMailer 4.1. Enviar un email es tan sencillo como utilizar el método composeAndSend() en una acción:
$this->getMailer()->composeAndSend('remitente@ejemplo.com', 'destinatario@ejemplo.com', 'Asunto', 'Contenido');
Si necesitas más flexibilidad, puedes utilizar el método compose() y después enviar el email. El siguiente ejemplo muestra cómo añadir un archivo adjunto en el mensaje:
$mensaje = $this->getMailer()-> compose('remitente@ejemplo.com', 'destinatario@ejemplo.com', 'Asunto', 'Contenido')-> attach(Swift_Attachment::fromPath('/ruta/hasta/el/archivo.zip')) ; $this->getMailer()->send($mensaje);
Como el nuevo mailer es muy potente, te recomendamos que leas su documentación para conocer todas sus posibilidades.
Seguridad
Cuando se crea una nueva aplicación con la tarea generate:app, las opciones de seguridad ahora están activadas por defecto:
escaping_strategy: su valor por defecto ahora estrue(se puede desactivar con la opción--escaping-strategy).csrf_secret: por defecto se genera un secreto aleatorio, por lo que la protección CSRF también se activa por defecto (se puede desactivar con la opción--csrf-secret). Te recomendamos encarecidamente que modifiques el secreto generado aleatoriamente. Para ello, modifica el archivo de configuraciónsettings.ymlo utiliza la opción--csrf-secret.
Widgets
Títulos por defecto
Cuando se genera automáticamente el título de un campo del formulario a partir de su nombre, ahora no se añade el sufijo _id:
nombre_completo=> Nombre Completo (como siempre)autor_id=> Autor (antes se generaba “Autor id”)
sfWidgetFormInputText
La clase sfWidgetFormInput ahora es abstracta. Los campos de texto se crean mediante la clase sfWidgetFormInputText. Este cambio se ha realizado para facilitar la introspección de las clases de formulario.
Widgets de internacionalización
Se han añadido los siguientes widgets:
sfWidgetFormI18nChoiceLanguagesfWidgetFormI18nChoiceCurrencysfWidgetFormI18nChoiceCountrysfWidgetFormI18nChoiceTimezone
Los tres primeros reemplazan respectivamente a los siguientes widgets declarados obsoletos: sfWidgetFormI18nSelectLanguage, sfWidgetFormI18nSelectCurrency y sfWidgetFormI18nSelectCountry.
Interfaz fluída
Los widgets ahora incluyen una interfaz fluída para los siguientes métodos:
sfWidgetForm:setDefault(),setLabel(),setIdFormat(),setHidden()sfWidget:addRequiredOption(),addOption(),setOption(),setOptions(),setAttribute(),setAttributes()sfWidgetFormSchema:setDefault(),setDefaults(),addFormFormatter(),setFormFormatterName(),setNameFormat(),setLabels(),setLabel(),setHelps(),setHelp(),setParent()sfWidgetFormSchemaDecorator:addFormFormatter(),setFormFormatterName(),setNameFormat(),setLabels(),setHelps(),setHelp(),setParent(),setPositions()
Validadores
sfValidatorRegex
El validador sfValidatorRegex ahora incluye una opción llamada must_match. Si el valor de esta opción es false, la expresión regular no se debe cumplir para que el valor sea considerado como válido.
La opción pattern de sfValidatorRegex ahora puede ser cualquier instancia de sfCallable que devuelva na expresión regular cuando se le invoque.
sfValidatorUrl
El validador sfValidatorUrl dispone de una nueva opción llamada protocols que permite especificar los protocolos permitidos:
$validador = new sfValidatorUrl(array('protocols' => array('http', 'https')));
Por defecto se permiten los siguientes protocolos:
httphttpsftpftps
sfValidatorSchemaCompare
La clase sfValidatorSchemaCompare incluye dos nuevos comparadores:
IDENTICAL, que es equivalente a===NOT_IDENTICAL, que es equivalente a!==
sfValidatorChoice, sfValidatorPropelChoice, sfValidatorDoctrineChoice
Los validadores sfValidatorChoice, sfValidatorPropelChoice y sfValidatorDoctrineChoice tienen dos nuevas opciones que sólo se activan si la opción multiple vale true:
min, el mínimo número de valores que deben ser seleccionadosmax, el máximo número de valores que deben ser seleccionados
Validadores de internacionalización
Se ha incluido un nuevo validador:
sfValidatorI18nTimezone
Mensajes de error por defecto
Ahora se puede definir globalmente un mensaje de error por defecto utilizando el método sfForm::setDefaultMessage():
sfValidatorBase::setDefaultMessage('required', 'Este campo es obligatorio.');
El código anterior redefine el mensaje que muestran por defecto todos los validadores (que en este caso sería “Required.”). Los mensajes por defecto se deben definir antes de crear el primer validador, por lo que puedes definirlos por ejemplo en la clase de configuración.
Los métodos setRequiredMessage() y setInvalidMessage() se han declarado obsoletos, por lo que internamente llaman al nuevo método setDefaultMessage().
Cuando Symfony muestra un error, el mensaje utilizado se determina de la siguiente manera:
- Symfony comprueba si al validador se le pasó un mensaje cuando se creo (mediante el segundo argumento del constructor del validador)
- Si no existe el mensaje anterior, comprueba si se ha definido un mensaje por defecto con el método
setDefaultMessage() - Si no existe el mensaje anterior, se muestra el mensaje por defecto definido por el propio validador (cuando se ha utilizado el método
addMessage()para añadir el mensaje)
Interfaz fluída
Los validadores ahora incluyen una interfaz fluída para los siguientes métodos:
sfValidatorSchema:setPreValidator(),setPostValidator()sfValidatorErrorSchema:addError(),addErrors()sfValidatorBase:addMessage(),setMessage(),setMessages(),addOption(),setOption(),setOptions(),addRequiredOption()
sfValidatorFile
Si la opción file_uploads se ha deshabilitado en el archivo de configuración php.ini, se lanza una excepción al crear una instancia de sfValidatorFile.
Formularios
sfForm::useFields()
El nuevo método sfForm::useFields() elimina todos los campos del formulario salvo los que se hayan indicado como argumento del método y salvo los campos de tipo oculto. En algunas ocasiones es más fácil indicar de forma explícita los campos que quieres mostrar en un formulario en vez de quitar todos los campos que no quieres mostrar. Si se añaden por ejemplo nuevos campos a un formulario base, no se mostrarán en el formulario hasta que se añadan de forma explícita (este comportamiento es como si el formulario fuese un modelo en el que se añade una nueva columna en la tabla asociada).
class ArticleForm extends BaseArticleForm { public function configure() { $this->useFields(array('title', 'content')); } }
El array de campos de formulario también se utiliza para determinar el orden en el que se muestran. Si quieres deshabilitar esta ordenación automática, puedes pasar un valor falsecomo segundo argumento del método useFields().
sfForm::getEmbeddedForm($nombre)
Ahora se puede acceder a un formulario embebido concreto utilizando el método ->getEmbeddedForm().
sfForm::renderHiddenFields()
El método ->renderHiddenFields() ahora obtiene todos los campos ocultos de los formularios embebidos. También se ha definido un argumento para evitar la recursión por todos los formularios, lo que es útil cuando utilizas un formateador propio para mostrar los formularios embebebidos.
// muestra todos los campos ocultos, incluyendo los de los formularios embebidos echo $form->renderHiddenFields(); // muestra los campos ocultos sin aplicar la recursión echo $form->renderHiddenFields(false);
sfFormSymfony
La nueva clase sfFormSymfony permite la notificación de eventos por parte de los formularios de Symfony. Para obtener el despachador de eventos (“event dispatcher”) dentro de una clase de formulario, puedes utilizar self::$dispatcher. Symfony notifica los siguientes eventos de formulario:
form.post_configure: este evento se notifica después de configurar cada formularioform.filter_values: este evento se notifica cuando se combinan los valores y los arrays de archivos enviados por los usuarios justo antes de asociar los datos con el formularioform.validation_error: este evento se notifica siempre que falla la validación del formularioform.method_not_found: este evento se notifica siempre que se invoca un método desconocido
BaseForm
Todos los proyectos nuevos de Symfony 1.3 y 1.4 incluyen una clase BaseForm que se puede utilizar para extender el componente Form añadiéndole funcionalidades específicas para el proyecto. Los formularios generados automáticamente por sfDoctrinePlugin y sfPropelPlugin también heredan de esta clase. Si creas clases de formulario a mano, deberían heredar de BaseForm en lugar de sfForm.
sfForm::doBind()
La limpieza de los parámetros enviados por el usuario ahora se realiza en un método aislado y fácil de acceder por parte del programador llamado ->doBind(), que recibe el array de parámetros y archivos del método ->bind().
sfForm(Doctrine|Propel)::doUpdateObject()
Las clases de formulario de Doctrine y Propel ahora incluyen un método llamado ->doUpdateObject(), que recibe de ->updateObject() un array con los valores que ha procesado ->processValues().
sfForm::enableLocalCSRFProtection() y sfForm::disableLocalCSRFProtection()
La protección CSRF ahora se puede configurar fácilmente desde el método configure() de las clases de formulario gracias a los nuevos métodos sfForm::enableLocalCSRFProtection() y sfForm::disableLocalCSRFProtection().
Si quieres deshabilitar la protección CSRF en un formulario, añade la siguiente línea en su método configure():
$this->disableLocalCSRFProtection();
Invocar al método disableLocalCSRFProtection() deshabilita la protección CSRF, incluso aunque después se pase un secreto CSRF al crear la instancia del formulario.
Interfaz fluída
Algunos métodos de sfForm ahora implementan una interfaz fluída: addCSRFProtection(), setValidators(), setValidator(), setValidatorSchema(), setWidgets(), setWidget(), setWidgetSchema(), setOption(), setDefault() y setDefaults().
Cargadores automáticos de clases
Los cargadores automáticos de clases de Symfony ahora no distinguen mayúsculas de minúsculas, tal y como sucede en el propio código PHP.
sfAutoloadAgain (EN PRUEBAS)
Se ha añadido un nuevo cargador automático de clases que sólo está disponible en el modo debug. La nueva clase sfAutoloadAgain recarga el autoloader normal de Symfony y busca la clase específica en el sistema de archivos. El resultado final es que ya no tienes que ejecutar el comando symfony cc después de añadir una nueva clase en el proyecto.
Pruebas unitarias y funcionales
Acelerando las pruebas
Si dispones de una gran cantidad de pruebas, resulta muy lento ejecutar todas ellas cada vez que haces un cambio en la aplicación, sobre todo cuando fallan algunas pruebas. El motivo es que cada vez que arregles una prueba que había fallado, tienes que volver a ejecutar todas las pruebas para asegurarte de que no has roto nada nuevo. No obstante, mientras no se arreglen las pruebas que fallan, no tiene sentido volver a ejecutar todas las demás pruebas. Por ello, la tarea test:all dispone de una opción llamada --only-failed (-f es el atajo) que obliga a ejecutar solamente las pruebas que fallaron la última vez:
$ php symfony test:all --only-failed
La primera vez se ejecutan todas las pruebas, pero en las siguientes veces sólo se ejecutan las pruebas que fallaron la última vez. A medida que arregles el código de la aplicación, algunas pruebas se corregirán y por tanto ya no se volverán a ejecutar. Cuando se ejecuten correctamente todas las pruebas que fallaban, ya puedes volver a ejecutar de nuevo todo el conjunto de pruebas unitarias y funcionales.
Pruebas funcionales
Cuando una petición provoca una excepción, el método debug() del tester de la respuesta muestra la excepción en forma de texto y no mediante su formato HTML habitual, por lo que se facilita la depuración de las aplicaciones.
sfTesterResponse dispone de un nuevo método llamado matches() que realiza una comprobación sobre todo el contenido de la respuesta utilizando una expresión regular. Se trata de una gran ayuda cuando la respuesta no es de tipo XML en las que no se muy útil el método checkElement(). Este nuevo método también reemplaza al viejo y limitado método contains():
$browser->with('response')->begin()-> matches('/I have \d+ apples/')-> // como argumento se indica una expresión regular matches('!/I have \d+ apples/')-> // si se añade ! como prefijo, la expresión regular no se debe cumplir matches('!/I have \d+ apples/i')-> // también se pueden utilizar modificadores de expresiones regulares end();
Salida XML compatible con JUnit
Las tareas de pruebas ahora disponen de una opción llamada --xml con la que pueden generar su salida en un formato XML compatible con JUnit:
$ php symfony test:all --xml=log.xml
Depuración sencilla
Para facilitar la depuración cuando se produce un error en un conjunto de pruebas, se puede hacer uso de una opción llamada --trace para que muestre detalladamente todos los errores producidos:
$ php symfony test:all -t
Uso del color en Lime
A partir de Symfony 1.3/1.4, la herramienta Lime detecta correctamente los entornos en los que puede mostrar sus mensajes con colores, por lo que prácticamente siempre puedes omitir el segundo argumento del constructor de lime_test:
$t = new lime_test(1);
sfTesterResponse::checkForm()
El tester de la respuesta ahora incluye un método para comprobar fácilmente que todos los campos del formulario se incluyen en el contenido de la respuesta:
$browser->with('response')->begin()-> checkForm('ArticleForm')-> end();
Si lo prefieres también puedes pasar el objeto del formulario:
$browser->with('response')->begin()-> checkForm($browser->getArticleForm())-> end();
Si la respuesta incluye varios formularios, también tienes la opción de indicar un formulario específico mediante un selector de CSS:
$browser->with('response')->begin()-> checkForm('ArticleForm', '#articleForm')-> end();
sfTesterResponse::isValid()
Si quieres comprobar si la respuesta obtenida es un documento XML bien formado, puedes hacer uso del método ->isValid() del tester:
$browser->with('response')->begin()-> isValid()-> end();
También puedes validar la respuesta según su tipo de documento o DTD pasando el valor true como argumento:
$browser->with('response')->begin()-> isValid(true)-> end();
Por otra parte, si dispones de un esquema XSD o RelaxNG para validar la respuesta, debes indicar su ruta como argumento de este método:
$browser->with('response')->begin()-> isValid('/ruta/hasta/schema.xsd')-> end();
Evento context.load_factories
Las pruebas funcionales ahora pueden definir listeners para el evento context.load_factories, algo que no era posible en las anteriores versiones de Symfony.
$browser->addListener('context.load_factories', array($browser, 'listenForNewContext'));
Método click() mejorado
El método ->click() ahora acepta cualquier selector CSS, haciendo que sea mucho más fácil seleccionar un elemento de forma semántica.
$browser ->get('/login') ->click('form[action$="/login"] input[type="submit"]') ;
