materiales del curso de symfony2

295
Raúl Fraile Beneyto Materiales del curso

Upload: raul-fraile

Post on 05-Dec-2014

2.594 views

Category:

Technology


4 download

DESCRIPTION

Materiales de apoyo utilizados para los cursos de Symfony2 impartidos en 2012 y 2013

TRANSCRIPT

Page 1: Materiales del curso de Symfony2

Raúl Fraile Beneyto

Materiales del curso

Page 2: Materiales del curso de Symfony2

Temario

• Tema 1: Prerequisitos

• PHP 5.3

• YAML

• MVC

• PSR-0 (Autoloading)

• PSR-1, PSR-2 (Coding Standards)

Page 3: Materiales del curso de Symfony2

Temario

• Tema 2: Introducción a symfony2

• Historia

• Arquitectura

• Gestión de dependencias con Composer

• Inyección de dependencias

• Entornos de ejecución

• Instalación y configuración

• Documentación

Page 4: Materiales del curso de Symfony2

Temario

• Tema 3: Primeras páginas

• Bundles

• Routing

• Controladores

Page 5: Materiales del curso de Symfony2

Temario

• Tema 4: Lenguaje de plantillas Twig

• Sintaxis básica

• Herencia

• Macros

• Twig extensions

Page 6: Materiales del curso de Symfony2

Temario

• Tema 5: Bases de datos con Doctrine2

• ORM

• DBAL

• Entidades y relaciones

• Configuración

• Fixtures

Page 7: Materiales del curso de Symfony2

Temario

• Tema 6: Frontend

• Gestión de assets

• Tratamiento de imágenes

• Formatos alternativos

Page 8: Materiales del curso de Symfony2

Temario

• Tema 7: Backend

• Formularios

• Conceptos básicos

• Generación automática de formularios

• Formularios avanzados

• Validación

Page 9: Materiales del curso de Symfony2

Temario

• Tema 8: Seguridad

• Autenticación VS Autorización

• Roles

• Configuración

• Login

Page 10: Materiales del curso de Symfony2

Temario

• Tema 9: Servicios y eventos

• Contenedor de inyección de dependencias

• Eventos

Page 11: Materiales del curso de Symfony2

Temario

• Tema 10: Extendiendo symfony2

• Comandos de consola

• Extensiones propias de Twig

Page 12: Materiales del curso de Symfony2

Temario

• Tema 11: Optimización y rendimiento

• Optimización de assets

• APC

• ESI

Page 13: Materiales del curso de Symfony2

Temario

• Proyecto: tienda “social” de libros

• Usuarios se registran y compran libros. Tienen un perfil público con los libros comprados.

• Los libros se agrupan por tecnologías (N:M).

• Backend para gestionar usuarios, libros y tecnologías.

• Multiidioma, optimización de assets, redimensión imágenes, canales RSS, etc.

Page 14: Materiales del curso de Symfony2

Prerequisitos

Page 15: Materiales del curso de Symfony2

PHP 5.3

Page 16: Materiales del curso de Symfony2

Namespaces

Page 17: Materiales del curso de Symfony2

<?phprequire_once(‘./libs/libA/foo.class.php’);require_once(‘./libs/libB/foo.class.php’);

$foo = new Foo();

Prerequisitos. PHP 5.3 / Namespaces

Page 18: Materiales del curso de Symfony2

// ./libs/libA/Foo.php<?phpnamespace LibA;

class Foo {}

// ./libs/libB/Foo.php<?phpnamespace LibB;

class Foo {}

Prerequisitos. PHP 5.3 / Namespaces

Page 19: Materiales del curso de Symfony2

// ./index.php<?phpnamespace MyApp;

use LibA\Foo;

$foo = new Foo();

Prerequisitos. PHP 5.3 / Namespaces

Page 20: Materiales del curso de Symfony2

// ./index.php<?phpnamespace MyApp;

use LibA\Foo as FooA;use LibB\Foo as FooB;

$fooA = new FooA();$fooB = new FooB();

Prerequisitos. PHP 5.3 / Namespaces

Page 21: Materiales del curso de Symfony2

// ./index.php<?phpnamespace MyApp;

$created = new \DateTime();$len = strlen(‘hola’);echo $len . PHP_EOL;

Prerequisitos. PHP 5.3 / Namespaces

Page 22: Materiales del curso de Symfony2

Closures

Page 23: Materiales del curso de Symfony2

<?php

echo preg_replace_callback('/[a-z]/',     function ($match) {        return strtoupper($match[0]);     },     'Hola'); // HOLA

Prerequisitos. PHP 5.3 / Closures

Page 24: Materiales del curso de Symfony2

Annotations

Page 25: Materiales del curso de Symfony2

<?php

/** * @ORM\Column(type="string") * @Assert\NotBlank() */protected $name;

Prerequisitos. PHP 5.3 / Annotations

Page 26: Materiales del curso de Symfony2

<?php

/** * @Route("/signup") * @Method({"POST"}) */public function signupAction(){    ...}

Prerequisitos. PHP 5.3 / Annotations

Page 27: Materiales del curso de Symfony2

OOP

Page 28: Materiales del curso de Symfony2

public $foo;

private $foo;

protected $foo;

Prerequisitos. PHP 5.3 / OOP

Page 29: Materiales del curso de Symfony2

<?php

class Foo{    private $a = 1;    protected $b = 2;    public $c = 3;}

$foo = new Foo();echo $foo->a;

Prerequisitos. PHP 5.3 / OOP

Page 30: Materiales del curso de Symfony2

<?php

class Foo{    private $a = 1;    protected $b = 2;    public $c = 3;}

$foo = new Foo();echo $foo->b;

Prerequisitos. PHP 5.3 / OOP

Page 31: Materiales del curso de Symfony2

<?php

class Foo{    private $a = 1;    protected $b = 2;    public $c = 3;}

class Bar extends Foo {    public function getA()    {        return $this->b;    }}

$foo = new Foo();echo $foo->getA();

Prerequisitos. PHP 5.3 / OOP

Page 32: Materiales del curso de Symfony2

¿Diferencia entre una interface y una clase abstracta?

Prerequisitos. PHP 5.3 / OOP

Page 33: Materiales del curso de Symfony2

Prerequisitos. PHP 5.3 / OOP// JsonInterface.phpinterface JsonInterface{    const MAX_DEPTH = 100;

    public function exportJson();}

// User.phpclass User implements{    public function exportJson()    {        return array(            'username' => $this->username;        );    }}

Page 34: Materiales del curso de Symfony2

Prerequisitos. PHP 5.3 / OOP

// BaseController.phpabstract class BaseController{    public function getCurrentUser()    {        ...    }}

// UserController.phpclass UserController extends BaseController{    ...}

Page 35: Materiales del curso de Symfony2

¿Puede haber herencia entre interfaces?

Prerequisitos. PHP 5.3 / OOP

Page 36: Materiales del curso de Symfony2

Prerequisitos. PHP 5.3 / OOP

<?php

namespace DemoBundle\Entity;

