post_ico4

Biblioteka do paginacji – MVC z Composer

Czytelnikom bloga chyba nie muszę tłumaczyć co to jest i do czego służy paginacja. W tym wpisie udostępnię bibliotekę dostosowaną do szkieletu aplikacji pokazanego w cyklu MVC w praktyce z composer – tworzymy system artykułów.

Żeby zrozumieć treść tego artykułu konieczne jest zapoznanie się powyższym cyklem.

Kod biblioteki jest zmodyfikowaną klasą paginacji z aplikacji Open Cart (na licencji opensource).

Myślę, że nie ma sensu wchodzić tutaj w teoretyzowanie, a więc przejdźmy od razu do kodu.

Skrypt paginacji

W moim szkielecie aplikacji należy utworzyć plik src/Library/Pagination.php:

<?php
namespace RacyMind\MVCWPraktyce\Library;

/**
 * Paginacja
 * @package RacyMind\MVCWPraktyce\Library
 * @author Łukasz Socha <kontakt@lukasz-socha.pl>
 * @version 1.0
 * @license http://www.gnu.org/copyleft/lesser.html
 */
final class Pagination
{
    /**
     * @var int Ilość wszystkich elementów
     */
    private $totalItems = 0;
    /**
     * @var int Aktualna strona
     */
    private $page = 1;
    /**
     * @var int Ilość elementów na stronie
     */
    private $itemsPerPage = 10;
    /**
     * @var int Ilość wyświetlanych lunków do stron
     */
    private $numLinks = 3;
    /**
     * @var string Adres URL
     */
    private $url = '';
    /**
     * @var string Tekst podsumowania
     */
    private $text = 'Pokazano od {start} do {end} zw {total} elementów ({pages} stron)';
    /**
     * @var string Tekst linka do pierwszej strony
     */
    private $textFirstPage = 'pierwsza';
    /**
     * @var string Tekst linka do ostaniej strony
     */
    private $textLastPage = 'ostania';
    /**
     * @var string Tekst linka do następnej strony
     */
    private $textNextPage = '&gt;';
    /**
     * @var string Tekst linka do poprzedniej strony
     */
    private $textPrevPage = '&lt;';
    /**
     * @var string Separator z lewej strony
     */
    private $dottedStringLeft = ' .... ';
    /**
     * @var string Separator z prawej strony
     */
    private $dottedStringRight = ' .... ';
    /**
     * @var int Ilość stron
     */
    private $numPages;
    /**
     * @var string do pierwszej strony
     */
    private $titleFirstPage = '';
    /**
     * @var string Tytuł linka do poprzedniej strony
     */
    private $titlePrevPage = '';
    /**
     * @var string Tytuł linka do x strony
     */
    private $titleXPage = '';
    /**
     * @var string Tytuł linka do następnej strony
     */
    private $titleNextPage = '';
    /**
     * @var string Tytuł linka do ostatniej strony
     */
    private $titleLastPage = '';

    /**
     * Pagination constructor.
     * @param string $url
     */
    public function __construct($url)
    {
        $this->url = $url;
    }

    /**
     * @return int
     */
    public function getTotalItems()
    {
        return $this->totalItems;
    }

    /**
     * @param int $totalItems
     */
    public function setTotalItems($totalItems)
    {
        $this->totalItems = $totalItems;
    }

    /**
     * @return int
     */
    public function getPage()
    {
        return $this->page;
    }

    /**
     * @param int $page
     */
    public function setPage($page)
    {
        $this->page = $page;
    }

    /**
     * @return int
     */
    public function getItemsPerPage()
    {
        return $this->itemsPerPage;
    }

    /**
     * @param int $itemsPerPage
     */
    public function setItemsPerPage($itemsPerPage)
    {
        $this->itemsPerPage = $itemsPerPage;
    }

    /**
     * @return int
     */
    public function getNumLinks()
    {
        return $this->numLinks;
    }

    /**
     * @param int $numLinks
     */
    public function setNumLinks($numLinks)
    {
        $this->numLinks = $numLinks;
    }

    /**
     * @return string
     */
    public function getUrl()
    {
        return $this->url;
    }

    /**
     * @param string $url
     */
    public function setUrl($url)
    {
        $this->url = $url;
    }

    /**
     * @return string
     */
    public function getText()
    {
        return $this->text;
    }

    /**
     * @param string $text
     */
    public function setText($text)
    {
        $this->text = $text;
    }

    /**
     * @return string
     */
    public function getTextFirstPage()
    {
        return $this->textFirstPage;
    }

    /**
     * @param string $textFirstPage
     */
    public function setTextFirstPage($textFirstPage)
    {
        $this->textFirstPage = $textFirstPage;
    }

