Change the PageUtility class into a service

By Ronald van Belzen | May 1, 2016

In the previous article of this series we concluded writing a class that will take care of the pagination, sorting and filtering of data in a tabular display. Next we are going to transform this class into a service. For this we are going to rename the class and place it in the proper directory in accordance with the namespace for this new class that will be:

namespace AppBundle\DependencyInjection;

For coming up with a new class name let's consider what the service will actually do. It will provide the data to be displayed on a page. For this reason I will call the new class PageDataProvider. With this name we can add the service to our "services.yml" that is imported by the configuration file of our application:

services:
    page.data.provider:
        class: AppBundle\DependencyInjection\PageDataProvider
        arguments: ["@request_stack", "@service_container"]

As you can see we pass the RequestStack and the ContainerInterface as arguments for the class constructor. These parameters do not coincide with the constructor parameter of the PageUtility class. So, let's change the constructor of our new class accordingly:

<?php
namespace AppBundle\DependencyInjection;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class PageDataProvider {
    protected $requestStack;
    private $request;
    private $em;
    private $entityName;

    // ...
    
    public function __construct(RequestStack $requestStack, Container $container) {
        $this->requestStack = $requestStack;
        $this->em = $container->get('doctrine')->getManager();
    }

In the constructor we set the new variable $requestStack and set the enity manager $em. We now need a function in which we can set the entity name for which the data need be be delivered:

    /**
     * Initialization of the page dataprovider
     */
    public function init($entity_name, $page_size = 20, $default_sort_field = null) {
        $this->request =  $this->requestStack->getCurrentRequest();
        $this->entityName = $entity_name;
        $this->pageSize = $page_size;
        $this->defaultSortField = $default_sort_field;
        $this->setFilter();
        $this->totalRecords = $this->getTotal();
        $this->setCurrentPage();
        $this->setSorting();
    }

What happens in the function init() is almost identical to what happened in the constructor of the PageUtility class. The main difference is that the Request is retrieved with the aid of the RequestStack. All other functions in the PageDataProvider class are identical to the functions in the PageUtility class.

The controller function listAction() for the tabular display needs to be changed into:

    /**
     * Handle the user listing display (includes pagination and sorting)
     */
    public function listAction(Request $request) {
        $dp = $this->get('page.data.provider');
        $dp->init('AppBundle:User', 5, 'username.asc');
        return $this->render('admin/list.html.twig',[
            'users' => $dp->getRecords(),
            'pager' => $dp->getDisplayParameters(),
        ]);
    }

Up till now I have only shown code snippets of the code changes that need to be applied to original code. The complete code of the PageDataProvider class is as follows:

<?php
namespace AppBundle\DependencyInjection;

//use Doctrine\ORM\EntityManager;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class PageDataProvider {
    protected $requestStack;
    private $request;
    private $em;
    private $entityName;
    // Number of records per page
    private $pageSize;
    // Current page number
    private $currentPage;
    // Total number of pages
    private $totalPages;
    // Total records
    private $totalRecords;
    // Offset of the current page for db table
    private $offset;
    // Sort field
    private $defaultSortField;
    private $sortField;
    private $sortDirection;
    private $sortReverse;
    // Filter array
    private $filter;
    
    public function __construct(RequestStack $requestStack, Container $container) {
        $this->requestStack = $requestStack;
        $this->em = $container->get('doctrine')->getManager();
    }

    /**
     * Initialization of the page dataprovider
     */
    public function init($entity_name, $page_size = 20, $default_sort_field = null) {
        $this->request =  $this->requestStack->getCurrentRequest();
        $this->entityName = $entity_name;
        $this->pageSize = $page_size;
        $this->defaultSortField = $default_sort_field;
        $this->setFilter();
        $this->totalRecords = $this->getTotal();
        $this->setCurrentPage();
        $this->setSorting();
    }
    