class User{    public function setBirthday(\DateTime $date)    {        $this->birthday = $date;    }{

Page 37: Materiales del curso de Symfony2

YAML

Page 38: Materiales del curso de Symfony2

“YAML is a human friendly data serialization standard for all programming languages. YAML is a great format for your configuration files. YAML

files are as expressive as XML files and as readable as INI files.”

Prerequisitos. YAML

Page 39: Materiales del curso de Symfony2

parameters:    database_driver: pdo_mysql    database_host: localhost    database_port: ~    database_name: db_test    database_user: db_user_test    database_password: db_pass_test

Prerequisitos. YAML

Page 40: Materiales del curso de Symfony2

parameters:    emails: [ ‘[email protected]’, ‘[email protected]’ ]    webs:         - ‘web1.com’        - ‘web2.com’    technologies: { PHP: 5.3, MySQL: 5.1 }

Prerequisitos. YAML

Page 41: Materiales del curso de Symfony2

MVC

Page 42: Materiales del curso de Symfony2

Prerequisitos. MVC

Page 43: Materiales del curso de Symfony2

PSR-0 (Autoloading)

Page 44: Materiales del curso de Symfony2

<?php

require_once(‘classes/page.php’);require_once(‘classes/chapter.php’);require_once(‘classes/book.php’);

$book = new Book(‘my book’);$chapter = new Chapter(‘chapter 1’);$book->addChapter($chapter);

Prerequisitos. PSR-0 (Autoloading)

Page 45: Materiales del curso de Symfony2

<?php

require_once(‘autoload.php’);

$book = new Book(‘my book’);$chapter = new Chapter(‘chapter 1’);$book->addChapter($chapter);

Prerequisitos. PSR-0 (Autoloading)

Page 46: Materiales del curso de Symfony2

FQN(Fully Qualified Name) Ruta

\Symfony\Component\Filesystem\Filesystem

[path]/Symfony/Component/Filesystem/Filesystem.php

\Twig_Function [path]/Twig/Function.php

Prerequisitos. PSR-0 (Autoloading)

Page 47: Materiales del curso de Symfony2

¿Impacto en el rendimiento de la aplicación?

Prerequisitos. PSR-0 (Autoloading)

Page 48: Materiales del curso de Symfony2

PSR-1, PSR-2 (Coding Standards)

Page 49: Materiales del curso de Symfony2

<?php

function a($b){    if ($b > 1) {        return $b;    }    return 0;}

function a($b) {    if ($b > 1) return $b;    return 0;}

Prerequisitos. PSR-1, PSR-2 (Coding Standards)

Page 51: Materiales del curso de Symfony2

Introducción a SF2

Page 52: Materiales del curso de Symfony2

Historia

Page 53: Materiales del curso de Symfony2

Fabien Potencier

Introducción a Symfony2. Historia

Page 54: Materiales del curso de Symfony2

Versión Fecha PHP

1.0 Enero 2007 >= 5.0

1.1 Junio 2008 >= 5.1

1.2 Diciembre 2008 >= 5.2

1.3 Noviembre 2009 >= 5.2.4

1.4 Noviembre 2009 >= 5.2.4

2.0 Julio 2011 >= 5.3.2

2.1 Septiembre 2012 >= 5.3.3

Introducción a Symfony2. Historia

Page 55: Materiales del curso de Symfony2

Arquitectura

Page 56: Materiales del curso de Symfony2

BrowserKit

ClassLoader

Config

Console

CssSelector

DependencyInjection

DomCrawler

EventDispatcher

Finder

Form

HttpFoundation

Locale

Process

HttpKernel

Routing

Security

Serializer

Templating

Translation

Validator

Yaml

Introducción a Symfony2. Arquitectura

Page 57: Materiales del curso de Symfony2

Introducción a Symfony2. Arquitectura

Componentes + Bundles + Librerías externas

Full-stack framework

Page 58: Materiales del curso de Symfony2

Distribuciones

Page 59: Materiales del curso de Symfony2

Gestión de dependencias con Composer

Page 60: Materiales del curso de Symfony2

Introducción a Symfony2. Composer

Page 61: Materiales del curso de Symfony2

“Composer is a tool for dependency management in PHP. It allows you to declare

the dependent libraries your project needs and it will install them in your project for you”

Introducción a Symfony2. Composer

Page 62: Materiales del curso de Symfony2

$ curl -s https://getcomposer.org/installer | php

$ sudo mv composer.phar /usr/local/bin/composer

Introducción a Symfony2. Composer

Page 63: Materiales del curso de Symfony2

Opción Descripción

list Lista de opciones

self-update Actualizar composer

create-project Crea proyecto a partir dependencia

init Crear composer.json básico

validate Valida el archivo composer.json

install Instala dependencias (.lock, .json)

update Actualiza dependencias + .lock

Introducción a Symfony2. Composer

Page 64: Materiales del curso de Symfony2

# composer.json

{ "name": "raulfraile/demo_composer", "description": "Demo composer", "require": { "symfony/console": "2.1.*" }}

Introducción a Symfony2. Composer

Page 65: Materiales del curso de Symfony2

# composer.json

{ "name": "raulfraile/demo_composer", "description": "Demo composer", "require": { "php": ">=5.3.3", "symfony/console": "2.1.*" }}

Introducción a Symfony2. Composer

Page 66: Materiales del curso de Symfony2

# composer.json

{ "name": "raulfraile/demo_composer", "description": "Demo composer", "require": { "php": ">=5.3.3", "symfony/console": "2.1.*", "doctrine/orm": ">=2.2.3,<2.4-dev", }}

Introducción a Symfony2. Composer

Page 67: Materiales del curso de Symfony2

Demo: Composer + Console component

Page 68: Materiales del curso de Symfony2

• Ejercicio. Crear un proyecto de consola que a partir de unos archivos en YAML con información sobre facturas, proporcione los siguientes comandos:

summary (year): Muestra el total del año introducido (por defecto el actual).

add date total: Añade un nuevo importe al archivo YAML correspondiente.

Utilizar los componentes Console, Yaml y Finder. Para hacer debug os puede ser de utilidad el paquete raulfraile/ladybug.

Introducción a Symfony2. Composer

Page 69: Materiales del curso de Symfony2

Inyección de dependencias

Page 70: Materiales del curso de Symfony2

"Dependency Injection is where components are given their dependencies through their constructors, methods, or directly

into fields."

Introducción a Symfony2. Inyección de dependencias

Page 71: Materiales del curso de Symfony2

class UserController{    private $em;

    public function __construct()    {        $this->em = new EntityManager();    }}

$userController = new UserController();

Introducción a Symfony2. Inyección de dependencias

Page 72: Materiales del curso de Symfony2

class UserController{    private $em;

    public function __construct()    {        $this->em = new EntityManager(‘conn2’);    }}

$userController = new UserController();

Introducción a Symfony2. Inyección de dependencias

Page 73: Materiales del curso de Symfony2

class UserController{    private $em;

    public function __construct(EntityManager $em)    {        $this->em = $em;    }}

$em = new EntityManager();$userController = new UserController($em);

Introducción a Symfony2. Inyección de dependencias

Page 74: Materiales del curso de Symfony2

DIC: Contenedor de Inyección de Dependencias

Page 75: Materiales del curso de Symfony2

use Symfony\Component\DependencyInjection;use Symfony\Component\DependencyInjection\Reference; $sc = new DependencyInjection\ContainerBuilder();$sc->register('mailer', 'MyMailer');$sc->register('em.main', 'MyEntityM')    ->setArguments(array('conn1'));$sc->register('logger', 'MyLogger')    ->setArguments(array(new Reference('em.main')));

$sc->get('logger');

Introducción a Symfony2. Inyección de dependencias

Page 76: Materiales del curso de Symfony2

Entornos de ejecución

Page 77: Materiales del curso de Symfony2

Mismo código

Diferentes configuraciones

Introducción a Symfony2. Entornos de ejecución

Page 78: Materiales del curso de Symfony2

Entorno ¿Mostrar errores?

Cachear consultas

Base de datos

dev Sí No bd_dev

prod No Sí bd_prod

Introducción a Symfony2. Entornos de ejecución

Page 79: Materiales del curso de Symfony2

Instalación y configuración

Page 80: Materiales del curso de Symfony2

$ composer create-project symfony/framework-standard-edition path/ 2.1.3

Introducción a Symfony2. Instalación y configuración

Page 81: Materiales del curso de Symfony2

Introducción a Symfony2. Instalación y configuración

Page 82: Materiales del curso de Symfony2

Documentación

Page 83: Materiales del curso de Symfony2

Introducción a Symfony2. Documentación

Page 84: Materiales del curso de Symfony2

Primeras páginas

Page 85: Materiales del curso de Symfony2

Bundles

Page 86: Materiales del curso de Symfony2

Todo el contenido de nuestra aplicaciónestará dentro de uno o más bundles

Primeras páginas. Bundles

Page 87: Materiales del curso de Symfony2

Primeras páginas. Bundles

$ php app/console generate:bundle

Page 88: Materiales del curso de Symfony2

Primeras páginas. Bundles

// app/AppKernel.php

class AppKernel extends Kernel{    public function registerBundles()    {        $bundles = array(            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),            new Symfony\Bundle\SecurityBundle\SecurityBundle(),            new Symfony\Bundle\TwigBundle\TwigBundle(),            new Symfony\Bundle\MonologBundle\MonologBundle(),            new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),            new Symfony\Bundle\AsseticBundle\AsseticBundle(),            new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(), ...        );