    /**
     * @return string
     */
    public function getTextLastPage()
    {
        return $this->textLastPage;
    }

    /**
     * @param string $textLastPage
     */
    public function setTextLastPage($textLastPage)
    {
        $this->textLastPage = $textLastPage;
    }

    /**
     * @return string
     */
    public function getTextNextPage()
    {
        return $this->textNextPage;
    }

    /**
     * @param string $textNextPage
     */
    public function setTextNextPage($textNextPage)
    {
        $this->textNextPage = $textNextPage;
    }

    /**
     * @return string
     */
    public function getTextPrevPage()
    {
        return $this->textPrevPage;
    }

    /**
     * @param string $textPrevPage
     */
    public function setTextPrevPage($textPrevPage)
    {
        $this->textPrevPage = $textPrevPage;
    }

    /**
     * @return string
     */
    public function getDottedStringLeft()
    {
        return $this->dottedStringLeft;
    }

    /**
     * @param string $dottedStringLeft
     */
    public function setDottedStringLeft($dottedStringLeft)
    {
        $this->dottedStringLeft = $dottedStringLeft;
    }

    /**
     * @return string
     */
    public function getDottedStringRight()
    {
        return $this->dottedStringRight;
    }

    /**
     * @param string $dottedStringRight
     */
    public function setDottedStringRight($dottedStringRight)
    {
        $this->dottedStringRight = $dottedStringRight;
    }

    /**
     * @return int
     */
    public function getNumPages()
    {
        return $this->numPages;
    }

    /**
     * @param int $numPages
     */
    public function setNumPages($numPages)
    {
        $this->numPages = $numPages;
    }

    /**
     * @return string
     */
    public function getTitleFirstPage()
    {
        return $this->titleFirstPage;
    }

    /**
     * @param string $titleFirstPage
     */
    public function setTitleFirstPage($titleFirstPage)
    {
        $this->titleFirstPage = $titleFirstPage;
    }

    /**
     * @return string
     */
    public function getTitlePrevPage()
    {
        return $this->titlePrevPage;
    }

    /**
     * @param string $titlePrevPage
     */
    public function setTitlePrevPage($titlePrevPage)
    {
        $this->titlePrevPage = $titlePrevPage;
    }

    /**
     * @return string
     */
    public function getTitleXPage()
    {
        return $this->titleXPage;
    }

    /**
     * @param string $titleXPage
     */
    public function setTitleXPage($titleXPage)
    {
        $this->titleXPage = $titleXPage;
    }

    /**
     * @return string
     */
    public function getTitleNextPage()
    {
        return $this->titleNextPage;
    }

    /**
     * @param string $titleNextPage
     */
    public function setTitleNextPage($titleNextPage)
    {
        $this->titleNextPage = $titleNextPage;
    }

    /**
     * @return string
     */
    public function getTitleLastPage()
    {
        return $this->titleLastPage;
    }

    /**
     * @param string $titleLastPage
     */
    public function setTitleLastPage($titleLastPage)
    {
        $this->titleLastPage = $titleLastPage;
    }


