What is used?
- PHP version 7.4.19 with Symfony Framework version 5.2.9
- KnpPaginatorBundle version 5.4
Create a new symfony project via composer by typing the following statement in CMD:
composer create-project symfony/website-skeleton my_project_name
Aim of Pagination
Pagination is a technique that helps us in increasing the performance of a symphony application. It gives us the ability to organize the returned records of a table in the database into pages, instead of displaying all the records together at the same time, and we can notice the difference when we have thousands of rows.
We applied the pagination technique on RESTful API, without using Twig. We just display each page into a Json format, and via a parameter in the route, which comes after the product section, we can fetch the different pages.
We focused on enhancing the performance of the project, because of that we applied specific code structure and used certain statements as we will see next.
In order to use pagination in our project we need to install KnpPaginatorBundle. That’s done via the following command:
composer require knplabs/knp-paginator-bundle
Enabling the bundle happens automatically on the app/config/bundles.php in Symfony 4 and in the versions later, due to Flex.
Before describing how to use Pagination in our project, we may have to describe the code structure being used. In the /src folder we divide the code flow into layers ordered as follows: Controller -> Service -> Manager -> Repository. Also, we have Request and Response layers, which include request and response objects respectively.
- First, follow the scenario to use the Paginator:
- Starting from the Repository layer: we define a function to get a specific group of Products which will form a page. The function takes two parameters, page index (named page), and number of items per page (named limit). Inside it we followed a specific route to increase the performance of getting the results. As a one step, we create a query which derives a group of Products by setting First Result and Max Results options.
$query = $this->createQueryBuilder('p') ->setFirstResult(($page - 1) * $limit) ->setMaxResults($limit);
Depending on the query, we made another select statement as following, and return the query as it is (without getResult() statement):
$query ->select('p.name', 'p.price', 'image.imagePath', 'supplier.fullName') ->leftJoin( Supplier::class, 'supplier', Join::WITH, 'p.supplier = supplier.id') ->leftJoin( Image::class, 'image', Join::WITH, 'p.image = image.id');
2) Now, in the Manager layer, we apply the Paginator on the query being returned from the Repository.
$paginatedQuery = new Paginator($query, false); $paginatedQuery->setUseOutputWalkers(false);
As we can note, in the first statement, we pass the option false as a second parameter to the Paginator constructor. This value indicates to not using the option fetchJoinCollection.
In the second statement, we told the paginator to not use an output walker.
A walker is an interface that walks each node of the Abstract Syntax Tree, generating the SQL statement. Abstract Syntax Tree is formed via parsing DQL language by a parser in Doctrine ORM.
Using output walker option set to true by default in Doctrine decreases the performance in large data situation, as discussed in this https://github.com/doctrine/orm/issues/4073
3) In the Service layer, we get the response from the function in the Manager layer and deliver it to the endpoint in the Controller layer.
4) In the Controller layer, we extend our controller from BaseController. And pass the response we got from the Service layer into a user-built function called response besides the option self::FETCH. And return the result to the consumer.
Note: the function response in the BaseController is responsible for serializing the returned result that we got from the Service layer into Json format.
Fetching the second page of the response:
Scrolling to the end of the response, we can check the final item, which has the name=Product 199, which means that we get 100 items per page.
Fetching the fifth page of the response: