Пагинация на PHP с применением ООП и Composer.

Пагинация на PHP с применением ООП и Composer.

@phpproglib

Для удобного перемещения по записям почти на каждом сайте есть пагинация. Если коротко, это постраничный вывод некоторого числа постов или новостей. В этой статье мы научимся делать простую пагинацию, используя ООП и Composer.

Приготовления

Для автозагрузки наших классов мы будем использовать Composer. У кого его нет, советуем скачать и в будущем пользоваться им постоянно. Composer позволяет устанавливать дополнительные библиотеки для проекта и определять собственные пространства имён в файле composer.json.

composer.json

{
  "autoload": {
    "psr-4": {
      "Engine\\": "src/"
    }
  }
}

Создаём файл composer.json и в директиве autoload указываем стандарт автозагрузки классов psr-4, который описывает спецификацию для автозагрузки классов на основе путей файлов. Мы указали, что нашим пространством будет папка Engine, которая находится в папке src. Поскольку обратный слеш экранирует кавычки, мы должны поставить два слеша. После всего в терминале прописываем команду composer dump-autoload, в результате чего у нас появится папка vendor, где в будущем будут храниться все сторонние библиотеки и файл autoload.php.

configs.php

Правилом хорошего тона является создание гибких классов, которые не заставят посторонних программистов лезть в него, поэтому все настройки подключения к базе данных мы вынесем в отдельный файл configs.php, чья единственная функция - вернуть массив настроек.

return array (
        'host' => '127.0.0.1',
        'dbname' => 'pagination',
        'username' => 'root',
        'password' => 'root'
);

Каждый, кто будет использовать наш класс, должен только указать свои настройки подключения к базе, а наш класс подключится сам.

Работа с базой данных

Database.php

В классе Database мы будем использовать PDO для подготовленных запросов и напишем только конструктор и два метода - метод для получения числа всех записей в базе и метод вывода записей по лимиту.

Указываем пространство имён, по которому будем загружать наш класс.

<?php

namespace Engine;

Поскольку мы используем пространство имён, мы можем загрузить встроенный класс PDO.

use PDO;

class Database
{

Создаём приватную переменную $pdo, которая будет хранить подключение к базе.

    private $pdo;

Переменная settings будет массивом, который будет хранить настройки подключения.

    private $settings = [];

В этой переменной будут храниться результаты выполнения наших методов.

    private $statement;

Отсюда мы будем считать записи.

    private $from;

И выводить определённое число записей.

    private $to;

Конструктор принимает массив настроек, ключами которого являются те самые ключи массива из файла configs.php, созданного ранее, и возвращает подключение, если оно удалось.

    public function __construct(array $settings)
    {
        $this->settings = $settings;
        try {
          $this->pdo = new PDO('mysql:host=' . $this->settings['host']. ';dbname=' . $this->settings['dbname'], $this->settings['username'], $this->settings['password']);
    $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
            } catch (\PDOException $e) {
                exit($e->getMessage());
            }
        return $this->pdo;
    }

Метод getDataCount() возвращает число записей из базы данных и сохраняет их в ключ total.

    public function getDataCount()
    {
        $this->statement = $this->pdo->query('SELECT COUNT(*) as total FROM posts');
        return $this->statement->fetch();
    }

С методом getLimitData() немного сложнее: он принимает два параметра - откуда считать посты и по сколько. Дальше мы делаем подготовленный запрос к базе и связываем параметры с помощью метода PDO bindParam, куда на всякий случай передаём константу PDO::PARAM_INT, чтобы точно передать целое число. Наконец, возвращаем массив с данными.

    public function getLimitData($from, $to)
    {
        $this->from = $from;
        $this->to   = $to;
        $this->statement = $this->pdo->prepare('SELECT * FROM posts LIMIT :from,:to');
        $this->statement->bindParam(':from', $this->from, PDO::PARAM_INT);
        $this->statement->bindParam(':to', $this->to, PDO::PARAM_INT);
        $this->statement->execute();
        return $this->statement->fetchAll(PDO::FETCH_OBJ);
    }

Отображение данных

index.php

Чтобы можно было загрузить класс Database по его неймспейсу, надо подключить файл autoload.php.

<?php

require_once 'vendor/autoload.php';

Загружаем класс Database.

use Engine\Database;

Создаём переменную, куда загружаем массив из файла configs.php.

$settings = require_once 'configs.php';

Передаём эти настройки в объект класса Database.

$some_object = new Database($settings);

Вызываем метод getDataCount() и сохраняем в переменной pages.

$pages = $some_object->geteDataCount()['total'];

В этой переменной храним значение числа записей на одну страницу (например, 5).

$per_page = 5;

Чтобы узнать количество страниц, нужно поделить количество записей в базе на число записей на страницу и округлить с помощью ceil() в большую сторону, чтобы не потерять последние записи.

$number_of_pages = ceil($pages / $per_page);

Проверяем, на какой странице находимся: если не существует GET-параметра page, то мы на первой странице. Если параметр GET существует, то берём его значение и присваиваем переменной $page для дальнейшего использования в запросе к базе.

$page = !isset($_GET['page']) ? 1 : $_GET['page'];

Тут мы получаем каждую первую запись следующей страницы, то есть

(1 - 1) * 10 = 0

(2 - 1) * 10 = 10

(3 - 1) * 10 = 20

(4 - 1) * 10 = 30, где первая цифра (1, 2, 3, 4) - это номер страницы,

а результат (0, 10, 20, 30) - ID первой записи на каждой новой странице.

$first_result_on_every_page = ($page - 1) * $per_page;

Вызываем метод getLimitData() и получаем число записей на страницу.

$posts = $some_object->getLimitData($first_result_on_every_page, $per_page);

Простой цикл, где выводим названия и контент статей. Используем двойные кавычки в echo, чтобы избежать конкатенации.

foreach ($posts as $post) {
    echo "<h1>$post->title</h1>";
    echo "<h4>$post->body</h4>";
}

Наконец, выводим пагинацию. В атрибут href передаём GET параметр page, значение которого будет использовано для определения необходимых записей на страницу.

for ($page = 1; $page <= $number_of_pages; $page++) {
    echo "<a href='?page=$page'>$page</a>" . ' ';
}

Собственно, такой мы получили результат. В качестве интересной возможности вы можете предложить пользователям самим выбирать, какое количество записей будет выводиться на странице. Так делает популярный сайт stackoverflow. Для этого с помощью HTML создайте option или кнопки, значения которых будете сохранять в переменной $page (напомню, что до этого мы там сохранили число 5). На этом логика работы над пагинацией заканчивается, вам остаётся сделать её вывод красивым.

Report Page