materiales del curso de symfony2

Post on 05-Dec-2014

2.595 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

Raúl Fraile Beneyto

Materiales del curso

Temario

• Tema 1: Prerequisitos

• PHP 5.3

• YAML

• MVC

• PSR-0 (Autoloading)

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

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

Temario

• Tema 3: Primeras páginas

• Bundles

• Routing

• Controladores

Temario

• Tema 4: Lenguaje de plantillas Twig

• Sintaxis básica

• Herencia

• Macros

• Twig extensions

Temario

• Tema 5: Bases de datos con Doctrine2

• ORM

• DBAL

• Entidades y relaciones

• Configuración

• Fixtures

Temario

• Tema 6: Frontend

• Gestión de assets

• Tratamiento de imágenes

• Formatos alternativos

Temario

• Tema 7: Backend

• Formularios

• Conceptos básicos

• Generación automática de formularios

• Formularios avanzados

• Validación

Temario

• Tema 8: Seguridad

• Autenticación VS Autorización

• Roles

• Configuración

• Login

Temario

• Tema 9: Servicios y eventos

• Contenedor de inyección de dependencias

• Eventos

Temario

• Tema 10: Extendiendo symfony2

• Comandos de consola

• Extensiones propias de Twig

Temario

• Tema 11: Optimización y rendimiento

• Optimización de assets

• APC

• ESI

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.

Prerequisitos

PHP 5.3

Namespaces

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

$foo = new Foo();

Prerequisitos. PHP 5.3 / Namespaces

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

class Foo {}

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

class Foo {}

Prerequisitos. PHP 5.3 / Namespaces

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

use LibA\Foo;

$foo = new Foo();

Prerequisitos. PHP 5.3 / Namespaces

// ./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

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

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

Prerequisitos. PHP 5.3 / Namespaces

Closures

<?php

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

Prerequisitos. PHP 5.3 / Closures

Annotations

<?php

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

Prerequisitos. PHP 5.3 / Annotations

<?php

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

Prerequisitos. PHP 5.3 / Annotations

OOP

public $foo;

private $foo;

protected $foo;

Prerequisitos. PHP 5.3 / OOP

<?php

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

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

Prerequisitos. PHP 5.3 / OOP

<?php

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

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

Prerequisitos. PHP 5.3 / OOP

<?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

¿Diferencia entre una interface y una clase abstracta?

Prerequisitos. PHP 5.3 / OOP

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;        );    }}

Prerequisitos. PHP 5.3 / OOP

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

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

¿Puede haber herencia entre interfaces?

Prerequisitos. PHP 5.3 / OOP

Prerequisitos. PHP 5.3 / OOP

<?php

namespace DemoBundle\Entity;

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

YAML

“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

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

parameters:    emails: [ ‘e1@gmail.com’, ‘e2@gmail.com’ ]    webs:         - ‘web1.com’        - ‘web2.com’    technologies: { PHP: 5.3, MySQL: 5.1 }

Prerequisitos. YAML

MVC

Prerequisitos. MVC

PSR-0 (Autoloading)

<?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)

<?php

require_once(‘autoload.php’);

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

Prerequisitos. PSR-0 (Autoloading)

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)

¿Impacto en el rendimiento de la aplicación?

Prerequisitos. PSR-0 (Autoloading)

PSR-1, PSR-2 (Coding Standards)

<?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)

Introducción a SF2

Historia

Fabien Potencier

Introducción a Symfony2. Historia

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

Arquitectura

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

Introducción a Symfony2. Arquitectura

Componentes + Bundles + Librerías externas

Full-stack framework

Distribuciones

Gestión de dependencias con Composer

Introducción a Symfony2. Composer

“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

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

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

Introducción a Symfony2. Composer

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

# composer.json

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

Introducción a Symfony2. Composer

# composer.json

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

Introducción a Symfony2. Composer

# 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

Demo: Composer + Console component

• 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

Inyección de dependencias

"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

