esteemed.squire
esteemed.squire

Reputation: 524

Symfony2 - How to pass in pagination code as a service?

I'm new to Symfony2 and would a step by step guidance how to pass in my pagination code as a service so I don't have to repeat the code and practice good coding habits.

Here is the original pagination code in the controller that I'd like to pass into a service.

I understand I need to setup my services: (beyond this I need help on how to call the variables in my pagination.php service class into the controller to pass into twig)

config.yml

services:

    pagination:
        class: Demo\ProjectBundle\Services\Pagination
        arguments: ["$request_stack", @doctrine]
        scope: request

Pagination class (service)

class Pagination
{
protected $request;

public function __construct(RequestStack $requestStack, Registry $doctrine)
{
    $this->request = $requestStack->getCurrentRequest();
    $this->doctrine = $doctrine;
}

public function pagination()
{
    //$page = $request->get('page');
    $page = $this->request->query->get('q');

    $count_per_page = 5;
    $total_count = $this->getTotalBlogs();
    $total_pages = ceil($total_count/$count_per_page);

    if (!is_numeric($page)) {
        $page = 1;
    } else {
        $page = floor($page);
    }

    if ($total_count <= $count_per_page) {
        $page = 1;
    }

    if (($page * $count_per_page) > $total_count) {
        $page = $total_pages;
    }

    $offset = 0;

    if ($page > 1) {
        $offset = $count_per_page * ($page - 1);
    }

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

    $blogQuery = $em->createQueryBuilder()
        ->select('b')
        ->from('DemoProjectBundle:Blog', 'b')
        ->addOrderBy('b.created', 'DESC')
        ->setFirstResult($offset)
        ->setMaxResults($count_per_page);

    $blogFinalQuery = $blogQuery->getQuery();

    $blogPage = $blogFinalQuery->getArrayResult();

    foreach ($blogPage as $blog) {
        $blog_id = $blog['id'];
        $commentRepository = $this->getDoctrine()
            ->getRepository('DemoProjectBundle:Comment');

        $comments[] = $commentRepository->findByBlog($blog_id);
    }

    return array(
        'blogPage'     => $blogPage,
        'total_pages'  => $total_pages,
        'current_page' => $page,
        'comments'     => $comments,
    );
}
}

PageController (with pagination code I want to put into a service)

class PageController extends Controller
{
public function indexAction(Request $request)
{
    // Pagination code
    $page = $request->get('page');

    $count_per_page = 5;
    $total_count = $this->getTotalBlogs();
    $total_pages = ceil($total_count/$count_per_page);

    if (!is_numeric($page)) {
        $page = 1;
    } else {
        $page = floor($page);
    }

    if ($total_count <= $count_per_page) {
        $page = 1;
    }

    if (($page * $count_per_page) > $total_count) {
        $page = $total_pages;
    }

    $offset = 0;

    if ($page > 1) {
        $offset = $count_per_page * ($page - 1);
    }

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

    $blogQuery = $em->createQueryBuilder()
        ->select('b')
        ->from('DemoProjectBundle:Blog', 'b')
        ->addOrderBy('b.created', 'DESC')
        ->setFirstResult($offset)
        ->setMaxResults($count_per_page);

    $blogFinalQuery = $blogQuery->getQuery();

    $blogPage = $blogFinalQuery->getArrayResult();

    foreach ($blogPage as $blog) {
        $blog_id = $blog['id'];
        $commentRepository = $this->getDoctrine()
            ->getRepository('DemoProjectBundle:Comment');

        $comments[] = $commentRepository->findByBlog($blog_id);
    }

    return $this->render('DemoProjectBundle:Default:index.html.twig', array(
        'blogPage'     => $blogPage,
        'total_pages'  => $total_pages,
        'current_page' => $page,
        'comments'     => $comments,
    ));
}

index.html.twig

{% block body %}
{% for blog in blogPage %}
    <div class="container">
        <div class="row">
            <div class="col-sm-8 blog-main">
                <div class="blog-post">

                    <h2 class="blog-post-title">{{ blog.title }}</h2>
                        <p class="blog-post-meta"><time datetime="{{ blog.created|date('c') }}">{{ blog.created|date('l, F j, Y') }}</time> by <a href="#">{{ blog.author }}</a></p>

                    <div class="comment">
                        <p><small>Comments: {{ comments[loop.index0]|length }}</small>   </p>
                    </div>
                        <p>{{ blog.blog|truncate(350, true) }}</p><br>

                    <div class="tags">
                        <p><strong><small>Tags: </small></strong><span class="highlight">{{ blog.tags }}</span></p>
                    </div>
                        <p class="continue"><a href="{{ path('demo_project_show', { 'id': blog.id, 'slug': blog.slug }) }}">More reading&raquo </a></p>
                    <hr>

                </div><!-- /.blog-post -->
            </div>
        </div>
    </div>
{% endfor %}
{% endblock %}


{% block pagination %}
<ul class="pager">
    {% if total_pages > 0 %}
        <div class="pagination">
            <ul>
                {% for i in 1..total_pages %}
                    {% if loop.first %}
                        <li class="{% if current_page == 1 %} {% endif %}"><a href="{{ path('demo_project_homepage', { 'page':current_page - 1 }) }}">Previous</a></li>
                    {%endif%}

                    {% if loop.last %}
                        <li class="{% if current_page == total_pages %} {% endif %}"><a href="{{ path('demo_project_homepage', { 'page':current_page + 1 }) }}">Next</a></li>
                    {% endif %}
                {% endfor %}
            </ul>
        </div>
    {% endif %}
</ul>
{% endblock %}

Upvotes: 1

Views: 1019

Answers (1)

Alberto Fern&#225;ndez
Alberto Fern&#225;ndez

Reputation: 1608

When you define the service, it's available in the service container, which you can access from the controller.

Service definition:

services:
    pagination:
        class: Demo\ProjectBundle\Services\Pagination
        arguments: [@request_stack] # use @ to refer another services
        scope: request

In your controller:

class PageController extends Controller
{
    public function indexAction(Request $request)
    {
        $pagination = $this->container->get("pagination");
        ...
    }
}

Then it's just a matter of putting your pagination code in the Pagination class. This class structure depends on your design, but normally you have one or more public methods to pass data from the controller for the service to do it's logic and then returning the expected response.

EDIT

To get data from your service to the controller, return them in a way you can use them later in your templates or wherever you need it. One way of doing this is using arrays:

Returning an array

class Pagination
{
    public function getPaginationData()
    {
        return array('blogPage' => $blogPage, total_pages' => $total_pages, 'current_page' => $page, 'comments' => $comments);
    }
}

In your controller

$pagination = $paginationService->getPaginationData();

// return to the Twig template
return ['pagination_data' => $pagination];

In your twig template you can access the array as normal:

{{ pagination_data.blogPage }}
{{ pagination_data.total_pages }}

And so on so forth. You can replace the array for a class if you need more internal logic, even passing the full Pagination service to the Twig template. At the end it all depends on your application design.

Upvotes: 3

Related Questions