Blog

Índice

5 Qué aprenderemos

  • A crear atributos de tipo Date que almacenen la fecha actual
  • A usar un slugger
  • A obtener el usuario registrado en controladores y plantillas
  • A formatear fechas en twig
  • A paginar resultados
  • A obtener parámetros del queryString

5.1 Entidad

En esta parte vamos a centrarnos en el blog. Seremos capaces de:

  • Crear entradas
  • Listar entradas
  • Dar a me gusta
  • Dejar comentarios
  • Buscar entradas

El primer paso va a ser crear la entidad Post

El atributo publishedAt va a almacenar la fecha actual por lo que hemos de modificar el constructor de la entidad Post para que la informe por defecto con la fecha actual.

1
2
3
4
public function __construct()
{
    $this->publishedAt = new \DateTime();
}

Y realizar la migración:

1
2
php bin/console make:migration
php bin/console doctrine:migrations:migrate

5.2 Formulario

El siguiente paso va a ser crear la ruta /blog/new/ para que podamos escribir las entradas de blog. En esta ruta mostraremos el formulario que creamos a continuación:

1
php bin/console make:Form PostFormType Post

Vamos a eliminar algunos atributos porque los vamos a informar a mano:

1
2
3
4
5
6
7
8
9
public function buildForm(FormBuilderInterface $builder, array $options): void
{
    $builder
        ->add('title')
        ->add('content')
        ->add('image')
        ->add('Send', SubmitType::class);
    ;
}

Y creamos el controlador:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
use Symfony\Component\String\Slugger\SluggerInterface;
...

#[Route('/blog/new', name: 'new_post')]
public function newPost(ManagerRegistry $doctrine, Request $request, SluggerInterface $slugger): Response
{
    $post = new Post();
    $form = $this->createForm(PostFormType::class, $post);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        $post = $form->getData();   
        $post->setSlug($slugger->slug($post->getTitle()));
        $post->setPostUser($this->getUser());
        $post->setNumLikes(0);
        $post->setNumComments(0);
        $entityManager = $doctrine->getManager();    
        $entityManager->persist($post);
        $entityManager->flush();
        return $this->render('blog/new_post.html.twig', array(
            'form' => $form->createView()    
        ));
    }
    return $this->render('blog/new_post.html.twig', array(
        'form' => $form->createView()    
    ));
}

La única parte que tiene nueva es el concepto de slug. Por ejemplo, si creo un post con el siguiente título título del post creará un slug así http://127.0.0.1:8080/single_post/titulo-del-post. Es decir lo hace SEO Friendly. Usar el slugger es tan fácil como inyectar la dependencia en el controlador: SluggerInterface $slugger

Según https://www.noticias.ltda/online-marketing/sito-web/slug-seo/

Si creas sitios web y, por ende, practicas el SEO, seguramente has escuchado la palabra slug. Esta palabra define la parte final de la URL que identifica una página dentro de un sitio web. En pocas palabras, debes verla como la parte de la URL que resume el contenido de la página en una o más palabras clave. Debes saber y aprender cómo optimizar el slug porque este juega un papel importante: es leído por los motores de búsqueda y entendido por los usuarios.

Y la plantilla

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% extends 'base.html.twig' %}
{% block title%}New Post{% endblock %}
{% block body%}
<!-- Principal Content Start -->
   <div id="new Post">
   	  <div class="container">
   	    <div class="col-xs-12 col-sm-8 col-sm-push-2">
       	   <h1>New Post</h1>
			   {{ form(form, {'attr': {'class':'form-horizontal'}}) }}
       	   <hr class="divider">
		</div>   
   	  </div>
   </div>
<!-- Principal Content Start -->
{% endblock %}

Comprobamos que podemos almacenar una entrada de Post:

image-20220328085401615

image-20220328085505136

5.3 RETO

Ya solo nos queda:

  • Crear un campo de tipo file para la imagen
  • Añadir las clases css a los campos del formulario
  • Hacer que los campos title, content e image sean obligatorios
  • Comprobar si el usuario ha iniciado sesión para reenviarlo a login

