sehrgutesoftware/laravel5-api

View on GitHub
src/Laravel5_Api/Plugins/Paginator.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace SehrGut\Laravel5_Api\Plugins;

use SehrGut\Laravel5_Api\Hooks\AdaptCollectionQuery;
use SehrGut\Laravel5_Api\Hooks\BeforeRespond;
use SehrGut\Laravel5_Api\Hooks\FormatCollection;

/**
 * A basic paginator that takes `limit` and `page` from the
 * request and adapts the colleciton queries accordingly.
 *
 * By default, the plugin adds http headers with meta info to the response:
 * `X-Pagination-Total`, `X-Pagination-Limit` and `X-Pagination-Page`
 *
 * These can be renamed or moved to the response payload
 * instead, through the plugin's configuration options.
 */
class Paginator extends Plugin implements AdaptCollectionQuery, FormatCollection, BeforeRespond
{
    /**
     * Configuration options for this Plugin:.
     *
     * - `limit_param`: Name of the request parameter that contains the limit value (page size)
     * - `page_param`: Name of the request parameter that contains the page number
     * - `limit_default`: Default limit value if none present in request
     * - `page_default`: Default page number if none present in request
     * - `meta_in_payload`: Key under which to meta info (total, limit, current page)
     *                      to response body (or `false` to disable)
     * - `meta_in_headers`: (Boolean) Whether or not to add meta info as http headers
     * - `meta_structure`: An array describing how meta info is formatted (in payload) or
     *                     naming headers (in http headers). '$total', '$limit',
     *                     '$page', '$last_page' will be replaced with their
     *                     actual values. (Remember to use single quotes)
     *
     * @var array
     */
    protected $default_config = [
        'limit_param' => 'limit',
        'page_param' => 'page',
        'limit_default' => 10,
        'page_default' => 1,
        'max_limit' => 25,
        'meta_in_payload' => false,
        'meta_in_headers' => true,
        'meta_structure' => [
            'X-Pagination-Total' => '$total',
            'X-Pagination-Limit' => '$limit',
            'X-Pagination-Page' => '$page',
        ],
    ];

    protected $meta_counts;

    /** {@inheritdoc} */
    public function adaptCollectionQuery()
    {
        $total = $this->context->query->count();

        $limit = (int) $this->context->controller->request_adapter->getValueByKey(
            $this->config['limit_param'],
            $this->config['limit_default']
        );
        $limit = min($limit, $this->config['max_limit']);

        $page = (int) $this->context->controller->request_adapter->getValueByKey(
            $this->config['page_param'],
            $this->config['page_default']
        );

        $this->saveMetaCounts($total, $limit, $page);

        $this->context->query->limit($limit)->skip(($page - 1) * $limit);
    }

    /** {@inheritdoc} */
    public function formatCollection()
    {
        if ($meta_key = $this->config['meta_in_payload']) {
            $this->context->collection[$meta_key] = $this->meta_counts;
        }
    }

    /** {@inheritdoc} */
    public function beforeRespond()
    {
        if ($this->config['meta_in_headers'] && $this->meta_counts) {
            $this->context->response->headers->add($this->meta_counts);
        }
    }

    /**
     * Replace $placeholders with their actual values in config['meta_structure']
     * and store the resulting array into `$this->meta_counts`.
     *
     * @param int $total
     * @param int $limit
     * @param int $page
     *
     * @return void
     */
    protected function saveMetaCounts(int $total, int $limit, int $page)
    {
        $last_page = $limit > 0 ? ceil($total / $limit) : null;
        $values = [
            '$total' => $total,
            '$limit' => $limit,
            '$page' => $page,
            '$last_page' => $last_page,
        ];
        $this->meta_counts = $this->config['meta_structure'];
        array_walk_recursive($this->meta_counts, function (&$node) use ($values) {
            if (array_key_exists($node, $values)) {
                $node = $values[$node];
            }

            return $node;
        });
    }
}