How to pagination for multipage component in Phalcon


Mar '15

Jun '17

4

2238

0


Thien
13.1k
edited Mar '15
Mar '15

You want to create paginator like example

<<back 1 2 3 ... 8 9 10 next>>

Currently. Phalcon paginator does not have a feature this, but you can use my tip to result you expect.

The first you create a controller is TestController

class TestController extends ControllerBase
{
    /**
     * Number of items to show on the search list.
     */
    const SEARCH_LIST_COUNT = 10;


    public function searchAction()
    {
        // Define the page
        $current_page = $this->request->getQuery('p', 'int');
        if (! $current_page) {
            $current_page = 1;
        }
        $search_query  = $this->request->getQuery('q', 'string');

        // @TODO refactor later
        $_product = [
            'product_id'    => rand(100000, 999999),
            'product_name'  => 'Sharp',
            'price'         => 1200
        ];

        $products = array_fill(1, 50, $_product);

        // Passing an array as data
        $paginator = new Paginator(
            [
                'data'  => $products,
                'limit' => self::SEARCH_LIST_COUNT,
                'page'  => $current_page,
            ]
        );

        $this->view->setVars(
            [
                'form'          => new SearchForm(null, ['search_query' => $search_query]),
                'search_query'  => $search_query,
                'products'      => (object) $products,
                'offset'        => ($current_page - 1 ) * self::SEARCH_LIST_COUNT,
                'paginator'     => $paginator->getPaginate()
            ]
        );
    }
}

I use function array_fill in PHP to create random items for paginator, then in view create a file name search.volt in directory Test with following content

<div class="col-md-12">
    {% if products is defined %}
    <div data-example-id="simple-table" class="bs-example">
        <table class="table">
        <caption>Optional table caption.</caption>
            <thead><tr>
                <th>#</th>
                <th>Product ID</th>
                <th>Product Name</th>
                <th>Prices</th>
            </tr></thead>
        {% for index, product in products %}
            <tbody><tr>
                <th scope="row">2</th>
                <td>{{ product['product_id'] }}</td>
                <td>{{ link_to('detail/' ~ product['product_id'], product['product_name']) }}</td>
                <td>{{ product['price'] }}</td>
                </tr></tbody>
        {% endfor %}
        </table>
    </div>
    {% endif %}
</div>
<--paginator !-->

