Пагинация

class Cake\Controller\Component\PaginatorComponent

Одним из основных препятствий для создания гибких и удобных веб-приложений является разработка интуитивно понятного пользовательского интерфейса. Многие приложения быстро растут по размеру и сложности, а разработчики и программисты считают, что они не могут справиться с отображением сотен или тысяч записей. Рефакторинг требует времени, и производительность и удовлетворенность пользователей могут пострадать.

Отображение разумного количества записей на странице всегда было важной частью каждого приложения и было для многих разработчиков головной болью. CakePHP облегчает нагрузку на разработчика, предоставляя быстрый и простой способ разбиения данных на страницы.

Пагинация в CakePHP предлагается компонентом в контроллере, чтобы упростить разбиение на страницы результатов запроса. В Представлении используется View\Helper\PaginatorHelper для простой генерации ссылок и кнопок разбиения на страницы.

Использование Controller::paginate()

В контроллере мы начинаем с определения условий запроса по умолчанию, которые будут использоваться в переменной $paginate. Эти условия служат основой для ваших запросов с разбивкой по страницам. Они дополняются параметрами sort, direction, limit и page, переданными из URL-адреса. Важно отметить, что ключ order должен быть определён в структуре массива, как показано ниже:

class ArticlesController extends AppController
{
    public $paginate = [
        'limit' => 25,
        'order' => [
            'Articles.title' => 'asc'
        ]
    ];

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Paginator');
    }
}

Вы также можете включить любой из параметров, поддерживаемых ORM\Table::find(), например fields:

class ArticlesController extends AppController
{
    public $paginate = [
        'fields' => ['Articles.id', 'Articles.created'],
        'limit' => 25,
        'order' => [
            'Articles.title' => 'asc'
        ]
    ];

    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Paginator');
    }
}

Хотя вы можете передать большинство параметров запроса из свойства paginate, часто бывает проще объединить свои варианты разбиения на страницы в Custom Finder Methods. Вы можете определить использование разбиения на страницы поиска, установив опцию finder:

class ArticlesController extends AppController
{
    public $paginate = [
        'finder' => 'published',
    ];
}

Поскольку методы пользовательского поиска также могут принимать параметры, вы можете передать параметры в пользовательский метод поиска в свойстве paginate:

class ArticlesController extends AppController
{
    // Найти статьи по тегу
    public function tags()
    {
        $tags = $this->request->getParam('pass');

        $customFinderOptions = [
            'tags' => $tags
        ];
        // Метод пользовательского поиска вызывает findTagged из ArticlesTable.php,
        // он должен выглядеть следующим образом:
        // public function findTagged(Query $query, array $options) {
        // поэтому мы используетем tagged как ключ
        $this->paginate = [
            'finder' => [
                'tagged' => $customFinderOptions
            ]
        ];
        $articles = $this->paginate($this->Articles);
        $this->set(compact('articles', 'tags'));
    }
}

В дополнение к определению общих значений разбиения на страницы, вы можете определить более одного набора значений по умолчанию для разбивки на страницы в контроллере, вы просто называете ключи массива после модели, которую вы хотите настроить:

class ArticlesController extends AppController
{
    public $paginate = [
        'Articles' => [],
        'Authors' => [],
    ];
}

Значения ключей Articles и Authors могут содержать все свойства, которые мог бы использовать массив модели/ключа без $paginate.

Как только свойство $paginate определено, мы можем использовать метод Controller\Controller::paginate() для создания данных разбивки на страницы и добавить PaginatorHelper если он еще не добавлен. Метод paginate контроллера возвращает результирующий набор запроса разбитого на страницы, и устанавливает метаданные разбиения на страницы для запроса. Вы можете получить доступ к метаданным разбиения на страницы с помощью $this->request->getParam('paging'). Более полным примером использования paginate() будет следующее:

class ArticlesController extends AppController
{
    public function index()
    {
        $this->set('articles', $this->paginate());
    }
}

По умолчанию метод paginate() будет использовать модель по умолчанию для контроллера. Полученный запрос, вы также, можете передать методом find():

public function index()
{
   $query = $this->Articles->find('popular')->where(['author_id' => 1]);
   $this->set('articles', $this->paginate($query));
}

Если вы хотите разбивать на страницы другую модель, вы можете предоставить для нее запрос, сам объект таблицы или её имя:

// Использование запроса
$comments = $this->paginate($commentsTable->find());

// Использование имени модели
$comments = $this->paginate('Comments');

