Search code examples
symfonypaginationknppaginatorknppaginatorbundle

Knp Paginator Bundle route error when using slug


I'd like to implement the Knp Pagination for a table.

It is a page where I edit documents, so my route looks like that: @Route("/document/edit/{id}/") and depending on the document, there is a different ID. There are several panels on the page and one of them contains a table of different agencies. For that table I used the knp_paginator:

/**
    * @var $paginator \Knp\Component\Pager\Paginator
    */
    $paginator = $this->get('knp_paginator');
    $agencyTable = $paginator->paginate(
      $docAgencies,
      $request->query->getId('page', 1),
      5
    );

the $docAgencies is defined like that so it's no query, but that's the way I need to get the information!

  $document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
      $docAgencies = $document->getAgencies();

When first accessing the page, everything is fine and page one of the table is being shown, but as soon as I try to click on another page of the table I get an error:

Controller "DocumentBundle\Controller\Document\EditController::editAction()" requires that you provide a value for the "$id" argument (because there is no default value or because there is a non optional argument after this one).

and my route looks like this:

http://localhost/sp/web/app_dev.php/document/document/edit?page=3

so the id of the document was just deleted and there's nothing to be accessed. I tried to add a /{page} to my route like it was recommended in other Stackoverflow questions but this doesn't seem to work for me either. That's how I tried it:

  * @Route("/document/edit/{id}/{pag}", name="documentBundle_edit", requirements={"id" = "\d+", "pag" = "\d+"}, defaults={"id" = 0, "pag":1})