        if (in_array($this->getEnvironment(), array('dev', 'test'))) {            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();        }

        return $bundles;    }}

Page 89: Materiales del curso de Symfony2

Primeras páginas. Bundles

Page 90: Materiales del curso de Symfony2

Routing

Page 91: Materiales del curso de Symfony2

Primeras páginas. Routing

/user/profile/raulfraile

Controlador: UserControllerAcción: showProfile($username)

Page 92: Materiales del curso de Symfony2

Primeras páginas. Routing

# routing.ymluser_profile:    pattern: /user/profile/{username}    defaults: { _controller: DemoBundle:User:profile }

Page 93: Materiales del curso de Symfony2

Primeras páginas. Routing

# routing.ymlusers_list:    pattern: /user/list/{page}    defaults: { _controller: DemoBundle:User:list, page: 1 }

Page 94: Materiales del curso de Symfony2

Primeras páginas. Routing

# routing.ymlusers_list:    pattern: /user/list/{page}    defaults: { _controller: DemoBundle:User:list, page: 1 } requirements: page: \d+

Page 95: Materiales del curso de Symfony2

Primeras páginas. Routing

# routing.ymlusers_list:    pattern: /user/list/{page}{subpage}    defaults: { _controller: DemoBundle:User:list, page: 1 } requirements: page: \d+ _method: GET

Page 96: Materiales del curso de Symfony2

Primeras páginas. Routing

# user.ymluser_profile:    pattern: /profile/{username}    defaults: { _controller: DemoBundle:User:profile }

# routing.ymluser_routing: resource: "@DemoBundle/Resources/config/user.yml" prefix: /user

Page 97: Materiales del curso de Symfony2

Primeras páginas. Routing

$ php app/console router:debug [route_name]

Page 98: Materiales del curso de Symfony2

Primeras páginas. Routing

$ php app/console router:match path

Page 99: Materiales del curso de Symfony2

Controladores

Page 100: Materiales del curso de Symfony2

Request Response

Primeras páginas. Controladores

Page 101: Materiales del curso de Symfony2

use Symfony\Component\HttpFoundation\Response;

class UserController{    public function helloAction()    {        return new Response('Hello world!');    }}

Primeras páginas. Controladores

Page 102: Materiales del curso de Symfony2

use Symfony\Component\HttpFoundation\Response;

class UserController{    public function helloAction($name)    {        return new Response('Hello '.$name.'!');    }}

Primeras páginas. Controladores

Page 103: Materiales del curso de Symfony2

use Symfony\Component\HttpFoundation\Response;use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class UserController extends Controller{    public function helloAction($name)    {        return new Response('Hello '.$name.'!');    }}

Primeras páginas. Controladores

Page 104: Materiales del curso de Symfony2

public function indexAction(){    return $this->redirect($this->generateUrl('home'));}

Primeras páginas. Controladores

Page 105: Materiales del curso de Symfony2

public function indexAction(){    throw $this->createNotFoundException('Not found');}

Primeras páginas. Controladores

Page 106: Materiales del curso de Symfony2

Demo: Controladores + objeto Request

Page 107: Materiales del curso de Symfony2

• Ejercicio. Crear todas las rutas del proyecto, con sus respectivos controladores:

Primeras páginas. Controladores

Ruta Controller

/register AccountController

/book/{slug} BookController

/technology/{slug} TechnologyController

/author/{slug} AuthorController

/book/buy/{slug} BookController

/book/buy_confirm/{slug} BookController

/user/{username} UserController

/account/profile AccountController

/account/password AccountController

/search?q={consulta}&offset={offset}&limit={limit} SearchController

/api/book/latest ApiController

/api/book/featured ApiController

Page 108: Materiales del curso de Symfony2

• Ejercicio. En la página de ‘search’, mostrar los valores de los 3 parámetros (query, offset y limit), además de:

• IP del usuario

• Idiomas del usuario (e idioma preferido)

• Nombre de la ruta que está cogiendo

Primeras páginas. Controladores

Page 109: Materiales del curso de Symfony2

• Ejercicio. En las páginas de la API generar un JSON con los datos solicitados (de momento de prueba) y devolviendo las cabeceras correctas.

Content-Type: application/json

Primeras páginas. Controladores

Page 110: Materiales del curso de Symfony2

• Ejercicio. En la página de ‘technology’, cuando se pidan libros de tecnología ‘java’, se debe redirigir al usuario para mostrar los libros de tecnología ‘php’ y además mostrar un mensaje diciendo ‘PHP es mejor’.

Ayuda: flash messages

• $session->getFlashBag()->add('notice', 'Profile updated');

• $session->getFlashBag()->get('notice', null);

Primeras páginas. Controladores

Page 111: Materiales del curso de Symfony2

Twig

Page 112: Materiales del curso de Symfony2

Sintaxis básica

Page 113: Materiales del curso de Symfony2

{{ }}

{% %}

