Post on 14-Jul-2015
SensioLabs
SYMFONYBUENAS PRÁCTICAS
Javier Eguiluz
Symfony Barcelona
gracias a
Marc Morera @mmoreram
gracias a
Elcodi Symfony components based e-commerce platform
gracias a
Introducción
13OCTUBRE
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 webproyecto/
!" 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 pluginplugin/
!" DependencyInjection/ # $" Configuration.php !" Resources/ # !" config/ # !" public/ # # !" css/ # # $" js/ # $" views/ $" ...
Estructura de un plugin
configuraciónplugin/
!" 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/bundlesbundle/
!" 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
8 directorios
4 archivos
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
8 directorios
4 archivos
2 directorios
4 archivos
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
8 directorios
8 archivos
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
8 directorios
8 archivos
3 directorios
8 archivos
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 privadosAcmeNetworksAcmeWebsiteMarketingBundle !
AcmeNetworksAcmeWebsiteMarketingBundle:Default:index.html.twig
!
{{ render(controller( 'AcmeNetworksAcmeWebsiteMarketingBundle:Default:latestNews' )) }}
No añadas un vendor a los bundles privadosAcmeNetworksAcmeWebsiteMarketingBundle !
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
8 directorios
5 archivos
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
8 directorios
5 archivos
4 directorios
5 archivos
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 plantillasaplicacion/
$" 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 tradicionalAcmeDemoBundle: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 webproyecto/ !" app/ !" src/ !" vendor/ $" web/ !" css/ # !" bootstrap.min.css # $" app.css $" js/ !" jquery.min.js $" app.js
Organizando los assets webproyecto/ !" 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 PHPuse AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; !
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 PHPuse AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; !
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 PHPuse AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; !
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 Acme\AdminBundle\Controller; !
class PostController { public function showAction($id) { ... } }
Enrutamiento con anotacionesnamespace AppBundle\Controller\Admin; !
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; !
/** * @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ácticause Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use AppBundle\Entity\Post;
!class CommentController extends Controller { /** * @Route("/edit/{id}", name = "post_edit") */ public function editAction(Post $post) { } }
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; !!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 ejemplonamespace AppBundle\Controller; !use AppBundle\Entity\Post; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
!
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 ACOPLAla
lógica de negociolos
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 comunesuse Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; !
/** * @Route("/new", name="admin_post_new") * @Security("has_role('ROLE_ADMIN')") */ public function newAction() { // ... }
Restricciones sencillas y comunesuse Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; !
/** * @Route("/new", name="admin_post_new") */ public function newAction() { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { throw $this->createAccessDeniedException(); } !! // ... }
Restricciones sencillas y comunesuse AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; !
/** * @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 comunesuse AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; !
/** * @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 AppBundle\Security; !use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; use Symfony\Component\Security\Core\User\UserInterface; !class PostVoter extends AbstractVoter { protected function getSupportedAttributes() { return array('create', 'edit'); } ! protected function getSupportedClasses() { return array('AppBundle\Entity\Post'); } ! 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: AppBundle\Utils\Slugger !
services: slugger: class: "%slugger.class%"
No definas parámetros para las clases# app/config/services.yml parameters: slugger.class: AppBundle\Utils\Slugger !
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 formulariosclass PostType extends AbstractType {
public function buildForm($builder, $options) {
$builder
// ...
->add('save', 'submit', array('label' => 'Create Post'))
;
}
!
// ...
}
No añadas botones en los formulariosclass 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 testspublic 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 testspublic 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ónproyecto/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ánticapublic 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