Las buenas prácticas oficiales para aplicaciones Symfony

Post on 14-Jul-2015

318 views 0 download

Transcript of Las buenas prácticas oficiales para aplicaciones Symfony

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