{# #}

Twig. Sintaxis básica

Page 114: Materiales del curso de Symfony2

{{ name }}

Twig. Sintaxis básica - Variables

{{ user.name }}

{{ user[‘name’] }}

Page 115: Materiales del curso de Symfony2

{% set foo = 'foo' %}

{% set foo = [1, 2] %}

{% set foo = {'foo': 'bar'} %}

Twig. Sintaxis básica - Variables

Page 116: Materiales del curso de Symfony2

{{ name|upper }}

{{ name|striptags|nlbr }}

Twig. Sintaxis básica - Filtros

Page 117: Materiales del curso de Symfony2

abscapitalize

convert_encodingdate

date_modifydefaultescapeformat

joinjson_encode

keyslengthlowermerge

nl2brnumber_format

rawreplacereverse

slicesortsplit

striptagstitletrim

upperurl_encode

Twig. Sintaxis básica - Filtros

Page 118: Materiales del curso de Symfony2

{{ random(['rojo', 'azul', 'verde']) }}

{{ random('ABC') }}

{{ random() }}

{{ random(5) }}

Twig. Sintaxis básica - Funciones

Page 119: Materiales del curso de Symfony2

{{ path('home') }}

<a href=”{{ path(‘user_profile’, {‘username’: user.username }) }}”>

Twig. Sintaxis básica - Funciones

Page 120: Materiales del curso de Symfony2

{{ url('home') }}

{{ url(‘user_profile’, {‘username’: user.username }) }}

Twig. Sintaxis básica - Funciones

Page 121: Materiales del curso de Symfony2

{% for user in users %} <li>{{ user.username }}</li>{% endfor %}

Twig. Sintaxis básica - Control de flujo

Page 122: Materiales del curso de Symfony2

{% for user in users %} <li>{{ user.username }}</li>{% else %} <li>No hay usuarios</li>{% endfor %}

Twig. Sintaxis básica - Control de flujo

Page 123: Materiales del curso de Symfony2

{% for user in users %} <li>{{ loop.index }} {{ user.username }}</li>

{% endfor %}

Twig. Sintaxis básica - Control de flujo

Page 124: Materiales del curso de Symfony2

{% for user in users if user.active %} <li>{{ user.username }}</li>{% endfor %}

Twig. Sintaxis básica - Control de flujo

Page 125: Materiales del curso de Symfony2

{% for key, user in users %} <li>{{ user.username }}</li>{% endfor %}

Twig. Sintaxis básica - Control de flujo

Page 126: Materiales del curso de Symfony2

{% if user.active %} <p>Usuario activo</p>{% endif %}

Twig. Sintaxis básica - Control de flujo

Page 127: Materiales del curso de Symfony2

{% if user.status == 0 %} <p>Inactivo</p>{% elseif user.status == 1 %} <p>Activo</p>{% else %} <p>Deshabilitado</p>{% endif %}

Twig. Sintaxis básica - Control de flujo

Page 128: Materiales del curso de Symfony2

• Ejercicio. Crear la vista search.html.twig, para que muestre exactamente los mismo que antes, pero utilizando Twig.

Ayuda: $this->render(‘DemoBundle::search.html.twig’, array());

Twig. Sintaxis básica

Page 129: Materiales del curso de Symfony2

• Ejercicio. En la página del autor, simular que se devuelven libros de la base de datos con un array y crear una tabla/lista con Twig de los títulos de los libros.

Los títulos deberían ser un enlace a la página de cada uno de los libros.

Twig. Sintaxis básica

Page 130: Materiales del curso de Symfony2

Herencia

Page 131: Materiales del curso de Symfony2

{% include %}

{% extends %}

{% render %}

Twig. Herencia

Page 132: Materiales del curso de Symfony2

{# _footer.html.twig #}<footer>&copy; {{ “now”|date(‘Y’) }}</footer>

{# home.html.twig #}<html> <head> <title>Ejemplo include</title> </head> <body> ... {% include ‘_footer.html.twig’ %} </body></html>

Twig. Herencia

Page 133: Materiales del curso de Symfony2

{# _footer.html.twig #}<footer>&copy; {{ “now”|date(‘Y’) }}</footer>

{# home.html.twig #}<html> <head> <title>Ejemplo include</title> </head> <body> ... {% include ‘DemoBundle:web:_footer.html.twig’ %} </body></html>

Twig. Herencia

Page 134: Materiales del curso de Symfony2

{# layout.html.twig #}<html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body></html>

{# user_profile.html.twig #}{% extends ‘layout.html.twig’ %}{% block title %}Perfil de usuario{% endblock %}{% block content %} <h1>{{ user.username }}</h1>{% endblock %}

Twig. Herencia

Page 135: Materiales del curso de Symfony2

{# layout.html.twig #}<html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body></html>

{# user_profile.html.twig #}{% extends ‘DemoBundle::layout.html.twig’ %}{% block title %}Perfil de usuario{% endblock %}{% block content %} <h1>{{ user.username }}</h1>{% endblock %}

Twig. Herencia

Page 136: Materiales del curso de Symfony2

{# layout.html.twig #}<html> <head> <title>{% block title %}Título{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body></html>

{# user_profile.html.twig #}{% extends ‘DemoBundle::layout.html.twig’ %}{% block title %}Perfil de usuario{% endblock %}{% block content %} <h1>{{ user.username }}</h1>{% endblock %}

Twig. Herencia

Page 137: Materiales del curso de Symfony2

{# index.html.twig #}<html> <head> <title>Título</title> </head> <body> ... {% render "DemoBundle:Notifications:show" %} </body></html>

Twig. Herencia

Page 138: Materiales del curso de Symfony2

• Ejercicio. Crear un layout.html.twig con el código común a todas las páginas y que incluya bloques para: título de la página, css, javascript y contenido.

Utilizar el layout en la página de search.

Twig. Herencia

Page 139: Materiales del curso de Symfony2

• Ejercicio. Suponed que queremos tener el código de Google Analytics en todas las páginas, y lo cremos en un archivo _analytics.html.twig, ¿dónde se añadiría?

Además, sólo queremos que salga el código cuando estemos en el entorno de producción. (Ayuda, variable global ‘app’ de Twig).

{{ app|ld }}{{ dump(app) }}

Twig. Herencia

Page 140: Materiales del curso de Symfony2

Macros

Page 141: Materiales del curso de Symfony2

{# macros.html.twig #}{% macro m_user_box(user) %} <div class=”user_box”> <p>{{ user.username }}</p> </div>{% endmacro %}

{# index.html.twig #}{% import "macros.html.twig" as modules %}

{{ modules.m_user_box(user) }}

Twig. Macros

Page 142: Materiales del curso de Symfony2

{# macros.html.twig #}{% macro m_user_box(user) %} <div class=”user_box”> <p>{{ user.username }}</p> </div>{% endmacro %}

{# index.html.twig #}{% import "DemoBundle::macros.html.twig" as modules %}

{{ modules.m_user_box(user) }}

Twig. Macros

Page 143: Materiales del curso de Symfony2

• Ejercicio. Crear 2 macros, que nos servirán para mostrar un libro (p.ej. en cualquier listado de libros: search, technologies, author...) y para mostrar un usuario (p.ej. para la lista de usuarios que han comprado ese libro).

Ambas macros deben recibir la información del libro/usuario y enlazar con la página de cada uno de ellos.

Utilizar las 2 macros en las páginas de author y book, simulando que obtenemos los datos de la bd.{# macros.html.twig #}{% macro m_user_box(user) %} <div class=”user_box”> <p>{{ user.username }}</p> </div>{% endmacro %}

{# index.html.twig #}{% import "DemoBundle::macros.html.twig" as modules %}{{ modules.m_user_box(user) }}

Twig. Macros

Page 144: Materiales del curso de Symfony2

Extensiones

Page 145: Materiales del curso de Symfony2

Oficiales: Twig Extensions Repository

Comunidad

Propias

Twig. Extensiones

Page 146: Materiales del curso de Symfony2

Doctrine 2

Page 147: Materiales del curso de Symfony2

ORM

Page 148: Materiales del curso de Symfony2

ORM: Object Relational Mapper

Doctrine 2. ORM

Page 149: Materiales del curso de Symfony2

Doctrine 2. ORM

Page 150: Materiales del curso de Symfony2

Doctrine 2. ORM

MySQL

PDO

DBAL

ORM

SQL Server Oracle ...

Page 151: Materiales del curso de Symfony2

DBAL

Page 152: Materiales del curso de Symfony2

class UserController extends Controller{    public function indexAction()    {        $conn = $this->get('database_connection');        $users = $conn->fetchAll('SELECT * FROM users');    }}

Doctrine 2. DBAL

Page 153: Materiales del curso de Symfony2

Entidades y relaciones

Page 154: Materiales del curso de Symfony2

namespace Demo\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/** * @ORM\Table(name="category") * @ORM\Entity */class Category{    /** * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */    protected $id;

    /** * @var string $name * @ORM\Column(name="name", type="string", length=255) */    protected $name;}

Doctrine 2. Entidades y relaciones

Page 155: Materiales del curso de Symfony2

/** * @ORM\Column(name="twitter", type="string", length=15, nullable=true) */protected $twitter;

/** * @ORM\Column(name="bio", type="text") */protected $bio;

/** * @ORM\Column(name="created_at", type="datetime") */protected $createdAt;

/** * @ORM\Column(name="languages", type="array") */private $languages;

Doctrine 2. Entidades y relaciones

Page 156: Materiales del curso de Symfony2

// Relación 1:N unidireccional

// User.php

/** * @var City $city * * @ORM\ManyToOne(targetEntity="City") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="city_id", referencedColumnName="id") * }) */private $city;

Doctrine 2. Entidades y relaciones

Page 157: Materiales del curso de Symfony2

// Relación 1:N unidireccional

// User.php

/** * @var City $city * * @ORM\ManyToOne(targetEntity="City") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="city_id", referencedColumnName="id") * }) */private $city;

Doctrine 2. Entidades y relaciones

Page 158: Materiales del curso de Symfony2

// Relación 1:N bidireccional

// Post.php

/** * @var User $user * * @ORM\ManyToOne(targetEntity="User", inversedBy="posts") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="user_id", referencedColumnName="id") * }) */protected $user;

// User.php

/** * @var $posts PersistentCollection * * @ORM\OneToMany(targetEntity="Post", mappedBy="user", cascade={"all"}) */protected $posts;

Doctrine 2. Entidades y relaciones

Page 159: Materiales del curso de Symfony2

// Relación N:M bidireccional

// Post.php/** * @var $categories PersistentCollection * * @ORM\ManyToMany(targetEntity="Category", inversedBy="posts") * @ORM\JoinTable(name="post_category") */protected $categories;

// Category.php/** * @var $secrets PersistentCollection * * @ORM\ManyToMany(targetEntity="Post", mappedBy="categories") */protected $posts;

Doctrine 2. Entidades y relaciones

Page 160: Materiales del curso de Symfony2

Doctrine 2. Entidades y relaciones

$ php app/console doctrine:generate:entity

Page 161: Materiales del curso de Symfony2

• Ejercicio. Crear todas las entidades necesarias para la siguiente base de datos:

Doctrine 2. Entidades y relaciones

Page 162: Materiales del curso de Symfony2

Configuración

Page 163: Materiales del curso de Symfony2

# parameters.yml

parameters:    database_driver: pdo_mysql    database_host: localhost    database_port: ~    database_name: db_name    database_user: db_user    database_password: db_pass

Doctrine 2. Configuración

Page 164: Materiales del curso de Symfony2

Doctrine 2. Configuración

$ php app/console doctrine:schema:update

--dump-sql--force

Page 165: Materiales del curso de Symfony2

• Ejercicio. Crear la base de datos del proyecto y el usuario que tendrá acceso. Configurar doctrine y generar el schema definido en las entities.

Recordad que la base de datos debe estar en UTF-8:

CREATE DATABASE books DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

Doctrine 2. Fixtures

Page 166: Materiales del curso de Symfony2

Fixtures

Page 167: Materiales del curso de Symfony2

{    "require": {        "doctrine/doctrine-fixtures-bundle": "dev-master"    }}

Doctrine 2. Fixtures

// app/AppKernel.phppublic function registerBundles(){    $bundles = array(        // ...        new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle()    );}

Page 168: Materiales del curso de Symfony2

Doctrine 2. Fixtures

// Demo/DemoBundle/DataFixtures/ORM/LoadUserData.php

namespace Demo\DemoBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;use Doctrine\Common\Persistence\ObjectManager;use Demo\DemoBundle\Entity\User;

class LoadUserData implements FixtureInterface{    public function load(ObjectManager $manager)    {        $user = new User();        $user->setUsername('raulfraile');

        $manager->persist($user);        $manager->flush();    }}

Page 169: Materiales del curso de Symfony2

Doctrine 2. Fixtures// Demo/DemoBundle/DataFixtures/ORM/LoadUserData.php

namespace Demo\DemoBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;use Doctrine\Common\DataFixtures\OrderedFixtureInterface;use Doctrine\Common\Persistence\ObjectManager;use Demo\DemoBundle\Entity\User;

class LoadUserData extends AbstractFixture implements OrderedFixtureInterface{    public function getOrder()    {        return 1;    }

    public function load(ObjectManager $manager)    {        // ...        $this->addReference('user_' . $user->getUsername(), $user);    }}

Page 170: Materiales del curso de Symfony2

Doctrine 2. Fixtures// Demo/DemoBundle/DataFixtures/ORM/LoadBookData.php

namespace Demo\DemoBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;use Doctrine\Common\DataFixtures\OrderedFixtureInterface;use Doctrine\Common\Persistence\ObjectManager;use Demo\DemoBundle\Entity\Book;

class LoadBookData extends AbstractFixture implements OrderedFixtureInterface{    public function getOrder()    {        return 2;    }

    public function load(ObjectManager $manager)    {        // ...        $book->setUser($this->getReference('user_'.$data[‘slug’]));    }}

Page 171: Materiales del curso de Symfony2

Doctrine 2. Fixtures

$ php app/console doctrine:fixtures:load

--purge-with-truncate

Page 172: Materiales del curso de Symfony2

Buenas prácticas

Page 173: Materiales del curso de Symfony2

users.yml

books.yml

authors.yml

technologies.yml

Doctrine 2. Fixtures

LoadUserData.php

LoadBookData.php

LoadAuthorData.php

LoadTechnologyData.php

CONST FIXTURE_REF = ‘user’

CONST FIXTURE_REF = ‘books’

CONST FIXTURE_REF = ‘author’

CONST FIXTURE_REF = ‘technology’

Page 174: Materiales del curso de Symfony2

• Ejercicio. Crear datos de prueba (incluidas imágenes) en archivos yml, además de los correspondientes scripts para cargar los fixtures.

Doctrine 2. Fixtures

Page 175: Materiales del curso de Symfony2

nelmio/alice

Page 176: Materiales del curso de Symfony2

• Ejercicio. Rehacer los fixtures para utilizar nelmio/alice y además generar usuarios de prueba utilizando fzaninotto/faker.

Doctrine 2. Fixtures

Page 177: Materiales del curso de Symfony2

Repositorios

Page 178: Materiales del curso de Symfony2

Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/User.php

/** * @ORM\Entity(repositoryClass="Demo\DemoBundle\Entity\UserRepository") */class User{    //...}

Page 179: Materiales del curso de Symfony2

Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/UserRepository.php

use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository{    public function findAllActive()    {        return $this->getEntityManager()            ->createQuery('SELECT u FROM DemoBundle:User u WHERE u.active = TRUE')            ->getResult();    }}

Page 180: Materiales del curso de Symfony2

Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/UserRepository.php

use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository{    public function findAllFromCountry($country)    {        return $this->getEntityManager()            ->createQuery('SELECT u FROM DemoBundle:User u WHERE u.country = :country') ->setParameter('country', $country);            ->getResult();    }}

Page 181: Materiales del curso de Symfony2

Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/UserRepository.php use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository{ public function findAllFromSpain($limit = null, $offset = null) { $query = $this->getEntityManager() ->createQuery('SELECT u FROM DemoBundle:User u WHERE u.country = :country') ->setParameter('country', 'es'); if (!is_null($limit)) { $query->setMaxResults($limit); } if (!is_null($limit)) { $query->setFirstResult($offset); } return $query->getResult(); }}

Page 182: Materiales del curso de Symfony2

Doctrine 2. Repositorios// Demo/DemoBundle/Entity/UserRepository.php

use Doctrine\ORM\EntityRepository;

class UserRepository extends EntityRepository{

    public function countUsersFromSpain() { $dql = 'SELECT COUNT(u.id) FROM DemoBundle:User u WHERE u.country = :country';  $query = $this->getEntityManager()->createQuery($dql) ->setParameter('country', 'es') ;  try { return $query->getSingleScalarResult(); } catch (\Doctrine\ORM\NoResultException $e) { return 0; }

}}

Page 183: Materiales del curso de Symfony2

Doctrine 2. Repositorios// Demo/DemoBundle/Entity/UserRepository.php use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository{ public function findAllFromSpain() { $qb = $this->getEntityManager()->createQueryBuilder() ->select('u') ->from('DemoBundle:User', 'u') ->where('u.country = :country') ->orderBy('u.id', 'desc') ;  $query = $qb->getQuery(); $query->setParameter('country', 'es'); try { return $query->getResult(); } catch (\Doctrine\ORM\NoResultException $e) { return false; } }}

Page 184: Materiales del curso de Symfony2

Doctrine 2. Repositorios

// Demo/DemoBundle/Controller/UserController.php

class UserController extends Controller{    public function listActiveAction()    {        $em = $this->getEntityManager();

        $userRepository = $em->getRepository('DemoBundle:User');

        $all = $userRepository->findAll();        $userId1 = $userRepository->find(1);        $userRaul = $userRepository->findOneByUsername('raul');        $usersValencia = $userRepository->findOneBy(array(            'active' => true,            'city' => 'Valencia'        ));        $usersActive = $userRepository->findAllActive(); // custom query    }}

Page 185: Materiales del curso de Symfony2

• Ejercicio. Una vez tenemos datos de prueba y sabemos como acceder a los datos, crear las siguientes páginas:

- Listado de autores- Perfil público de un usuario

Doctrine 2. Repositorios

Page 186: Materiales del curso de Symfony2

Frontend

Page 187: Materiales del curso de Symfony2

Gestión de assets

Page 188: Materiales del curso de Symfony2

{{ asset('/bundles/books/images/logo.png') }}

Frontend. Gestión de assets

Page 189: Materiales del curso de Symfony2

Frontend. Gestión de assets

framework:    ...    templating:        engines: ['twig']        assets_version: v0.2

Page 190: Materiales del curso de Symfony2

Frontend. Gestión de assets

http://ejemplo.com/bundles/books/images/logo.png?v0.2

Page 191: Materiales del curso de Symfony2

• Ejercicio. Añadir en el layout (utilizando la función ‘asset’ de Twig):

- Logo de la web- Dos archivos CSS con algunos estilos básicos

Una vez añadidos, utilizar la opción para versionar assets y comprobar como la URL cambia en cada cambio de versión.

Frontend. Gestión de assets

php app/console assets:install web/

Page 192: Materiales del curso de Symfony2

Assetic

Page 193: Materiales del curso de Symfony2

Frontend. Assetic

“Assetic is an asset management framework for PHP, based on the Python webassets library.”

Page 194: Materiales del curso de Symfony2

Frontend. Assetic

reset.css

bootstrap.css

global.css

Assets Filtros

styles.css

Page 195: Materiales del curso de Symfony2

Frontend. Assetic

CoffeeScriptFilterCssEmbedFilterCssImportFilterCssMinFilterCssRewriteFilterGoogleClosure\CompilerApiFilterGoogleClosure\CompilerJarFilterHandlebarsFilterJpegoptimFilterJpegtranFilterLessFilter

LessphpFilterOptiPngFilterPackerFilterPngoutFilterCompassFilterSass\SassFilterSass\ScssFilterSprocketsFilterStylusFilterYui\CssCompressorFilterYui\JsCompressorFilter

Page 196: Materiales del curso de Symfony2

Frontend. Assetic

{% javascripts '@BooksBundle/Resources/public/js/*'%}    <script type="text/javascript" src="{{ asset_url }}"> </script>{% endjavascripts %}

Page 197: Materiales del curso de Symfony2

Frontend. Assetic

{% javascripts '@BooksBundle/Resources/public/js/1.js' '@BooksBundle/Resources/public/js/2.js' '@BooksBundle/Resources/public/js/3.js'%}    <script type="text/javascript" src="{{ asset_url }}"> </script>{% endjavascripts %}

Page 198: Materiales del curso de Symfony2

Frontend. Assetic

{% javascripts '@BooksBundle/Resources/public/js/1.js' '@BooksBundle/Resources/public/js/2.js' '@BooksBundle/Resources/public/js/3.js' filter='yui_js' output='js/script.js'%}    <script type="text/javascript" src="{{ asset_url }}"> </script>{% endjavascripts %}

Page 199: Materiales del curso de Symfony2

Frontend. Assetic

{% javascripts '@BooksBundle/Resources/public/js/1.js' '@BooksBundle/Resources/public/js/2.js' '@BooksBundle/Resources/public/js/3.js' filter='?yui_js' output='js/script.js'%}    <script type="text/javascript" src="{{ asset_url }}"> </script>{% endjavascripts %}

Page 200: Materiales del curso de Symfony2

Frontend. Assetic

$ php app/console assetic:dump --env=prod

--watch

Page 201: Materiales del curso de Symfony2

• Ejercicio. Utilizando Assetic, combinar por separado los 2 css y los 2 javascripts, y comprimiéndolos con YUI Compressor.

Nota: ver entrada del cookbock para detalles de configuración:

http://symfony.com/doc/current/cookbook/assetic/yuicompressor.html

Frontend. Assetic

Page 202: Materiales del curso de Symfony2

Tratamiento de imágenes

Page 203: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

AvalancheImagineBundle

Imagine

GD2

Imagick

Gmagick

Page 204: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

// GD resize

$width = 80$height = 80$src = imagecreatefrompng('image.png');$dest = imagecreatetruecolor($width, $height);imagealphablending($dest, false);imagesavealpha($dest, true);imagecopyresampled($dest, $src, 0, 0, 0, 0, $width, $height, imagesx($src), imagesy($src));imagepng($dest,'image_resized.png');

Page 205: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

// Imagick resize

$width = 80;$height = 80;$image = new Imagick('image.png');$image->adaptiveResizeImage($width, $height);$image->writeImage('image_resized.png');

Page 206: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

// Imagine resize

$width = 80;$height = 80;

$imagine = new Imagine\Gd\Imagine();$imagine = new Imagine\Imagick\Imagine();

$imagine->open('image.png')    ->resize(new Imagine\Box($width, $height))    ->save('image_resized.png');

Page 208: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

¿Cómo usarlo en Symfony para hacer thumbnails de imágenes subidas por los usuarios?

Page 209: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

# app/config.yml

avalanche_imagine:   filters:       user_small:           type: thumbnail           options: { size: [30, 30], mode: outbound }       user_medium:           type: thumbnail           options: { size: [60, 60], mode: outbound }       user_big:           type: thumbnail           options: { size: [90, 90], mode: outbound }

Page 210: Materiales del curso de Symfony2

Frontend. Tratamiento de imágenes

<img src="{{ user.webPath|apply_filter('user_small') }}" />

Page 211: Materiales del curso de Symfony2

Demo: AvalancheImagineBundle + Upload Doctrine

Page 212: Materiales del curso de Symfony2

Formatos alternativos

Page 213: Materiales del curso de Symfony2

Frontend. Formatos alternativos

# routing.yml

api_books_latest:    pattern: /api/latest.{_format}    defaults: { _controller: BooksBundle:Api:latest }

Page 214: Materiales del curso de Symfony2

Frontend. Formatos alternativos

# routing.yml

api_books_latest:    pattern: /api/latest.{_format}    defaults: { _controller: BooksBundle:Api:latest, _format: html }

Page 215: Materiales del curso de Symfony2

Frontend. Formatos alternativos

public function latestAction(){    $format = $this->getRequest()->getRequestFormat();}

Page 216: Materiales del curso de Symfony2

• Ejercicio. Generar la API /api/books/latest para que permitan obtener el contenido en JSON y XML, dependiendo del formato pasado. Por ejemplo:

/api/books/latest.xml: contenido en XML

Por defecto, el formato será JSON.

Nota: Algunos formatos son más propensos a ser generados a través de Twig que otros, tenedlo en cuenta.

Frontend. Formatos alternativos

Page 217: Materiales del curso de Symfony2

Backend

Page 218: Materiales del curso de Symfony2

Formularios

Page 219: Materiales del curso de Symfony2

Backend. Formulariosnamespace Books\BooksBundle\Form;

use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class UserType extends AbstractType{    public function buildForm(FormBuilderInterface $builder, array $options)    {        $builder->add('username', 'text');    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)    {        $resolver->setDefaults(array(            'data_class' => 'Books\BooksBundle\Entity\User'        ));    }

    public function getName()    {        return 'form_user';    }}

Page 220: Materiales del curso de Symfony2

Backend. Formularios

public function createAction(Request $request){    $user = new User();    $form = $this->createForm(new UserType(), $user);    $form->bind($request);

    if ($form->isValid()) {        $em = $this->getEntityManager();        $em->persist($entity);        $em->flush();

        return $this->redirect($this->generateUrl('user_created'));    }

    return $this->render('BooksBundle:Users:new.html.twig', array(        'entity' => $user,        'form' => $form->createView(),    ));}

Page 221: Materiales del curso de Symfony2

Backend. Formularios

<form action="{{ path('user_create') }}" method="post" {{ form_enctype(form) }}>

    {{ form_errors(form) }}

    {{ form_row(form) }}

    {{ form_rest(form) }}

    <input type="submit" /></form>

Page 222: Materiales del curso de Symfony2

Backend. Formularios

<form action="{{ path('user_create') }}" method="post" {{ form_enctype(form) }}>

    {{ form_errors(form) }}

    {{ form_row(form.username) }}

    {{ form_rest(form) }}

    <input type="submit" /></form>

Page 223: Materiales del curso de Symfony2

Backend. Formularios

namespace Books\BooksBundle\Form;

use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class UserType extends AbstractType{    public function buildForm(FormBuilderInterface $builder, array $options)    {        $builder->add('username', 'text', array(            'required' => true,            'label' => 'Nombre de usuario'            'attr' => array(                'class' => 'text_big'            )        ));

        $builder->add('email', 'email');    }

    ...}

Page 224: Materiales del curso de Symfony2

Backend. Formularios

text textarea email

integer money number

password percent search

url choice entity

country language locale

timezone date datetime

time birthday checkbox

file radio collection

repeated hidden csrf

field form

Page 225: Materiales del curso de Symfony2

Backend. Formularios

http://symfony.com/doc/current/reference/forms/types.html

Page 226: Materiales del curso de Symfony2

• Ejercicio. Generar tres nuevas rutas, que servirán para dar de alta un libro desde el backend:

/admin/books/new/admin/books/create/admin/books/created

Crear el formulario para poder introducir nuevos libros en la base de datos. Los campos deberán ser: título (text), descripción (textarea), isbn (text), price (money) y techonology (entity).

Backend. Formularios

Page 227: Materiales del curso de Symfony2

• Ejercicio. Realizar el mismo proceso para editar un libro desde el backend, utilizando el mismo formulario.

/admin/books/edit/{id}/admin/books/update/{id}/admin/books/updated

Backend. Formularios

Page 228: Materiales del curso de Symfony2

• Ejercicio. Crear el listado de libros con enlaces para editarlos, y un link para añadir un nuevo libro en la parte superior:

/admin/books/list

Backend. Formularios

Page 229: Materiales del curso de Symfony2

Backend. Formularios

Generación de CRUDs automáticos

Create Read Update Delete

Page 230: Materiales del curso de Symfony2

Backend. Formularios

Page 231: Materiales del curso de Symfony2

Backend. Formularios

$ php app/console generate:doctrine:crud

Page 232: Materiales del curso de Symfony2

• Ejercicio. Utilizando el generador de CRUD, crear las acciones necesarias para gestionar los libros, a partir de la ruta /admin/v2/books.

Una vez que lo tengamos en funcionamiento, estudiar las acciones que ha creado y añadir lo que considereis necesario en las plantillas, namespaces, formulario... Finalmente, sustituir el CRUD que habíamos hecho a mano por éste, para que se muestre en /admin/books.

Backend. Formularios

Page 233: Materiales del curso de Symfony2

Validación

Page 234: Materiales del curso de Symfony2

Backend. Validación

/** * @var string $title * * @Assert\NotBlank * @Assert\MaxLength(250) * @ORM\Column(name="title", type="string", length=250) */protected $title;

Page 235: Materiales del curso de Symfony2

Backend. Validación

public function createAction(Request $request){    $user = new User();    $form = $this->createForm(new UserType(), $user);    $form->bind($request);

    if ($form->isValid()) {        $em = $this->getEntityManager();        $em->persist($entity);        $em->flush();

        return $this->redirect($this->generateUrl('user_created'));    }

    return $this->render('BooksBundle:Users:new.html.twig', array(        'entity' => $user,        'form' => $form->createView(),    ));}

Page 236: Materiales del curso de Symfony2

Backend. Validación

<form action="{{ path('user_create') }}" method="post" {{ form_enctype(form) }}>

    {{ form_errors(form) }}

    {{ form_row(form.username) }}

    {{ form_rest(form) }}

    <input type="submit" /></form>

Page 237: Materiales del curso de Symfony2

Backend. Validación

NotBlank Blank NotNullNull TRUE FALSEType Email MinLength

MaxLength Length UrlRegex Ip MaxMin Range Date

DateTime Time ChoiceCollection Count UniqueEntityLanguage Locale Country

File Image CallbackAll UserPassword Valid

Constraints

Page 238: Materiales del curso de Symfony2

Backend. Validación

http://symfony.com/doc/current/reference/constraints.html

Page 239: Materiales del curso de Symfony2

• Ejercicio. Añadir las siguientes reglas de validación al modelo de datos:

Author: - country: código de país válido - name: valor requerido

Book: - isbn: isbn válido de 10 dígitos - price: no puede ser negativo

User: - email: email válido - locale: locale válido - username: letras, números o caracter “_”, de una longitud mínima de 3 y máxima de 15

Backend. Validación

Page 240: Materiales del curso de Symfony2

Seguridad

Page 241: Materiales del curso de Symfony2

Autenticación VS Autorización

Page 242: Materiales del curso de Symfony2

Seguridad. Autenticación VS Autorización

Firewall Control de acceso

Page 243: Materiales del curso de Symfony2

Seguridad. Autenticación VS Autorización

Page 244: Materiales del curso de Symfony2

Seguridad. Autenticación VS Autorización

Page 245: Materiales del curso de Symfony2

Seguridad. Autenticación VS Autorización

Page 246: Materiales del curso de Symfony2

Seguridad. Autenticación VS Autorización

Page 247: Materiales del curso de Symfony2

Roles

Page 248: Materiales del curso de Symfony2

Seguridad. Roles

Usuario

ROLE_USER

ROLE_ADMIN

ROLE_TRANSLATOR

Page 249: Materiales del curso de Symfony2

Seguridad. Roles

Usuario

ROLE_USER

ROLE_ADMIN

ROLE_TRANSLATOR

Page 250: Materiales del curso de Symfony2

Configuración

Page 251: Materiales del curso de Symfony2

Seguridad. Configuraciónsecurity:    encoders:        Symfony\Component\Security\Core\User\User: plaintext

    role_hierarchy:        ROLE_ADMIN: ROLE_USER        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:        in_memory:            memory:                users:                    user: { password: userpass, roles: [ 'ROLE_USER' ] }                    admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }

    firewalls:        dev:            pattern: ^/(_(profiler|wdt)|css|images|js)/            security: false

        login:            pattern: ^/demo/secured/login$            security: false

        secured_area:            pattern: ^/demo/secured/            form_login:                check_path: /demo/secured/login_check                login_path: /demo/secured/login            logout:                path: /demo/secured/logout                target: /demo/            #anonymous: ~

    access_control:        #- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }        #- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }

Page 252: Materiales del curso de Symfony2

Seguridad. Configuración

security:    encoders:        Empresa\BooksBundle\Entity\User: sha512     role_hierarchy:        ROLE_ADMIN: ROLE_USER        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]     providers:        main: entity: { class: Empresa\BooksBundle\Entity\User, property: username }      firewalls:        dev:            pattern: ^/(_(profiler|wdt)|css|images|js)/            security: false         main:            pattern: ^/ form_login: check_path: /login_check                login_path: /login logout: path: /logout target: / anonymous: true     access_control:        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }        - { path: ^/admin/, role: ROLE_ADMIN }

Symfony\Component\Security\Core\User\UserInterface

getRoles()getPassword()

getSalt()getUsername()

eraseCredentials()

Page 253: Materiales del curso de Symfony2

Login

Page 254: Materiales del curso de Symfony2

Seguridad. Login

# app/config/routing.ymllogin: pattern: /login defaults: { _controller: EmpresaBooksBundle:Security:login }

login_check: pattern: /login_checklogout: pattern: /logout

Page 255: Materiales del curso de Symfony2

Seguridad. Login

// src/Empresa/BooksBundle/Controller/SecurityController.php;namespace Empresa\BooksBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\Security\Core\SecurityContext; class SecurityController extends Controller{ public function loginAction() { $request = $this->getRequest(); $session = $request->getSession();  // get the login error if there is one if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { $error = $request->attributes->get( SecurityContext::AUTHENTICATION_ERROR ); } else { $error = $session->get(SecurityContext::AUTHENTICATION_ERROR); $session->remove(SecurityContext::AUTHENTICATION_ERROR); }  return $this->render( 'EmpresaBooksBundle:Security:login.html.twig', array( // last username entered by the user 'last_username' => $session->get(SecurityContext::LAST_USERNAME), 'error' => $error, ) ); }}

Page 256: Materiales del curso de Symfony2

Seguridad. Login

{# src/Empresa/BooksBundle/Resources/views/Security/login.html.twig #}{% if error %} <div>{{ error.message }}</div>{% endif %} <form action="{{ path('login_check') }}" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="_username" value="{{ last_username }}" />  <label for="password">Password:</label> <input type="password" id="password" name="_password" />  <button type="submit">login</button></form>

Page 257: Materiales del curso de Symfony2

Servicios y eventos

Page 258: Materiales del curso de Symfony2

Servicios

Page 259: Materiales del curso de Symfony2

Demo: Servicios

Page 260: Materiales del curso de Symfony2

Eventos

Page 261: Materiales del curso de Symfony2

Demo: Eventos

Page 262: Materiales del curso de Symfony2

Extendiendo SF2

Page 263: Materiales del curso de Symfony2

Comandos personalizados

Page 264: Materiales del curso de Symfony2

Extendiendo Symfony2. Comandos personalizados

// src/Empresa/BooksBundle/Command/BooksListCommand.php

class BooksListCommand extends ContainerAwareCommand{    protected function configure()    {        $this            ->setName('books:list')            ->setDescription('Books list')        ;    }

    protected function execute(InputInterface $input, OutputInterface $output)    {        $em = $this->getContainer()->get('doctrine.orm.entity_manager');

        $books = $em->getRepository('BooksBundle:Book')->findAll();

        foreach ($books as $book) {            $output->writeln($book->getTitle());        }            }}

Page 265: Materiales del curso de Symfony2

• Ejercicio. Crear el comando books:author, que muestre por consola los libros de un determinado autor, introducido por parámetro:

php app/console books:author author_id

Backend. Validación

Page 266: Materiales del curso de Symfony2

Extensiones de Twig

Page 267: Materiales del curso de Symfony2

Extendiendo Symfony2. Extensiones de Twig

// src/Empresa/BooksBundle/Twig/HtmlExtension.php

class HtmlExtension extends \Twig_Extension{

    public function getFunctions()    {        return array(            'strong' => new \Twig_Function_Method($this, 'getStrong', array('is_safe' => array('html')))        );    }

    public function getStrong($text)    {        return '<strong>' . $text . '</strong>';    }

    public function getName()    {        return 'html';    }}

Page 268: Materiales del curso de Symfony2

Extendiendo Symfony2. Extensiones de Twig

# services.yml

books.twig.html_extension:    class: Empresa\BooksBundle\Twig\HtmlExtension    tags:        - { name: twig.extension }

Page 269: Materiales del curso de Symfony2

• Ejercicio. Crear una función de Twig que reciba un objeto de tipo libro y devuelva una cadena de texto con el título del libro y el nombre del autor entre paréntesis.

Backend. Validación

Page 270: Materiales del curso de Symfony2

Rendimiento

Page 271: Materiales del curso de Symfony2
Page 272: Materiales del curso de Symfony2
Page 273: Materiales del curso de Symfony2

Optimización y rendimiento

Ejecutar controlador frontal (app[_dev].php)

Procesar archivos de configuración

Cargar bundles

Cargar rutas y decidir la ruta solicitada

Ejecutar controlador interno

Parsear plantilla Twig

Generar respuesta (contenido + headers)

Page 274: Materiales del curso de Symfony2

Optimización y rendimiento

Cache

Page 275: Materiales del curso de Symfony2

Optimización y rendimiento

Page 276: Materiales del curso de Symfony2

APC

Page 277: Materiales del curso de Symfony2

Optimización y rendimiento. APC

Byte Code Cache

Page 278: Materiales del curso de Symfony2

Optimización y rendimiento. APC

Lee el archivo index.php y lo introduce en memoria

GET /index.php HTTP/1.1

El analizador léxico (lexer) lee el código fuente y genera una serie de “tokens”

El analizador sintáctico (parser) parsea los tokens y genera una serie de “opcodes”, los cuales son ejecutados

directamente por el motor de PHP

Se ejecutan los “opcodes”

Page 279: Materiales del curso de Symfony2

Optimización y rendimiento. APC

La primera vez se realizan los mismos pasos (lexer + parser) para generar los “opcodes”. Una vez generados

se guardan en memoria.

GET /index.php HTTP/1.1 (con APC)

Las siguientes veces utiliza los “opcodes” guardados en memoria y los ejecuta directamente

Page 280: Materiales del curso de Symfony2

Optimización y rendimiento. APC

System/User cache

Page 281: Materiales del curso de Symfony2

Optimización y rendimiento. APC

// app.php

$loader = require_once __DIR__.'/../app/bootstrap.php.cache';

$loader = new ApcClassLoader('books', $loader);$loader->register(true);

Page 282: Materiales del curso de Symfony2

Optimización y rendimiento. APC

# app/config/config_prod.yml

doctrine:    orm:        metadata_cache_driver: apc        result_cache_driver: apc        query_cache_driver: apc

Page 283: Materiales del curso de Symfony2

Composer

Page 284: Materiales del curso de Symfony2

Optimización y rendimiento. Composer

$ composer dump-autoload --optimize

Page 285: Materiales del curso de Symfony2

Extra

Page 286: Materiales del curso de Symfony2

Seguridad

Page 287: Materiales del curso de Symfony2

• Ejercicio. Configurar la seguridad de Symfony2 para utilizar la entity User como proveedor de usuarios, codificando los passwords con sha512 y definiendo roles. Crear la página de login, y cuando el usuario esté logueado mostrar su nombre y un enlace para hacer logout.

Extra. Seguridad

Page 288: Materiales del curso de Symfony2

• Ejercicio. Crear la página /myprofile, que mostrará los datos del usuario actual, y solo se podrá acceder si está logueado y tiene el rol ROLE_USER.

Extra. Seguridad

Page 289: Materiales del curso de Symfony2

• Ejercicio. Crear la página /myadmin, a la que solo se podrá acceder si el usuario logueado tiene el rol ROLE_ADMIN.

Extra. Seguridad

Page 290: Materiales del curso de Symfony2

Doctrine 2. Repositorios

Page 291: Materiales del curso de Symfony2

• Ejercicio. Crear la página /search, que recibirá dos parámetros por GET: type y query:

/search?type=book&query=programming

Dependiendo de ‘type’ hará una búsqueda fulltext de libros (por título, descripción e isbn), autores (por nombre), tecnologías (por nombre) o usuarios (por username).

Extra. Doctrine2 Repositorios

Page 292: Materiales del curso de Symfony2

• Ejercicio. Crear la página /sales, que mostrará los 5 libros más baratos.

Extra. Doctrine2 Repositorios

Page 293: Materiales del curso de Symfony2

• Ejercicio. Añadir un campo llamado ‘active’ a las entidades ‘Book’ y ‘Author’, de tipo boolean. Cambiar los fixtures para tener libros/autores activos e inactivos. Crear la página /stats que muestre:

- Número de libros activos- Número de libros inactivos- Número de autores activos- Número de autores inactivos- Media de autores por libro

Extra. Doctrine2 Repositorios

Page 294: Materiales del curso de Symfony2

Formularios

Page 295: Materiales del curso de Symfony2

• Ejercicio. Añadir tres campos a la entity User:

- birthday- country- bio

Crear la página /myprofile/edit para editar los datos del perfil del usuario actual y /myprofile/password para cambiar el password (solicitando el password actual y pidiéndolo por duplicado).

Crear el método hasLegalAgeForDrink() en la entity User, que devolverá TRUE si el usuario es mayor de edad, dependiendo del país: (es: 18, us: 21, ir: ilegal)

http://en.wikipedia.org/wiki/Legal_drinking_age

Extra. Formularios