SlideShare a Scribd company logo
1 of 295
Download to read offline
Materiales del curso

Raúl Fraile Beneyto
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
Prerequisitos. PHP 5.3 / Namespaces

<?php
require_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
<?php
namespace LibA;
class Foo {}
// ./libs/libB/Foo.php
<?php
namespace LibB;
class Foo {}
Prerequisitos. PHP 5.3 / Namespaces

// ./index.php
<?php
namespace MyApp;
use LibAFoo;
$foo = new Foo();
Prerequisitos. PHP 5.3 / Namespaces

// ./index.php
<?php
namespace MyApp;
use LibAFoo as FooA;
use LibBFoo as FooB;
$fooA = new FooA();
$fooB = new FooB();
Prerequisitos. PHP 5.3 / Namespaces

// ./index.php
<?php
namespace MyApp;
$created = new DateTime();
$len = strlen(‘hola’);
echo $len . PHP_EOL;
Closures
Prerequisitos. PHP 5.3 / Closures

<?php
echo preg_replace_callback('/[a-z]/',
    function ($match) {
        return strtoupper($match[0]);
    },
    'Hola'); // HOLA
Annotations
Prerequisitos. PHP 5.3 / Annotations

<?php
/**
* @ORMColumn(type="string")
* @AssertNotBlank()
*/
protected $name;
Prerequisitos. PHP 5.3 / Annotations

<?php
/**
* @Route("/signup")
* @Method({"POST"})
*/
public function signupAction()
{
    ...
}
OOP
Prerequisitos. PHP 5.3 / 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
// JsonInterface.php
interface JsonInterface
{
    const MAX_DEPTH = 100;
    public function exportJson();
}
// User.php
class User implements
{
    public function exportJson()
    {
        return array(
            'username' => $this->username;
        );
    }
}
Prerequisitos. PHP 5.3 / OOP
// BaseController.php
abstract class BaseController
{
    public function getCurrentUser()
    {
        ...
    }
}
// UserController.php
class UserController extends BaseController
{
    ...
}
Prerequisitos. PHP 5.3 / OOP

¿Puede haber herencia entre interfaces?
Prerequisitos. PHP 5.3 / OOP

<?php
namespace DemoBundleEntity;
class User
{
    public function setBirthday(DateTime $date)
    {
        $this->birthday = $date;
    }
{
YAML
Prerequisitos. 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 }
MVC
Prerequisitos. MVC
PSR-0 (Autoloading)
Prerequisitos. 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

SymfonyComponentFilesystem
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?
PSR-1, PSR-2 (Coding Standards)
Prerequisitos. 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)

https://github.com/php-fig/fig-standards/blob/master/
accepted/PSR-1-basic-coding-standard.md
https://github.com/php-fig/fig-standards/blob/master/
accepted/PSR-2-coding-style-guide.md
Introducción a SF2
Historia
Introducción a Symfony2. 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
Arquitectura
Introducción a Symfony2. Arquitectura

BrowserKit

EventDispatcher Routing

ClassLoader

Finder

Security

Config

Form

Serializer

Console

HttpFoundation

Templating

CssSelector

HttpKernel

Translation

DependencyInjection Locale

Validator

DomCrawler

Yaml

Process
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
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",
}
}
Demo: Composer + Console component
Introducción a Symfony2. Composer

•

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.
Inyección de dependencias
Introducción a Symfony2. 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);
DIC: Contenedor de Inyección de Dependencias
Introducción a Symfony2. Inyección de dependencias

use SymfonyComponentDependencyInjection;
use SymfonyComponentDependencyInjectionReference;
 
$sc = new DependencyInjectionContainerBuilder();
$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');
Entornos de ejecución
Introducción a Symfony2. 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
Instalación y configuración
Introducción a Symfony2. 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
Documentación
Introducción a Symfony2. Documentación
Primeras páginas
Bundles
Primeras páginas. Bundles

Todo el contenido de nuestra aplicación
estará dentro de uno o más 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 SymfonyBundleFrameworkBundleFrameworkBundle(),
            new SymfonyBundleSecurityBundleSecurityBundle(),
            new SymfonyBundleTwigBundleTwigBundle(),
            new SymfonyBundleMonologBundleMonologBundle(),
            new SymfonyBundleSwiftmailerBundleSwiftmailerBundle(),
            new SymfonyBundleAsseticBundleAsseticBundle(),
            new DoctrineBundleDoctrineBundleDoctrineBundle(),
...
        );
        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new SymfonyBundleWebProfilerBundleWebProfilerBundle();
        }
        return $bundles;
    }
}
Primeras páginas. Bundles
Routing
Primeras páginas. Routing

/user/profile/raulfraile

Controlador: UserController
Acción: showProfile($username)
Primeras páginas. Routing

# routing.yml
user_profile:
    pattern: /user/profile/{username}
    defaults: { _controller: DemoBundle:User:profile }
Primeras páginas. Routing

# routing.yml
users_list:
    pattern: /user/list/{page}
    defaults: { _controller: DemoBundle:User:list, page: 1 }
Primeras páginas. Routing

# routing.yml
users_list:
    pattern: /user/list/{page}
    defaults: { _controller: DemoBundle:User:list, page: 1 }
requirements:
page: d+
Primeras páginas. Routing

# routing.yml
users_list:
    pattern: /user/list/{page}{subpage}
    defaults: { _controller: DemoBundle:User:list, page: 1 }
requirements:
page: d+
_method: GET
Primeras páginas. Routing

# user.yml
user_profile:
    pattern: /profile/{username}
    defaults: { _controller: DemoBundle:User:profile }
# routing.yml
user_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
Primeras páginas. Controladores

Request

Response
Primeras páginas. Controladores

use SymfonyComponentHttpFoundationResponse;
class UserController
{
    public function helloAction()
    {
        return new Response('Hello world!');
    }
}
Primeras páginas. Controladores

use SymfonyComponentHttpFoundationResponse;
class UserController
{
    public function helloAction($name)
    {
        return new Response('Hello '.$name.'!');
    }
}
Primeras páginas. Controladores

use SymfonyComponentHttpFoundationResponse;
use SymfonyBundleFrameworkBundleController
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');

}
Demo: Controladores + objeto Request
Primeras páginas. Controladores

•

Ejercicio. Crear todas las rutas del proyecto, con sus
respectivos 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
Primeras páginas. Controladores

•

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);
Twig
Sintaxis básica
Twig. Sintaxis básica

{{

}}

{% %}
{# #}
Twig. Sintaxis básica - Variables

{{ name }}
{{ user.name }}
{{ user[‘name’] }}
Twig. Sintaxis básica - Variables

{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
Twig. Sintaxis básica - Filtros

{{ name|upper }}
{{ name|striptags|nlbr }}
Twig. Sintaxis básica - Filtros

abs
capitalize
convert_encoding
date
date_modify
default
escape
format
join
json_encode
keys
length
lower
merge

nl2br
number_format
raw
replace
reverse
slice
sort
split
striptags
title
trim
upper
url_encode
Twig. Sintaxis básica - Funciones

{{ 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 - Control de flujo

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

•

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.
Herencia
Twig. 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) }}
Macros
Twig. 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) }}
Extensiones
Twig. Extensiones

Oficiales: Twig Extensions Repository
Comunidad
Propias
Doctrine 2
ORM
Doctrine 2. ORM

ORM: Object Relational Mapper
Doctrine 2. ORM
Doctrine 2. ORM

ORM
DBAL
PDO
SQL Server

MySQL

Oracle

...
DBAL
Doctrine 2. DBAL

