Está en la página 1de 12

Crear bundle

php app/console generate:bundle

Los nombre del bundle deben tener el sufijo bundle, y en camel case

el namespace : un nombre de proveedor (el que quieras) / nombre del bundle

Do you want to generate the whole directory structure [no]? Yes

php app/console generate:bundle --namespace=Netelip/UserBundle –format=yml

Route

comando para ver como estan gestionadas las rutas


php app/console debug:router

comando para ver el detalle de una ruta en concreto


php app/console debug:router netelip_user_index

Base de Datos y Doctrine

crear entidad (Objeto que representa cada una de las tablas de nuestra bd)

php app/console doctrine:generate:entity

Nos pedirá el nombre del bundle para el que vamos a crear una entidad, seguido de dos puntos y el
nombre que le vamos a dar a la entidad

NetelipUserBundle:User

Despues pregunta el formato de la configuracion, lo dejamos en anotaciones

User sería la tabla User (por decirlo de alguna manera)

Despues vamos creando los campos pertencientes a esa tabla (Objeto)

*is_active (por ejemplo) nos pedirá si queremos que sea un boolean


*created_at (por ejemplo) nos pedirá si queremos que sea un datetime
* esto se debe a is en el boolean y a at en el datetime

Para dejar de crear campos le damos a enter

preguntara si queremos crear un repositorio vacio: yes

Esto nos creara un directorio Entity dentro de nuestro bundle seleccionado, con dos archivos
Uno sera el que hemos especificado a la hora de crear nuestro objeto / entidad y otro con el mismo
nombre pero con la terminacion Repository

En el archivo User.php estarán los getters y setters con los que accederemos a los datos.

*Para comunicar esta entidad con la base de datos, que aún está vacia, en la clase de la entidad
User.php, en la primera anotación (la de la clase), donde pone @ORM \ Table() , dentro de los
parentesis hay que especificar la tabla que será su semejante en la base de datos.
Ejm: @ORM\Table(name="users").

Si queremos que un campo editado como string sea un ENUM se hará tambien desde las
anotaciones de esa variable, por ejemplo:
@ORM\Column(name="role", type="string", columnDefinition="ENUM('ROLE_ADMIN',
'ROLE_USER')", length=50)

Siguiente paso: crear la tabla en la bd, lo hacemos desde comandos:

php app/console doctrine:schema:update - -force

Con este comando se creará en nuestra bd, automáticamente, la tabla users con todos los datos que
hemos proporcionado a la entidad.

Doctrine

Conectar con los servicios y todo lo que provee doctrine.


$em = $this→getDoctrine()→getManager();

Para realizar la consulta a un determinado objeto está el método getRepository(), dentro se


especifica el bundle y la entidad a la que nos vamos a referir (User).
FindAll() trae todos los registros

$users = $em→getRepository('NetelipUserBundle:User')→findAll();

GENERAR TABLA APARTIR DE UNA BASE DE DATOS Y TABLA YA CREADAS

En la consola:

*previamente tendremos la tabla VpsUsers creada en la BD, con sus campos y sus relaciones si las
hubiera.

php app/console doctrine:mapping:import NetelipVpsBundle yml --filter=VpsUsers --em=default

-donde NetelipVpsBundle es el nombre del bundle donde vamos a crear la entidad y VpsUsers el
nombre de la tabla. em es el nombre de la base de datos
Pon tu repositorio en el siguiente archivo:
app/config/config.yml

orm:
auto_generate_proxy_classes: %kernel.debug%
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
NetelipVpsBundle: ~

---------------------------------------------------------------------------------------------------------

php app/console doctrine:generate:entities NetelipVpsBundle:VpsUsers --path=src

Vistas / Twig

Con esto hacia donde estamos apuntando nuestra vista; el nombre del bundle, nuestras vistas se van
a encontrar dentro del directorio views, y en el crearemos diferentes directorios y archivos, en este
caso creamos una carpeta User y un archivo llamado index.html.twig (index , seria el mismo
nombre de la accion / metodo).
Como segundo parametro enviaremos lo que queramos que utilice la vista en concreto que
renderizamos, en este caso enviamos un array.