image-20220328091514301

5.4 Ruta single_post

Ahora ya podemos renderizar el contenido de un post. Primero modificamos la ruta single_post para que acepte un parámetro en la ruta. Por ejemplo single_post/mi-primera-entrada-de-blog y que busque en la base de datos un Post con dicho slug

1
2
3
4
5
6
7
8
9
#[Route('/single_post/{slug}', name: 'single_post')]
public function post(ManagerRegistry $doctrine, $slug): Response
{
    $repositorio = $doctrine->getRepository(Post::class);
    $post = $repositorio->findOneBy(["slug"=>$slug]);
    return $this->render('blog/single_post.html.twig', [
        'post' => $post,
    ]);
}

Creamos una plantilla donde usaremos los siguientes datos:

1
2
3
4
5
{{post.title}}
{{asset('images/blog/' ~ post.image) }}
{{post.content}}
{{post.postUser.name}}
{{post.publishedAt | date('d') }} {{ post.publishedAt | date('F') }}

Y ensa plantilla en la que usaremos una instrucción condicional para mostrar un texto si no se ha encontrado la entrada de blog:

1
2
3
4
5
{% if not post %}
<h2>Post not found</h2>
{% else %}
resto de plantilla
{% endif %}

Una vez tenemos la ruta para mostrar un post, vamos a modificar el controlador blog/new para que reenvíe a esta nueva ruta:

1
return $this->redirectToRoute('single_post', ["slug" => $post->getSlug()]);

5.5 Listado de posts

Ahora que ya tenemos la posibilidad de escribir posts, vamos a listarlos en la ruta /blog.

Como siempre, en el controlador realizamos una búsqueda de posts ordenados por fecha descendente (de momento los mostramos todos dejando para un paso posterior la paginación)

En el repositorio

1
2
3
4
5
6
7
8
9
10
11
/**
* Returns an array of Post objects
*/
public function findAll()
{
    return $this->createQueryBuilder('p')
        ->orderBy('p.publishedAt', 'DESC')
        ->getQuery()
        ->getResult()
    ;
}

En el controlador,

1
2
3
4
5
6
7
8
9
10
11
#[Route('/blog/{page}', name: 'blog', requirements: ['page' => '\d+'])]
public function index(ManagerRegistry $doctrine, int $page = 1): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $posts = $repository->findAll($page);

    return $this->render('blog/blog.html.twig', [
        'posts' => $posts,
    ]);
}

Y en la plantilla, creamos un partial para el post, _post.html.twig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="col-xs-12 col-sm-12">
    <div class="post">
    <div class="post-heading">
        <span>{{ post.publishedAt | date('d') }} {{ post.publishedAt | date('F') }}</span>
        <img class="img-responsive" src="{{ asset('images/blog/' ~ post.image) }}" alt="{{post.title}}">
    </div>
    <div class="post-body">
        <h3><a href="{{ path('single_post', {'slug':post.slug}) }}"><strong>{{post.title}}</strong></a></h3>
        <hr>
    <p>{{post.content[:300] ~ '...' }}</p>
    </div>
    <div class="post-footer">
        <a class="btn" href="{{ path('single_post', {'slug':post.slug}) }}">READ MORE...</a>
        <span>
        <i class="fa fa-heart sr-icons"></i> {{post.numLikes}}
        <i class="fa fa-comments sr-icons"></i> {{post.numComments}}
        </span>
    </div>
    </div>
</div>

Y hacemos un recorrido por todos los posts:

1
2
3
{% for post in posts %}
{{ include ('partials/_post.html.twig', {'post': post}) }}
{% endfor %}

image-20220330093111103

5.6 Lista de posts recientes

Vamos a crear la lista con los últimos 5 posts

image-20220328094912201