class UserController extends Controller
{
    public function indexAction()
    {
        $conn = $this->get('database_connection');
        $users = $conn->fetchAll('SELECT * FROM
users');
    }
}
Entidades y relaciones
Doctrine 2. Entidades y relaciones
namespace DemoDemoBundleEntity;
use DoctrineORMMapping as ORM;
/**
* @ORMTable(name="category")
* @ORMEntity
*/
class Category
{
    /**
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
    protected $id;
    /**
* @var string $name
* @ORMColumn(name="name", type="string", length=255)
*/
    protected $name;
}
Doctrine 2. Entidades y relaciones
/**
* @ORMColumn(name="twitter", type="string", length=15,
nullable=true)
*/
protected $twitter;
/**
* @ORMColumn(name="bio", type="text")
*/
protected $bio;
/**
* @ORMColumn(name="created_at", type="datetime")
*/
protected $createdAt;
/**
* @ORMColumn(name="languages", type="array")
*/
private $languages;
Doctrine 2. Entidades y relaciones
// Relación 1:N unidireccional
// User.php
/**
* @var City $city
*
* @ORMManyToOne(targetEntity="City")
* @ORMJoinColumns({
*
@ORMJoinColumn(name="city_id",
referencedColumnName="id")
* })
*/
private $city;
Doctrine 2. Entidades y relaciones
// Relación 1:N unidireccional
// User.php
/**
* @var City $city
*
* @ORMManyToOne(targetEntity="City")
* @ORMJoinColumns({
*
@ORMJoinColumn(name="city_id",
referencedColumnName="id")
* })
*/
private $city;
Doctrine 2. Entidades y relaciones
// Relación 1:N bidireccional
// Post.php
/**
* @var User $user
*
* @ORMManyToOne(targetEntity="User", inversedBy="posts")
* @ORMJoinColumns({
*
@ORMJoinColumn(name="user_id", referencedColumnName="id")
* })
*/
protected $user;
// User.php
/**
* @var $posts PersistentCollection
*
* @ORMOneToMany(targetEntity="Post", mappedBy="user",
cascade={"all"})
*/
protected $posts;
Doctrine 2. Entidades y relaciones
// Relación N:M bidireccional
// Post.php
/**
* @var $categories PersistentCollection
*
* @ORMManyToMany(targetEntity="Category", inversedBy="posts")
* @ORMJoinTable(name="post_category")
*/
protected $categories;
// Category.php
/**
* @var $secrets PersistentCollection
*
* @ORMManyToMany(targetEntity="Post", mappedBy="categories")
*/
protected $posts;
Doctrine 2. Entidades y relaciones

$ php app/console doctrine:generate:entity
Doctrine 2. Entidades y relaciones

•

Ejercicio. Crear todas las entidades necesarias para la
siguiente base de datos:
Configuración
Doctrine 2. Configuración

# parameters.yml
parameters:
    database_driver:
    database_host:
    database_port:
    database_name:
    database_user:
    database_password:

pdo_mysql
localhost
~
db_name
db_user
db_pass
Doctrine 2. Configuración

$ php app/console doctrine:schema:update
--dump-sql
--force
Doctrine 2. Fixtures

•

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;
Fixtures
Doctrine 2. Fixtures
{
    "require": {
        "doctrine/doctrine-fixtures-bundle": "dev-master"
    }
}

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        // ...
        new DoctrineBundleFixturesBundle
DoctrineFixturesBundle()
    );
}
Doctrine 2. Fixtures
// Demo/DemoBundle/DataFixtures/ORM/LoadUserData.php
namespace DemoDemoBundleDataFixturesORM;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonPersistenceObjectManager;
use DemoDemoBundleEntityUser;
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 DemoDemoBundleDataFixturesORM;
use
use
use
use

DoctrineCommonDataFixturesAbstractFixture;
DoctrineCommonDataFixturesOrderedFixtureInterface;
DoctrineCommonPersistenceObjectManager;
DemoDemoBundleEntityUser;

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 DemoDemoBundleDataFixturesORM;
use
use
use
use

DoctrineCommonDataFixturesAbstractFixture;
DoctrineCommonDataFixturesOrderedFixtureInterface;
DoctrineCommonPersistenceObjectManager;
DemoDemoBundleEntityBook;

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
Doctrine 2. Fixtures

CONST FIXTURE_REF = ‘user’

users.yml

LoadUserData.php
CONST FIXTURE_REF = ‘books’

books.yml

LoadBookData.php
CONST FIXTURE_REF = ‘author’

authors.yml

LoadAuthorData.php
CONST FIXTURE_REF = ‘technology’

technologies.yml

LoadTechnologyData.php
Doctrine 2. Fixtures

•

Ejercicio. Crear datos de prueba (incluidas imágenes) en
archivos yml, además de los correspondientes scripts
para cargar los fixtures.
nelmio/alice
Doctrine 2. Fixtures

•

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

// Demo/DemoBundle/Entity/User.php
/**
* @ORMEntity(repositoryClass="DemoDemoBundle
EntityUserRepository")
*/
class User
{
    //...
}
Doctrine 2. Repositorios

// Demo/DemoBundle/Entity/UserRepository.php
use DoctrineORMEntityRepository;
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 DoctrineORMEntityRepository;
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 DoctrineORMEntityRepository;
 
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 DoctrineORMEntityRepository;
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 (DoctrineORMNoResultException $e) {
return 0;
}
}
}
Doctrine 2. Repositorios
// Demo/DemoBundle/Entity/UserRepository.php
 
use DoctrineORMEntityRepository;
 
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 (DoctrineORMNoResultException $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
    }
}
Doctrine 2. Repositorios

•

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
Frontend
Gestión de assets
Frontend. Gestión de assets

{{ asset('/bundles/books/images/logo.png') }}
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
Frontend. Gestión de assets

php app/console assets:install web/

•

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.
Assetic
Frontend. Assetic

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

Assets

Filtros

reset.css
bootstrap.css
global.css

styles.css
Frontend. Assetic

LessphpFilter
OptiPngFilter
PackerFilter
PngoutFilter
CompassFilter
GoogleClosureCompilerApiFilter SassSassFilter
GoogleClosureCompilerJarFilter SassScssFilter
HandlebarsFilter
SprocketsFilter
JpegoptimFilter
StylusFilter
JpegtranFilter
YuiCssCompressorFilter
LessFilter
YuiJsCompressorFilter
CoffeeScriptFilter
CssEmbedFilter
CssImportFilter
CssMinFilter
CssRewriteFilter
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
Frontend. Assetic

•

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
Tratamiento de imágenes
Frontend. Tratamiento de imágenes

AvalancheImagineBundle

GD2

Imagine

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 ImagineGdImagine();
$imagine = new ImagineImagickImagine();
$imagine->open('image.png')
    ->resize(new ImagineBox($width, $height))
    ->save('image_resized.png');
Frontend. Tratamiento de imágenes

http://www.slideshare.net/avalanche123/introductiontoimagine
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:
           options:
       user_medium:
           type:
           options:
       user_big:
           type:
           options:

thumbnail
{ size: [30, 30], mode: outbound }
thumbnail
{ size: [60, 60], mode: outbound }
thumbnail
{ 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();

}
Frontend. Formatos alternativos

•

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.
Backend
Formularios
Backend. Formularios
namespace BooksBooksBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
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' => 'BooksBooksBundleEntityUser'
        ));
    }
    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 BooksBooksBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
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
Backend. Formularios

•

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

Generación de CRUDs automáticos
Create

Read

Update

Delete
Backend. Formularios
Backend. Formularios

$ php app/console generate:doctrine:crud
Backend. Formularios

•

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.
Validación
Backend. Validación