// Использование объекта таблицы
$comments = $this->paginate($commentTable);

Непосредственное использование пагинатора

Если вам нужно разделить данные из другого компонента, вы можете напрямую использовать PaginatorComponent. Он имеет аналогичный API для метода контроллера:

$articles = $this->Paginator->paginate($articleTable->find(), $config);

// Или
$articles = $this->Paginator->paginate($articleTable, $config);

Первым параметром должен быть объект запроса метода find в таблицу объекта, из которого вы хотите выполнить разбиение на страницы. При желании вы можете передать объект таблицы и создать запрос. Второй параметр должен быть массивом параметров, используемый для разбиения на страницы. Этот массив должен иметь ту же структуру, что и свойство $paginate в контроллере. При разбиении на страницы объекта Query параметр finder будет проигнорирован. Предполагается, что вы передаёте запрос, который требуется разбивать на страницы.

Пагинация по нескольким запросам

Вы можете разбивать на страницы несколько моделей в одном экшене контроллера, используя опцию scope как в свойстве $paginate контроллера, так и в вызове метода paginate():

// Свойство Paginate
public $paginate = [
    'Articles' => ['scope' => 'article'],
    'Tags' => ['scope' => 'tag']
];

// В экшене контроллера
$articles = $this->paginate($this->Articles, ['scope' => 'article']);
$tags = $this->paginate($this->Tags, ['scope' => 'tag']);
$this->set(compact('articles', 'tags'));

Опция scope приведёт к тому, что PaginatorComponent будет искать в параметрах строку запроса. Например, следующий URL-адрес может использоваться для одновременного разбиения на страницы как тегов, так и статей:

/dashboard?article[page]=1&tag[page]=3

См. раздел Разбиение на несколько результатов для создания области видимости HTML-элементов и URL-адресов для разбивки на страницы.

Добавлено в версии 3.3.0: Множественное разбиение на страницы было добавлено в 3.3.0

Управление полями используемыми для сортировки

Сортировка по умолчанию может выполняться на любом не виртуальном столбце, который есть в таблице. Иногда это нежелательно, так как позволяет пользователям сортировать по неиндексированным столбцам, которые могут быть дорогостоящими для запроса. Вы можете установить белый список полей, которые можно сортировать, используя опцию sortWhitelist. Эта опция требуется, если вы хотите сортировать любые связанные данные или вычисленные поля, которые могут быть частью вашего запроса разбиения на страницы:

public $paginate = [
    'sortWhitelist' => [
        'id', 'title', 'Users.username', 'created'
    ]
];

Любые запросы, которые пытаются сортировать по полям, не входящим в белый список, будут игнорироваться.

Ограничиние максимального количества строк на страницу

Количество результатов, полученных на странице, отображается пользователю как параметр limit. Как правило, нежелательно разрешать пользователям получать сразу все строки разбитые на страницы. Опция maxLimit ограничит запрос так, что никто не сможет превысить этот предел из вне. По умолчанию CakePHP ограничивает максимальное количество строк, которые могут быть выбраны, до 100. Если это значение по умолчанию не подходит для вашего приложения, вы можете настроить его как часть параметров разбивки на страницы, например, уменьшив это кол-во до 10:

public $paginate = [
    'maxLimit' => 10,
            // ну а здесь другие ключи..
];

Если параметр ограничения запроса больше этого значения, он будет уменьшен до значения maxLimit.

Присоединение дополнительных ассоциаций

Дополнительные ассоциации могут быть загружены в разбитую таблицу с помощью параметра contain:

public function index()
{
    $this->paginate = [
        'contain' => ['Authors', 'Comments']
    ];

    $this->set('articles', $this->paginate($this->Articles));
}

Запросы на заданную страницу

PaginatorComponent будет выбрасывать NotFoundException при попытке доступа к несуществующей странице, например, когда запрошенный номер страницы превышает общий счёт страниц.

Таким образом, вы можете либо разрешить отображение нормальной страницы ошибки, либо использовать блок catch try и предпринять соответствующие действия при обнаружении NotFoundException:

use Cake\Network\Exception\NotFoundException;

public function index()
{
    try {
        $this->paginate();
    } catch (NotFoundException $e) {
        // Сделайте что-то здесь, например, перенаправление на первую или последнюю страницу.
        // $this->request->getParam('paging') // предоставит вам необходимую информацию
    }
}

Разбиение страницы в Виде

Проверьте документацию View\Helper\PaginatorHelper, чтобы создать ссылки для навигацию по страницам.