Сейчас я хочу остановиться на конкретном примере использования гридов, чтобы нагляднее показать гриды с точки зрения разработчика.[spoiler]
Пусть источником данных для грида будет список пользователей системы. Мы выведем в публичке список пользователей с возможностью редактировать данные пользователя в форме. Поскольку это публичный раздел, то логично будет сделать свои компоненты для вывода и редактирования пользователей. Тело компонента списка будет выбирать данные, а шаблон компонента - выводить их с помощью грида. Аналогично для компонента формы редактирования.
Комментировать буду в коде компонента.
1. Компонент списка.
component.php:
<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die(); //здесь обработка POST (см. ниже) //это список групп пользователей, нужен будет для фильтра $aGroups = array(); $gr_res = CGroup::GetDropDownList("AND ID!=2"); while($aGr = $gr_res->Fetch()) $aGroups[$aGr["REFERENCE_ID"]] = $aGr["REFERENCE"]; //уникальный идентификатор грида $arResult["GRID_ID"] = "user_grid"; //определяем фильтр, поля фильтра типизированы //по умолчанию тип text, поддерживается date, list, number, quick (поле ввода и список), custom $arResult["FILTER"] = array( array("id"=>"FIND", "name"=>"Найти", "type"=>"quick", "items"=>array("login"=>"Логин", "email"=>"Email", "name"=>"Имя")), array("id"=>"PERSONAL_BIRTHDAY", "name"=>"День рождения", "type"=>"date"), array("id"=>"PERSONAL_PROFESSION", "name"=>"Профессия"), array("id"=>"PERSONAL_WWW", "name"=>"Веб"), array("id"=>"PERSONAL_ICQ", "name"=>"АйСикЮ", "params"=>array("size"=>15)), array("id"=>"PERSONAL_GENDER", "name"=>"Пол", "type"=>"list", "items"=>array(""=>"(пол)", "M"=>"Мужской", "F"=>"Женский")), array("id"=>"GROUPS_ID", "name"=>"Группы пользователей", "type"=>"list", "items"=>$aGroups, "params"=>array("size"=>5, "multiple"=>"multiple"), "valign"=>"top"), array("id"=>"PERSONAL_PHONE", "name"=>"Телефон"), array("id"=>"PERSONAL_MOBILE", "name"=>"Мобильник"), array("id"=>"PERSONAL_CITY", "name"=>"Город"), ); //инициализируем объект с настройками пользователя для нашего грида $grid_options = new CGridOptions($arResult["GRID_ID"]); //какую сортировку сохранил пользователь (передаем то, что по умолчанию) $aSort = $grid_options->GetSorting(array("sort"=>array("id"=>"desc"), "vars"=>array("by"=>"by", "order"=>"order"))); //размер страницы в постраничке (передаем умолчания) $aNav = $grid_options->GetNavParams(array("nPageSize"=>10)); //получим текущий фильтр (передаем описанный выше фильтр) $aFilter = $grid_options->GetFilter($arResult["FILTER"]); //некоторые названия полей фильтра могут не совпадать с API if(isset($aFilter["PERSONAL_BIRTHDAY_from"])) $aFilter["PERSONAL_BIRTHDAY_1"] = $aFilter["PERSONAL_BIRTHDAY_from"]; if(isset($aFilter["PERSONAL_BIRTHDAY_to"])) $aFilter["PERSONAL_BIRTHDAY_2"] = $aFilter["PERSONAL_BIRTHDAY_to"]; if(isset($aFilter["FIND"])) $aFilter[strtoupper($aFilter["FIND_list"])] = $aFilter["FIND"]; //это собственно выборка данных с учетом сортировки и фильтра, указанных пользователем $aSortArg = each($aSort["sort"]); $db_res = CUser::GetList($aSortArg["key"], $aSortArg["value"], $aFilter); //постраничка с учетом размера страницы $db_res->NavStart($aNav["nPageSize"]); //в этом цикле построчно заполняем данные для грида $aRows = array(); while($aRes = $db_res->GetNext()) { //в этой переменной - поля, требующие нестандартного отображения (не просто значение) $aCols = array( "PERSONAL_GENDER" => ($aRes["PERSONAL_GENDER"] == "M"? "Мужской":($aRes["PERSONAL_GENDER"] == "F"? "Женский":"")), "EMAIL" => '<a href="mailto:'.$aRes["EMAIL"].'">'.$aRes["EMAIL"].'</a>', "ID" => '<a href="'.$APPLICATION->GetCurPage().'?ID='.$aRes["ID"].'">'.$aRes["ID"].'</a>', "LOGIN" => '<a href="main.interface.form.php?ID='.$aRes["ID"].'">'.$aRes["LOGIN"].'</a>', ); //это определения для меню действий над строкой $aActions = Array( array("ICONCLASS"=>"edit", "TEXT"=>"Изменить", "ONCLICK"=>"jsUtils.Redirect(arguments, 'main.interface.form.php?ID=".$aRes["ID"]."')", "DEFAULT"=>true), array("ICONCLASS"=>"copy", "TEXT"=>"Добавить копию", "ONCLICK"=>"jsUtils.Redirect(arguments, '/bitrix/admin/user_edit.php?COPY_ID=".$aRes["ID"]."')"), array("SEPARATOR"=>true), array("ICONCLASS"=>"delete", "TEXT"=>"Удалить", "ONCLICK"=>"if(confirm('Вы уверены, что хотите удалить данного пользователя?')) window.location='/bitrix/admin/user_admin.php?action=delete&ID=".$aRes["ID"]."&".bitrix_sessid_get()."';"), ); //запомнили данные. "data" - вся выборка, "editable" - можно редактировать строку или нет $aRows[] = array("data"=>$aRes, "actions"=>$aActions, "columns"=>$aCols, "editable"=>($aRes["ID"]==11? false:true)); } //наши накопленные данные $arResult["ROWS"] = $aRows; //информация для футера списка $arResult["ROWS_COUNT"] = $db_res->SelectedRowsCount(); //сортировка $arResult["SORT"] = $aSort["sort"]; $arResult["SORT_VARS"] = $aSort["vars"]; //объект постранички - нужен гриду. Убираем ссылку "все". $db_res->bShowAll = false; $arResult["NAV_OBJECT"] = $db_res; //самое интересное - в шаблоне компонента $this->IncludeComponentTemplate(); ?> |
template.php:
<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die(); ?> <? //покажем панель с кнопками $APPLICATION->IncludeComponent( "bitrix:main.interface.toolbar", "", array( "BUTTONS"=>array( array( "TEXT"=>"Список", "TITLE"=>"Список пользователей", "LINK"=>$APPLICATION->GetCurPage(), "ICON"=>"btn-list", ), array("SEPARATOR"=>true), array( "TEXT"=>"Скопировать админа", "TITLE"=>"Скопировать пользователя номер 1", "LINK"=>"/bitrix/admin/user_edit.php?COPY_ID=1", "ICON"=>"btn-copy", ), array( "TEXT"=>"Скопировать себя", "TITLE"=>"Скопировать пользователя номер 1", "LINK"=>"/bitrix/admin/user_edit.php?COPY_ID=".$GLOBALS["USER"]->GetID(), "ICON"=>"btn-copy", ), array("NEWBAR"=>true), array( "TEXT"=>"Добавить", "TITLE"=>"Добавить пользователя или группу", "MENU"=>array( array("ICONCLASS"=>"add", "TEXT"=>"Пользователя", "ONCLICK"=>"jsUtils.Redirect(arguments, '/bitrix/admin/user_edit.php')"), array("ICONCLASS"=>"add", "TEXT"=>"Группу пользователей", "ONCLICK"=>"jsUtils.Redirect(arguments, '/bitrix/admin/group_edit.php')"), ), "ICON"=>"btn-new", ), ), ), $component );?> <? //вызовем компонент грида для отображения данных $APPLICATION->IncludeComponent( "bitrix:main.interface.grid", "", array( //уникальный идентификатор грида "GRID_ID"=>$arResult["GRID_ID"], //описание колонок грида, поля типизированы "HEADERS"=>array( array("id"=>"LOGIN", "name"=>"Логин", "sort"=>"login", "default"=>true, "editable"=>false), array("id"=>"ACTIVE", "name"=>"Активен", "default"=>true, "type"=>"checkbox", "editable"=>true), array("id"=>"TIMESTAMP_X", "name"=>"Изменено", "sort"=>"timestamp_x", "default"=>false), array("id"=>"NAME", "name"=>"Имя", "sort"=>"name", "default"=>true, "editable"=>array("size"=>20, "maxlength"=>255)), array("id"=>"LAST_NAME", "name"=>"Фамилия", "sort"=>"last_name", "default"=>true, "editable"=>array("size"=>20, "maxlength"=>255)), array("id"=>"SECOND_NAME", "name"=>"Отчество", "sort"=>"second_name"), array("id"=>"EMAIL", "name"=>"Email", "sort"=>"email", "default"=>true, "editable"=>array("size"=>20, "maxlength"=>255)), array("id"=>"LAST_LOGIN", "name"=>"Аторизовывался", "sort"=>"last_login"), array("id"=>"DATE_REGISTER", "name"=>"Зарегистрирован", "sort"=>"date_register"), array("id"=>"ID", "name"=>"ИД", "sort"=>"id", "default"=>true, "align"=>"right"), array("id"=>"PERSONAL_BIRTHDAY", "name"=>"День рождения", "sort"=>"personal_birthday", "default"=>true, "type"=>"date", "editable"=>true), array("id"=>"PERSONAL_PROFESSION", "name"=>"Профессия", "sort"=>"personal_profession"), array("id"=>"PERSONAL_WWW", "name"=>"Веб", "sort"=>"personal_www"), array("id"=>"PERSONAL_ICQ", "name"=>"АйСикЮ", "sort"=>"personal_icq"), array("id"=>"PERSONAL_GENDER", "name"=>"Пол", "sort"=>"personal_gender", "default"=>true, "type"=>"list", "editable"=>array("items"=>array(""=>"(пол)", "M"=>"Мужской", "F"=>"Женский"))), array("id"=>"PERSONAL_PHONE", "name"=>"Телефон", "sort"=>"personal_phone"), array("id"=>"PERSONAL_MOBILE", "name"=>"Мобильник", "sort"=>"personal_mobile"), array("id"=>"PERSONAL_CITY", "name"=>"Город", "sort"=>"personal_city"), array("id"=>"PERSONAL_STREET", "name"=>"Улица", "sort"=>"personal_street"), array("id"=>"WORK_COMPANY", "name"=>"Компания", "sort"=>"work_company"), array("id"=>"WORK_DEPARTMENT", "name"=>"Отдел", "sort"=>"work_department"), array("id"=>"WORK_POSITION", "name"=>"Должность", "sort"=>"work_position"), array("id"=>"WORK_WWW", "name"=>"Раб. веб", "sort"=>"work_www"), array("id"=>"WORK_PHONE", "name"=>"Раб. тел.", "sort"=>"work_phone"), array("id"=>"WORK_CITY", "name"=>"Раб. город", "sort"=>"work_city"), array("id"=>"XML_ID", "name"=>"Символьный код", "sort"=>"xml_id"), array("id"=>"EXTERNAL_AUTH_ID", "name"=>"Внешний код"), ), //сортировка "SORT"=>$arResult["SORT"], //это необязательный "SORT_VARS"=>$arResult["SORT_VARS"], //данные "ROWS"=>$arResult["ROWS"], //футер списка, можно задать несколько секций "FOOTER"=>array(array("title"=>"Всего", "value"=>$arResult["ROWS_COUNT"])), //групповые действия "ACTIONS"=>array( //можно удалять "delete"=>true, //выпадающий список действий "list"=>array("activate"=>"Активировать", "deactivate"=>"Деактивировать"), //либо произвольный html "custom_html"=>' <select name="action_on_files" onchange="this.form.folder_id.style.display=(this.value==\'move\'? \'\':\'none\');"> <option>- действия -</option> <option value="move">Переместить</option> </select> <select name="folder_id" style="display:none"> <option value="folder1">folder1</option> <option value="folder2">folder2</option> </select> ', ), //разрешить действия над всеми элементами "ACTION_ALL_ROWS"=>true, //разрешено редактирование в списке "EDITABLE"=>true, //объект постранички "NAV_OBJECT"=>$arResult["NAV_OBJECT"], //можно использовать в режиме ajax "AJAX_MODE"=>"Y", "AJAX_OPTION_JUMP"=>"N", "AJAX_OPTION_STYLE"=>"Y", //фильтр "FILTER"=>$arResult["FILTER"], ), $component ); ?> |
При редактировании в списке или при групповых действиях, наш компонент получит обычный $_POST из формы. Типичный код обработки формы:
//Form submitted if( $_SERVER["REQUEST_METHOD"] == "POST" && check_bitrix_sessid() && isset($_POST["action_button_".$arResult["GRID_ID"]]) ) { if($_POST["action_button_".$arResult["GRID_ID"]] == "delete" && isset($_POST["ID"]) && is_array($_POST["ID"])) { //групповое удаление foreach($_POST["ID"] as $ID) // Delete($ID); } if($_POST["action_button_".$arResult["GRID_ID"]] == "edit" && isset($_POST["FIELDS"]) && is_array($_POST["FIELDS"])) { //групповое редактирование foreach($_POST["FIELDS"] as $ID => $arFields) //Update($ID, $arFields); } if(!isset($_POST["AJAX_CALL"])) LocalRedirect($path); } |
2. Компонент формы редактирования.
component.php:
<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die(); //здесь обработка POST (см. ниже) //получаем данные пользователя $db_res = CUser::GetByID($_REQUEST["ID"]); $aRes = $db_res->GetNext()) //уникальный идентификатор формы $arResult["FORM_ID"] = "user_form"; //передаем все данные $arResult["DATA"] = $aRes; $this->IncludeComponentTemplate(); ?> |
template.php:
<? if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die(); ?> <?$APPLICATION->IncludeComponent( "bitrix:main.interface.toolbar", "", array( "BUTTONS"=>array( array( "TEXT"=>"Список", "TITLE"=>"Список пользователей", "LINK"=>"main.interface.grid.php", "ICON"=>"btn-list", ), ), ), $component );?> <?$APPLICATION->IncludeComponent( "bitrix:main.interface.form", "", array( //идентификатор формы "FORM_ID"=>$arResult["FORM_ID"], //описание вкладок формы "TABS"=>array( array("id"=>"tab1", "name"=>"Пользователь", "title"=>"Регистрационная информация", "icon"=>"", "fields"=>array( //на каждой вкладке - определение типизированных полей array("id"=>"DATE_REGISTER", "name"=>"Зарегистрирован", "type"=>"label"), array("id"=>"ACTIVE", "name"=>"Активен", "type"=>"checkbox"), array("id"=>"NAME", "name"=>"Имя"), array("id"=>"EMAIL", "name"=>"Мыло", "required"=>true), array("id"=>"LAST_NAME", "name"=>"Фамилия"), )), array("id"=>"tab11", "name"=>"Группы", "title"=>"Группы пользователя", "icon"=>"", "fields"=>array( array("id"=>"GROUPS", "name"=>"Группы", "type"=>"custom", "value"=>'группы пользователя1', "colspan"=>true), array("id"=>"GROUPS1", "name"=>"Группы", "type"=>"custom", "value"=>'группы пользователя2'), )), array("id"=>"tab2", "name"=>"Личные данные", "title"=>"Персональная информация", "icon"=>"", "fields"=>array( array("id"=>"PERSONAL_BIRTHDAY", "name"=>"День рождения", "type"=>"date"), array("id"=>"PERSONAL_PHOTO", "name"=>"Фотография", "type"=>"file", $params=>array("iMaxW"=>150, "iMaxH"=>150, "sParams"=>"border=0", "strImageUrl"=>"", "bPopup"=>true, "sPopupTitle"=>false)), array("id"=>"PERSONAL_PROFESSION", "name"=>"Профессия"), array("id"=>"PERSONAL_WWW", "name"=>"Веб"), array("id"=>"PERSONAL_ICQ", "name"=>"АйСикЮ", "params"=>array("size"=>15)), array("id"=>"section1", "name"=>"Разделительная секция", "type"=>"section"), array("id"=>"PERSONAL_GENDER", "name"=>"Пол", "type"=>"list", "items"=>array(""=>"(пол)", "M"=>"Мужской", "F"=>"Женский")), array("id"=>"PERSONAL_PHONE", "name"=>"Телефон"), array("id"=>"PERSONAL_MOBILE", "name"=>"Мобильник"), array("id"=>"PERSONAL_CITY", "name"=>"Город"), array("id"=>"PERSONAL_STREET", "name"=>"Улица", "type"=>"textarea", "params"=>array("rows"=>5)), )), array("id"=>"tab3", "name"=>"Работа", "title"=>"Информация о работе", "icon"=>"", "fields"=>array( array("id"=>"WORK_COMPANY", "name"=>"Компания"), array("id"=>"WORK_DEPARTMENT", "name"=>"Отдел"), array("id"=>"WORK_POSITION", "name"=>"Должность"), )), ), //кнопки формы, возможны кастомные кнопки в виде html в "custom_html" "BUTTONS"=>array("back_url"=>"main.interface.grid.php", "custom_html"=>"", "standard_buttons"=>true), //данные для редактировани "DATA"=>$arResult["DATA"], ), $component ); ?> |
Наш компонент при сохранении данных получает обычный POST. Типичная обработка выглядит примерно так:
//Form submitted if($_SERVER["REQUEST_METHOD"] == "POST" && check_bitrix_sessid()) { //When Save or Apply buttons was pressed if(isset($_POST["save"]) || isset($_POST["apply"])) { //Gather fields for update $arFields = array( "SORT" => $_POST["SORT"], "NAME" => $_POST["NAME"], ); //$ob->Update($_REQUEST["ID"], $arFields); } } |
Данные примеры показывают скорее принципы работы с компонентами грида, формы и тулбара. Для дальнейшего изучения я рекомендую исходный код компонентов модуля "Универсальные списки" (lists).
Вообще все эти нововведения, которые просто обязан использовать интегратор, надо как-то более усиленно пиарить.
Есть еще ряд задач которые эти списки выполнить не могут.
Только эти списки совсем из другой оперы. Честно говоря, очень сложно представить место на типовом сайте, где бы эти списки пригодились, не говоря уже о повсеместном их использовании.
А для админки - вещь весьма ценная.
Уважаемые разработчики системы, договоритесь уже между собой, наконец!
Хороший аналогичный пример - компонент постраничной навигации.
Идеала не бывает. Где-то всегда приходится ставить ограничения кому-то неудобные. Се ля ви, как говорят французы..
Гриды показывают на презентации.
Затем в блоге демонстрируют конкретные примеры использования гридов.
Но при этом документировать не собираются.
"Солдаты, вот вам автомат, но чтобы вы случайно не стрельнули, он будет у вас деревянный". А вроде же не в игрушки играем?
Может вообще ну её нафиг, документацию эту? Хорошо подготовленный спец и так разберётся. Стратегия "Если нет документации, то слабоподготовленного разработчика это отпугнет." - это шедевр. Воспитание реальных разработчиков для суровой российской CMS.
Чересчур уж вы себе жизнь облегчаете. Вам удобно, чтобы у вас не спрашивали, поэтому вы решили так, как решили. Это очень неприятный, если не сказать более, факт отношения к своим покупателям и продлятелям.
Я-то думал, что документация должна описывать, как правильно использовать API, компоненты и т.д. Заметьте, если действать по нормальной документации, то ничего сломать нельзя. Если разработчик отклонился от заданного регламента работы с компонентом - ему ТП так и скажет. А если он все делал по документации, то уж извините, должно и работать правильно.
Опять же, если фича внутренняя и не должна использоваться разработчиками - не нужно ее документировать. Тогда ее можно менять в последующих версиях, не отвечать на запросы в ТП по ней и т.д. Но если сделаны гриды и предполагается, что разработчики будут этим пользоваться - будьте добры.
Пытаться самому угадать, как работать с компонентом - это постоянный источник неожиданных багов. Когда вдруг оказывается, что компонент работает не так, как предполагалось.
> Для хорошо подготовленного спеца разобраться в коде - проблем нет. Он и разберется и не сломает.
Гениальные слова от главы отдела документации. Да, в небольшом куске кода разобраться - это не проблема. Но разбираться в тысячах строк каждый раз, когда используешь какую-то фичу - на это у разработчиков нету ни времени, ни желания. Проще свое написать.
Представьте, если бы разработчики PHP ничего не документировали и сказали "хотите писать скрипты - разбирайтесь в исходниках PHP". Да что я объясняю главе отдела документации необходимость документирования...
Евгений, есть два момента.
Первый - недоработки нашего отдела, мои в частности.
Второе - официальная позиция компании по определенным вопросам.
За первое - можете "пинать" меня сколько сочтете нужным, если виноват, значит виноват.
ВО втором случае вы не правы. Разработчик компонентов счел необходимым недокументировать часть компонентов. Это его право.
Ждать ли его для БУС Эксперт\Бизнес?
В стандарте не нашел...
В презентации говорилось о модуле "Универсальные списки".
Я пытался найти его среди других компонентом в визуальном редакторе "Стандарта" но не нашел.
Планируется ли его включение в другие редакции битрикса?
Пробовал на стандарте Кстати )
Почему так?(
1С-Битрикс: Управление сайтом 9.0.1 Эксперт
если я обновлю ядро на локальной машине, я смогу потом второй раз обновить его уже на удаленном хостинге?
Есть вопросик.
Как можно в гридах реализовать суммириующую строку ?
Т.е. строка, в которй будет общий результат по столбцу.
Действия начальника основываются на презентациях, а мои на доках апи.
Только пока не совсем понятно, можно ли как-то управлять представлениями.
А то сейчас каждый раз приходится выбирать из списка — совсем неудобно.
зы. Хотя сейчас ещё стили посмотрел - видимо таки нету. Имхо, не помешало бы.
В конце template.php или страницы с гридом вставляем код:
Теперь через в параметре highlight GET-запроса можно передать ID документов, которые нужно подсветить (через запятую):
http://#SITE_URL#/grid/?highlight=1834562,1834566,1834612
Поправьте, плз, если на сегодняшний день есть более изящный способ)
В конец файла template.php помещаем следующий код:
Если после обработки POST-данных делается редирект, то нужно добавить в ссылку GET-параметр с именем $arResult["FORM_ID"]."_active_tab" и значением $_REQUEST[$arResult["FORM_ID"]."_active_tab"].
Возинк вопрос по настройке. При исользованиии Ajax-режима грид обновляется без перезагрузки страницы. В вашем примере это работат для поиска и при переходе по ссылкам постраничной навигации, но при попытке отсортировать список по определённой колонке происходит перезагрузка странцы (к адресу страницы добавляются параметры, например: by=login&order=asc). При вызове на странице своего (родительского) компонента использую в параметрах "AJAX_MODE" => "Y".
Как модифицировать пример, что бы сортировка по колонкам тоже проходила в Ajax-режиме?
Обратился в техподдрежку, где мне очень помог Александр Корякин.
Решение:
При вызове на странице своего (родительского) компонента, в параметрах надо указать "AJAX_MODE" => "Y" и "AJAX_ID"=> уникальный идентификатор.
В шаблоне (template.php) своего компонента, в параметрах вызова bitrix:main.interface.grid, нужно указать "AJAX_ID" => $arParams["AJAX_ID"]. Ещё, там же для красоты "AJAX_OPTION_HISTORY" => "N", что бы к url страницы ничего не добавлялось после #.
$arFilter = $grid_options->GetFilter($arResult["FILTER");
$arFilterBase = Array(
'IBLOCK_ID' => $arResult['IBLOCK_ID'],
'GLOBAL_ACTIVE' => 'Y',
'CHECK_PERMISSIONS' => 'Y'
);
$arFilter = array_merge($arFilterBase, $arFilter);
Как то так )
вот пример с main.ui.filter и main.ui.grid
Но не понятно как обновлять гриды без перезагрузки страницы по событию.
Например, нажал удалить элемент, и чтобы грид актуализировался.
page: currentPage
не пашет
// номер текущей страницы = $('.modern-page-current')[0].innerHTML
let reloadParams = { apply_filter: 'Y', clear_nav: 'N', page: $('.modern-page-current')[0].innerHTML };
let grid = BX.Main.gridManager.getInstanceById('WA_GRID');
grid.reloadTable('POST', reloadParams);
Для этого дополняем код так:
'filter' => $filter,
'select' => [
"*",
],
'offset' => $nav->getOffset(),
'limit' => $nav->getLimit(),
'order' => $sort['sort'],
'count_total' => true
]);
то есть добавляем 'count_total' => true
и дальше добавляем
далее появится постраничная навигация