Search code examples
symfonypaginationsymfony-2.3knppaginator

The right way of using a pagination class in Symfony


I'm trying to use this Symfony bundle: https://github.com/KnpLabs/KnpPaginatorBundle

In the docs, they use it a controller. So they have easy access to service container or the request object.

But as far as I understand, the Doctrine query should be in a repository, not a controller, right? And I already do have a function returning records. It's just that the pagination service doesn't expect "results" upon instantiating. It wants the query. So I can't return the "results" to the controller, but rather in middle of this function use a paginator.

On the other hand, stuff like playing with services or requests indeed belong to controllers.

So how this should be done? At first I thought about injecting the "knp_paginator" service and the request object into the repository. But I don't think this is the right way.


Solution

  • I'd say that the Request object should not go further down the stack than from the Controller.

    Nothing prevents you from injecting the paginator directly into your custom repository, so why not doing that?

    your.repository.service.definition:
        class: Your\Repository\Class
    
        # for symfony 2.3
        factory_service: doctrine
        factory_method: getRepository
    
        # for symfony 2.8 and higher
        factory: ["@doctrine.orm.entity_manager", getRepository]
    
        arguments:
          - YourBundle:YourEntity
        calls:
            - [setPaginator, ["@knp_paginator"]]
    

    In the repository, you then should have the paginator available for use with the QueryBuilder:

    public function setPaginator($paginator)
    {
        $this->paginator = $paginator;
    }
    
    ...
    
    $this->paginator->paginate($qb->getQuery(), $page, $limit);
    

    In order to get your $page and $limit variables into the repository, you don't need the Request object. Simply pass them as a parameter to the repository call:

    // In your controller
    // You can use forms here if you want, but for brevity:
    $criteria = $request->get('criteria');
    $page = $request->get('page');
    $limit = $request->get('limit');
    
    $paginatedResults = $myCustomRepository->fetchPaginatedData($criteria, $page, $limit);
    

    Passing the request object further down the Controller means that you have a leak in your abstractions. It's no concern of your application to know about the Request object. Actually, the request might well come from other sources such as the CLI command. You don't want to be creating a Request object from there because of a wrong level of abstraction.