Modificamos el controlador de la ruta /single_post/{slug} para que obtenga los 5 post más recientes, para lo que hemos de crear un método en el repositorio:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @return Post[] Returns an array of Post objects
*/
public function findRecents()
{
    return $this->createQueryBuilder('p')
        ->orderBy('p.publishedAt', 'DESC')
        ->setMaxResults(5)
        ->getQuery()
        ->getResult()
    ;
}

El controlador:

1
2
3
4
5
6
7
8
9
10
11
#[Route('/single_post/{slug}', name: 'single_post')]
public function post(ManagerRegistry $doctrine, $slug): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $post = $repository->findOneBy(["slug"=>$slug]);
    $recents = $repository->findRecents();
    return $this->render('blog/single_post.html.twig', [
        'post' => $post,
        'recents' => $recents
    ]);
}

Y modificamos la plantilla, creando primero un partial para cada una de las entradas de blog recientes

Partial _recent_post.html.twig:

1
2
3
4
5
6
7
8
9
10
 <div class="new new1">
    <hr>
        <a href="{{ path('single_post', {'slug':recent.slug}) }}">{{ recent.title }}</a>
        <h5> By <span>{{ recent.postUser.name }}</span></h5>
        <p>{{ recent.publishedAt | date('d') }} {{ recent.publishedAt | date('F') }}</p>
        <p><i class="fa fa-clock-o sr-icons"></i>
         {{ recent.publishedAt | date('h') }}
         {{ recent.publishedAt | date('A') }}
         </p>
</div>

Y por último la plantilla single_post.html.twig

1
2
3
{% for recent in recents %}
{{ include ('partials/_recent_post.html.twig', {'recent': recent}) }}
{% endfor %}

6.6.1 RETO

Realiza la lista de posts recientes de la ruta blog

image-20220401093210389

5.7 Comentarios

Vamos a implementar la funcionalidad para enviar comentarios a los mensajes. No implementaremos la opción de poner estrellas.

Crearemos una entidad llamada Comment:

Y hacemos la migración:

1
2
php bin/console make:migration
php bin/console doctrine:migrations:migrate

Un formulario;

1
php bin/console make:Form CommentForm Comment
1
2
3
4
5
6
7
8
9
public function buildForm(FormBuilderInterface $builder, array $options): void
{
    $builder
        ->add('name',  null, ['attr' => ['class'=>'form-control']])
        ->add('email', EmailType::class, ['attr' => ['class'=>'form-control']])
        ->add('text',  null, ['label' => 'Type Your Comment', 'attr' => ['class'=>'form-control']])
        ->add('Send', SubmitType::class, array('label' => 'Send', 'attr' => ['class'=>'pull-right btn btn-lg sr-button']));
    ;
}

Plantilla _form_comment.html.twig:

1
2
3
4
<div id="form" class="col-xs-12 col-sm-6 col-sm-push-3">
{{ form(commentForm, {'attr': {'class':'form-horizontal'}}) }}
</div>
</form>

Modificamos la plantilla single_post para incluir el formulario:

1
 {{ include ('partials/_form_comment.html.twig') }}

Y, por último, el controlador:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#[Route('/single_post/{slug}', name: 'single_post')]
public function post(ManagerRegistry $doctrine, Request $request, $slug): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $post = $repository->findOneBy(["slug"=>$slug]);
    $recents = $repository->findRecents();
    $comment = new Comment();
    $form = $this->createForm(CommentFormType::class, $comment);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        $comment = $form->getData();
        $comment->setPost($post);  
        //Aumentamos en 1 el número de comentarios del post
        $post->setNumComments($post->getNumComments() + 1);
        $entityManager = $doctrine->getManager();    
        $entityManager->persist($comment);
        $entityManager->flush();
        return $this->redirectToRoute('single_post', ["slug" => $post->getSlug()]);
    }
    return $this->render('blog/single_post.html.twig', [
        'post' => $post,
        'recents' => $recents,
        'commentForm' => $form->createView()
    ]);
}

Y en la entidad hacemos que la fecha de publicación se informe automáticamente:

1
2
3
4
public function __construct()
{
    $this->publishedAt = new \DateTime();
}

