This Chapter isn't quite ready yet
Rest assured, the gnomes are hard at work on completing this video
On production - because The SpaceBar is going to be a huge hit with a lot of insightful comments - this list will eventually become, way long. Not only will it be hard to use, it'll become a performance issue. If you ever need to query and render for more than 100 Doctrine entities, you're going to have slow loading times. If you try to print 1000, your page just won't load.
But no problem! The Internet has a tried-and-true solution for this: pagination. Doctrine itself doesn't come with any pagination features. But, it doesn't need to: there are a few great libraries for this.
Search for KnpPaginatorBundle. As usual, my disclaimer is that I did nothing
to help build this bundle, I just think it's great. Find the
line, copy that, find your terminal and paste:
composer require knplabs/knp-paginator-bundle
While that's installing, go back to its documentation. As I love to tell you, over and over again, the main reason to install a bundle is because that bundle will give you new services. And that's 100% true for this bundle.
Before, we talk more about that, notice that this has some details about enabling your bundle. That happens automatically in Symfony 4 thanks to Flex.
Anyways, look down at the Usage example. Hmm, from a controller, it says to use
$this->get('knp_paginator') to get some paginator service. Then, you pass that
a query, the current page number, read from a
?page= query parameter, and the
number of items you want per page. Then the paginator handles the rest! If you
want 10 results per page and you're on page 3, the paginator will fetch the
exact results you need by adding a LIMIT and OFFSET to your query.
The one tricky thing is that the documentation is a little bit out of date. The
$this->get() method - which is the same as saying
$this->container->get() - is
the historic way to fetch a service out of the container by using its id. Depending
on your setup, that may or may not even be possible in Symfony 4. And, in general,
it's no longer considered a best-practice to do this. Instead, you should use
dependency injection, which almost always means, autowiring.
But, hmm, it doesn't say anything about autowiring here. That's a problem: the bundle needs to tell us what class or interface we can use to autowire the paginator service. No problem! Let's figure it out on our own.
Go back to your terminal Excellent! The install finished. Now run:
php bin/console debug:autowiring
Search for pager. Boom! Apparently there is a
PaginatorInterface we can use to
get that exact service. We are in business!
CommentAdminController, add that as the 3rd argument:
Make sure to use auto-complete to get the
use statement. Call the arg
Next, go back to the docs and copy the
$pagination = section, go back, and
Ok, so what should we use for
$query? So, when you use a paginator, an important
thing happens: we are no longer responsible for actually making the query. Nope,
we're now responsible for building a query and then passing it to the paginator.
$query variable should be a QueryBuilder.
Ok! Back in
CommentRepository, let's refactor this method to return that instead.
@returns and, instead, use a
QueryBuilder return type. Next, at the
bottom, remove the
getResults() lines. Finally, rename the method
Perfect! Back in the controller, add
$queryBuilder = $repository->getWithSearchQueryBuilder($q). Pass this variable
Finally, instead of passing
comments into the template, pass this
variable instead. Open
index.html.twig so we can make changes there. First, at
the top, let's print the total number of comment because, we may only show 10
results on the page, but there are actually 100 in the table. To do that, go back
to the docs. Ah, this is perfect. Use:
Then, down in the loop, change this to
for comment in pagination.
pagination is an object. But, you can loop over it to get the comments
for the current page only.
Oh, and at the bottom, we need some navigation to help the user go to the other
pages. That's really easy: on the docs, copy the
Phew! Let's go check it out! Yes! 100 total results, but only 10 on this page. We
can click to page 2, then 3 and so-on. Heck, the search even works! Try something
really common, like
est. The URL has the
?q= query parameter. And, if you change
pages, it stays: this is page 2 of that search. Dang, that's awesome.
Of course, there's one super minor problem... um... dang, that navigation looks
horrible. But, that's easy to fix. The bundle comes with a bunch of different
themes for the navigation. Scroll back up to the configuration example. Obviously,
you don't need to configure anything on this bundle. But, there are a lot of
options. The most important one is this:
template.pagination. This determines
which template is used to build the navigation links. And, it ships with one
for Bootstrap 4, which is what we're using. Booya!
So, first question: where should this configuration live? Sometimes, a recipe will
create a file for us, like the
stof_doctrine_extensions. But in this case that
didn't happen. And that's ok! Not every bundle needs to give you a config file.
Just create it by hand:
As usual, the filename matches the root config key. But, we know from pervious
tutorials that the filename is actually meaningless. Next, copy the config down
pagination line, move over, paste, then remove all the stuff we don't
need. Finally, copy the bootstrap v4 template name and, paste.
We're ready! Move back and refresh. Boom! It still works! It's beautiful! We rock! Our pagination is awesome! I'm super happy about this.
With this perfect, let's turn to our last big topic: generating a totally new, ManyToMany relationship.