    public function render()
    {

        $numPages = ceil($this->totalItems / $this->itemsPerPage);

        $output = '';

        if ($this->page > 1) {
            if ($this->textFirstPage) {
                $firstUrl = str_replace('{page}', 1, $this->url);
                $firstUrl = str_replace(array('_strona-1'), array(''), $firstUrl);
                $output .= '<a title="' . $this->titleFirstPage . '" href="' . $firstUrl . '">' . $this->textFirstPage . '</a>';
            }
            if ($this->textPrevPage) {
                $prevUrl = str_replace('{page}', $this->page - 1, $this->url);
                if ($this->page - 1 == 1) $prevUrl = str_replace(array('_strona-1'), array(''), $prevUrl);
                $output .= '<a title="' . $this->titlePrevPage . '" href="' . $prevUrl . '">' . $this->textPrevPage . '</a> ';
            }
        }


        if ($numPages > 1) {
            if ($numPages <= $this->numLinks) {
                $start = 1;
                $end = $numPages;
            } else {
                $start = $this->page - floor($this->numLinks / 2);
                $end = $this->page + floor($this->numLinks / 2);

                if ($start < 1) {
                    $end += abs($start) + 1;
                    $start = 1;
                }

                if ($end > $numPages) {
                    $start -= ($end - $numPages);
                    $end = $numPages;
                }
            }

            if ($start > 1) {
                $output .= $this->dottedStringLeft;
            }

            for ($i = $start; $i <= $end; $i++) {
                if ($this->page == $i) {
                    $output .= ' <span>' . $i . '</span> ';
                } else {
                    $output .= ' <a title="' . str_replace('{page}', $i, $this->titleXPage) . '" href="' . str_replace('{page}', $i, $this->url) . '">' . $i . '</a> ';
                }
                if ($i == 1) {
                    $output = str_replace(array('_strona-1'), array(''), $output);
                }
            }

            if ($end < $numPages) {
                if (strpos($this->dottedStringRight, '{all_pages}') !== false) {
                    $output .= str_replace('{all_pages}', '<a title="' . str_replace('{page}', $numPages, $this->titleXPage) . '" href="' . str_replace('{page}', $numPages, $this->url) . '">' . $numPages . '</a>', $this->dottedStringRight);
                } else {
                    $output .= str_replace('{pages}', $numPages, $this->dottedStringRight);
                }
            }
        }

        if ($this->page < $numPages) {
            if ($this->textNextPage) $output .= '<a title="' . $this->titleNextPage . '" href="' . str_replace('{page}', $this->page + 1, $this->url) . '">' . $this->textNextPage . '</a> ';
            if ($this->textLastPage) $output .= '<a title="' . $this->titleLastPage . '" href="' . str_replace('{page}', $numPages, $this->url) . '">' . $this->textLastPage . '</a> ';
        }

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

        $replace = array(
            ($this->totalItems) ? (($this->page - 1) * $this->itemsPerPage) + 1 : 0,
            ((($this->page - 1) * $this->itemsPerPage) > ($this->totalItems - $this->itemsPerPage)) ? $this->totalItems : ((($this->page - 1) * $this->itemsPerPage) + $this->itemsPerPage),
            $this->totalItems,
            $numPages
        );

        $this->numPages = $numPages;

        return ($output ? '<div class="pagination-links">' . $output . '</div>' : '') . '<div class="pagination-result">' . str_replace($find, $replace, $this->text) . '</div>';
    }
}

?>

Wszystkie możliwości konfiguracji dostępne są za pomocą pól zainicjowanych na początku klasy. W komentarzach opisałem za co odpowiada dane pole. Metodą odpowiedzialną za wygenerowanie paginacji jest render().

Żeby korzystać z paginacji należy zmodyfikować kilka plików systemu artykułów.

Aktualizacja modelu artykułów

W pliku src/Model/Article.php należy zmodyfikować metodę getAll().

public function getAll($offset=null, $limit=null)
    {
        $sql = "SELECT a.id, a.title, a.date_add, a.author, c.name FROM articles AS a LEFT JOIN categories AS c ON a.id_categories=c.id ";
        if (isset($offset) && isset($limit)) {
            $sql .= "LIMIT " . (int)$offset . ", " . (int)$limit;
        }
        $query = $this->pdo->query($sql);
        $items = $query->fetchAll(\PDO::FETCH_ASSOC);
        if (isset($items)) {
            return $items;
        } else {
            return null;
        }
    }

Aktualizacja kontrolera artykułów

W pliku src/Controller/Article.php należy zmodyfikować metodę index().

public function index()
    {
        if(!empty($_GET['page'])) {
            $page=(int)$_GET['page'];
        } else {
            $page=1;
        }
        $model = new \RacyMind\MVCWPraktyce\Model\Article();
        $total=sizeof($model->getAll());
        $pagination = new \RacyMind\MVCWPraktyce\Library\Pagination($this->generateUrl('article/index/pages', array('page' => '{page}')));
        $pagination->setTotalItems($total);
        $pagination->setPage($page);
        $pagination->setItemsPerPage(10);
        $articles = $model->getAll(($page-1)*$pagination->getItemsPerPage(), $pagination->getItemsPerPage());
        $view = new \RacyMind\MVCWPraktyce\View\Article();
        $view->pagination=$pagination->render();
        $view->articles = $articles;
        $view->renderHTML('index', 'front/article/');
    }

Aktualizacja szablonu z listą artykułów

W pliku src/template/front/article/index.html.php należy dopisać w dowolnym miejscu:

<?php echo $this->pagination; ?>

Dodanie reguły routera

Na koniec, w pliku config-router.php trzeba dodać nową regułę:

$collection->add('article/index/pages', new \RacyMind\MVCWPraktyce\Engine\Router\Route(
    HTTP_SERVER.'artykuly_strona-<page>?',
    array(
        'file' => DIR_CONTROLLER.'Article.php',
        'method' => 'index',
        'class' => '\RacyMind\MVCWPraktyce\Controller\Article'
    ),
    array(
        'page' => '\d+'
    ),
    array(
        'page' => 1
    )
));

Myślę, że dodawanie paginacji w aplikacjach korzystających z mojego szkieletu nie powinno sprawiać większego problemu :).

Co sądzisz o wpisie?
BeżnadziejnySłabyŚredniDobryBardzo dobry (1 głosów, średnia ocen: 5,00 z 5)
Loading...