5.6.1 RETO: Lista de comentarios

Ahora ya podemos crear la lista de comentarios

Igual que hemos hecho con las lista de posts recientes, crearemos un partial y modificamos la plantilla para recorrer los comentarios.

image-20220329184705441

5.8 Paginar los resultados

Esta es una de las partes más laboriosas de cualquier página web: paginar.

Para poder paginar hemos de conocer los siguientes datos:

  1. La consulta a realizar
  2. Cuántos resultados devuelve la misma
  3. En qué pagina estamos
  4. Cuántos elementos caben en cada página

Pero Symfony y Doctrine nos ayudan a resolverlo.

En primer lugar creamos una clase Paginator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace App\Pagination;

use Doctrine\ORM\QueryBuilder as DoctrineQueryBuilder;
use Doctrine\ORM\Tools\Pagination\CountWalker;
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;

/**
 * @author Javier Eguiluz <javier.eguiluz@gmail.com>
 */
class Paginator
{
    /**
     * Use constants to define configuration options that rarely change instead
     * of specifying them under parameters section in config/services.yaml file.
     *
     * See https://symfony.com/doc/current/best_practices.html#use-constants-to-define-options-that-rarely-change
     */
    public const PAGE_SIZE = 2;

    private int $currentPage;
    private \Traversable $results;
    private int $numResults;
    private DoctrineQueryBuilder $queryBuilder;
    private int $pageSize;

    public function __construct(DoctrineQueryBuilder $queryBuilder, int $pageSize = self::PAGE_SIZE) {
        $this->queryBuilder = $queryBuilder;
        $this->pageSize = $pageSize;
    }

    public function paginate(int $page = 1): self
    {
        $this->currentPage = max(1, $page);
        $firstResult = ($this->currentPage - 1) * $this->pageSize;
		// A partir de la consulta que le pasamos en el constructor, Symfony y Doctrine conocen cuál es el primer resultado y cuántos elementos totales existen
        $query = $this->queryBuilder
        ->setFirstResult($firstResult)
        ->setMaxResults($this->pageSize)
        ->getQuery();
        $paginator = new DoctrinePaginator($query, false);

        $this->results = $paginator->getIterator();
        $this->numResults = $paginator->count();

        return $this;
    }

    public function getCurrentPage(): int
    {
        return $this->currentPage;
    }

    public function getLastPage(): int
    {
        return (int) ceil($this->numResults / $this->pageSize);
    }

    public function getPageSize(): int
    {
        return $this->pageSize;
    }

    public function hasPreviousPage(): bool
    {
        return $this->currentPage > 1;
    }

    public function getPreviousPage(): int
    {
        return max(1, $this->currentPage - 1);
    }

    public function hasNextPage(): bool
    {
        return $this->currentPage < $this->getLastPage();
    }

    public function getNextPage(): int
    {
        return min($this->getLastPage(), $this->currentPage + 1);
    }

    public function hasToPaginate(): bool
    {
        return $this->numResults > $this->pageSize;
    }

    public function getNumResults(): int
    {
        return $this->numResults;
    }

    public function getResults(): \Traversable
    {
        return $this->results;
    }
}

Ahora modificamos el método del repositorio para que haga uso del paginador:

1
2
3
4
5
6
7
8
9
10
11
/**
* @return Post[] Returns an array of Post objects
*/
public function findAllPaginated(int $page): Paginator
{
    $qb =  $this->createQueryBuilder('p')
        ->orderBy('p.publishedAt', 'DESC')            
    ;
	//Devolvemos los resutados de la página
    return (new Paginator($qb))->paginate($page);
}

Modificamos el controlador para que haga uso de este nuevo método

1
2
3
4
5
6
7
8
9
10
#[Route('/blog/{page}', name: 'blog')]
public function index(ManagerRegistry $doctrine, int $page = 1): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $posts = $repository->findAllPaginated($page);

    return $this->render('blog/blog.html.twig', [
        'posts' => $posts,
    ]);
}

