tighten / craft-build-query

A plugin for Craft CMS, demonstrating how to build complex or optimized queries by modifying an ElementCriteriaModel.
MIT License
39 stars 3 forks source link

Pagination? #5

Open alexmglover opened 6 years ago

alexmglover commented 6 years ago

Pagination is expecting ElementCriteriaModel. Is there a way around this?

damiani commented 6 years ago

I don't know of any way to convert an EntryModel (which is typically what you'd end up with after performing a custom query) into an ElementCriteriaModel. In one instance where I needed pagination on a custom query result, I ended up writing a plugin and template to build the pagination element manually.

bkmorse commented 6 years ago

I built pagination for a custom search within the comments plugin for craft 2

<?php
namespace Craft;

class Comments_PaginationService extends BaseApplicationComponent
{
    // Properties
    // =========================================================================

    public $activeComment;

    /**
     * @var integer
     */
    public $elementsPerPage;

    /**
     * @var integer
     */
    public $totalElements;

    /**
     * @var string
     */
    public $pageParam;

    /**
     * @var integer
     */
    public $totalPages;

    /**
     * @var integer
     */
    public $currentPage;

    /**
     * @var integer
     */
    public $count;

    /**
     * @var
     */
    public $first;

    /**
     * @var
     */
    public $last;

    // Public Methods
    // =========================================================================

    /**
     * Get curret page for use in the pagination
     * 
     * @return [type] [description]
     */
    public function getCurrentPage()
    {
        $currentPage = craft()->request->getQuery($this->pageParam, 1);

        if (is_numeric($currentPage) && $currentPage > $this->totalPages)
        {
            $currentPage = $this->totalPages > 0 ? $this->totalPages : 1;
        }
        else if (!is_numeric($currentPage) || $currentPage < 0)
        {
            $currentPage = 1;
        }

        return (int) $currentPage;
    }

    /**
     * Returns previous page URLs up to a certain distance from the current page.
     *
     * @param int $dist
     *
     * @return array
     */
    public function getPrevUrls($dist = null, $currentPage, $totalPages)
    {
        $this->currentPage = $currentPage;
        $this->totalPages = $totalPages;

        if ($dist)
        {
            $start = $this->currentPage - $dist;
        }
        else
        {
            $start = 1;
        }

        return $this->getRangeUrls($start, $this->currentPage - 1);
    }

    /**
     * Returns next page URLs up to a certain distance from the current page.
     *
     * @param int $dist
     *
     * @return array
     */
    public function getNextUrls($dist = null, $currentPage, $totalPages)
    {
        $this->currentPage = $currentPage;
        $this->totalPages = $totalPages;

        if ($dist)
        {
            $end = $this->currentPage + $dist;
        }
        else
        {
            $end = $this->totalPages;
        }

        return $this->getRangeUrls($this->currentPage + 1, $end);
    }

    /**
     * Returns the URL to a specific page
     *
     * @param int $page
     *
     * @return string|null
     */
    public function getPageUrl($page, $totalPages = false)
    {
        if ($totalPages)$this->totalPages = $totalPages;

        if ($page >= 1 && $page <= $this->totalPages)
        {
            $path = craft()->request->getPath();
            $params = array();

            if ($page != 1)
            {
                // $pageTrigger = craft()->config->get('pageTrigger');
                $pageTrigger = '?page';

                if (!is_string($pageTrigger) || !strlen($pageTrigger))
                {
                    $pageTrigger = 'p';
                }

                // Is this query string-based pagination?
                if ($pageTrigger[0] === '?')
                {
                    $pageTrigger = trim($pageTrigger, '?=');

                    if ($pageTrigger === 'p')
                    {
                        // Avoid conflict with the main 'p' param
                        $pageTrigger = 'pg';
                    }

                    $params = array($pageTrigger => $page);
                }
                else
                {
                    if ($path)
                    {
                        $path .= '/';
                    }

                    $path .= $pageTrigger.$page;
                }
            }

            return UrlHelper::getUrl($path, $params);
        }
    }

    /**
     * Returns a range of page URLs.
     *
     * @param int $start
     * @param int $end
     *
     * @return array
     */
    public function getRangeUrls($start, $end)
    {
        if ($start < 1)
        {
            $start = 1;
        }

        if ($end > $this->totalPages)
        {
            $end = $this->totalPages;
        }

        $urls = array();

        for ($page = $start; $page <= $end; $page++)
        {
            $urls[$page] = $this->getPageUrl($page);
        }

        return $urls;
    }
}

Set this within the same service (as the code you see below this code block) that returns the comments and the pagination data:

$this->elementsPerPage = $limit;
$this->totalElements = $total;
$this->pageParam = 'page';
$this->totalPages = (int) ceil($this->totalElements / $this->elementsPerPage);
$this->currentPage = craft()->comments->getCurrentPage();

Then in the return, I set the pagination like this:

$result = [
    'comments'      =>  $comments,
    'pagination'    =>  [
        'total'         =>  $total,
        'currentPage'   =>  $this->currentPage,
        'totalPages'    =>  $this->totalPages,
        'nextUrl'       =>  craft()->comments_pagination->getPageUrl($this->currentPage + 1, $this->totalPages),
        'prevUrl'       =>  craft()->comments_pagination->getPageUrl(($this->currentPage > 1 ? $this->currentPage - 1 : 1), $this->totalPages),
        'prevUrls'      =>  craft()->comments_pagination->getPrevUrls(5, $this->currentPage, $this->totalPages),
        'nextUrls'      =>  craft()->comments_pagination->getNextUrls(5, $this->currentPage, $this->totalPages),
        'offset'    =>  $offset,
    ]
];

return $result;
alexmglover commented 6 years ago

Yeah, that’s what I ended up doing. I actually had to write a field type to get my particular use case working. Thanks so much for responding!

On Jan 18, 2018, at 9:40 AM, Keith Damiani notifications@github.com wrote:

I don't know of any way to convert an EntryModel (which is typically what you'd end up with after performing a custom query) into an ElementCriteriaModel. In one instance where I needed pagination on a custom query result, I ended up writing a plugin and template to build the pagination element manually.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/tightenco/craft-build-query/issues/5#issuecomment-358665978, or mute the thread https://github.com/notifications/unsubscribe-auth/AAW71WwYPP8YYFfiTz83ZnY6zg2qw--yks5tL1fFgaJpZM4Rh4gF.