Search code examples
phppaginationzend-framework2laminas-api-tools

Limit the results in Apigility


I created a code connected API with Apigility. For now I am using the standard create stubs. In my PostResource there is a method called fetchAll($params = array()). I created the code for the method so that it returns a paginatable set of results:

/** @var HydratorInterface $hydrator */
$hydrator = new \Zend\Stdlib\Hydrator\ClassMethods();

/** @var PostService $postService */
$postService = new PostService();

$posts = $postService->findAll(/* Limit, default 10 */);
$apiData = array();
foreach ($posts as $post) {
    $apiData[] = $hydrator->extract($post);
}
return new Paginator(new ArrayAdapter($apiData));

This works fine so far. If i navigate to the API URL I will get a paginated json representation of my DB data. If I set the page size for my API to 5. It will give me 2 pages and 5 results. So far so good. The problem is, that on every call (page 1 or page 2) all 10 results will be fetched from the DB. It only returns 5 at one page but 10 are hydrated etc.

Is there a way to use a limit but also let Apigility or the paginator know, how much results there are in total so that I will get 5 rows and still the pagination?


Solution

  • I don't know exactly, how you're retrieving the data, but however the following approach works as wished: The call chain looks like AddressResource#fetchAll(...) -> AddressService#getBar(...) -> AddressMapper#findAll(...) and the data retrieving method returns a Collection object.

    AddressResource.php

    ...
    
    class AddressResource ... {
    
        ...
    
        public function fetchAll($params = array()) {
            $service = $this->getAddressService();
            $collection = $service->getAddresses($params->toArray());
            return $collection;
        }
    
        ...
    
    }
    

    AddressService.php

    ...
    
    class AddressService ... {
    
        ...
    
        public function getAddresses($params = array()) {
            $collection = $this->getMapper()->findAll($params);
            return $collection;
        }
    
        ...
    
    }
    

    AddressMapper.php

    ...
    
    class AddressMapper extends AbstractDbMapper {
    
        ...
    
        public function findAll($params = array()) {
            $select = $this->getSelect();
            $select->where(
                ...
            );
            $paginatorAdapter = $this->createPaginationAdapter($select);
            $collection = new AddressCollection($paginatorAdapter);
            return $collection;
        }
    
        ...
    
    }
    

    AddressCollection.php

    ...
    
    use Zend\Paginator\Paginator;
    
    class AddressCollection extends Paginator {
    }
    

    module.config.php

    return array(
        ...
        'zf-rest' => array(
            ...
            'AddressBookAPI\\V1\\Rest\\Address\\Controller' => array(
                ...
                'page_size' => 5,
                ...
            ),
            ...
        ),
        ...
    );
    

    Now a test: I'm calling /addresses and observing the MySQL query log:

    $ tail -f /var/log/mysql_query.log
    
    ...
    
    3 Connect   root@localhost on address-book-api
    3 Query     SET NAMES 'UTF8'
    3 Query     SELECT COUNT(1) AS `C` FROM (SELECT `addresses`.* FROM `addresses`) AS `original_select`
    3 Query     SELECT `addresses`.* FROM `addresses` LIMIT 5 OFFSET 0
    3 Quit
    
    ...