    /**
     * Get the records for the current page
     */
    public function getRecords() {
        $records = $this->em->getRepository($this->entityName)
            ->createQueryBuilder('t');
        if(!empty($this->sortField)) {
            $records = $records->orderBy('t.' . $this->sortField, $this->sortDirection);
        }
        if(!empty($this->filter)) {
            foreach($this->filter as $key => $value) {
                if( $this->em->getClassMetadata($this->entityName)->hasField($key) ) {
                    $records = $records->andWhere("t.{$key} LIKE :{$key}");
                    if( $this->em->getClassMetadata($this->entityName)->getTypeOfField($key) === 'string' || 
                        $this->em->getClassMetadata($this->entityName)->getTypeOfField($key) === 'text' ) {
                        $records = $records->setParameter($key, '%' . $value . '%');
                    } else {
                        $records = $records->setParameter($key, $value);
                    }
                }
            }
        }
        $records = $records->setFirstResult($this->offset)
            ->setMaxResults($this->pageSize)
            ->getQuery()
            ->getResult();
        return $records;
    }
    
    /**
     * Get the parameters for the page display
     */
    public function getDisplayParameters() {
        $return = array(
            'current_page' => $this->currentPage,
            'total_pages' => $this->totalPages,
            'sort_field' => $this->sortField,
            'sort_order' => $this->sortDirection,
            'sort_reverse' => $this->sortReverse,
        );
        if(empty($this->sortField)) {
            $return['sort'] = '';
        } else {
            $return['sort'] = $this->sortField . '.' . strtolower($this->sortDirection);
        }
        $return['filter'] = $this->filter;
        return $return;
    }
    
    /**
     * Get the total number of records
     */
    private function getTotal() {
        $total = $this->em->getRepository($this->entityName)
            ->createQueryBuilder('t')
            ->select('count(t.id)');
        if(!empty($this->filter)) {
            foreach($this->filter as $key => $value) {
                if( $this->em->getClassMetadata($this->entityName)->hasField($key) ) {
                    $total = $total->andWhere("t.{$key} LIKE :{$key}");
                    if( $this->em->getClassMetadata($this->entityName)->getTypeOfField($key) === 'string' || 
                        $this->em->getClassMetadata($this->entityName)->getTypeOfField($key) === 'text' ) {
                        $total = $total->setParameter($key, '%' . $value . '%');
                    } else {
                        $total = $total->setParameter($key, $value);
                    }
                }
            }
        }
        $total = $total->getQuery()->getSingleScalarResult();
        return $total;
    }
    
    /**
     * Set the current page and total number of pages
     */
    private function setCurrentPage() {
        $this->currentPage = $this->request->get('page');
        if(empty($this->currentPage)) {
            $this->currentPage = 1;
        }
        $this->totalPages = ceil($this->totalRecords/$this->pageSize);
        if(($this->currentPage * $this->pageSize) > $this->totalRecords) {
            $this->currentPage = $this->totalPages;
        }
        // Offset for db table
        if($this->currentPage > 1) {
            $this->offset = ($this->currentPage - 1) * $this->pageSize;
        } else {
            $this->offset = 0;
        }
    }
    
    /**
     * Set the sorting fields
     */
    private function setSorting() {
        $sort = $this->request->get('sort');
        if(empty($sort) && empty($this->defaultSortField)) {
            $this->sortField = '';
            $this->sortDirection = '';
        } else {
            if(empty($sort)) {
                $arr = explode('.', $this->defaultSortField);
            } else {
                $arr = explode('.', $sort);
            }
            if(empty($arr[0])) {
                $this->sortField = '';
                $this->sortDirection = '';
            } elseif(count($arr) == 1 || empty($arr[1])) {
                $this->sortField = $arr[0];
                $this->sortDirection = 'ASC';
                $this->sortReverse = $this->sortField . '.desc';
            } else {
                $this->sortField = $arr[0];
                if(strtolower($arr[1]) == 'desc') {
                    $this->sortDirection = 'DESC';
                    $this->sortReverse = $this->sortField . '.asc';
                } else {
                    $this->sortDirection = 'ASC';
                    $this->sortReverse = $this->sortField . '.desc';
                }
            }
            // Validate sort field
            if( !$this->em->getClassMetadata($this->entityName)->hasField($this->sortField) ) {
                $this->sortField = '';
                $this->sortDirection = '';
            }
        }
    }
    
    /**
     * Set Filter (remove keys for empty values)
     */
    private function setFilter() {
        $this->filter = array();
        $filters = $this->request->get('filter');
        if(is_array($filters)) {
            foreach($filters as $key => $value) {
                if(!empty($value) || $value == '0') {
                    $this->filter[$key] = $value;
                }
            }
        }
    }
}

 

Add new comment