class UserController{    private $em;

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

$userController = new UserController();

Introducción a Symfony2. Inyección de dependencias

class UserController{    private $em;

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

$userController = new UserController();

Introducción a Symfony2. Inyección de dependencias

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

DIC: Contenedor de Inyección de Dependencias

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

Entornos de ejecución

Mismo código

Diferentes configuraciones

Introducción a Symfony2. Entornos de ejecución

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

Instalación y configuración

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

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

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

Documentación

Introducción a Symfony2. Documentación

Primeras páginas

Bundles

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

Primeras páginas. Bundles

Primeras páginas. Bundles

$ php app/console generate:bundle

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;    }}

Primeras páginas. Bundles

Routing

Primeras páginas. Routing

/user/profile/raulfraile

Controlador: UserControllerAcción: showProfile($username)

Primeras páginas. Routing

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

Primeras páginas. Routing

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

Primeras páginas. Routing

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

Primeras páginas. Routing

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

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

Primeras páginas. Routing

$ php app/console router:debug [route_name]

Primeras páginas. Routing

$ php app/console router:match path

Controladores

Request Response

Primeras páginas. Controladores

use Symfony\Component\HttpFoundation\Response;

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

Primeras páginas. Controladores

use Symfony\Component\HttpFoundation\Response;

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

Primeras páginas. Controladores

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

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

Primeras páginas. Controladores

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

Primeras páginas. Controladores

Demo: Controladores + objeto Request

• 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

• 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

• 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

• 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

Twig

Sintaxis básica

{{ }}

{% %}

{# #}

Twig. Sintaxis básica

{{ name }}

Twig. Sintaxis básica - Variables

{{ user.name }}

{{ user[‘name’] }}

{% set foo = 'foo' %}

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

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

Twig. Sintaxis básica - Variables

{{ name|upper }}

{{ name|striptags|nlbr }}

Twig. Sintaxis básica - Filtros

abscapitalize

convert_encodingdate

date_modifydefaultescapeformat

joinjson_encode

keyslengthlowermerge

nl2brnumber_format

rawreplacereverse

slicesortsplit

striptagstitletrim

upperurl_encode

Twig. Sintaxis básica - Filtros

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

{{ random('ABC') }}

{{ random() }}

{{ random(5) }}

Twig. Sintaxis básica - Funciones

{{ path('home') }}

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

Twig. Sintaxis básica - Funciones

{{ url('home') }}

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

Twig. Sintaxis básica - Funciones

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

Twig. Sintaxis básica - Control de flujo

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

Twig. Sintaxis básica - Control de flujo

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

{% endfor %}

Twig. Sintaxis básica - Control de flujo

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

Twig. Sintaxis básica - Control de flujo

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

Twig. Sintaxis básica - Control de flujo

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

Twig. Sintaxis básica - Control de flujo

{% 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

• 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

• 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

Herencia

{% include %}

{% extends %}

{% render %}

Twig. Herencia

{# _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

{# _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

{# 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

{# 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

{# 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

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

Twig. Herencia

• 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

• 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

Macros

{# 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

{# 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

• 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

Extensiones

Oficiales: Twig Extensions Repository

Comunidad

Propias

Twig. Extensiones

Doctrine 2

ORM

ORM: Object Relational Mapper

Doctrine 2. ORM

Doctrine 2. ORM

Doctrine 2. ORM

MySQL

PDO

DBAL

ORM

SQL Server Oracle ...

DBAL

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

Doctrine 2. DBAL

Entidades y relaciones

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

/** * @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

// 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

// 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

// 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

// 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

Doctrine 2. Entidades y relaciones

$ php app/console doctrine:generate:entity

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

Doctrine 2. Entidades y relaciones

Configuración

# 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

Doctrine 2. Configuración

$ php app/console doctrine:schema:update

--dump-sql--force

• 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

Fixtures

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

Doctrine 2. Fixtures

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

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();    }}

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);    }}

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’]));    }}

Doctrine 2. Fixtures

$ php app/console doctrine:fixtures:load

--purge-with-truncate

Buenas prácticas

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’

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

Doctrine 2. Fixtures

nelmio/alice

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

Doctrine 2. Fixtures

Repositorios

Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/User.php

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

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();    }}

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();    }}

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(); }}

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; }

}}

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; } }}

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    }}

• 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

Frontend

Gestión de assets

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

Frontend. Gestión de assets

Frontend. Gestión de assets

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

Frontend. Gestión de assets

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

• 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/

Assetic

Frontend. Assetic

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

Frontend. Assetic

reset.css

bootstrap.css

global.css

Assets Filtros

styles.css

Frontend. Assetic

CoffeeScriptFilterCssEmbedFilterCssImportFilterCssMinFilterCssRewriteFilterGoogleClosure\CompilerApiFilterGoogleClosure\CompilerJarFilterHandlebarsFilterJpegoptimFilterJpegtranFilterLessFilter

LessphpFilterOptiPngFilterPackerFilterPngoutFilterCompassFilterSass\SassFilterSass\ScssFilterSprocketsFilterStylusFilterYui\CssCompressorFilterYui\JsCompressorFilter

Frontend. Assetic

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

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 %}

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 %}

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 %}

Frontend. Assetic

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

--watch

• 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

Tratamiento de imágenes

Frontend. Tratamiento de imágenes

AvalancheImagineBundle

Imagine

GD2

Imagick

Gmagick

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');

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');

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');

Frontend. Tratamiento de imágenes

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

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 }

Frontend. Tratamiento de imágenes

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

Demo: AvalancheImagineBundle + Upload Doctrine

Formatos alternativos

Frontend. Formatos alternativos

# routing.yml

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

Frontend. Formatos alternativos

# routing.yml

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

Frontend. Formatos alternativos

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

• 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

Backend

Formularios

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';    }}

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(),    ));}

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>

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>

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');    }

    ...}

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

Backend. Formularios

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

• 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

• 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

• 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

Backend. Formularios

Generación de CRUDs automáticos

Create Read Update Delete

Backend. Formularios

Backend. Formularios

$ php app/console generate:doctrine:crud

• 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

Validación

Backend. Validación

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

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(),    ));}

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>

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

Backend. Validación

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

• 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

Seguridad

Autenticación VS Autorización

Seguridad. Autenticación VS Autorización

Firewall Control de acceso

Seguridad. Autenticación VS Autorización

Seguridad. Autenticación VS Autorización

Seguridad. Autenticación VS Autorización

Seguridad. Autenticación VS Autorización

Roles

Seguridad. Roles

Usuario

ROLE_USER

ROLE_ADMIN

ROLE_TRANSLATOR

Seguridad. Roles

Usuario

ROLE_USER

ROLE_ADMIN

ROLE_TRANSLATOR

Configuración

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 }

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()

Login

Seguridad. Login

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

login_check: pattern: /login_checklogout: pattern: /logout

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, ) ); }}

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>

Servicios y eventos

Servicios

Demo: Servicios

Eventos

Demo: Eventos

Extendiendo SF2

Comandos personalizados

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());        }            }}

• 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

Extensiones de Twig

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';    }}

Extendiendo Symfony2. Extensiones de Twig

# services.yml

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

• 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

Rendimiento

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)

Optimización y rendimiento

Cache

Optimización y rendimiento

APC

Optimización y rendimiento. APC

Byte Code Cache

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”

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

Optimización y rendimiento. APC

System/User cache

Optimización y rendimiento. APC

// app.php

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

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

Optimización y rendimiento. APC

# app/config/config_prod.yml

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

Composer

Optimización y rendimiento. Composer

$ composer dump-autoload --optimize

Extra

Seguridad

• 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

• 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

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

Extra. Seguridad

Doctrine 2. Repositorios

• 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

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

Extra. Doctrine2 Repositorios

• 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

Formularios

• 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

top related