the editAction:

    public function editAction(Request $request, $id, $pag) {
..

and within the paginate function:

  $paginator = $this->get('knp_paginator');
    $agencyTable = $paginator->paginate(
      $docAgencies,
      $pag,
      5
    );

any ideas why this is not working?

update twig view

    {% block content %}

{{ form_start(form) }}

<div class="row">
{% include 'Form/form_errors_banner.html.twig' %}
<div class="col-md-6">
  <div class="panel panel-default">
    <div class="panel-body">
      <legend>{{ 'header.info'|trans }}</legend>
      {% include 'DocumentBundle:Panels:DocumentInfo.html.twig' %}
    </div>
  </div>

  <div class="panel panel-default">
    <div class="panel-body">
      <legend>{{ 'label.comments'|trans }}</legend>
      {% include 'AppBundle::CommentTable.html.twig' with {
      'entity': document,
      'add_comments': true
      } %}
    </div>
  </div>

</div>

<div class="col-md-6">
  <div class="panel panel-default">
    <div class="panel-body">

      <legend>{{ 'label.distribution'|trans }}</legend>


<div id="upload_profile_no">
{% trans_default_domain 'AppBundle' %}

{# CONFIG #}
{% if deleteButton is not defined %}
    {% set deleteButton = true %}
{% endif %}
    {{ form_label(form.agencies) }}<br>
    {{ form_widget(form.agencies) }} <br> <br>
    <table class="footable">
        {# header fields #}
        <thead>
            <tr>
                <th data-field="iata">{{ 'label.iata'|trans }}</th>
                <th data-field="name">{{ 'label.name'|trans }}</th>
                <th data-field="city">{{ 'label.city'|trans }}</th>
                <th data-field="country">{{ 'label.country'|trans }}</th>
                {% if deleteButton %}<th data-field="delete"></th>{% endif %}
            </tr>
        </thead>

      {% block tablebody %}
        {# body fields #}
        <tbody>
            {% for agency in docAgencies %}
                <tr>
                    <td data-title="iata" data-market="{{ agency.market.id }}"><a href="{{ path('appBundle_agencyEdit', {'id': agency.id}) }}">{{ agency.id }}</a></td>
                    <td data-title="name">{{ agency.account }}</td>
                    <td data-title="city">{{ agency.cityName }}</td>
                    <td data-title="country">{{ agency.market.id }}</td>
                    {% if deleteButton %}
                        <td data-title="delete">
                            <button class="btn btn-danger btn-xs btn-remove-row" type="button" data-index="{{ agency.id }}">
                                <span class="glyphicon glyphicon-minus"></span>
                            </button>
                            <button class="btn btn-primary btn-xs btn-undo-remove hidden" type="button" data-index="{{ agency.id }}">
                                <span class="glyphicon glyphicon-repeat"></span>
                            </button>
                        </td>
                    {% endif %}
                </tr>
            {% endfor %}
        </tbody>
    {% endblock %}
    </table>
    {% if docAgencies.pageCount > 1 %}
    <div class="navigation text-center">
        {{ knp_pagination_render(docAgencies) }}
    </div>
    {% endif %}
  </div>

  <br>
  <div class="row">
    <div class="col-md-6">
      {{ form_row(form.airlines, { 'attr': { 'class': 'form-inline'} }) }}
    </div>
    <div class="col-md-6">
      {{ form_row(form.products, { 'attr': { 'class': 'form-inline'} }) }}
    </div>
  </div>

</div>
</div>

<div class="panel panel-default">
  <div class="panel-body">
    <legend>{{ 'label.attachments'|trans }}</legend>
    {% set fileDomain = 'document' %}
    {% include 'AppBundle::AttachmentConfig.html.twig' %}
    {% include 'AppBundle::DisplayAttachments.html.twig' with {'entity': document} %}
    {% include 'AppBundle::FileUpload.html.twig' %}
  </div>
</div>


</div>

</div>


<div class="row">
  {% include 'AppBundle::HelpSubmitButton.html.twig' %}
</div>
  {{ form_end(form) }}
  {% include 'DocumentBundle:Action/UploadProfile:modals.html.twig' %}

{% endblock content %}

the part for the pagination is the table with {% for agency in docAgencies %}

**update 2 ** my full controller:

class EditController extends Controller {


   /**
    * @Route("/document/edit", name="documentBundle_edit2")
    * @Route("/document/edit/{id}/{pag}", name="documentBundle_edit", requirements={"id" = "\d+", "pag" = "\d+"}, defaults={"id" = 0, "pag" = 1})
    * @Template()
    */

    public function editAction(Request $request, $id, $pag) {

      $currentuser = $this->container->get('security.token_storage')->getToken()->getUser();
      $em = $this->getDoctrine()->getManager();
      $document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
      $allComments = $document->getComments();
      $docAgencies = $document->getAgencies();
      $statusRepository = $em->getRepository('DocumentBundle:Status');

      if($this->get('security.authorization_checker')->isGranted('ROLE_DOCUMENT_EDIT') && $document->getStatus()->getName() == 'Effective' && $document->isActive()
      || $this->get('security.authorization_checker')->isGranted('ROLE_CONTRACT_ADMIN')){

        /*
        * GET DOCUMENT FROM DB
        */

        /*
        * CHECK Document
        */
        if($document == null)
        {
          return $this->get('app.error_helper')->throwError('error.404');
        }

        $form = $this->createForm(DocumentEditType::class, $document);

        /*
        * CREATE PAGINATION FOR TABLES
        */
        /**
        * @var $paginator \Knp\Component\Pager\Paginator
        */
        $paginator = $this->get('knp_paginator');
        $agencyTable = $paginator->paginate(
          $docAgencies,
          $pag,
          5
        );
        $agencyTable->setTemplate('DocumentBundle::table_pagination.html.twig');
        $agencyTable->setUsedRoute('documentBundle_edit');
        $agencyTable->setParam('id', $id);
        $commentTable = $paginator->paginate(
          $allComments,
          $pag,
          5
        );
        $commentTable->setTemplate('DocumentBundle::table_pagination.html.twig');

        /*
        * IF REQUEST IS VALID
        */
        $form->handleRequest($request);
        if ($form->isValid()) {

        $effective = date_format($document->getEffective(), 'Y-m-d');
        $expires = date_format($document->getExpires(), 'Y-m-d');
        $now = date('Y-m-d');

        if($now < $effective) {
            $status = $statusRepository->findOneById('3');
        } elseif ($now >= $expires) {
            $status = $statusRepository->findOneById('2');
        } else {
            $status = $statusRepository->findOneById('1');
        }

        $document->setStatus($status);
          $em->flush();
          $this->addFlash(
          'success',
          'The document has been edited!'
          );
          return $this->redirectToRoute('documentBundle_document_list');
        }
        /*
        * DISPLAY Document
        */
        return $this->render('DocumentBundle:Document:edit.html.twig', Array(
          'document' => $document,
          'docAgencies' => $agencyTable,
          'comments' => $commentTable,
          'form' => $form->createView(),
        ));
      } else {
        /*
        * THROW ERROR
        * If the acting user is not allowed to perform this action
        */
        return $this->redirectToRoute('documentBundle_view_document', array(
          'id' => $id
      ));

    }
    }
}

table_pagination.html.twig

<nav>
<ul class="pagination">
  {# Previous #}
  {% if startPage == 1 %}
    <li class="disabled">
      <a href="#" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
  {% else %}
    <li>
      <a href="{{ path(app.request.attributes.get('_route'), {'page': startPage - 1}) }} " aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
  {% endif %}
    {# Pages #}
  {% if pageCount < 12 %}{# less than 12 Pages #}
    {% for i in 1..pageCount %}
        <li {% if startPage == i %}class="active"{% endif %}><a href="{{ path(app.request.attributes.get('_route'), {'page': i}) }} ">{{i}}</a></li>
    {% endfor %}
    {% else %}
      {% if startPage < 8 %}{# any current page before 7 #}
      {# Pages #}
      {% for i in 1..8 %}
          <li {% if startPage == i %}class="active"{% endif %}><a href="{{ path(app.request.attributes.get('_route'), {'page': i}) }} ">{{i}}</a></li>
      {% endfor %}
      <li class="disabled"><a href="#">...</a></li>
      <li><a href="{{ path(app.request.attributes.get('_route'), {'page': pageCount - 1}) }} ">{{pageCount - 1}}</a></li>
      <li><a href="{{ path(app.request.attributes.get('_route'), {'page': pageCount}) }} ">{{pageCount}}</a></li>
      {% else %}
        {% if startPage > pageCount - 7 %}{# any current page in the last 7 #}
          {# Pages #}
          <li><a href="{{ path(app.request.attributes.get('_route'), {'page': 1}) }} ">1</a></li>
          <li><a href="{{ path(app.request.attributes.get('_route'), {'page': 2}) }} ">2</a></li>
          <li class="disabled"><a href="#">...</a></li>
          {% for i in 7..0 %}
              <li {% if startPage == pageCount - i %}class="active"{% endif %}><a href="{{ path(app.request.attributes.get('_route'), {'page': pageCount - i}) }} ">{{pageCount - i}}</a></li>
          {% endfor %}
          {% else %}{# any other Page #}
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': 1}) }} ">1</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': 2}) }} ">2</a></li>
            <li class="disabled"><a href="#">...</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': startPage - 2}) }} ">{{startPage - 2}}</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': startPage - 1}) }} ">{{startPage - 1}}</a></li>
            <li class="active"><a href="{{ path(app.request.attributes.get('_route'), {'page': startPage}) }} ">{{startPage}}</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': startPage + 1}) }} ">{{startPage + 1}}</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': startPage + 2}) }} ">{{startPage + 2}}</a></li>
            <li class="disabled"><a href="#">...</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': pageCount - 1}) }} ">{{pageCount - 1}}</a></li>
            <li><a href="{{ path(app.request.attributes.get('_route'), {'page': pageCount}) }} ">{{pageCount}}</a></li>
          {% endif %}{# any current page in the last 7 #}
        {% endif %}{# any current page before 8 #}
      {% endif %}{# less than 12 Pages #}
  {# Next #}
  {% if startPage == pageCount %}
    <li class="disabled">
      <a href="#" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  {% else %}
    <li>
      <a href="{{ path(app.request.attributes.get('_route'), {'page': startPage + 1}) }} " aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  {% endif %}
</ul>
</nav>

**update including @ste 's help ** Edit Controller:

 * @Route("/document/edit/{id}/{page}", name="documentBundle_edit", requirements={"id" = "\d+", "page" = "\d+"}, defaults={"page" = 1})
        * @Template()
        */

        public function editAction(Request $request, $id, $page) {
$em = $this->getDoctrine()->getManager();
      $document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
      $allComments = $document->getComments();
      $docAgencies = $document->getAgencies();
    /*
        * CREATE PAGINATION FOR TABLES
        */
        /**
        * @var $paginator \Knp\Component\Pager\Paginator
        */
        $paginator = $this->get('knp_paginator');
        $agencyTable = $paginator->paginate(
          $docAgencies,
          $page,
          5
        );
        $agencyTable->setTemplate('DocumentBundle::table_pagination.html.twig');
        $commentTable = $paginator->paginate(
          $allComments,
          $page,
          5
        );
        $commentTable->setTemplate('DocumentBundle::table_pagination.html.twig');
  return $this->render('DocumentBundle:Document:edit.html.twig', Array(
          'document' => $document,
          'docAgencies' => $agencyTable,
          'comments' => $commentTable,
          'form' => $form->createView(),
        ));
}

the pagination template

{# Pagination to be included anywhere. Needs {page, pageCount} #}
<nav>
<ul class="pagination">
  {# Previous #}
  {% if startPage == 1 %}
    <li class="disabled">
      <a href="#" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
  {% else %}
    <li>
      <a href="{{ path(route, query|merge({'page': startPage - 1})) }} " aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
  {% endif %}
    {# Pages #}
  {% if pageCount < 12 %}{# less than 12 Pages #}
    {% for i in 1..pageCount %}
        <li {% if startPage == i %}class="active"{% endif %}><a href="{{ path(route, query|merge({'page': i})) }} ">{{i}}</a></li>
    {% endfor %}
    {% else %}
      {% if startPage < 8 %}{# any current page before 7 #}
      {# Pages #}
      {% for i in 1..8 %}
          <li {% if startPage == i %}class="active"{% endif %}><a href="{{ path(route, query|merge({'page': i})) }}">{{i}}</a></li>
      {% endfor %}
      <li class="disabled"><a href="#">...</a></li>
      <li><a href="{{ path(route, query|merge({'page': pageCount - 1})) }}">{{pageCount - 1}}</a></li>
      <li><a href="{{ path(route, query|merge({'page': pageCount})) }}">{{pageCount}}</a></li>
      {% else %}
        {% if startPage > pageCount - 7 %}{# any current page in the last 7 #}
          {# Pages #}
          <li><a href="{{ path(route, query|merge({'page': 1})) }} ">1</a></li>
          <li><a href="{{ path(route, query|merge({'page': 2})) }} ">2</a></li>
          <li class="disabled"><a href="#">...</a></li>
          {% for i in 7..0 %}
              <li {% if startPage == pageCount - i %}class="active"{% endif %}><a href="{{ path(route, query|merge({'page': pageCount - i})) }} ">{{pageCount - i}}</a></li>
          {% endfor %}
          {% else %}{# any other Page #}
            <li><a href="{{ path(route, query|merge({'page': 1})) }}">1</a></li>
            <li><a href="{{ path(route, query|merge({'page': 2})) }}">2</a></li>
            <li class="disabled"><a href="#">...</a></li>
            <li><a href="{{ path(route, query|merge({'page': startPage - 2})) }} ">{{startPage - 2}}</a></li>
            <li><a href="{{ path(route, query|merge({'page': startPage - 1})) }} ">{{startPage - 1}}</a></li>
            <li class="active"><a href="{{ path(route, query|merge({'page': startPage})) }} ">{{startPage}}</a></li>
            <li><a href="{{ path(route, query|merge({'page': startPage + 1})) }}">{{startPage + 1}}</a></li>
            <li><a href="{{ path(route, query|merge({'page': startPage + 2})) }} ">{{startPage + 2}}</a></li>
            <li class="disabled"><a href="#">...</a></li>
            <li><a href="{{ path(route, query|merge({'page': pageCount - 1})) }} ">{{pageCount - 1}}</a></li>
            <li><a href="{{ path(route, query|merge({'page': pageCount})) }} ">{{pageCount}}</a></li>
          {% endif %}{# any current page in the last 7 #}
        {% endif %}{# any current page before 8 #}
      {% endif %}{# less than 12 Pages #}
  {# Next #}
  {% if startPage == pageCount %}
    <li class="disabled">
      <a href="#" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  {% else %}
    <li>
      <a href="{{ path(route, query|merge({'page': startPage + 1})) }} " aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  {% endif %}
</ul>
</nav>

and this is how I add the pagination to my template:

{{ knp_pagination_render(docAgencies) }}

Solution

  • It seems that you have a problem in table_pagination.html.twig.

    You should replace this:

    {{ path(app.request.attributes.get('_route'), {'page': THE_PAGE_YOU_HAVE}) }}
    

    with this:

    {{ path(route, query|merge({'pag': THE_PAGE_YOU_HAVE})) }}
    

    or this

    {{ path(app.request.attributes.get('_route'), app.request.query.all|merge({'pag': THE_PAGE_YOU_HAVE})) }}
    

    Let me know if it works.

    Further, you should remove the default value for id in your route for editAction