SensioLabs
SYMFONY
BUENAS PRÁCTICAS
Javier Eguiluz
Symfony
Barcelona
gracias a
Marc Morera
@mmoreram
gracias a
Elcodi
Symfony components based
e-commerce platform
gracias a
Introducción
13
OCTUBRE
Las buenas prácticas oficiales
Conjunto de técnicas que
puedes utilizar para desarrollar
aplicaciones Symfony como
recomiendan sus creadores.
symfony.com/best-practices
LIBROSWEB
FABIEN POTENCIER
RYAN WEAVER
JAVIER EGUILUZ
BUENAS
PRÁCTICAS
PARA APLICACIONES
SYMFONY
bit.ly/buenas-practicas-symfony
50
páginas
57
páginas
¿Por qué?
Las buenas prácticas oficiosas
complican mucho el desarrollo
de aplicaciones y no siguen la
filosofía pragmática de los
creadores de Symfony.
Definición de “Best Practice”
A well defined procedure
that is known to produce
near-optimum results.
Definición de “Pragmatic”
Concerned with making
decisions and actions that
are useful in practice, not
just theory.
Las buenas prácticas Symfony
Las buenas prácticas Symfony
• Reflejan las ideas de su creador.
Las buenas prácticas Symfony
• Reflejan las ideas de su creador.
• Son opcionales.
Las buenas prácticas Symfony
• Reflejan las ideas de su creador.
• Son opcionales.
• Symfony no cambiará para
obligarte a usarlas.
Usa las buenas prácticas …
Usa las buenas prácticas …
• En proyectos pequeños y medianos.
Usa las buenas prácticas …
• En proyectos pequeños y medianos.
• En proyectos web estándar.
Usa las buenas prácticas …
• En proyectos pequeños y medianos.
• En proyectos web estándar.
• Si eres nuevo/a en Symfony.
No uses las buenas prácticas …
No uses las buenas prácticas …
• En bundles compartidos (públicos o
privados).
No uses las buenas prácticas …
• En bundles compartidos (públicos o
privados).
• En aplicaciones muy complejas o con
arquitecturas muy especiales.
No uses las buenas prácticas …
• En bundles compartidos (públicos o
privados).
• En aplicaciones muy complejas o con
arquitecturas muy especiales.
• Si tienes tus propias buenas prácticas.
Aplicaciones vs
bundles
Estructura de una aplicación web
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Estructura de una aplicación web
configuración
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Estructura de una aplicación web
configuración
plantillas
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Estructura de una aplicación web
configuración
plantillas
tu código
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Estructura de una aplicación web
configuración
plantillas
tu código
dependencias
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Estructura de una aplicación web
configuración
plantillas
tu código
dependencias
assets
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
¿Cómo crear un sistema de plugins?
¿Cómo crear un sistema de plugins?
• Deben funcionar de manera
autónoma.
¿Cómo crear un sistema de plugins?
• Deben funcionar de manera
autónoma.
• Pueden definir su propia
configuración.
¿Cómo crear un sistema de plugins?
• Deben funcionar de manera
autónoma.
• Pueden definir su propia
configuración.
• Pueden incluir plantillas y assets.
Estructura de un plugin
plugin/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Estructura de un plugin
configuración
plugin/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Estructura de un plugin
configuración
assets
plugin/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Estructura de un plugin
configuración
plantillas
assets
plugin/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Estructura de un plugin
configuración
plantillas
tu código
assets
plugin/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
!" vendor/
$" web/
!" css/
$" js/
Aplicaciones vs plugins/bundles
bundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Los bundles son mini-aplicaciones
Configuración Plantillas
Código!
fuente
Assets
Contenedor
servicios
Kernel
Caché!
y logs
Aplicación ✔ ✔ ✔ ✔ ✔ ✔ ✔
Bundle ✔ ✔ ✔ ✔ ✔ ✘ ✘
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
#
#
#
#
#
#
!" vendor/
$" web/
!" css/
$" js/
Los bundles en aplicaciones privadas
AcmeUserBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeProductBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeOfferBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeInvoiceBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
#
#
#
#
#
#
!" vendor/
$" web/
!" css/
$" js/
Los bundles en aplicaciones privadas
AcmeUserBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeProductBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeOfferBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeInvoiceBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
proyecto/
!" app/
# !" config/
# $" Resources/
# $" views/
!" src/
#
#
#
#
#
#
!" vendor/
$" web/
!" css/
$" js/
Los bundles en aplicaciones privadas
AcmeUserBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeProductBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeOfferBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
AcmeInvoiceBundle/
!" DependencyInjection/
# $" Configuration.php
!" Resources/
# !" config/
# !" public/
# # !" css/
# # $" js/
# $" views/
$" ...
Una verdad incómoda
Es muy probable que los
bundles de tus aplicaciones
no sean bundles, sólo
directorios que molestan.
Buenas prácticas en
la práctica
Organizando el
proyecto
Seguridad
Anotaciones
Simplificaciones
Organizando el
proyecto
Seguridad
Anotaciones
Simplificaciones
Crea un solo
bundle llamado
AppBundle
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
# $" DefaultController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" UserController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
# $" DefaultController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" UserController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
4archivos
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
# $" DefaultController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" UserController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
4archivos
2directorios
4archivos
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
# $" DefaultController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" UserController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" User/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" User/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
8archivos
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Controller/
# # $" DefaultController.php
# !" PaymentBundle/
# # $" Controller/
# # $" DefaultController.php
# !" ProductBundle/
# # $" Controller/
# # $" DefaultController.php
# $" UserBundle/
# $" Controller/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
aplicacion/
!" app/
!" src/
# $" AppBundle/
# $" Controller/
# !" ContactController.php
# !" PaymentController.php
# !" ProductController.php
# $" User/
| !" GroupController.php
| !" ProfileController.php
| !" RegistrationController.php
| !" ResettingController.php
| $" SecurityController.php
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
8archivos
3directorios
8archivos
Configuración del enrutamiento
# app/config/routing.yml
app:
resource: @AppBundle/Controller/
type: annotation
No añadas un
vendor en los
bundles que no
compartas
No añadas un vendor a los bundles privados
AcmeNetworksAcmeWebsiteMarketingBundle
!
AcmeNetworksAcmeWebsiteMarketingBundle:Default:index.html.twig
!
{{ render(controller(
'AcmeNetworksAcmeWebsiteMarketingBundle:Default:latestNews'
)) }}
No añadas un vendor a los bundles privados
AcmeNetworksAcmeWebsiteMarketingBundle
!
AcmeNetworksAcmeWebsiteMarketingBundle:Default:index.html.twig
!
{{ render(controller(
'AcmeNetworksAcmeWebsiteMarketingBundle:Default:latestNews'
)) }}
Esto lo he visto con
mis propios ojos
Guarda todas
tus plantillas
en app/
Esta buena práctica es
la que produce un
mayor impacto positivo
AVISO IMPORTANTE
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Resources/
# # $" views/
# # $" Default/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" ProductBundle/
# $" Resources/
# $" views/
# $" Default/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
your-application/
!" app/
# $" Resources/
# $" views/
# !" contact/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" product/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Resources/
# # $" views/
# # $" Default/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" ProductBundle/
# $" Resources/
# $" views/
# $" Default/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
your-application/
!" app/
# $" Resources/
# $" views/
# !" contact/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" product/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
5archivos
aplicacion/
!" app/
!" src/
# !" ContactBundle/
# # $" Resources/
# # $" views/
# # $" Default/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" ProductBundle/
# $" Resources/
# $" views/
# $" Default/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
your-application/
!" app/
# $" Resources/
# $" views/
# !" contact/
# # !" index.html.twig
# # $" show.html.twig
# . . .
# $" product/
# !" index.html.twig
# !" category.html.twig
# $" show.html.twig
!" vendor/
$" web/
Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
8directorios
5archivos
4directorios
5archivos
Centralizar las plantillas
Centralizar las plantillas
Cambia la vida
a tus diseñadores/as
Centralizar las plantillas
Cambia la vida
a tus diseñadores/as
Simplifica
mucho tu código
Nueva organización de plantillas
aplicacion/
$" app/Resources/views/
!" contact/
# !" index.html.twig
# $" show.html.twig
. . .
!
$" product/
!" index.html.twig
!" category.html.twig
$" show.html.twig
La nueva notación de las plantillas
$this->render('AcmeDemoBunde:Default:index.html.twig');
$this->render('default/index.html.twig');
!
$this->render('AcmeDemoBundle::index.html.twig');
$this->render('index.html.twig');
La nueva notación de las plantillas
{% extends '::layout.html.twig' %}
{% extends 'layout.html.twig' %}
!
{{ include('AcmeDemoBundle:Default:subdir/index.html.twig') }}
{{ include('default/subdir/index.html.twig') }}
!
{{ include('AcmeDemoBundle:Default/subdir:index.html.twig') }}
{{ include('default/subdir/index.html.twig') }}
Los problemas de la notación tradicional
AcmeDemoBundle:Default:subdir/index.html.twig
Los problemas de la notación tradicional
• Requiere explicársela a cada diseñador/programador.
AcmeDemoBundle:Default:subdir/index.html.twig
Los problemas de la notación tradicional
• Requiere explicársela a cada diseñador/programador.
• Tiene excepciones (alguna de sus partes puede estar
vacía) e inconsistencias (subdirectorios).
AcmeDemoBundle:Default:subdir/index.html.twig
Los problemas de la notación tradicional
• Requiere explicársela a cada diseñador/programador.
• Tiene excepciones (alguna de sus partes puede estar
vacía) e inconsistencias (subdirectorios).
• No es inmediato saber dónde está la plantilla (debes
traducir la notación a un directorio).
AcmeDemoBundle:Default:subdir/index.html.twig
Guarda todos
tus assets en
web/
Centralizar los assets (CSS, JavaScript)
• Tiene las mismas ventajas que centralizar
las plantillas.
Organizando los assets web
proyecto/
!" app/
!" src/
!" vendor/
$" web/
!" css/
# !" bootstrap.min.css
# $" app.css
$" js/
!" jquery.min.js
$" app.js
Organizando los assets web
proyecto/
!" app/
!" src/
!" vendor/
$" web/
!" css/
# !" bootstrap.min.css
# $" app.css
$" js/
!" jquery.min.js
$" app.js
proyecto/
!" app/
# $" Resources/
# $" assets/
# !" scss/
# # !" bootstrap/
# # $" app.scss
# $" js/
!" src/
!" vendor/
$" web/
!" css/app.css
$" js/app.js
En resumen
• Si utilizas mal los bundles, estás repitiendo
la estructura de la aplicación sin necesidad
• Si no vas a compartir tus bundles, utiliza
los directorios de la aplicación (app/
Resources/ y web/).
• El número de archivos se mantiene, los
directorios y la complejidad se reducen.
Organizando el
proyecto
Seguridad
Anotaciones
Simplificaciones
Escala de sensibilidad
para programadores
Escala de sensibilidad
para programadores
Política
Religión
Fútbol
Estándar de código
Editor de código
Anotaciones
Anotaciones PHP
use AppBundleEntityPost;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
class CommentController extends Controller
{
/**
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
!
/*
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
}
Anotaciones PHP
use AppBundleEntityPost;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
class CommentController extends Controller
{
/**
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
!
/*
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
}
Anotación
Anotaciones PHP
use AppBundleEntityPost;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
class CommentController extends Controller
{
/**
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
!
/*
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{ ... }
}
Anotación
Comentario
Anotaciones PHP
/** @Route(...) */Anotación
Comentario /* @Route(...) */
Utiliza @Route,
@Security y
@Cache
Enrutamiento tradicional
# app/config/routing.yml
_admin_post:
resource: '@AcmeAdminBundle/Resources/config/routing/post.yml'
prefix: '/admin/post'
!
# src/Acme/AdminBundle/Resources/config/routing/post.yml
admin_post_show:
pattern: '/{id}'
defaults: { _controller: 'AcmeAdminBundle:Post:show' }
!
!
namespace AcmeAdminBundleController;
!
class PostController {
public function showAction($id) { ... }
}
Enrutamiento con anotaciones
namespace AppBundleControllerAdmin;
!
use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
/**
* @Route("/admin/post")
*/
class PostController extends Controller
{
/**
* @Route("/{id}", name="admin_post_show")
*/
public function showAction($id) { ... }
}
No utilices la
anotación
@Template
La anotación @Template es mágica
/** @Template() */
public function indexAction()
{
// ...
return array('posts' => $posts);
}
!
!
public function indexAction()
{
// ...
return $this->render('blog/index.html.twig', array(
'posts' => $posts
));
}
Utiliza los
ParamConverter
cuando sea
sencillo
Los ParamConvertes en la práctica
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use AppBundleEntityPost;
!
class CommentController extends Controller
{
/**
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction(Post $post)
{
}
}
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
!
class CommentController extends Controller
{
/**
* @Route("/edit/{id}", name = "post_edit")
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$post = $em->getRepository('AppBundle:Post')->find($id);
!
if (!$post) {
throw $this->createNotFoundException();
}
}
}
Un controlador Symfony de ejemplo
namespace AppBundleController;
!
use AppBundleEntityPost;
use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
!
class BlogController extends Controller
{
/**
* @Route("/edit/{id}", name="post_edit")
*/
public function editAction(Post $post)
{
return $this->render('blog/edit.html.twig', array(
'post' => $post
));
}
}
Nuestro consejo
DESACOPLA ACOPLA
la
lógica de negocio
los
controladores
Los atajos de los controladores
$this->forward();
$this->redirect();
$this->redirectToRoute();
$this->getUser();
$this->getDoctrine();
$this->generateUrl();
$this->createNotFoundException();
$this->createAccessDeniedException()
Organizando el
proyecto
Seguridad
Anotaciones
Simplificaciones
Utiliza bcrypt
para codificar
las contraseñas
Combina varios
sistemas de
autorización
Restricciones poco granulares
# app/config/security.yml
security:
encoders:
# ...
!
providers:
# ...
!
firewalls:
# ...
!
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
Restricciones sencillas y comunes
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationSecurity;
!
/**
* @Route("/new", name="admin_post_new")
* @Security("has_role('ROLE_ADMIN')")
*/
public function newAction()
{
// ...
}
Restricciones sencillas y comunes
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationSecurity;
!
/**
* @Route("/new", name="admin_post_new")
*/
public function newAction()
{
if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) {
throw $this->createAccessDeniedException();
}
!
!
// ...
}
Restricciones sencillas y comunes
use AppBundleEntityPost;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationSecurity;
!
/**
* @Route("/{id}/edit", name="admin_post_edit")
* @Security("user.getEmail() == post.getAuthorEmail()")
*/
public function editAction(Post $post)
{
// ...
}
Restricciones sencillas y comunes
// src/AppBundle/Entity/Post.php
// ...
!
class Post
{
// ...
!
public function isAuthor(User $user = null)
{
return $user && $user->getEmail() == $this->getAuthorEmail();
}
}
Restricciones sencillas y comunes
use AppBundleEntityPost;
use SensioBundleFrameworkExtraBundleConfigurationSecurity;
!
/**
* @Route("/{id}/edit", name="admin_post_edit")
* @Security("post.isAuthor(user)")
*/
public function editAction(Post $post)
{
// ...
}
!
!
{% if post.isAuthor(app.user) %}
<a href=""> ... </a>
{% endif %}
Restricciones avanzadas (voters)
namespace AppBundleSecurity;
!
use SymfonyComponentSecurityCoreAuthorizationVoterAbstractVoter;
use SymfonyComponentSecurityCoreUserUserInterface;
!
class PostVoter extends AbstractVoter
{
protected function getSupportedAttributes()
{
return array('create', 'edit');
}
!
protected function getSupportedClasses()
{
return array('AppBundleEntityPost');
}
!
protected function isGranted($attribute, $post, $user = null)
{
// ...
}
}
Organizando el
proyecto
Seguridad
Anotaciones
Simplificaciones
Nombres de servicios en apps Symfony
// Servicios Symfony
$this->get('doctrine')
$this->get('logger')
$this->get('session')
$this->get('validator')
!
// Servicios de terceros
$this->get('imagine.filter.loader.thumbnail')
$this->get('knp_menu.renderer_provider')
$this->get('sonata.admin.form.filter.type.datetime_range')
Nombres de servicios propios
$this->get('slugger')
$this->get('parser')
$this->get('markdown_parser')
$this->get('stats_aggregator')
!
// Aceptable también
$this->get('app.slugger')
$this->get('app.parser')
$this->get('app.markdown_parser')
$this->get('app.stats_aggregator')
No definas parámetros para las clases
# app/config/services.yml
parameters:
slugger.class: AppBundleUtilsSlugger
!
services:
slugger:
class: "%slugger.class%"
No definas parámetros para las clases
# app/config/services.yml
parameters:
slugger.class: AppBundleUtilsSlugger
!
services:
slugger:
class: "%slugger.class%"
Innecesario y
poco útil en la
práctica
No definas parámetros que no cambian
# app/config/config.yml
parameters:
homepage.num_items: 10
!
// src/AppBundle/Entity/Post.php
class Post {
const NUM_ITEMS = 10;
!
// ...
}
No definas parámetros que no cambian
# app/config/config.yml
parameters:
homepage.num_items: 10
!
// src/AppBundle/Entity/Post.php
class Post {
const NUM_ITEMS = 10;
!
// ...
}
Este valor
seguramente no
cambia nunca
No añadas botones en los formularios
class PostType extends AbstractType {
public function buildForm($builder, $options) {
$builder
// ...
->add('save', 'submit', array('label' => 'Create Post'))
;
}
!
// ...
}
No añadas botones en los formularios
class PostType extends AbstractType {
public function buildForm($builder, $options) {
$builder
// ...
->add('save', 'submit', array('label' => 'Create Post'))
;
}
!
// ...
}
Te dificulta
reutilizar los
formularios
No utilices form_start y form_end
<form method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
!
<input type="submit" value="Create"
class="btn btn-default pull-right" />
</form>
No generes las URLs en los tests
public function testBlogArchives()
{
$client = self::createClient();
$url = $client->getContainer()->get('router')->generate('blog_archives');
$client->request('GET', $url);
// ...
}
!
!
public function testBlogArchives()
{
$client = self::createClient();
$client->request('GET', '/blog/archives/');
// ...
}
No generes las URLs en los tests
public function testBlogArchives()
{
$client = self::createClient();
$url = $client->getContainer()->get('router')->generate('blog_archives');
$client->request('GET', $url);
// ...
}
!
!
public function testBlogArchives()
{
$client = self::createClient();
$client->request('GET', '/blog/archives/');
// ...
}
Si rompes la URL
no te enteras
Configuración
proyecto/app/config/
!" config.yml
!" parameters.yml
$" parameters.yml.dist
Configuración
no cambia de un ordenador a otro
proyecto/app/config/
!" config.yml
!" parameters.yml
$" parameters.yml.dist
Configuración
no cambia de un ordenador a otro
cambia de un ordenador a otro
no se sube al repositorio
proyecto/app/config/
!" config.yml
!" parameters.yml
$" parameters.yml.dist
Configuración
no cambia de un ordenador a otro
cambia de un ordenador a otro
no se sube al repositorio
este sí se sube al repositorio
proyecto/app/config/
!" config.yml
!" parameters.yml
$" parameters.yml.dist
No utilices una configuración semántica
public function getConfigTreeBuilder() {
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('framework');
!
$rootNode
->children()
->scalarNode('secret')->end()
->scalarNode('http_method_override')
->info("Set true to enable support for ...”)
->defaultTrue()
->end()
->arrayNode('trusted_proxies')
->beforeNormalization()
->ifTrue(function ($v) { return !is_array($v) && null !== $v; })
->then(function ($v) { return is_bool($v) ? array() : preg_split('/s*,s*/', $v); })
->end()
->prototype('scalar')
->validate()
->ifTrue(function ($v) {
if (empty($v)) {
return false;
}
!
if (false !== strpos($v, '/')) {
Sólo es útil en
configuraciones
muy complejas
Conclusiones
Conjunto de técnicas que
puedes utilizar para desarrollar
aplicaciones Symfony como
recomiendan sus creadores.
Todo es opcional y no es
necesario utilizar todas las
buenas prácticas a la vez.
Estas buenas prácticas no
sirven en algunos proyectos
y escenarios concretos.
Aunque no las sigas, te
pueden servir para crear tus
propias buenas prácticas.
Nadie conoce tu trabajo y tus
circunstancias como tu. Por eso
las mejores buenas prácticas
son tus buenas prácticas.
Muchas gracias.
¿Preguntas, comentarios?
SensioLabs

Las buenas prácticas oficiales para aplicaciones Symfony

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
    Las buenas prácticasoficiales Conjunto de técnicas que puedes utilizar para desarrollar aplicaciones Symfony como recomiendan sus creadores.
  • 8.
    symfony.com/best-practices LIBROSWEB FABIEN POTENCIER RYAN WEAVER JAVIEREGUILUZ BUENAS PRÁCTICAS PARA APLICACIONES SYMFONY bit.ly/buenas-practicas-symfony 50 páginas 57 páginas
  • 9.
    ¿Por qué? Las buenasprácticas oficiosas complican mucho el desarrollo de aplicaciones y no siguen la filosofía pragmática de los creadores de Symfony.
  • 10.
    Definición de “BestPractice” A well defined procedure that is known to produce near-optimum results.
  • 11.
    Definición de “Pragmatic” Concernedwith making decisions and actions that are useful in practice, not just theory.
  • 12.
  • 13.
    Las buenas prácticasSymfony • Reflejan las ideas de su creador.
  • 14.
    Las buenas prácticasSymfony • Reflejan las ideas de su creador. • Son opcionales.
  • 15.
    Las buenas prácticasSymfony • Reflejan las ideas de su creador. • Son opcionales. • Symfony no cambiará para obligarte a usarlas.
  • 16.
    Usa las buenasprácticas …
  • 17.
    Usa las buenasprácticas … • En proyectos pequeños y medianos.
  • 18.
    Usa las buenasprácticas … • En proyectos pequeños y medianos. • En proyectos web estándar.
  • 19.
    Usa las buenasprácticas … • En proyectos pequeños y medianos. • En proyectos web estándar. • Si eres nuevo/a en Symfony.
  • 20.
    No uses lasbuenas prácticas …
  • 21.
    No uses lasbuenas prácticas … • En bundles compartidos (públicos o privados).
  • 22.
    No uses lasbuenas prácticas … • En bundles compartidos (públicos o privados). • En aplicaciones muy complejas o con arquitecturas muy especiales.
  • 23.
    No uses lasbuenas prácticas … • En bundles compartidos (públicos o privados). • En aplicaciones muy complejas o con arquitecturas muy especiales. • Si tienes tus propias buenas prácticas.
  • 24.
  • 25.
    Estructura de unaaplicación web proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 26.
    Estructura de unaaplicación web configuración proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 27.
    Estructura de unaaplicación web configuración plantillas proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 28.
    Estructura de unaaplicación web configuración plantillas tu código proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 29.
    Estructura de unaaplicación web configuración plantillas tu código dependencias proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 30.
    Estructura de unaaplicación web configuración plantillas tu código dependencias assets proyecto/ !" app/ # !" config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/
  • 31.
    ¿Cómo crear unsistema de plugins?
  • 32.
    ¿Cómo crear unsistema de plugins? • Deben funcionar de manera autónoma.
  • 33.
    ¿Cómo crear unsistema de plugins? • Deben funcionar de manera autónoma. • Pueden definir su propia configuración.
  • 34.
    ¿Cómo crear unsistema de plugins? • Deben funcionar de manera autónoma. • Pueden definir su propia configuración. • Pueden incluir plantillas y assets.
  • 35.
    Estructura de unplugin plugin/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 36.
    Estructura de unplugin configuración plugin/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 37.
    Estructura de unplugin configuración assets plugin/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 38.
    Estructura de unplugin configuración plantillas assets plugin/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 39.
    Estructura de unplugin configuración plantillas tu código assets plugin/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 40.
    proyecto/ !" app/ # !"config/ # $" Resources/ # $" views/ !" src/ !" vendor/ $" web/ !" css/ $" js/ Aplicaciones vs plugins/bundles bundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 41.
    Los bundles sonmini-aplicaciones Configuración Plantillas Código! fuente Assets Contenedor servicios Kernel Caché! y logs Aplicación ✔ ✔ ✔ ✔ ✔ ✔ ✔ Bundle ✔ ✔ ✔ ✔ ✔ ✘ ✘
  • 42.
    proyecto/ !" app/ # !"config/ # $" Resources/ # $" views/ !" src/ # # # # # # !" vendor/ $" web/ !" css/ $" js/ Los bundles en aplicaciones privadas AcmeUserBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeProductBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeOfferBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeInvoiceBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 43.
    proyecto/ !" app/ # !"config/ # $" Resources/ # $" views/ !" src/ # # # # # # !" vendor/ $" web/ !" css/ $" js/ Los bundles en aplicaciones privadas AcmeUserBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeProductBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeOfferBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeInvoiceBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 44.
    proyecto/ !" app/ # !"config/ # $" Resources/ # $" views/ !" src/ # # # # # # !" vendor/ $" web/ !" css/ $" js/ Los bundles en aplicaciones privadas AcmeUserBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeProductBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeOfferBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ... AcmeInvoiceBundle/ !" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
  • 45.
    Una verdad incómoda Esmuy probable que los bundles de tus aplicaciones no sean bundles, sólo directorios que molestan.
  • 46.
  • 47.
  • 48.
  • 49.
    Crea un solo bundlellamado AppBundle
  • 50.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ # $" DefaultController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" UserController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
  • 51.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ # $" DefaultController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" UserController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 4archivos
  • 52.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ # $" DefaultController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" UserController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 4archivos 2directorios 4archivos
  • 53.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ # $" DefaultController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" UserController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
  • 54.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" User/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
  • 55.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" User/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 8archivos
  • 56.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Controller/ # # $" DefaultController.php # !" PaymentBundle/ # # $" Controller/ # # $" DefaultController.php # !" ProductBundle/ # # $" Controller/ # # $" DefaultController.php # $" UserBundle/ # $" Controller/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ aplicacion/ !" app/ !" src/ # $" AppBundle/ # $" Controller/ # !" ContactController.php # !" PaymentController.php # !" ProductController.php # $" User/ | !" GroupController.php | !" ProfileController.php | !" RegistrationController.php | !" ResettingController.php | $" SecurityController.php !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 8archivos 3directorios 8archivos
  • 57.
    Configuración del enrutamiento #app/config/routing.yml app: resource: @AppBundle/Controller/ type: annotation
  • 58.
    No añadas un vendoren los bundles que no compartas
  • 59.
    No añadas unvendor a los bundles privados AcmeNetworksAcmeWebsiteMarketingBundle ! AcmeNetworksAcmeWebsiteMarketingBundle:Default:index.html.twig ! {{ render(controller( 'AcmeNetworksAcmeWebsiteMarketingBundle:Default:latestNews' )) }}
  • 60.
    No añadas unvendor a los bundles privados AcmeNetworksAcmeWebsiteMarketingBundle ! AcmeNetworksAcmeWebsiteMarketingBundle:Default:index.html.twig ! {{ render(controller( 'AcmeNetworksAcmeWebsiteMarketingBundle:Default:latestNews' )) }} Esto lo he visto con mis propios ojos
  • 61.
  • 62.
    Esta buena prácticaes la que produce un mayor impacto positivo AVISO IMPORTANTE
  • 63.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Resources/ # # $" views/ # # $" Default/ # # !" index.html.twig # # $" show.html.twig # . . . # $" ProductBundle/ # $" Resources/ # $" views/ # $" Default/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ your-application/ !" app/ # $" Resources/ # $" views/ # !" contact/ # # !" index.html.twig # # $" show.html.twig # . . . # $" product/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales
  • 64.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Resources/ # # $" views/ # # $" Default/ # # !" index.html.twig # # $" show.html.twig # . . . # $" ProductBundle/ # $" Resources/ # $" views/ # $" Default/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ your-application/ !" app/ # $" Resources/ # $" views/ # !" contact/ # # !" index.html.twig # # $" show.html.twig # . . . # $" product/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 5archivos
  • 65.
    aplicacion/ !" app/ !" src/ #!" ContactBundle/ # # $" Resources/ # # $" views/ # # $" Default/ # # !" index.html.twig # # $" show.html.twig # . . . # $" ProductBundle/ # $" Resources/ # $" views/ # $" Default/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ your-application/ !" app/ # $" Resources/ # $" views/ # !" contact/ # # !" index.html.twig # # $" show.html.twig # . . . # $" product/ # !" index.html.twig # !" category.html.twig # $" show.html.twig !" vendor/ $" web/ Aplicaciones Symfony tradicionales Buenas Prácticas oficiales 8directorios 5archivos 4directorios 5archivos
  • 66.
  • 67.
    Centralizar las plantillas Cambiala vida a tus diseñadores/as
  • 68.
    Centralizar las plantillas Cambiala vida a tus diseñadores/as Simplifica mucho tu código
  • 69.
    Nueva organización deplantillas aplicacion/ $" app/Resources/views/ !" contact/ # !" index.html.twig # $" show.html.twig . . . ! $" product/ !" index.html.twig !" category.html.twig $" show.html.twig
  • 70.
    La nueva notaciónde las plantillas $this->render('AcmeDemoBunde:Default:index.html.twig'); $this->render('default/index.html.twig'); ! $this->render('AcmeDemoBundle::index.html.twig'); $this->render('index.html.twig');
  • 71.
    La nueva notaciónde las plantillas {% extends '::layout.html.twig' %} {% extends 'layout.html.twig' %} ! {{ include('AcmeDemoBundle:Default:subdir/index.html.twig') }} {{ include('default/subdir/index.html.twig') }} ! {{ include('AcmeDemoBundle:Default/subdir:index.html.twig') }} {{ include('default/subdir/index.html.twig') }}
  • 72.
    Los problemas dela notación tradicional AcmeDemoBundle:Default:subdir/index.html.twig
  • 73.
    Los problemas dela notación tradicional • Requiere explicársela a cada diseñador/programador. AcmeDemoBundle:Default:subdir/index.html.twig
  • 74.
    Los problemas dela notación tradicional • Requiere explicársela a cada diseñador/programador. • Tiene excepciones (alguna de sus partes puede estar vacía) e inconsistencias (subdirectorios). AcmeDemoBundle:Default:subdir/index.html.twig
  • 75.
    Los problemas dela notación tradicional • Requiere explicársela a cada diseñador/programador. • Tiene excepciones (alguna de sus partes puede estar vacía) e inconsistencias (subdirectorios). • No es inmediato saber dónde está la plantilla (debes traducir la notación a un directorio). AcmeDemoBundle:Default:subdir/index.html.twig
  • 76.
  • 77.
    Centralizar los assets(CSS, JavaScript) • Tiene las mismas ventajas que centralizar las plantillas.
  • 78.
    Organizando los assetsweb proyecto/ !" app/ !" src/ !" vendor/ $" web/ !" css/ # !" bootstrap.min.css # $" app.css $" js/ !" jquery.min.js $" app.js
  • 79.
    Organizando los assetsweb proyecto/ !" app/ !" src/ !" vendor/ $" web/ !" css/ # !" bootstrap.min.css # $" app.css $" js/ !" jquery.min.js $" app.js proyecto/ !" app/ # $" Resources/ # $" assets/ # !" scss/ # # !" bootstrap/ # # $" app.scss # $" js/ !" src/ !" vendor/ $" web/ !" css/app.css $" js/app.js
  • 80.
    En resumen • Siutilizas mal los bundles, estás repitiendo la estructura de la aplicación sin necesidad • Si no vas a compartir tus bundles, utiliza los directorios de la aplicación (app/ Resources/ y web/). • El número de archivos se mantiene, los directorios y la complejidad se reducen.
  • 81.
  • 82.
  • 83.
    Escala de sensibilidad paraprogramadores Política Religión Fútbol Estándar de código Editor de código Anotaciones
  • 84.
    Anotaciones PHP use AppBundleEntityPost; useSensioBundleFrameworkExtraBundleConfigurationRoute; ! class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } ! /* * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } }
  • 85.
    Anotaciones PHP use AppBundleEntityPost; useSensioBundleFrameworkExtraBundleConfigurationRoute; ! class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } ! /* * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } } Anotación
  • 86.
    Anotaciones PHP use AppBundleEntityPost; useSensioBundleFrameworkExtraBundleConfigurationRoute; ! class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } ! /* * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { ... } } Anotación Comentario
  • 87.
    Anotaciones PHP /** @Route(...)*/Anotación Comentario /* @Route(...) */
  • 88.
  • 89.
    Enrutamiento tradicional # app/config/routing.yml _admin_post: resource:'@AcmeAdminBundle/Resources/config/routing/post.yml' prefix: '/admin/post' ! # src/Acme/AdminBundle/Resources/config/routing/post.yml admin_post_show: pattern: '/{id}' defaults: { _controller: 'AcmeAdminBundle:Post:show' } ! ! namespace AcmeAdminBundleController; ! class PostController { public function showAction($id) { ... } }
  • 90.
    Enrutamiento con anotaciones namespaceAppBundleControllerAdmin; ! use SymfonyBundleFrameworkBundleControllerController; use SensioBundleFrameworkExtraBundleConfigurationRoute; ! /** * @Route("/admin/post") */ class PostController extends Controller { /** * @Route("/{id}", name="admin_post_show") */ public function showAction($id) { ... } }
  • 91.
  • 92.
    La anotación @Templatees mágica /** @Template() */ public function indexAction() { // ... return array('posts' => $posts); } ! ! public function indexAction() { // ... return $this->render('blog/index.html.twig', array( 'posts' => $posts )); }
  • 93.
  • 94.
    Los ParamConvertes enla práctica use SensioBundleFrameworkExtraBundleConfigurationRoute; use AppBundleEntityPost; ! class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { } } use SensioBundleFrameworkExtraBundleConfigurationRoute; ! ! class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction($id) { $em = $this->getDoctrine()->getManager(); $post = $em->getRepository('AppBundle:Post')->find($id); ! if (!$post) { throw $this->createNotFoundException(); } } }
  • 95.
    Un controlador Symfonyde ejemplo namespace AppBundleController; ! use AppBundleEntityPost; use SymfonyBundleFrameworkBundleControllerController; use SensioBundleFrameworkExtraBundleConfigurationRoute; ! class BlogController extends Controller { /** * @Route("/edit/{id}", name="post_edit") */ public function editAction(Post $post) { return $this->render('blog/edit.html.twig', array( 'post' => $post )); } }
  • 96.
    Nuestro consejo DESACOPLA ACOPLA la lógicade negocio los controladores
  • 97.
    Los atajos delos controladores $this->forward(); $this->redirect(); $this->redirectToRoute(); $this->getUser(); $this->getDoctrine(); $this->generateUrl(); $this->createNotFoundException(); $this->createAccessDeniedException()
  • 98.
  • 99.
  • 100.
  • 101.
    Restricciones poco granulares #app/config/security.yml security: encoders: # ... ! providers: # ... ! firewalls: # ... ! access_control: - { path: ^/admin, roles: ROLE_ADMIN }
  • 102.
    Restricciones sencillas ycomunes use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationSecurity; ! /** * @Route("/new", name="admin_post_new") * @Security("has_role('ROLE_ADMIN')") */ public function newAction() { // ... }
  • 103.
    Restricciones sencillas ycomunes use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationSecurity; ! /** * @Route("/new", name="admin_post_new") */ public function newAction() { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { throw $this->createAccessDeniedException(); } ! ! // ... }
  • 104.
    Restricciones sencillas ycomunes use AppBundleEntityPost; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SensioBundleFrameworkExtraBundleConfigurationSecurity; ! /** * @Route("/{id}/edit", name="admin_post_edit") * @Security("user.getEmail() == post.getAuthorEmail()") */ public function editAction(Post $post) { // ... }
  • 105.
    Restricciones sencillas ycomunes // src/AppBundle/Entity/Post.php // ... ! class Post { // ... ! public function isAuthor(User $user = null) { return $user && $user->getEmail() == $this->getAuthorEmail(); } }
  • 106.
    Restricciones sencillas ycomunes use AppBundleEntityPost; use SensioBundleFrameworkExtraBundleConfigurationSecurity; ! /** * @Route("/{id}/edit", name="admin_post_edit") * @Security("post.isAuthor(user)") */ public function editAction(Post $post) { // ... } ! ! {% if post.isAuthor(app.user) %} <a href=""> ... </a> {% endif %}
  • 107.
    Restricciones avanzadas (voters) namespaceAppBundleSecurity; ! use SymfonyComponentSecurityCoreAuthorizationVoterAbstractVoter; use SymfonyComponentSecurityCoreUserUserInterface; ! class PostVoter extends AbstractVoter { protected function getSupportedAttributes() { return array('create', 'edit'); } ! protected function getSupportedClasses() { return array('AppBundleEntityPost'); } ! protected function isGranted($attribute, $post, $user = null) { // ... } }
  • 108.
  • 109.
    Nombres de serviciosen apps Symfony // Servicios Symfony $this->get('doctrine') $this->get('logger') $this->get('session') $this->get('validator') ! // Servicios de terceros $this->get('imagine.filter.loader.thumbnail') $this->get('knp_menu.renderer_provider') $this->get('sonata.admin.form.filter.type.datetime_range')
  • 110.
    Nombres de serviciospropios $this->get('slugger') $this->get('parser') $this->get('markdown_parser') $this->get('stats_aggregator') ! // Aceptable también $this->get('app.slugger') $this->get('app.parser') $this->get('app.markdown_parser') $this->get('app.stats_aggregator')
  • 111.
    No definas parámetrospara las clases # app/config/services.yml parameters: slugger.class: AppBundleUtilsSlugger ! services: slugger: class: "%slugger.class%"
  • 112.
    No definas parámetrospara las clases # app/config/services.yml parameters: slugger.class: AppBundleUtilsSlugger ! services: slugger: class: "%slugger.class%" Innecesario y poco útil en la práctica
  • 113.
    No definas parámetrosque no cambian # app/config/config.yml parameters: homepage.num_items: 10 ! // src/AppBundle/Entity/Post.php class Post { const NUM_ITEMS = 10; ! // ... }
  • 114.
    No definas parámetrosque no cambian # app/config/config.yml parameters: homepage.num_items: 10 ! // src/AppBundle/Entity/Post.php class Post { const NUM_ITEMS = 10; ! // ... } Este valor seguramente no cambia nunca
  • 115.
    No añadas botonesen los formularios class PostType extends AbstractType { public function buildForm($builder, $options) { $builder // ... ->add('save', 'submit', array('label' => 'Create Post')) ; } ! // ... }
  • 116.
    No añadas botonesen los formularios class PostType extends AbstractType { public function buildForm($builder, $options) { $builder // ... ->add('save', 'submit', array('label' => 'Create Post')) ; } ! // ... } Te dificulta reutilizar los formularios
  • 117.
    No utilices form_starty form_end <form method="post" {{ form_enctype(form) }}> {{ form_widget(form) }} ! <input type="submit" value="Create" class="btn btn-default pull-right" /> </form>
  • 118.
    No generes lasURLs en los tests public function testBlogArchives() { $client = self::createClient(); $url = $client->getContainer()->get('router')->generate('blog_archives'); $client->request('GET', $url); // ... } ! ! public function testBlogArchives() { $client = self::createClient(); $client->request('GET', '/blog/archives/'); // ... }
  • 119.
    No generes lasURLs en los tests public function testBlogArchives() { $client = self::createClient(); $url = $client->getContainer()->get('router')->generate('blog_archives'); $client->request('GET', $url); // ... } ! ! public function testBlogArchives() { $client = self::createClient(); $client->request('GET', '/blog/archives/'); // ... } Si rompes la URL no te enteras
  • 120.
  • 121.
    Configuración no cambia deun ordenador a otro proyecto/app/config/ !" config.yml !" parameters.yml $" parameters.yml.dist
  • 122.
    Configuración no cambia deun ordenador a otro cambia de un ordenador a otro no se sube al repositorio proyecto/app/config/ !" config.yml !" parameters.yml $" parameters.yml.dist
  • 123.
    Configuración no cambia deun ordenador a otro cambia de un ordenador a otro no se sube al repositorio este sí se sube al repositorio proyecto/app/config/ !" config.yml !" parameters.yml $" parameters.yml.dist
  • 124.
    No utilices unaconfiguración semántica public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); $rootNode = $treeBuilder->root('framework'); ! $rootNode ->children() ->scalarNode('secret')->end() ->scalarNode('http_method_override') ->info("Set true to enable support for ...”) ->defaultTrue() ->end() ->arrayNode('trusted_proxies') ->beforeNormalization() ->ifTrue(function ($v) { return !is_array($v) && null !== $v; }) ->then(function ($v) { return is_bool($v) ? array() : preg_split('/s*,s*/', $v); }) ->end() ->prototype('scalar') ->validate() ->ifTrue(function ($v) { if (empty($v)) { return false; } ! if (false !== strpos($v, '/')) { Sólo es útil en configuraciones muy complejas
  • 125.
  • 126.
    Conjunto de técnicasque puedes utilizar para desarrollar aplicaciones Symfony como recomiendan sus creadores.
  • 127.
    Todo es opcionaly no es necesario utilizar todas las buenas prácticas a la vez.
  • 128.
    Estas buenas prácticasno sirven en algunos proyectos y escenarios concretos.
  • 129.
    Aunque no lassigas, te pueden servir para crear tus propias buenas prácticas.
  • 130.
    Nadie conoce tutrabajo y tus circunstancias como tu. Por eso las mejores buenas prácticas son tus buenas prácticas.
  • 131.
  • 132.
  • 133.