Y ahora modificamos la plantilla blog.html.twig para recorrer los resultados de la paginación posts.results y, de paso, creamos una plantilla para el contenido de cada post partials/_post.html.twig

1
2
3
{% for post in posts.results %}
{{ include ('partials/_post.html.twig', {'post': post}) }}
{% endfor %}

Y ahora creamos la navegación.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{% if posts.hasToPaginate %}
    <div class="text-left">
        <ul class="pagination">
            {% if posts.hasPreviousPage %}
                <li class="prev"><a href="{{ path('blog', {page: posts.previousPage}) }}" rel="previous"><i class="fa fw fa-long-arrow-left"></i> Previous</a></li>
            {% else %}
                <li class="prev disabled"><span><i class="fa fw fa-arrow-left"></i> Previous </span></li>
            {% endif %}

            {% for i in 1..posts.lastPage %}
                {% if i == posts.currentPage %}
                    <li class="active"><span>{{ i }}</span></li>
                {% else %}
                    <li><a href="{{ path('blog', {page: i}) }}">{{ i }}</a></li>
                {% endif %}
            {% endfor %}

            {% if posts.hasNextPage %}
                <li class="next"><a href="{{ path('blog', {page: posts.nextPage}) }}" rel="next">Next <i class="fa fw fa-arrow-right"></i></a></li>
            {% else %}
                <li class="next disabled"><span>Next <i class="fa fw fa-arrow-right"></i></span></li>
            {% endif %}
        </ul>
    </div>
    {% endif %}

image-20220330121128892

Es un poco lío, pero una vez hecho uno y asimilado, es muy fácil replicar un paginador en otro proyecto.

5.9 Me gusta

En este apartado veremos cómo implementar el botón Like.

Crearemos la ruta /single_post/{slug}/like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#[Route('/single_post/{slug}/like', name: 'post_like')]
public function like(ManagerRegistry $doctrine, $slug): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $post = $repository->findOneBy(["slug"=>$slug]);
    if ($post){
        //Haz un método llamado like() en la entidad Post que aumenente en 1 numLikes
        $post->like();
        $entityManager = $doctrine->getManager();    
        $entityManager->persist($post);
        $entityManager->flush();
    }
    return $this->redirectToRoute('single_post', ["slug" => $post->getSlug()]);

}

Y ahora modificamos la plantilla single_post.html.twig

1
<li><a href="{{ path('post_like', {slug: post.slug}) }}">LIKE</a> <i class="fa fa-heart sr-icons"></i> {{post.numLikes}}</li>

Vamos a mostrar también el número de comentarios en esta ruta:

1
<li><a class="page-scroll" href="#form">COMMENT</a> <i class="fa fa-comments sr-icons"></i> {{post.numComments}} |</li>

image-20220330122429929

5.10 Enlace para añadir posts

En el caso que el usuario esté registrado, hemos de mostrar un enlace a la ruta /blog/new

Modificamos la plantilla _navigation.html.twig

1
2
3
4
{% if app.user %}
  <li><a href="{{ path('app_logout') }}"><i class="fa fa-sign-out sr-icons"></i> {{ app.user.name }} - Logout</a></li>
  <li><a href="{{ path('new_post') }}"><i class="fa fa-plus-square sr-icons"></i> New post</a></li>
{% else %}

image-20220330123223493

5.11 Buscar entradas

Vamos a implementar un buscador.

image-20220401090554057

Necesitaremos:

  • Modificar un poco la plantilla para incluir el formulario de búsqueda
  • Un método en el repositorio que nos devuelva los posts que contengan la cadena buscada
  • Una nueva ruta, con la particularidad de que va a tener un parámetro en el querystring http://127.0.0.1:8080/blog/buscar?searchTerm=entrada
  • Como plantilla usaremos la misma que en blog, pero los enlaces del paginador los hemos de modificar para poner la ruta de la búsqueda:

Plantilla blog.html.twig:

Sustituimos