/**
* @var string $title
*
* @AssertNotBlank
* @AssertMaxLength(250)
* @ORMColumn(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

Constraints
NotBlank
Null
Type
MaxLength
Regex
Min
DateTime
Collection
Language
File
All

Blank
TRUE
Email
Length
Ip
Range
Time
Count
Locale
Image
UserPassword

NotNull
FALSE
MinLength
Url
Max
Date
Choice
UniqueEntity
Country
Callback
Valid
Backend. Validación

http://symfony.com/doc/current/reference/constraints.html
Backend. Validación

•

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

ROLE_USER
Usuario

ROLE_ADMIN
ROLE_TRANSLATOR
Seguridad. Roles

ROLE_USER
Usuario

ROLE_ADMIN
ROLE_TRANSLATOR
Configuración
Seguridad. Configuración
security:
    encoders:
        SymfonyComponentSecurityCoreUserUser: 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:
        EmpresaBooksBundleEntityUser: sha512
 
    role_hierarchy:
        ROLE_ADMIN:
ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
 
    providers:
SymfonyComponentSecurityCoreUserUserInterface
        main:
entity: { class: EmpresaBooksBundleEntityUser, property: username }
 
getRoles()
 
getPassword()
    firewalls:
getSalt()
        dev:
getUsername()
            pattern: ^/(_(profiler|wdt)|css|images|js)/
eraseCredentials()
            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 }
Login
Seguridad. Login

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

login_check:
pattern:
logout:
pattern:

/login_check
/logout
Seguridad. Login
// src/Empresa/BooksBundle/Controller/SecurityController.php;
namespace EmpresaBooksBundleController;
 
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentSecurityCoreSecurityContext;
 
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());
        }
        
    }
}
Backend. Validación

•

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
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: EmpresaBooksBundleTwigHtmlExtension
    tags:
        - { name: twig.extension }
Backend. Validación

•

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

GET /index.php HTTP/1.1
Lee el archivo index.php y lo introduce en memoria
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

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

La primera vez se realizan los mismos pasos (lexer +
parser) para generar los “opcodes”. Una vez generados
se guardan en memoria.
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
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.
Doctrine 2. Repositorios
Extra. Doctrine2 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
Formularios
Extra. 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

More Related Content

What's hot

Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosJavier Eguiluz
 
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHP
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHPIntroducción a Silex. Aprendiendo a hacer las cosas bien en PHP
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHPDaniel Primo
 
Desarrollo Web Ágil con Symfony, Bootstrap y Angular
Desarrollo Web Ágil con Symfony, Bootstrap y AngularDesarrollo Web Ágil con Symfony, Bootstrap y Angular
Desarrollo Web Ágil con Symfony, Bootstrap y AngularFreelancer
 
Las buenas prácticas oficiales para aplicaciones Symfony
Las buenas prácticas oficiales para aplicaciones SymfonyLas buenas prácticas oficiales para aplicaciones Symfony
Las buenas prácticas oficiales para aplicaciones Symfonysymfony_bcn
 
Clase 2 conceptos fundamentales
Clase 2   conceptos fundamentalesClase 2   conceptos fundamentales
Clase 2 conceptos fundamentaleshydras_cs
 
Symfony en Drupal 8 - DrupalCamp Spain
Symfony en Drupal 8 - DrupalCamp Spain Symfony en Drupal 8 - DrupalCamp Spain
Symfony en Drupal 8 - DrupalCamp Spain Raul Fraile
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPJavier Eguiluz
 
PHP Avanzado: Patrones de diseño
PHP Avanzado: Patrones de diseñoPHP Avanzado: Patrones de diseño
PHP Avanzado: Patrones de diseñoRightster
 
PHP Avanzado: Características avanzadas de PHP
PHP Avanzado: Características avanzadas de PHPPHP Avanzado: Características avanzadas de PHP
PHP Avanzado: Características avanzadas de PHPRightster
 
Adentrándonos al Framework Symfony
Adentrándonos al  Framework SymfonyAdentrándonos al  Framework Symfony
Adentrándonos al Framework SymfonyRodrigo Miranda
 
Iniciación PHP 5. Programación Orientada a Objetos
Iniciación PHP 5. Programación Orientada a ObjetosIniciación PHP 5. Programación Orientada a Objetos
Iniciación PHP 5. Programación Orientada a ObjetosRightster
 
Clase 5 controller
Clase 5 controllerClase 5 controller
Clase 5 controllerhydras_cs
 
Introducción a PHP - Programador PHP - UGR
Introducción a PHP - Programador PHP - UGRIntroducción a PHP - Programador PHP - UGR
Introducción a PHP - Programador PHP - UGRJuan Belón Pérez
 
PHP Avanzado: PHP, XML y Servicios Web
PHP Avanzado: PHP, XML y Servicios WebPHP Avanzado: PHP, XML y Servicios Web
PHP Avanzado: PHP, XML y Servicios WebRightster
 

What's hot (20)

Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHP
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHPIntroducción a Silex. Aprendiendo a hacer las cosas bien en PHP
Introducción a Silex. Aprendiendo a hacer las cosas bien en PHP
 
Desarrollo Web Ágil con Symfony, Bootstrap y Angular
Desarrollo Web Ágil con Symfony, Bootstrap y AngularDesarrollo Web Ágil con Symfony, Bootstrap y Angular
Desarrollo Web Ágil con Symfony, Bootstrap y Angular
 
Las buenas prácticas oficiales para aplicaciones Symfony
Las buenas prácticas oficiales para aplicaciones SymfonyLas buenas prácticas oficiales para aplicaciones Symfony
Las buenas prácticas oficiales para aplicaciones Symfony
 
Ejemplosencillocon rmi
Ejemplosencillocon rmiEjemplosencillocon rmi
Ejemplosencillocon rmi
 
Composer: Gestionando dependencias en PHP
Composer: Gestionando dependencias en PHP Composer: Gestionando dependencias en PHP
Composer: Gestionando dependencias en PHP
 
Clase 2 conceptos fundamentales
Clase 2   conceptos fundamentalesClase 2   conceptos fundamentales
Clase 2 conceptos fundamentales
 
Symfony en Drupal 8 - DrupalCamp Spain
Symfony en Drupal 8 - DrupalCamp Spain Symfony en Drupal 8 - DrupalCamp Spain
Symfony en Drupal 8 - DrupalCamp Spain
 
Sf2 pr5 mi primer proyecto
Sf2 pr5 mi primer proyectoSf2 pr5 mi primer proyecto
Sf2 pr5 mi primer proyecto
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
PHP Avanzado: Patrones de diseño
PHP Avanzado: Patrones de diseñoPHP Avanzado: Patrones de diseño
PHP Avanzado: Patrones de diseño
 
PHP Avanzado: Características avanzadas de PHP
PHP Avanzado: Características avanzadas de PHPPHP Avanzado: Características avanzadas de PHP
PHP Avanzado: Características avanzadas de PHP
 
Java Rmi
Java RmiJava Rmi
Java Rmi
 
Curso Php
Curso PhpCurso Php
Curso Php
 
Adentrándonos al Framework Symfony
Adentrándonos al  Framework SymfonyAdentrándonos al  Framework Symfony
Adentrándonos al Framework Symfony
 
Iniciación PHP 5. Programación Orientada a Objetos
Iniciación PHP 5. Programación Orientada a ObjetosIniciación PHP 5. Programación Orientada a Objetos
Iniciación PHP 5. Programación Orientada a Objetos
 
Clase 5 controller
Clase 5 controllerClase 5 controller
Clase 5 controller
 
Introducción a PHP - Programador PHP - UGR
Introducción a PHP - Programador PHP - UGRIntroducción a PHP - Programador PHP - UGR
Introducción a PHP - Programador PHP - UGR
 
PHP Avanzado: PHP, XML y Servicios Web
PHP Avanzado: PHP, XML y Servicios WebPHP Avanzado: PHP, XML y Servicios Web
PHP Avanzado: PHP, XML y Servicios Web
 
Fabric más allá de lo básico
Fabric más allá de lo básicoFabric más allá de lo básico
Fabric más allá de lo básico
 

Similar to Materiales del curso de Symfony2

Introducción al framework Symfony
Introducción al framework SymfonyIntroducción al framework Symfony
Introducción al framework SymfonyAlejandro Hernández
 
Elementos sobre Symfony 2.1
Elementos sobre Symfony 2.1Elementos sobre Symfony 2.1
Elementos sobre Symfony 2.1Yaismel Miranda
 
Symfony: construyendo aplicaciones web
Symfony: construyendo aplicaciones webSymfony: construyendo aplicaciones web
Symfony: construyendo aplicaciones websergiovier
 
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdfPHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdfRaaulroodriguez
 
Masterclass ADWE Madrid
Masterclass ADWE MadridMasterclass ADWE Madrid
Masterclass ADWE MadridKubide
 
Actividad 3 . funciones en php
Actividad 3 . funciones en phpActividad 3 . funciones en php
Actividad 3 . funciones en phpLeonardoPuerta2
 
Spring OSGI , dm server - Leonardo Torres Altez
Spring OSGI , dm server - Leonardo Torres AltezSpring OSGI , dm server - Leonardo Torres Altez
Spring OSGI , dm server - Leonardo Torres Alteza19987225
 
Symfony2 Formacion y primeros pasos
Symfony2  Formacion y primeros pasosSymfony2  Formacion y primeros pasos
Symfony2 Formacion y primeros pasosSoni BM
 
Symfony-Community: Introducción a Symfony Framework
Symfony-Community: Introducción a Symfony FrameworkSymfony-Community: Introducción a Symfony Framework
Symfony-Community: Introducción a Symfony Frameworkexcedesoft
 
La web como Plataforma con Dojo Toolkit
La web como Plataforma con Dojo ToolkitLa web como Plataforma con Dojo Toolkit
La web como Plataforma con Dojo ToolkitAlex Fuentes
 
Framework kahana
Framework kahanaFramework kahana
Framework kahanaH31D1
 
Taller introduccion symfony2
Taller introduccion symfony2Taller introduccion symfony2
Taller introduccion symfony2Mario IC
 

Similar to Materiales del curso de Symfony2 (20)

Introducción al framework Symfony
Introducción al framework SymfonyIntroducción al framework Symfony
Introducción al framework Symfony
 
Elementos sobre Symfony 2.1
Elementos sobre Symfony 2.1Elementos sobre Symfony 2.1
Elementos sobre Symfony 2.1
 
Symfony: construyendo aplicaciones web
Symfony: construyendo aplicaciones webSymfony: construyendo aplicaciones web
Symfony: construyendo aplicaciones web
 
Symfony Parte 2
Symfony Parte 2Symfony Parte 2
Symfony Parte 2
 
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdfPHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf
PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf PHP.pdf
 
Composer
ComposerComposer
Composer
 
Masterclass ADWE Madrid
Masterclass ADWE MadridMasterclass ADWE Madrid
Masterclass ADWE Madrid
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Gestión Remota de Equipos con Python
Gestión Remota de Equipos con PythonGestión Remota de Equipos con Python
Gestión Remota de Equipos con Python
 
Actividad 3 . funciones en php
Actividad 3 . funciones en phpActividad 3 . funciones en php
Actividad 3 . funciones en php
 
Spring OSGI , dm server - Leonardo Torres Altez
Spring OSGI , dm server - Leonardo Torres AltezSpring OSGI , dm server - Leonardo Torres Altez
Spring OSGI , dm server - Leonardo Torres Altez
 
Symfony2 Formacion y primeros pasos
Symfony2  Formacion y primeros pasosSymfony2  Formacion y primeros pasos
Symfony2 Formacion y primeros pasos
 
Symfony-Community: Introducción a Symfony Framework
Symfony-Community: Introducción a Symfony FrameworkSymfony-Community: Introducción a Symfony Framework
Symfony-Community: Introducción a Symfony Framework
 
APIREST LARAVEL Y PHP.pptx
APIREST LARAVEL Y PHP.pptxAPIREST LARAVEL Y PHP.pptx
APIREST LARAVEL Y PHP.pptx
 
Python para desarrolladores web
Python para desarrolladores webPython para desarrolladores web
Python para desarrolladores web
 
Introducción a Kohana Framework
Introducción a Kohana FrameworkIntroducción a Kohana Framework
Introducción a Kohana Framework
 
La web como Plataforma con Dojo Toolkit
La web como Plataforma con Dojo ToolkitLa web como Plataforma con Dojo Toolkit
La web como Plataforma con Dojo Toolkit
 
Framework kahana
Framework kahanaFramework kahana
Framework kahana
 
Taller introduccion symfony2
Taller introduccion symfony2Taller introduccion symfony2
Taller introduccion symfony2
 
php y mysql
php y mysqlphp y mysql
php y mysql
 

More from Raul Fraile

Aplicaciones CLI profesionales con Symfony
Aplicaciones CLI profesionales con SymfonyAplicaciones CLI profesionales con Symfony
Aplicaciones CLI profesionales con SymfonyRaul Fraile
 
Steganography: Hiding your secrets with PHP
Steganography: Hiding your secrets with PHPSteganography: Hiding your secrets with PHP
Steganography: Hiding your secrets with PHPRaul Fraile
 
How GZIP compression works - JS Conf EU 2014
How GZIP compression works - JS Conf EU 2014How GZIP compression works - JS Conf EU 2014
How GZIP compression works - JS Conf EU 2014Raul Fraile
 
How GZIP works... in 10 minutes
How GZIP works... in 10 minutesHow GZIP works... in 10 minutes
How GZIP works... in 10 minutesRaul Fraile
 
$kernel->infect(): Creating a cryptovirus for Symfony2 apps
$kernel->infect(): Creating a cryptovirus for Symfony2 apps$kernel->infect(): Creating a cryptovirus for Symfony2 apps
$kernel->infect(): Creating a cryptovirus for Symfony2 appsRaul Fraile
 
Sistemas de ficheros para dispositivos embebidos
Sistemas de ficheros para dispositivos embebidosSistemas de ficheros para dispositivos embebidos
Sistemas de ficheros para dispositivos embebidosRaul Fraile
 
Refactoring PHP/Symfony2 apps
Refactoring PHP/Symfony2 appsRefactoring PHP/Symfony2 apps
Refactoring PHP/Symfony2 appsRaul Fraile
 
Refactorización de aplicaciones PHP/Symfony2
Refactorización de aplicaciones PHP/Symfony2Refactorización de aplicaciones PHP/Symfony2
Refactorización de aplicaciones PHP/Symfony2Raul Fraile
 
MidwestPHP Symfony2 Internals
MidwestPHP Symfony2 InternalsMidwestPHP Symfony2 Internals
MidwestPHP Symfony2 InternalsRaul Fraile
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]Raul Fraile
 
DeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsDeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsRaul Fraile
 
Symfony2: Interacción con CSS, JS y HTML5
Symfony2: Interacción con CSS, JS y HTML5Symfony2: Interacción con CSS, JS y HTML5
Symfony2: Interacción con CSS, JS y HTML5Raul Fraile
 
Symfony2: Optimización y rendimiento
Symfony2: Optimización y rendimientoSymfony2: Optimización y rendimiento
Symfony2: Optimización y rendimientoRaul Fraile
 
Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Raul Fraile
 
Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Raul Fraile
 
Presentacion Symfony2
Presentacion Symfony2Presentacion Symfony2
Presentacion Symfony2Raul Fraile
 

More from Raul Fraile (16)

Aplicaciones CLI profesionales con Symfony
Aplicaciones CLI profesionales con SymfonyAplicaciones CLI profesionales con Symfony
Aplicaciones CLI profesionales con Symfony
 
Steganography: Hiding your secrets with PHP
Steganography: Hiding your secrets with PHPSteganography: Hiding your secrets with PHP
Steganography: Hiding your secrets with PHP
 
How GZIP compression works - JS Conf EU 2014
How GZIP compression works - JS Conf EU 2014How GZIP compression works - JS Conf EU 2014
How GZIP compression works - JS Conf EU 2014
 
How GZIP works... in 10 minutes
How GZIP works... in 10 minutesHow GZIP works... in 10 minutes
How GZIP works... in 10 minutes
 
$kernel->infect(): Creating a cryptovirus for Symfony2 apps
$kernel->infect(): Creating a cryptovirus for Symfony2 apps$kernel->infect(): Creating a cryptovirus for Symfony2 apps
$kernel->infect(): Creating a cryptovirus for Symfony2 apps
 
Sistemas de ficheros para dispositivos embebidos
Sistemas de ficheros para dispositivos embebidosSistemas de ficheros para dispositivos embebidos
Sistemas de ficheros para dispositivos embebidos
 
Refactoring PHP/Symfony2 apps
Refactoring PHP/Symfony2 appsRefactoring PHP/Symfony2 apps
Refactoring PHP/Symfony2 apps
 
Refactorización de aplicaciones PHP/Symfony2
Refactorización de aplicaciones PHP/Symfony2Refactorización de aplicaciones PHP/Symfony2
Refactorización de aplicaciones PHP/Symfony2
 
MidwestPHP Symfony2 Internals
MidwestPHP Symfony2 InternalsMidwestPHP Symfony2 Internals
MidwestPHP Symfony2 Internals
 
Symfony internals [english]
Symfony internals [english]Symfony internals [english]
Symfony internals [english]
 
DeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internalsDeSymfony 2012: Symfony internals
DeSymfony 2012: Symfony internals
 
Symfony2: Interacción con CSS, JS y HTML5
Symfony2: Interacción con CSS, JS y HTML5Symfony2: Interacción con CSS, JS y HTML5
Symfony2: Interacción con CSS, JS y HTML5
 
Symfony2: Optimización y rendimiento
Symfony2: Optimización y rendimientoSymfony2: Optimización y rendimiento
Symfony2: Optimización y rendimiento
 
Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Symfony2: Framework para PHP5
Symfony2: Framework para PHP5
 
Symfony2: Framework para PHP5
Symfony2: Framework para PHP5Symfony2: Framework para PHP5
Symfony2: Framework para PHP5
 
Presentacion Symfony2
Presentacion Symfony2Presentacion Symfony2
Presentacion Symfony2
 

Recently uploaded

Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..RobertoGumucio2
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxAlexander López
 
La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafiosFundación YOD YOD
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadMiguelAngelVillanuev48
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxJOSEMANUELHERNANDEZH11
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxaylincamaho
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.241514949
 
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxGoogle-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxAlexander López
 
definicion segun autores de matemáticas educativa
definicion segun autores de matemáticas  educativadefinicion segun autores de matemáticas  educativa
definicion segun autores de matemáticas educativaAdrianaMartnez618894
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfSergioMendoza354770
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxAlexander López
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxJOSEFERNANDOARENASCA
 
Mapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMidwarHenryLOZAFLORE
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaarkananubis
 
dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptMiguelAtencio10
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxNombre Apellidos
 
tics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxtics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxazmysanros90
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx241523733
 
Segunda ley de la termodinámica TERMODINAMICA.pptx
Segunda ley de la termodinámica TERMODINAMICA.pptxSegunda ley de la termodinámica TERMODINAMICA.pptx
Segunda ley de la termodinámica TERMODINAMICA.pptxMariaBurgos55
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELmaryfer27m
 

Recently uploaded (20)

Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..Plan Sarmiento - Netbook del GCBA 2019..
Plan Sarmiento - Netbook del GCBA 2019..
 
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptxLAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
LAS_TIC_COMO_HERRAMIENTAS_EN_LA_INVESTIGACIÓN.pptx
 
La era de la educación digital y sus desafios
La era de la educación digital y sus desafiosLa era de la educación digital y sus desafios
La era de la educación digital y sus desafios
 
Presentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidadPresentación inteligencia artificial en la actualidad
Presentación inteligencia artificial en la actualidad
 
Hernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptxHernandez_Hernandez_Practica web de la sesion 11.pptx
Hernandez_Hernandez_Practica web de la sesion 11.pptx
 
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptxMedidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
Medidas de formas, coeficiente de asimetría y coeficiente de curtosis.pptx
 
El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.El uso de las TIC's en la vida cotidiana.
El uso de las TIC's en la vida cotidiana.
 
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptxGoogle-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
Google-Meet-como-herramienta-para-realizar-reuniones-virtuales.pptx
 
definicion segun autores de matemáticas educativa
definicion segun autores de matemáticas  educativadefinicion segun autores de matemáticas  educativa
definicion segun autores de matemáticas educativa
 
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdfPARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
PARTES DE UN OSCILOSCOPIO ANALOGICO .pdf
 
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptxEl_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
El_Blog_como_herramienta_de_publicacion_y_consulta_de_investigacion.pptx
 
Arenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptxArenas Camacho-Practica tarea Sesión 12.pptx
Arenas Camacho-Practica tarea Sesión 12.pptx
 
Mapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptxMapa-conceptual-del-Origen-del-Universo-3.pptx
Mapa-conceptual-del-Origen-del-Universo-3.pptx
 
R1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en minaR1600G CAT Variables de cargadores en mina
R1600G CAT Variables de cargadores en mina
 
dokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.pptdokumen.tips_36274588-sistema-heui-eui.ppt
dokumen.tips_36274588-sistema-heui-eui.ppt
 
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptxCrear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
Crear un recurso multimedia. Maricela_Ponce_DomingoM1S3AI6-1.pptx
 
tics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptxtics en la vida cotidiana prepa en linea modulo 1.pptx
tics en la vida cotidiana prepa en linea modulo 1.pptx
 
GonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptxGonzalezGonzalez_Karina_M1S3AI6... .pptx
GonzalezGonzalez_Karina_M1S3AI6... .pptx
 
Segunda ley de la termodinámica TERMODINAMICA.pptx
Segunda ley de la termodinámica TERMODINAMICA.pptxSegunda ley de la termodinámica TERMODINAMICA.pptx
Segunda ley de la termodinámica TERMODINAMICA.pptx
 
El uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFELEl uso delas tic en la vida cotidiana MFEL
El uso delas tic en la vida cotidiana MFEL
 

Materiales del curso de Symfony2

  • 1. Materiales del curso Raúl Fraile Beneyto
  • 2. Temario • Tema 1: Prerequisitos • • • • • PHP 5.3 YAML MVC PSR-0 (Autoloading) PSR-1, PSR-2 (Coding Standards)
  • 3. 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
  • 4. Temario • Tema 3: Primeras páginas • • • Bundles Routing Controladores
  • 5. Temario • Tema 4: Lenguaje de plantillas Twig • • • • Sintaxis básica Herencia Macros Twig extensions
  • 6. Temario • Tema 5: Bases de datos con Doctrine2 • • • • • ORM DBAL Entidades y relaciones Configuración Fixtures
  • 7. Temario • Tema 6: Frontend • • • Gestión de assets Tratamiento de imágenes Formatos alternativos
  • 8. Temario • Tema 7: Backend • Formularios • • • • Conceptos básicos Generación automática de formularios Formularios avanzados Validación
  • 9. Temario • Tema 8: Seguridad • • • • Autenticación VS Autorización Roles Configuración Login
  • 10. Temario • Tema 9: Servicios y eventos • Contenedor de inyección de dependencias • Eventos
  • 11. Temario • Tema 10: Extendiendo symfony2 • • Comandos de consola Extensiones propias de Twig
  • 12. Temario • Tema 11: Optimización y rendimiento • • • Optimización de assets APC ESI
  • 13. 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.
  • 17. Prerequisitos. PHP 5.3 / Namespaces <?php require_once(‘./libs/libA/foo.class.php’); require_once(‘./libs/libB/foo.class.php’); $foo = new Foo();
  • 18. Prerequisitos. PHP 5.3 / Namespaces // ./libs/libA/Foo.php <?php namespace LibA; class Foo {} // ./libs/libB/Foo.php <?php namespace LibB; class Foo {}
  • 19. Prerequisitos. PHP 5.3 / Namespaces // ./index.php <?php namespace MyApp; use LibAFoo; $foo = new Foo();
  • 20. Prerequisitos. PHP 5.3 / Namespaces // ./index.php <?php namespace MyApp; use LibAFoo as FooA; use LibBFoo as FooB; $fooA = new FooA(); $fooB = new FooB();
  • 21. Prerequisitos. PHP 5.3 / Namespaces // ./index.php <?php namespace MyApp; $created = new DateTime(); $len = strlen(‘hola’); echo $len . PHP_EOL;
  • 23. Prerequisitos. PHP 5.3 / Closures <?php echo preg_replace_callback('/[a-z]/',     function ($match) {         return strtoupper($match[0]);     },     'Hola'); // HOLA
  • 25. Prerequisitos. PHP 5.3 / Annotations <?php /** * @ORMColumn(type="string") * @AssertNotBlank() */ protected $name;
  • 26. Prerequisitos. PHP 5.3 / Annotations <?php /** * @Route("/signup") * @Method({"POST"}) */ public function signupAction() {     ... }
  • 27. OOP
  • 28. Prerequisitos. PHP 5.3 / OOP public $foo; private $foo; protected $foo;
  • 29. Prerequisitos. PHP 5.3 / OOP <?php class Foo {     private $a = 1;     protected $b = 2;     public $c = 3; } $foo = new Foo(); echo $foo->a;
  • 30. Prerequisitos. PHP 5.3 / OOP <?php class Foo {     private $a = 1;     protected $b = 2;     public $c = 3; } $foo = new Foo(); echo $foo->b;
  • 31. 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();
  • 32. Prerequisitos. PHP 5.3 / OOP ¿Diferencia entre una interface y una clase abstracta?
  • 33. Prerequisitos. PHP 5.3 / OOP // JsonInterface.php interface JsonInterface {     const MAX_DEPTH = 100;     public function exportJson(); } // User.php class User implements {     public function exportJson()     {         return array(             'username' => $this->username;         );     } }
  • 34. Prerequisitos. PHP 5.3 / OOP // BaseController.php abstract class BaseController {     public function getCurrentUser()     {         ...     } } // UserController.php class UserController extends BaseController {     ... }
  • 35. Prerequisitos. PHP 5.3 / OOP ¿Puede haber herencia entre interfaces?
  • 36. Prerequisitos. PHP 5.3 / OOP <?php namespace DemoBundleEntity; class User {     public function setBirthday(DateTime $date)     {         $this->birthday = $date;     } {
  • 37. YAML
  • 38. Prerequisitos. 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.”
  • 39. 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
  • 40. Prerequisitos. YAML parameters:     emails: [ ‘e1@gmail.com’, ‘e2@gmail.com’ ]     webs:         - ‘web1.com’         - ‘web2.com’     technologies: { PHP: 5.3, MySQL: 5.1 }
  • 41. MVC
  • 45. Prerequisitos. PSR-0 (Autoloading) <?php require_once(‘autoload.php’); $book = new Book(‘my book’); $chapter = new Chapter(‘chapter 1’); $book->addChapter($chapter);
  • 46. Prerequisitos. PSR-0 (Autoloading) FQN (Fully Qualified Name) Ruta SymfonyComponentFilesystem Filesystem [path]/Symfony/Component/ Filesystem/Filesystem.php Twig_Function [path]/Twig/Function.php
  • 47. Prerequisitos. PSR-0 (Autoloading) ¿Impacto en el rendimiento de la aplicación?
  • 48. PSR-1, PSR-2 (Coding Standards)
  • 49. Prerequisitos. 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; }
  • 50. Prerequisitos. PSR-1, PSR-2 (Coding Standards) https://github.com/php-fig/fig-standards/blob/master/ accepted/PSR-1-basic-coding-standard.md https://github.com/php-fig/fig-standards/blob/master/ accepted/PSR-2-coding-style-guide.md
  • 53. Introducción a Symfony2. Historia Fabien Potencier
  • 54. 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
  • 56. Introducción a Symfony2. Arquitectura BrowserKit EventDispatcher Routing ClassLoader Finder Security Config Form Serializer Console HttpFoundation Templating CssSelector HttpKernel Translation DependencyInjection Locale Validator DomCrawler Yaml Process
  • 57. Introducción a Symfony2. Arquitectura Componentes + Bundles + Librerías externas Full-stack framework
  • 59. Gestión de dependencias con Composer
  • 61. 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”
  • 62. Introducción a Symfony2. Composer $ curl -s https://getcomposer.org/installer | php $ sudo mv composer.phar /usr/local/bin/composer
  • 63. 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
  • 64. Introducción a Symfony2. Composer # composer.json { "name": "raulfraile/demo_composer", "description": "Demo composer", "require": { "symfony/console": "2.1.*" } }
  • 65. Introducción a Symfony2. Composer # composer.json { "name": "raulfraile/demo_composer", "description": "Demo composer", "require": { "php": ">=5.3.3", "symfony/console": "2.1.*" } }
  • 66. 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", } }
  • 67. Demo: Composer + Console component
  • 68. Introducción a Symfony2. Composer • 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.
  • 70. Introducción a Symfony2. Inyección de dependencias "Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields."
  • 71. Introducción a Symfony2. Inyección de dependencias class UserController {     private $em;     public function __construct()     {         $this->em = new EntityManager();     } } $userController = new UserController();
  • 72. Introducción a Symfony2. Inyección de dependencias class UserController {     private $em;     public function __construct()     {         $this->em = new EntityManager(‘conn2’);     } } $userController = new UserController();
  • 73. 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);
  • 74. DIC: Contenedor de Inyección de Dependencias
  • 75. Introducción a Symfony2. Inyección de dependencias use SymfonyComponentDependencyInjection; use SymfonyComponentDependencyInjectionReference;   $sc = new DependencyInjectionContainerBuilder(); $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');
  • 77. Introducción a Symfony2. Entornos de ejecución Mismo código Diferentes configuraciones
  • 78. 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
  • 80. Introducción a Symfony2. Instalación y configuración $ composer create-project symfony/framework-standard-edition path/ 2.1.3
  • 81. Introducción a Symfony2. Instalación y configuración
  • 83. Introducción a Symfony2. Documentación
  • 86. Primeras páginas. Bundles Todo el contenido de nuestra aplicación estará dentro de uno o más bundles
  • 87. Primeras páginas. Bundles $ php app/console generate:bundle
  • 88. Primeras páginas. Bundles // app/AppKernel.php class AppKernel extends Kernel {     public function registerBundles()     {         $bundles = array(             new SymfonyBundleFrameworkBundleFrameworkBundle(),             new SymfonyBundleSecurityBundleSecurityBundle(),             new SymfonyBundleTwigBundleTwigBundle(),             new SymfonyBundleMonologBundleMonologBundle(),             new SymfonyBundleSwiftmailerBundleSwiftmailerBundle(),             new SymfonyBundleAsseticBundleAsseticBundle(),             new DoctrineBundleDoctrineBundleDoctrineBundle(), ...         );         if (in_array($this->getEnvironment(), array('dev', 'test'))) {             $bundles[] = new SymfonyBundleWebProfilerBundleWebProfilerBundle();         }         return $bundles;     } }
  • 91. Primeras páginas. Routing /user/profile/raulfraile Controlador: UserController Acción: showProfile($username)
  • 92. Primeras páginas. Routing # routing.yml user_profile:     pattern: /user/profile/{username}     defaults: { _controller: DemoBundle:User:profile }
  • 93. Primeras páginas. Routing # routing.yml users_list:     pattern: /user/list/{page}     defaults: { _controller: DemoBundle:User:list, page: 1 }
  • 94. Primeras páginas. Routing # routing.yml users_list:     pattern: /user/list/{page}     defaults: { _controller: DemoBundle:User:list, page: 1 } requirements: page: d+
  • 95. Primeras páginas. Routing # routing.yml users_list:     pattern: /user/list/{page}{subpage}     defaults: { _controller: DemoBundle:User:list, page: 1 } requirements: page: d+ _method: GET
  • 96. Primeras páginas. Routing # user.yml user_profile:     pattern: /profile/{username}     defaults: { _controller: DemoBundle:User:profile } # routing.yml user_routing: resource: "@DemoBundle/Resources/config/user.yml" prefix: /user
  • 97. Primeras páginas. Routing $ php app/console router:debug [route_name]
  • 98. Primeras páginas. Routing $ php app/console router:match path
  • 101. Primeras páginas. Controladores use SymfonyComponentHttpFoundationResponse; class UserController {     public function helloAction()     {         return new Response('Hello world!');     } }
  • 102. Primeras páginas. Controladores use SymfonyComponentHttpFoundationResponse; class UserController {     public function helloAction($name)     {         return new Response('Hello '.$name.'!');     } }
  • 103. Primeras páginas. Controladores use SymfonyComponentHttpFoundationResponse; use SymfonyBundleFrameworkBundleController Controller; class UserController extends Controller {     public function helloAction($name)     {         return new Response('Hello '.$name.'!');     } }
  • 104. Primeras páginas. Controladores public function indexAction() {     return $this->redirect($this->generateUrl('home')); }
  • 105. Primeras páginas. Controladores public function indexAction() {     throw $this->createNotFoundException('Not found'); }
  • 106. Demo: Controladores + objeto Request
  • 107. Primeras páginas. Controladores • Ejercicio. Crear todas las rutas del proyecto, con sus respectivos 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
  • 108. Primeras páginas. Controladores • 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
  • 109. 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
  • 110. 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);
  • 111. Twig
  • 114. Twig. Sintaxis básica - Variables {{ name }} {{ user.name }} {{ user[‘name’] }}
  • 115. Twig. Sintaxis básica - Variables {% set foo = 'foo' %} {% set foo = [1, 2] %} {% set foo = {'foo': 'bar'} %}
  • 116. Twig. Sintaxis básica - Filtros {{ name|upper }} {{ name|striptags|nlbr }}
  • 117. Twig. Sintaxis básica - Filtros abs capitalize convert_encoding date date_modify default escape format join json_encode keys length lower merge nl2br number_format raw replace reverse slice sort split striptags title trim upper url_encode
  • 118. Twig. Sintaxis básica - Funciones {{ random(['rojo', 'azul', 'verde']) }} {{ random('ABC') }} {{ random() }} {{ random(5) }}
  • 119. Twig. Sintaxis básica - Funciones {{ path('home') }} <a href=”{{ path(‘user_profile’, {‘username’: user.username }) }}”>
  • 120. Twig. Sintaxis básica - Funciones {{ url('home') }} {{ url(‘user_profile’, {‘username’: user.username }) }}
  • 121. Twig. Sintaxis básica - Control de flujo {% for user in users %} <li>{{ user.username }}</li> {% endfor %}
  • 122. Twig. Sintaxis básica - Control de flujo {% for user in users %} <li>{{ user.username }}</li> {% else %} <li>No hay usuarios</li> {% endfor %}
  • 123. Twig. Sintaxis básica - Control de flujo {% for user in users %} <li>{{ loop.index }} {{ user.username }}</li> {% endfor %}
  • 124. Twig. Sintaxis básica - Control de flujo {% for user in users if user.active %} <li>{{ user.username }}</li> {% endfor %}
  • 125. Twig. Sintaxis básica - Control de flujo {% for key, user in users %} <li>{{ user.username }}</li> {% endfor %}
  • 126. Twig. Sintaxis básica - Control de flujo {% if user.active %} <p>Usuario activo</p> {% endif %}
  • 127. 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 %}
  • 128. Twig. Sintaxis básica • 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());
  • 129. 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.
  • 131. Twig. Herencia {% include %} {% extends %} {% render %}
  • 132. 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>
  • 133. 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>
  • 134. 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 %}
  • 135. 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 %}
  • 136. 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 %}
  • 137. Twig. Herencia {# index.html.twig #} <html> <head> <title>Título</title> </head> <body> ... {% render "DemoBundle:Notifications:show" %} </body> </html>
  • 138. 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.
  • 139. 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) }}
  • 140. Macros
  • 141. Twig. 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) }}
  • 142. 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) }}
  • 143. 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) }}
  • 145. Twig. Extensiones Oficiales: Twig Extensions Repository Comunidad Propias
  • 147. ORM
  • 148. Doctrine 2. ORM ORM: Object Relational Mapper
  • 150. Doctrine 2. ORM ORM DBAL PDO SQL Server MySQL Oracle ...
  • 151. DBAL
  • 152. Doctrine 2. DBAL class UserController extends Controller {     public function indexAction()     {         $conn = $this->get('database_connection');         $users = $conn->fetchAll('SELECT * FROM users');     } }
  • 154. Doctrine 2. Entidades y relaciones namespace DemoDemoBundleEntity; use DoctrineORMMapping as ORM; /** * @ORMTable(name="category") * @ORMEntity */ class Category {     /** * @ORMColumn(name="id", type="integer") * @ORMId * @ORMGeneratedValue(strategy="AUTO") */     protected $id;     /** * @var string $name * @ORMColumn(name="name", type="string", length=255) */     protected $name; }
  • 155. Doctrine 2. Entidades y relaciones /** * @ORMColumn(name="twitter", type="string", length=15, nullable=true) */ protected $twitter; /** * @ORMColumn(name="bio", type="text") */ protected $bio; /** * @ORMColumn(name="created_at", type="datetime") */ protected $createdAt; /** * @ORMColumn(name="languages", type="array") */ private $languages;
  • 156. Doctrine 2. Entidades y relaciones // Relación 1:N unidireccional // User.php /** * @var City $city * * @ORMManyToOne(targetEntity="City") * @ORMJoinColumns({ * @ORMJoinColumn(name="city_id", referencedColumnName="id") * }) */ private $city;
  • 157. Doctrine 2. Entidades y relaciones // Relación 1:N unidireccional // User.php /** * @var City $city * * @ORMManyToOne(targetEntity="City") * @ORMJoinColumns({ * @ORMJoinColumn(name="city_id", referencedColumnName="id") * }) */ private $city;
  • 158. Doctrine 2. Entidades y relaciones // Relación 1:N bidireccional // Post.php /** * @var User $user * * @ORMManyToOne(targetEntity="User", inversedBy="posts") * @ORMJoinColumns({ * @ORMJoinColumn(name="user_id", referencedColumnName="id") * }) */ protected $user; // User.php /** * @var $posts PersistentCollection * * @ORMOneToMany(targetEntity="Post", mappedBy="user", cascade={"all"}) */ protected $posts;
  • 159. Doctrine 2. Entidades y relaciones // Relación N:M bidireccional // Post.php /** * @var $categories PersistentCollection * * @ORMManyToMany(targetEntity="Category", inversedBy="posts") * @ORMJoinTable(name="post_category") */ protected $categories; // Category.php /** * @var $secrets PersistentCollection * * @ORMManyToMany(targetEntity="Post", mappedBy="categories") */ protected $posts;
  • 160. Doctrine 2. Entidades y relaciones $ php app/console doctrine:generate:entity
  • 161. Doctrine 2. Entidades y relaciones • Ejercicio. Crear todas las entidades necesarias para la siguiente base de datos:
  • 163. Doctrine 2. Configuración # parameters.yml parameters:     database_driver:     database_host:     database_port:     database_name:     database_user:     database_password: pdo_mysql localhost ~ db_name db_user db_pass
  • 164. Doctrine 2. Configuración $ php app/console doctrine:schema:update --dump-sql --force
  • 165. Doctrine 2. Fixtures • 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;
  • 167. Doctrine 2. Fixtures {     "require": {         "doctrine/doctrine-fixtures-bundle": "dev-master"     } } // app/AppKernel.php public function registerBundles() {     $bundles = array(         // ...         new DoctrineBundleFixturesBundle DoctrineFixturesBundle()     ); }
  • 168. Doctrine 2. Fixtures // Demo/DemoBundle/DataFixtures/ORM/LoadUserData.php namespace DemoDemoBundleDataFixturesORM; use DoctrineCommonDataFixturesFixtureInterface; use DoctrineCommonPersistenceObjectManager; use DemoDemoBundleEntityUser; class LoadUserData implements FixtureInterface {     public function load(ObjectManager $manager)     {         $user = new User();         $user->setUsername('raulfraile');         $manager->persist($user);         $manager->flush();     } }
  • 169. Doctrine 2. Fixtures // Demo/DemoBundle/DataFixtures/ORM/LoadUserData.php namespace DemoDemoBundleDataFixturesORM; use use use use DoctrineCommonDataFixturesAbstractFixture; DoctrineCommonDataFixturesOrderedFixtureInterface; DoctrineCommonPersistenceObjectManager; DemoDemoBundleEntityUser; class LoadUserData extends AbstractFixture implements OrderedFixtureInterface {     public function getOrder()     {         return 1;     }     public function load(ObjectManager $manager)     {         // ...         $this->addReference('user_' . $user->getUsername(), $user);     } }
  • 170. Doctrine 2. Fixtures // Demo/DemoBundle/DataFixtures/ORM/LoadBookData.php namespace DemoDemoBundleDataFixturesORM; use use use use DoctrineCommonDataFixturesAbstractFixture; DoctrineCommonDataFixturesOrderedFixtureInterface; DoctrineCommonPersistenceObjectManager; DemoDemoBundleEntityBook; class LoadBookData extends AbstractFixture implements OrderedFixtureInterface {     public function getOrder()     {         return 2;     }     public function load(ObjectManager $manager)     {         // ...         $book->setUser($this->getReference('user_'.$data[‘slug’]));     } }
  • 171. Doctrine 2. Fixtures $ php app/console doctrine:fixtures:load --purge-with-truncate
  • 173. Doctrine 2. Fixtures CONST FIXTURE_REF = ‘user’ users.yml LoadUserData.php CONST FIXTURE_REF = ‘books’ books.yml LoadBookData.php CONST FIXTURE_REF = ‘author’ authors.yml LoadAuthorData.php CONST FIXTURE_REF = ‘technology’ technologies.yml LoadTechnologyData.php
  • 174. Doctrine 2. Fixtures • Ejercicio. Crear datos de prueba (incluidas imágenes) en archivos yml, además de los correspondientes scripts para cargar los fixtures.
  • 176. Doctrine 2. Fixtures • Ejercicio. Rehacer los fixtures para utilizar nelmio/alice y además generar usuarios de prueba utilizando fzaninotto/ faker.
  • 178. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/User.php /** * @ORMEntity(repositoryClass="DemoDemoBundle EntityUserRepository") */ class User {     //... }
  • 179. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/UserRepository.php use DoctrineORMEntityRepository; class UserRepository extends EntityRepository {     public function findAllActive()     {         return $this->getEntityManager()             ->createQuery('SELECT u FROM DemoBundle:User u WHERE u.active = TRUE')             ->getResult();     } }
  • 180. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/UserRepository.php use DoctrineORMEntityRepository; 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();     } }
  • 181. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/UserRepository.php   use DoctrineORMEntityRepository;   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(); } }
  • 182. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/UserRepository.php use DoctrineORMEntityRepository; 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 (DoctrineORMNoResultException $e) { return 0; } } }
  • 183. Doctrine 2. Repositorios // Demo/DemoBundle/Entity/UserRepository.php   use DoctrineORMEntityRepository;   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 (DoctrineORMNoResultException $e) { return false; } } }
  • 184. 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     } }
  • 185. Doctrine 2. Repositorios • 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
  • 188. Frontend. Gestión de assets {{ asset('/bundles/books/images/logo.png') }}
  • 189. Frontend. Gestión de assets framework:     ...     templating:         engines: ['twig']         assets_version: v0.2
  • 190. Frontend. Gestión de assets http://ejemplo.com/bundles/books/images/logo.png?v0.2
  • 191. Frontend. Gestión de assets php app/console assets:install web/ • 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.
  • 193. Frontend. Assetic “Assetic is an asset management framework for PHP, based on the Python webassets library.”
  • 195. Frontend. Assetic LessphpFilter OptiPngFilter PackerFilter PngoutFilter CompassFilter GoogleClosureCompilerApiFilter SassSassFilter GoogleClosureCompilerJarFilter SassScssFilter HandlebarsFilter SprocketsFilter JpegoptimFilter StylusFilter JpegtranFilter YuiCssCompressorFilter LessFilter YuiJsCompressorFilter CoffeeScriptFilter CssEmbedFilter CssImportFilter CssMinFilter CssRewriteFilter
  • 196. Frontend. Assetic {% javascripts '@BooksBundle/Resources/public/js/*'%}     <script type="text/javascript" src="{{ asset_url }}"> </script> {% endjavascripts %}
  • 200. Frontend. Assetic $ php app/console assetic:dump --env=prod --watch
  • 201. Frontend. Assetic • 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
  • 203. Frontend. Tratamiento de imágenes AvalancheImagineBundle GD2 Imagine Imagick Gmagick
  • 204. 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');
  • 205. 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');
  • 206. Frontend. Tratamiento de imágenes // Imagine resize $width = 80; $height = 80; $imagine = new ImagineGdImagine(); $imagine = new ImagineImagickImagine(); $imagine->open('image.png')     ->resize(new ImagineBox($width, $height))     ->save('image_resized.png');
  • 207. Frontend. Tratamiento de imágenes http://www.slideshare.net/avalanche123/introductiontoimagine
  • 208. Frontend. Tratamiento de imágenes ¿Cómo usarlo en Symfony para hacer thumbnails de imágenes subidas por los usuarios?
  • 209. Frontend. Tratamiento de imágenes # app/config.yml avalanche_imagine:    filters:        user_small:            type:            options:        user_medium:            type:            options:        user_big:            type:            options: thumbnail { size: [30, 30], mode: outbound } thumbnail { size: [60, 60], mode: outbound } thumbnail { size: [90, 90], mode: outbound }
  • 210. Frontend. Tratamiento de imágenes <img src="{{ user.webPath|apply_filter('user_small') }}" />
  • 211. Demo: AvalancheImagineBundle + Upload Doctrine
  • 213. Frontend. Formatos alternativos # routing.yml api_books_latest:     pattern: /api/latest.{_format}     defaults: { _controller: BooksBundle:Api:latest }
  • 214. Frontend. Formatos alternativos # routing.yml api_books_latest:     pattern: /api/latest.{_format}     defaults: { _controller: BooksBundle:Api:latest, _format: html }
  • 215. Frontend. Formatos alternativos public function latestAction() {     $format = $this->getRequest()->getRequestFormat(); }
  • 216. Frontend. Formatos alternativos • 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.
  • 219. Backend. Formularios namespace BooksBooksBundleForm; use SymfonyComponentFormAbstractType; use SymfonyComponentFormFormBuilderInterface; use SymfonyComponentOptionsResolverOptionsResolverInterface; 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' => 'BooksBooksBundleEntityUser'         ));     }     public function getName()     {         return 'form_user';     } }
  • 220. 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(),     )); }
  • 221. 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>
  • 222. 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>
  • 223. Backend. Formularios namespace BooksBooksBundleForm; use SymfonyComponentFormAbstractType; use SymfonyComponentFormFormBuilderInterface; use SymfonyComponentOptionsResolverOptionsResolverInterface; 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');     }     ... }
  • 226. Backend. Formularios • 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).
  • 227. 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
  • 228. 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
  • 229. Backend. Formularios Generación de CRUDs automáticos Create Read Update Delete
  • 231. Backend. Formularios $ php app/console generate:doctrine:crud
  • 232. Backend. Formularios • 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.
  • 234. Backend. Validación /** * @var string $title * * @AssertNotBlank * @AssertMaxLength(250) * @ORMColumn(name="title", type="string", length=250) */ protected $title;
  • 235. 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(),     )); }
  • 236. 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>
  • 239. Backend. Validación • 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
  • 242. Seguridad. Autenticación VS Autorización Firewall Control de acceso
  • 247. Roles
  • 251. Seguridad. Configuración security:     encoders:         SymfonyComponentSecurityCoreUserUser: 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 }
  • 252. Seguridad. Configuración security:     encoders:         EmpresaBooksBundleEntityUser: sha512       role_hierarchy:         ROLE_ADMIN: ROLE_USER         ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]       providers: SymfonyComponentSecurityCoreUserUserInterface         main: entity: { class: EmpresaBooksBundleEntityUser, property: username }   getRoles()   getPassword()     firewalls: getSalt()         dev: getUsername()             pattern: ^/(_(profiler|wdt)|css|images|js)/ eraseCredentials()             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 }
  • 253. Login
  • 254. Seguridad. Login # app/config/routing.yml login: pattern: /login defaults: { _controller: EmpresaBooksBundle:Security:login } login_check: pattern: logout: pattern: /login_check /logout
  • 255. Seguridad. Login // src/Empresa/BooksBundle/Controller/SecurityController.php; namespace EmpresaBooksBundleController;   use SymfonyBundleFrameworkBundleControllerController; use SymfonyComponentSecurityCoreSecurityContext;   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, ) ); } }
  • 256. 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>
  • 264. 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());         }              } }
  • 265. Backend. Validación • 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
  • 267. 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';     } }
  • 268. Extendiendo Symfony2. Extensiones de Twig # services.yml books.twig.html_extension:     class: EmpresaBooksBundleTwigHtmlExtension     tags:         - { name: twig.extension }
  • 269. Backend. Validación • 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.
  • 271.
  • 272.
  • 273. 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)
  • 276. APC
  • 277. Optimización y rendimiento. APC Byte Code Cache
  • 278. Optimización y rendimiento. APC GET /index.php HTTP/1.1 Lee el archivo index.php y lo introduce en memoria 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”
  • 279. Optimización y rendimiento. APC GET /index.php HTTP/1.1 (con APC) La primera vez se realizan los mismos pasos (lexer + parser) para generar los “opcodes”. Una vez generados se guardan en memoria. Las siguientes veces utiliza los “opcodes” guardados en memoria y los ejecuta directamente
  • 280. Optimización y rendimiento. APC System/User cache
  • 281. Optimización y rendimiento. APC // app.php $loader = require_once __DIR__.'/../app/ bootstrap.php.cache'; $loader = new ApcClassLoader('books', $loader); $loader->register(true);
  • 282. Optimización y rendimiento. APC # app/config/config_prod.yml doctrine:     orm:         metadata_cache_driver: apc         result_cache_driver: apc         query_cache_driver: apc
  • 284. Optimización y rendimiento. Composer $ composer dump-autoload --optimize
  • 285. Extra
  • 287. 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.
  • 288. 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.
  • 289. Extra. Seguridad • Ejercicio. Crear la página /myadmin, a la que solo se podrá acceder si el usuario logueado tiene el rol ROLE_ADMIN.
  • 291. Extra. Doctrine2 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).
  • 292. Extra. Doctrine2 Repositorios • Ejercicio. Crear la página /sales, que mostrará los 5 libros más baratos.
  • 293. 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
  • 295. Extra. 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