{% if paginator.total_pages > 1 %}
    {% set controller = this.view.getControllerName() | lower  %}
    {% set action = this.view.getActionName() | lower %}
    {% set startIndex = 1, q = search_query | trim %}
    {% if paginator.total_pages > 5 %}
        {% if paginator.current > 3 %}
            {% set startIndex = startIndex + paginator.current - 3 %}
        {% endif %}
        {% if paginator.total_pages - paginator.current < 5 %}
            {% set startIndex = paginator.total_pages - 4 %}
        {% endif %}
    {% endif %}

        <ul class="pagination pagination-sm m-t-none m-b-none">
            {% if paginator.current > 1 %}
                <li>{{ link_to(action ~ '?q=' ~ q, 'data-page' : paginator.first, '<i class="fa fa-angle-double-left"></i>', 'title' : 'Go to page ' ~ paginator.next) }}</li>
                {#<li class="prev">{{ link_to('#', 'data-page' : paginator.before, '<i class="fa fa-angle-left"></i>', 'title' : 'Go to page ' ~ paginator.last) }}</li>#}
            {% endif %}
            {% for pageIndex in startIndex..paginator.total_pages %}
                {% if pageIndex is startIndex + 5 %}
                    {% break %}
                {% endif %}

                <li {% if pageIndex is paginator.current %}class="active"{% endif %}>
                    {{ link_to(action ~ '?q=' ~ q ~ '&p=' ~ pageIndex, pageIndex, 'data-page' : pageIndex, 'title' : 'Go to page ' ~ pageIndex) }}
                </li>
            {% endfor %}

            {% if paginator.current < paginator.total_pages %}
                {#{<li class="next">{{ link_to(action ~ '?q=' ~ q ~ '&p=' ~ paginator.next, 'data-page' : paginator.next, '<i class="fa fa-angle-right"></i>', 'title' : 'Go to page ' ~ (paginator.next))</li>}#}

                <li>{{ link_to(action ~ '?q=' ~ q ~ '&p=' ~ paginator.last, 'data-page' : paginator.last, '<i class="fa fa-angle-double-right"></i>', 'title' : 'Go to page ' ~ paginator.last) }}</li>
            {% endif %}
        </ul>
{% endif %}

And that, It couldn't be easier. For more information on Paginator check out the online documentation. Did you enjoy this tip? Let us know your thoughts!

edited Jul '15
Jul '15

I think my solution is mutch better =)

<?php

/**
 * Taken from Opencart (c)
 */

namespace ddd\Components;

use ddd\Helper\Helper;

class Pagination extends \Phalcon\Mvc\User\Component {

    public $total = 1;
    public $page = 1;
    public $limit = 20;
    public $num_links = 10;
    public $url = '';
    public $text = 'Showing from {start} to {end} of {total} ({pages} Page{s})';
    public $text_first = '|&lt;';
    public $text_last = '&gt;|';
    public $text_next = '&gt;';
    public $text_prev = '&lt;';
    public $style_links = 'links';
    public $style_results = 'results';

    private $paginator;

    public function __construct($paginator, $queryArray, $options = []){
        $this->paginator = $paginator;
        $this->total = $paginator->getPaginate()->total_items;
        $this->limit = $paginator->getLimit();
        $this->page = $paginator->getCurrentPage();

        // Create url
        if(isset($options['url'])) {
            $url = $options['url'];
        } else {
            $url = array_shift($queryArray);
        }
        $url .= '?';
        foreach($queryArray as $key => $val){
            if(!$val || $key == 'page')
                continue;
            $url .= $key . '=' . $val . '&';
        }
        $url .= 'page={page}';
        $this->url = $url;

        // Options
        if(isset($options['num_links']))
            $this->total = $options['num_links'];
    }

    public function render() {
        $total = $this->total;
        if ($this->page < 1) {
            $page = 1;
        } else {
            $page = $this->page;
        }
        if (!(int)$this->limit) {
            $limit = 10;
        } else {
            $limit = $this->limit;
        }

        $num_links = $this->num_links;
        $num_pages = ceil($total / $limit);

        $output = '';

        if ($page > 1) {
            $output .= ' <a href="' . str_replace('{page}', 1, $this->url) . '">' . $this->text_first . '</a> <a href="' . str_replace('{page}', $page - 1, $this->url) . '" rel="prev">' . $this->text_prev . '</a> ';
        }
        if ($num_pages > 1) {
            if ($num_pages <= $num_links) {
                $start = 1;
                $end = $num_pages;
            } else {
                $start = $page - floor($num_links / 2);
                $end = $page + floor($num_links / 2);
                if ($start < 1) {
                    $end += abs($start) + 1;
                    $start = 1;
                }
                if ($end > $num_pages) {
                    $start -= ($end - $num_pages);
                    $end = $num_pages;
                }
            }
            if ($start > 1) {
                $output .= ' .... ';
            }
            for ($i = $start; $i <= $end; $i++) {
                if ($page == $i) {
                    $output .= ' <b>' . $i . '</b> ';
                } else {
                    $output .= ' <a href="' . str_replace('{page}', $i, $this->url) . '">' . $i . '</a> ';
                }   
            }           
            if ($end < $num_pages) {
                $output .= ' .... ';
            }
        }

        if ($page < $num_pages) {
            $output .= ' <a href="' . str_replace('{page}', $page + 1, $this->url) . '" rel="next">' . $this->text_next . '</a> <a href="' . str_replace('{page}', $num_pages, $this->url) . '">' . $this->text_last . '</a> ';
        }

        $find = array(
            '{start}',
            '{end}',
            '{total}',
            '{pages}',
            '{s}'
        );

        $replace = array(
            ($total) ? (($page - 1) * $limit) + 1 : 0,
            ((($page - 1) * $limit) > ($total - $limit)) ? $total : ((($page - 1) * $limit) + $limit),
            $total, 
            $num_pages,
            $num_pages > 1 ? 's' : ''
        );

        $str = ($output ? '<div class="' . $this->style_links . '">' . $output . '</div>' : '');

        $str1 = str_replace('<div class="links">', '<ul class="pagination">', $str);
        $str2 = str_replace('</div>', '</ul>', $str1);
        $str3 = str_replace('<a', '<li><a', $str2);
        $str4 = str_replace('</a>', '</a></li>', $str3);
        $str5 = str_replace('<b>', '<li class="active"><a>', $str4);
        $str6 = str_replace('</b>', '<span class="sr-only">(current)</span></a></li>', $str5);
        $str7 = str_replace('&gt;|', '<i class="fa fa-angle-double-right"></i>', $str6);
        $str8 = str_replace('&gt;', '<i class="fa fa-angle-right"></i>', $str7);
        $str9 = str_replace('|&lt;', '<i class="fa fa-angle-double-left"></i>', $str8);
        $str10 = str_replace('&lt;', '<i class="fa fa-angle-left"></i>', $str9);
        $str11 = str_replace('....', '<li class="disabled"><span>....</span></li>', $str10);

        $view = $str11;
        if(!$output) {
            $view = '';
        }

        return Helper::arrayToObject([
            'total' => $total,
            'view' => $view,
            'text' => '<div class="' . $this->style_results . '">' . str_replace($find, $replace, $this->text) . '</div>'
        ]);

    }

    public function getLast() {
        $total = $this->total;
        $end = 1;
        if ($this->page < 1) {
            $page = 1;
        } else {
            $page = $this->page;
        }
        if (!(int)$this->limit) {
            $limit = 10;
        } else {
            $limit = $this->limit;
        }
        $num_links = $this->num_links;
        $num_pages = ceil($total / $limit);
        if ($num_pages > 1) {
            if ($num_pages <= $num_links) {
                $start = 1;
                $end = $num_pages;
            } else {
                $start = $page - floor($num_links / 2);
                $end = $page + floor($num_links / 2);                   
                if ($start < 1) {
                    $end += abs($start) + 1;
                    $start = 1;
                }
                if ($end > $num_pages) {
                    $start -= ($end - $num_pages);
                    $end = $num_pages;
                }
            }
        }
        return $end;
    }
}
?>

In controller:

$paginator = new \Phalcon\Paginator\Adapter\QueryBuilder([
            'builder'  => $queryBuilder,
            'limit' => $pageLimit,
            'page'  => $pageIndex
        ]);
$this->view->paginatorView = (new Pagination($paginator, $this->request->getQuery()))->render();

And in the template:

 {{ paginatorView.view }}

Thien
13.1k

yup, maybe the solution your good, anyway the tip and comment to wellcome

Also I have vote for you :)


GBraL
85

Dear Dima,

When try use your code, one error is showed in this line:

return Helper::arrayToObject([

Where is class HELPER?

edited Jul '15
Jul '15

Oh it's just my simple function

static function arrayToObject($data){
        $obj = new \stdClass();
        foreach($data as $key => $val){
            $obj->{$key} = $val;
        }
        return $obj;
    }

GBraL
85

Thank you very mutch.