return $this→render('NetelipUserBundle:User:index.html.twig', array(‘users’ => $users);

Esto será lo que devuelva nuestra acción / metodo

A la hora de editar la el twig o plantilla:


- {{ …valor... }} → con esto imprimimos una variable o un resultado (echo)
- {% .. valor … %} → para controlar la logica de la plantilla y trabaajar por ejemplo con bucles for,
por ejemplo:

{% for item in navigation %}


<li><a href=”{{ item.href }}”>{{item.caption }} </a></li>
{% endfor %}

- {# ….comentarios … #}

Se debe crear una plantilla global html.twig global de todo el proyecto, en app/resources/views.
Esta plantilla sera la que contenga todo el html global y se hará referencia al resto de plantillas por
bloques
{% block stylesheets %}{% endblock %}
{% block body%}{% endblock %}
{% block javascripts %}{% endblock %}

Como llamar a esta plantilla global desde las plantillas más concretas:

{% extends layout.html.twig %}

y lo que tengamos en esa plantilla lo encerramos dentro de un {% block body %} {% endblock %}

ese nombre body, hará referencia en la plantilla base (este caso es layout.html.twig), al bloque del
mismo nombre, y ahi se encerrará todo la plantilla más pequeña.

Linkear los archivos css (o tambien javascript):

{% block stylesheets %}
<link rel="stylesheet" href="{{ asset ('public/css/style.css')}}">
{% endblock %}

donde public / css es una carpeta que hemos creado en el directorio web, que es donde deben ir
todos los archivos públicos, como css o javascript.

Si queremos incluir dentro de la plantilla común otro bloque común a todas las plantillas, como por
ejemplo en este caso, un menú, crearemos otro twig en app/resources/views.
En este caso al ser un menu (común a todo el proyecto) , en la plantilla general layout.html.twig,
dentro del block body, deberemos incluir la plantilla menú de la siguiente manera:

{{ include(‘menu.html.twig’) }}

Si estamos utilizando nuestro bloque en el layout general para dos plantillas, nos iremos a la
plantilla de nuestro bundle que teniamos ya metida y le deberemos indicar la siguiente sentencia:

{% block body %}
{{ parent() }}
…………
para que en el bloque se inserten ambas plantillas

Formularios

Crear una clase independiente para crear nuestro formulario, donde vamos a definir los campos que
va a tener el form:

php app/console doctrine:generate:form NetelipUserBundle:User

Esto creará un nuevo directorio en nuestro bundle y dentro un nuevo archivo con la terminación
type

En esta clase se crearán automáticamente los campos de nuestro formulario de acuerdo con nuestra
entidad User.
(Ver documentación oficial sobre los campos del formulario en la pag de Symfony/built-in fields
types)

El ultimo método de la clase type será getName(), que retornará el nombre del formulario (como le
quieras llamar).

Desde el controlador deberemos importar la clase type, por ejemplo:

use Netelip\UserBundle\Form\UserType;

Crear el form:

por ejemplo

public function addAction(){

$user = new User();


$form = $this->createCreateForm($user);

return $this->render('NetelipUserBundle:User:add.html.twig', ['form' => $form-


>createView()]);
}

private function createCreateForm(User $entity){

$form = $this->createForm(new UserType(), $entity, [


'action' => $this->generateUrl('netelip_user_create'),
'method' => POST
]);

return $form;
}

en el archivo routing.yml debemos definir la ruta donde se creará el formulario (señalada más arriba
en negrita)

routing.yml

netelip_user_create:
path: /user/create
defaults: { _controller: NetelipUserBundle:User:create }
methods: POST

Debemos renderizar la vista de un nuestro formulario, creamos un twig para el formulario, dentro
de nuestro bundle por supuesto.

Los componentes que necesitamos para renderizar un form lo encontramos en:


http://symfony.com/doc/current/forms.html (Rendering the form)

sobre todo los mas importantes: form_start(form), form_widget(form), form_end(form).


En vez de utilizar la sentencia form_widget sería mejor esto: (ver aquí
http://symfony.com/doc/current/form/form_themes.html#form-template-blocks)

podria quedar asi:

{{ form_start(form, { 'attr' : { 'role' : 'form' } }) }}

<div class="form-group">
{{ form_label(form.username) }}
{{ form_widget(form.username, { 'attr' : {'class' : 'form-control', 'placeholder' : 'Your
username'} }) }}
<span class="text-danger">{{ form_errors(form.username) }}</span>
</div>

{{ form_end(form) }}

Luego, en el controlador, debemos importar el objeto request, para ir recibiendo la petición de


nuestro formulario para ir guardandolo a partir de nuestra entidad en la bd.
Luego creamos la Action createAction, anteriormente definida en routing.yml, que recibirá el objeto
Request
public function createAction(Request $request){

*(para todo esto ver los archivos del proyecto demo llamado users)

El concepto Lifecycle Callbacks

Estos van a ser metodos que van a realizar una acción inmediata antes o despues de insertar una
entidad desencadenando una serie de eventos.
Por ejemplo, en el ejemplo de este proyecto, en la entidad users teniamos dos campos datetime,
create_at, y update_at. En el formulario no aparecen porque esos campos se deben ingresar en la bd
automáticamente con la hora actual. Para eso hemos de realizar lo siguiente:

en la entidad en cuestión, en las anotaciones de la clase debemos ingresar esta sentencia:


@ORM\HasLifecycleCallbacks()

quedando así:

/**
* User
*
* @ORM\Table(name="users")
* @ORM\Entity(repositoryClass="Netelip\UserBundle\Entity\UserRepository")
* @ORM\HasLifecycleCallbacks()
*/
class User
{

Despues debemos añadir los eventos necesarios (podemos ver todos los eventos en la doc oficial de
Doctrine/ Lifecycle Callbacks)
Para crear automáticamente la hora actual y actualizar, en este caso en este proyecto, creamos en
nuestra entidad dos metodos, con las siguientes anotaciones y eventos:

/**
* @ORM\PrePersist
*/

public function setCreateAtValue()


{
$this->createAt = new \DateTime();
}

/**
* @ORM\PrePersist
* @ORM\PreUpdate
*/

public function setUpdateAtValue()


{
$this->update = new \DateTime();
}

Encriptación de un password para guardarlo en la bd

Nos dirigimos a app/security.yml y añadimos lo siguiente:


encoders:
Netelip\UserBundle\Entity\User:
algorithm: bcrypt
cost: 12

En nuestra entidad importar la clase UserInterface:


use Symfony\Component\Security\Core\User\UserInterface;
e implementar en la clase:
class User implements UserInterface

Después debemos definir en la entidad 5 metodos concretos vacios


dos de ellos son getPassword y getUsername que en nuestro caso ya los teniamos definidos, con lo
cual sólo estos tres:

public function getRoles(){}

public function getSalt(){}

public function eraseCredentials(){}

para codificar nuestro password nos vamos a nuestro controlador, al metodo createAction() y
añadimos: (lineas negritas)

public function createAction(Request $request){


$user = new User();
$form = $this->createCreateForm($user);
$form->handleRequest($request);

if ($form->isValid()){

$password = $form->get('password')->getData();
$encoder = $this->container->get('security.password_encoder');
$encoded = $encoder->encodePassword($user, $password);
$user->setPassword($encoded);

$em = $this->getDoctrine()->getManager();

Validaciones / Constraints

documentación constrains (http://symfony.com/doc/current/reference/constraints.html)

Para desactivar las validaciones del lado del cliente vamos al twig donde se encuentra el form y
dentro del la sentencia del form_start agregar el atributo ‘novalidate’:

{{ form_start(form, { 'attr' : { 'novalidate' : 'novalidate', 'role' : 'form' } }) }}

Agregamos el componente de validación y los constraints de symfony en nuestra entidad a validar,


mas un alias para poder llamar a los métodos de validación de este componente:

use Symfony\Component\Validator\Constraints as Assert;

Por ejemplo, para no permitir campos vacios → NotBlank

Añadimos en las anotaciones de cada campo en nuestra entidad la siguiente anotación


@Assert\NotBlank()

Para el email → @Assert\NotBlank() @Assert\Email()

Para un input select → @Assert\Choice(choices = {"ROLE_ADMIN", "ROLE_USER"})

Validar que haya campos únicos

importar la siguiente Constraints:

use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

En la cabecera de la clase de nuestra entidad (anotaciones) llamamos al constraints uniqueEntity

Por ejemplo, que no haya un username e email repetidos:

* @UniqueEntity("username")
* @UniqueEntity("email")
Redirecciones

En nuestro proyecto tenemos una ruta que es /user/add, en la cual creamos un usuario, pero tambien
tenemos en el controlador, aparte del método addAction un createAction que cuando le damos al
botón enviar formulario de redirige a esa función. Si la cambiamos la ruta desde el navegador en
vez de add ponemos create, nos da un error puesto que no se ha procesado ninguna información,
hemos de crear una redirección, para cuando se escriba create en el navegador nos redireccione por
ejemplo a /user/add.

Para esto debemos crear otra ruta en nuestro routing.yml, (el yml de nuestro bundle)

netelip_user_redirect_add:
path: /user/create
defaults:
_controller: NetelipUserBundle:User:add
path: /user/add
permanent: true

donde el primer path es la ruta a la que accedemos accidentalmente, y en defaults especificamos


donde ha de redirigirse.

Internacionalización (i18n)

documentación translations (http://symfony.com/doc/current/reference/constraints.html)

En el archivo app/config.php, cambiamos las siguientes lineas:

#translator: { fallbacks: ['%locale%'] } (comentada)


translator: { fallbacks: [en] }

Con esto se configuramos regionalmente nuestro proyecto: ->


#default_locale: '%locale%' (comentada)
default_locale: es

Con esto ya tenemos habilitado el servicio de traducción de symfony

Vamos a hacer la traducción dentro de las plantillas. Para esto twig utiliza una etiqueta especial
llamada trans.

Nos dirigimos a nuestra plantilla / twig y envolvemos en esta etiqueta los textos que se quieran
traducir:

ejp: <h2>{% trans %}New User{% endtrans %}</h2>

Buscamos el archivo de traducción, el cual se encuentra en nuestro bundle/resources/translations,


symfony crea uno por defecto, el cual podemos copiar y crear nuestro archivo que llevará las
traducciones. Es un formato xml, pero con extensión xlf

messages.es.xlf → este sería el archivo, “es” por la configuración regional que hemos especificado
en el archivo app/config.php.
Para traducir el nodeText del h2 anterior, nuestro archivo xml quedaría así:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1"> (El id debe ser único para cada frase o bloque que queramos traducir)
<source>New User</source>
<target>Crear Usuario</target>
</trans-unit>
</body>
</file>
</xliff>

Esto aún no funcionará, puesto que hemos tocado el archivo app/config.php, que es un archivo
importante de symfony, ahora debemos limpiar la cache de symfony, lo hacemos por consola:

php app/console cache:clear

Con esto ya se realiza la traducción. Para los campos de un formulario, si se han pintado utilizando
el objeto form, {{ form_label(form.email) }}, no es necesario utilizar la etiqueta
{% trans %}

Para personalizar mensajes en la validación de formularios y traducirlos

Por ejemplo, el username; @Assert\NorBlank(message=”user.username.not_blank”)


Luego, debemos crear un nuevo archivo xml de traducción en nuestro bundle con el nombre
validators.es.xlf

<trans-unit id="30">
<source>user.username.not_blank</source>
<target>El nombre de usuario debe estar rellenado</target>
</trans-unit>

Tambien se puede traducir por filtros en vez de {% trans %} {% endtrans %}:


<th>{{ 'Username' | trans }}</th>

Enviar Mensajes al Usuario

documentación (http://symfony.com/doc/current/controller.html#flash-messages)

Por ejemplo, si se crea un usuario se debe enviar un mensaje informando que el usuario se ha
creado correctamente.

Una vez se haga $em→ flush(); y con esto se guarden los datos del usuario creado:
$this->addFlash('userCreate', 'The user has been create');

Pero si estamos con la internacionalización haremos lo siguiente:

$successMessage = $this->get('translator')->trans('The user has been create');


$this->addFlash('userCreate', $successMessage);
Donde el primer parámetro es la key o nombre del mensaje y el segundo el mensaje en sí.

Luego procedemos a ir a la vista donde queremos que nos muestre el mensaje.

{% block body %}
{{ parent() }}

{% for flashMessaje in app.session.flashbag.get('userCreate')%}


<div class="alert alert-success" role="alert">
<div class="container">{{ flashMessaje }}</div>
</div>
{% endfor %}

Paginación

Para la paginación se utilizará un bundle de terceros.


https://github.com/KnpLabs/KnpPaginatorBundle

Cambiar de propietario a la carpeta app/cache . Usuario:www-data

Con este comando en la raiz del proyecto Composer se encarga de instalar el bundle

añadimos nuestro nuevo bundle en el archivo AppKernel que está en app →


new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(), en $bundles (array)

Pegamos lo siguiente en el app/config/config.yml

# KnpPaginationBundle
knp_paginator:
page_range: 5 # default page range used in pagination control
default_options:
page_name: page # page query parameter name
sort_field_name: sort # sort field query parameter name
sort_direction_name: direction # sort direction query parameter name
distinct: true # ensure distinct results, useful when ORM queries are using
GROUP BY statements
template:
pagination:
'KnpPaginatorBundle:Pagination:twitter_bootstrap_v3_pagination.html.twig' # sliding
pagination controls template
sortable: 'KnpPaginatorBundle:Pagination:sortable_link.html.twig' # sort link
template

En la paginación estamos utilizando un tipo de plantilla definida para bootstrap, que es nuestro
caso.
Todo esto se encuentra en el repo de github antes detallado.

En el index action, que es donde cogiamos con doctrine los usuarios, ahora vamos a utilizar dql,
que es una manera que tiene doctrine para hacer consultas tambien.

En el indexAction necesitaremos el objeto Request, así que lo recibiremos como parámetro.


CREAR ENLACE SIMBOLICO DE LAS CARPETAS PUBLIC DE CADA BUNDLE

php app/console assets:install --symlink

Importante para que funcionen correctamente los archivos js y css que estén en un bundle en
concreto.

BUSCADOR ELASTICSEARCH

Cuando deja de functionar el servicio:

sudo sed -i 's/#START_DAEMON/START_DAEMON/' /etc/default/elasticsearch

sudo systemctl restart elasticsearch

systemctl status elasticsearch

También podría gustarte