1
2
3
4
5
6
7
8
<form class="form-horizontal">
    <div class="input-group">
    <input class="form-control" type="text" placeholder="Research">
    <span class="input-group-btn">
    	<a href="" class="btn"><i class="fa fa-search"></i></a>
    </span>
    </div>
</form>

por un formulario con un botón se submit con un input type='text' con el nombre searchTerm

1
2
3
4
5
<form class="" action='{{ path('blog_buscar') }}'>
    <input type="text" class="form-control" style="display:unset; width:85%" placeholder="Research" name='searchTerm'>
    <button type='submit' class="btn btn-primary btn-danger"><i class="fa fa-search"></i>
    </button>
</form>

Repositorio de posts

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @return Post[] Returns an array of Post objects
*/
public function findByTextPaginated(int $page, string $searchTerm)
{
    $qb = $this->createQueryBuilder('p')
        ->andWhere("p.content LIKE :val")
        ->setParameter('val', '%'.$searchTerm.'%')
        ->orderBy('p.publishedAt', 'DESC');

    return (new Paginator($qb))->paginate($page);
}

En este caso estamos usando un parámetro llamado val al que le fijamos el valor '%'.$searchTerm.'%' lo que convertirá la consulta en:

1
SELECT * FROM POSTS WHERE CONTENT LIKE = '%searchTerm%'

Controlador

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * @Route("/blog/buscar/{page}", name="blog_buscar")
 */
public function buscar(ManagerRegistry $doctrine,  Request $request, int $page = 1): Response
{
    $repository = $doctrine->getRepository(Post::class);
    $searchTerm = $request->query->get('searchTerm', '');
    $posts = $repository->findByTextPaginated($page, $searchTerm);
    $recents = $repository->findRecents();
    return $this->render('blog/blog.html.twig', [
        'posts' => $posts,
        'recents' => $recents,
        'searchTerm' => $searchTerm
    ]);
}

Cuando se pulsa el botón buscar, se genera la siguiente petición: http://127.0.0.1:8080/blog/buscar?searchTerm=texto. Fíjate que en la url aparece un queryString:searchTerm=texto. Pues bien, para acceder a dicho parámetro desde un controlador usamos :

1
$searchTerm = $request->query->get('searchTerm', '');

Y en la plantilla blog.html.twig, discriminamos si la ruta es blog o blog_buscar mediante

1
{% if (app.request.attributes.get('_route') == 'blog') %}

Y de esta forma modificamos los enlaces del paginador. Por ejemplo:

image-20221017181221709

Así que el código queda como sigue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{% if (app.request.attributes.get('_route') == 'blog') %}
	{% if posts.hasToPaginate %}
		//Código anterior
	{% endif %}
{% else %}
	{% if posts.hasToPaginate %}
		<div class="text-left">
		  <ul class="pagination">
		      {% if posts.hasPreviousPage %}
		          <li class="prev"><a href="{{ path('blog_buscar', {page: posts.previousPage}) }}?searchTerm={{searchTerm}}" rel="previous"><i class="fa fw fa-long-arrow-left"></i> Previous</a></li>
		      {% else %}
		          <li class="prev disabled"><span><i class="fa fw fa-arrow-left"></i> Previous </span></li>
		      {% endif %}
		      {% for i in 1..posts.lastPage %}
		          {% if i == posts.currentPage %}
		              <li class="active"><span>{{ i }}</span></li>
		          {% else %}
		              <li><a href="{{ path('blog_buscar', {page: i}) }}?searchTerm={{searchTerm}}">{{ i }}</a></li>
		          {% endif %}
		      {% endfor %}
		      {% if posts.hasNextPage %}
		          <li class="next"><a href="{{ path('blog_buscar', {page: posts.nextPage}) }}?searchTerm={{searchTerm}}" rel="next">Next <i class="fa fw fa-arrow-right"></i></a></li>
		      {% else %}
		          <li class="next disabled"><span>Next <i class="fa fw fa-arrow-right"></i></span></li>
		      {% endif %}
		  </ul>
	      </div>
	{% endif %}
{% endif %}