Создание таблицы в стиле админ-панели?
Какой рекомендуемый способ создания страницы с таблицей в стиле таблиц, отображающих записи или пользователей в области администрирования?
Я расширяю плагин Cache Images, и он содержит таблицу с доменами и количеством изображений с этого домена. Поэтому нет эквивалентной существующей таблицы, на которой я мог бы основываться (в первой версии этого вопроса я спрашивал о таблице с записями, но там я мог (возможно) расширить существующую таблицу записей).
Стоит ли мне просто основываться на странице обзора записей и начать с <table class="widefat">
, или есть лучшие функции, которые сейчас обрабатывают это? Знаете ли вы чистый, пустой пример таблицы с пагинацией, на котором я мог бы основывать свою работу?

Вот что я обычно использую:
<table class="widefat fixed" cellspacing="0">
<thead>
<tr>
<th id="cb" class="manage-column column-cb check-column" scope="col"></th> // эта колонка содержит чекбоксы
<th id="columnname" class="manage-column column-columnname" scope="col"></th>
<th id="columnname" class="manage-column column-columnname num" scope="col"></th> // "num" добавлен, потому что колонка содержит числа
</tr>
</thead>
<tfoot>
<tr>
<th class="manage-column column-cb check-column" scope="col"></th>
<th class="manage-column column-columnname" scope="col"></th>
<th class="manage-column column-columnname num" scope="col"></th>
</tr>
</tfoot>
<tbody>
<tr class="alternate">
<th class="check-column" scope="row"></th>
<td class="column-columnname"></td>
<td class="column-columnname"></td>
</tr>
<tr>
<th class="check-column" scope="row"></th>
<td class="column-columnname"></td>
<td class="column-columnname"></td>
</tr>
<tr class="alternate" valign="top"> // эта строка содержит действия
<th class="check-column" scope="row"></th>
<td class="column-columnname">
<div class="row-actions">
<span><a href="#">Действие</a> |</span>
<span><a href="#">Действие</a></span>
</div>
</td>
<td class="column-columnname"></td>
</tr>
<tr valign="top"> // эта строка содержит действия
<th class="check-column" scope="row"></th>
<td class="column-columnname">
<div class="row-actions">
<span><a href="#">Действие</a> |</span>
<span><a href="#">Действие</a></span>
</div>
</td>
<td class="column-columnname"></td>
</tr>
</tbody>
</table>
Надеюсь, это поможет.

можно ли также автоматически вставлять пагинацию вот так? (например, показывать записи 1-20)

@MichielStandaert если вам нужен пагинированный результат, вы можете использовать paginate_links

Спасибо! (Но я всё ещё спрашиваю себя, почему они не использовали :odd
для строк вместо того, чтобы заставлять нас добавлять класс каждые две строки...)

Используйте Core API, а не только его CSS
Обычно вы просто используете экземпляр класса WP_List_Table
.
Руководства:
- Подробнее об этом в Codex здесь.
- Также руководство от WP Engineer - слишком объемное, чтобы копировать его сюда.
- И еще одно руководство на Smashing Magazine в сети.
Преимущества?
ДА!
Вы можете добавить пагинацию, поисковые поля, действия и любую магию, которую только сможете вообразить (и сумеете запрограммировать).

Небольшая подсказка в виде ссылки для просмотра разметки и классов административного интерфейса, только с целью создания таблиц: https://github.com/bueltge/WordPress-Admin-Style

>Доступ к этому классу помечен как приватный. Это означает, что он не предназначен для использования разработчиками плагинов и тем, так как может быть изменен без предупреждения в любом будущем релизе WordPress. Если вы все же хотите использовать этот класс, вам следует создать его копию для использования и распространения в рамках вашего проекта, либо использовать его на свой страх и риск.

@AustinPray Копию? Нет, пожалуйста, не делайте этого. Существуют бета-версии, релиз-кандидаты и другие предварительные версии WordPress. Просто обновите свою реализацию/расширение. Если вам действительно нужно пойти другим путем, просто напишите что-то лучше самостоятельно. Код ядра не настолько хорош.

@kaiser Не стреляйте в гонца, это не мои слова. Я цитировал WP Codex. Хотя подписаться на бесконечное регрессионное тестирование с каждым бета- и RC-релизом не звучит намного лучше, чем копирование класса. Согласен, что написание собственного простого класса — это более правильный путь.

@AustinPray Без обид :) Codex написан такими же людьми, как вы и я. Фактически, вы можете прямо сейчас исправить это утверждение, и люди будут цитировать его.

К сожалению, оба руководства безнадежно устарели, начиная с WordPress 5, что я понял на собственном горьком опыте, ломая вещи (также в руководстве от Smashing Magazine есть вопиющие синтаксические ошибки — кто-то не справился с копированием и вставкой). Я не нашел актуальной альтернативы (custom-list-table-example
лишь немного лучше), а в Codex многое отсутствует. Мне удалось написать простую реализацию, изучая сам WordPress, особенно class-wp-users-list-table.php
, который почти полностью функционален, но проще других WP-классов.

@Guss Этот ответ из 2012 года. Возможно, вы захотите добавить свой собственный ответ, чтобы предложить более современное решение :)

Используйте этот пример (написанный в виде плагина) для создания своих административных таблиц:
http://wordpress.org/extend/plugins/custom-list-table-example/
Он использует встроенный класс WP_List_Table.

Я считаю, что этот ответ должен быть принятым. Также ознакомьтесь с этой статьей в Smashing Magazine, где описывается похожий подход: http://wp.smashingmagazine.com/2011/11/03/native-admin-tables-wordpress/

Также вы можете использовать этот небольшой плагин для просмотра возможностей бэкенда в WP: https://github.com/bueltge/WordPress-Admin-Style

Здесь представлено много хороших вариантов. Но нет быстрого и грязного решения:
<table class="widefat striped fixed">
<thead>
<tr>
<th>Заголовок1</th>
<th>Заголовок2</th>
<th>Заголовок3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Содержимое1</td>
<td>Содержимое2</td>
<td>Содержимое3</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Подвал1</th>
<th>Подвал2</th>
<th>Подвал3</th>
</tr>
</tfoot>
</table>
Пояснение
- Класс
widefat
используется вcommon.css
, который загружается в админке, чтобы таблица выглядела как стандартная WP-таблица. - Класс
striped
добавляет полосатый фон (как ни удивительно). - Класс
fixed
добавляет CSS-свойство:table-layout: fixed;

Для тех, кто хочет реализовать WP_List_Table
, обратите внимание, что все найденные мной руководства устарели и либо содержат избыточный код, либо предлагают решения, которые больше не работают.
Вот минимальный рабочий пример, который должен быть понятен без дополнительных "руководств" и поможет вам начать.
Включено:
- быстрые фильтры (представления)
- поисковая строка
- действия со строками
Отсутствует:
- настройка размера страницы (я не видел, чтобы WordPress использовал это)
- массовые действия
- выпадающие фильтры
class My_List_Table extends WP_List_Table {
function __construct() {
parent::__construct([
'singular' => 'employee',
'plural' => 'employees',
]);
}
function get_columns() {
return [
'name' => __('Имя'),
'employer' => __('Работодатель'),
'rank' => __('Должность'),
'phone' => __('Телефон'),
'joined' => __('Дата приема'),
];
}
/* Опционально - без этого столбцы нельзя сортировать */
public function get_sortable_columns() {
return [
// ключи - названия столбцов, как выше
// значения - названия полей для сортировки, которые требуются вашей модели данных
'name' => 'name',
'employer' => 'employer',
'rank' => 'rank',
'joined' => 'joined',
];
}
public function prepare_items() {
// поддержка поисковой строки
$search = @$_REQUEST['s'] ? wp_unslash(trim($_REQUEST['s']))) : '';
// получаем количество записей на страницу из настроек
$per_page = $this->get_items_per_page('my_list_table_per_page');
// заполняем массив данных элементами вашей модели. В моей реализации это
// объекты StdClass с различными полями, но это может быть что угодно,
// как мы увидим далее.
$this->items = get_model_items([
'offset' => ($this->get_pagenum()-1)*$per_page,
'count' => $per_page,
'orderby' => @$_GET["orderby"] ?: 'id', // поле для сортировки по умолчанию, если не указано
'order' => @$_GET["order"] ?: 'ASC', // направление сортировки по умолчанию
'search' => $search, // передаем строку поиска, если задана
'status' => @$_REQUEST['status'] // передаем фильтр представления, если задан [см. get_views()]
);
$this->set_pagination_args([
"total_items" => get_model_item_count(),
"per_page" => $per_page,
]);
// `get_model_item_count` должно быть общим количеством записей после
// фильтрации (представления и поиск), но до пагинации. Это может быть сложно/неэффективно
// реализовать в MySQL. Если вы хотите использовать здесь результат `COUNT(*)`,
// вас никто не осудит.
}
public function column_default($item, $column_name) {
// стандартное представление столбца
// Большинство полей моего объекта можно выводить как есть, поэтому у нас есть
// общий метод для обработки этого.
return $item->$column_name;
}
/* Опционально, если у вас есть данные, требующие специального форматирования */
public function column_joined($item) {
// Поле 'joined' является объектом DateTime и не может быть неявно
// преобразовано в строку встроенной логикой, поэтому нам нужно сделать это вручную
return $item->joined->format("Y-m-d");
}
/* Опционально - рисуем быстрые фильтры над таблицей */
public function get_views() {
$makelink = function($filter_val, $name) { // инструмент для DRY при создании представлений
$filter_name = 'status';
return '<a href="'
. esc_url(add_query_arg($filter_name, $filter_val)) . '" ' .
(@$_REQUEST[$filter_name]==$filter_val ?
'class="current" aria-current="page"' : ''). ">" .
$name . "</a>";
};
return [
'all' => $makelink(false, __('Все')),
'green' => $makelink('green', __('Новички')),
'pros' => $makelink('pro', __('Профессионалы')),
'bofh' => $makelink('veteran', __('Опытные сотрудники')),
];
}
/* Опционально: действия со строками */
public function handle_row_actions($item, $column_name, $primary) {
$out = parent::handle_row_actions($item, $column_name, $primary);
if ($column_name === $primary)
$out .= $this->row_actions([
'edit' => sprintf('<a href="%s">%s</a>',
add_query_arg('employee_id', $item->id, admin_url('admin.php?page=edit-employee')),
__("Редактировать")),
'delete' => sprintf('<a href="%s">%s</a>',
add_query_arg('employee_id', $item->id, admin_url('admin.php?page=delete-employee')),
__("Удалить")),
]);
return $out;
}
}
Функция для страницы администратора (для add_menu_page
/add_submenu_page
) может выглядеть так:
function drawAdminPage() {
$my_list_table = new My_List_Table();
$my_list_table->prepare_items();
?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php _e('Заголовок страницы администратора')?></h1>
<hr class="wp-header-end">
<?php $my_list_table->views() ?>
<form id="employee-filter" method="get">
<input type="hidden" name="page" value="<?php echo $_REQUEST['page']?>">
<?php $my_list_table->search_box(__('Поиск'), 'employee') ?>
<?php $my_list_table->display(); ?>
</form>
</div>
<?php
}

Возможно, вы хотели бы добавить фильтр к списку вашего пользовательского типа записей в админке? В приведённом ниже ответе показано, как это сделать с таксономией, но вы легко можете использовать другие критерии в вашем хуке restrict_manage_posts
:
Дайте мне знать, если у вас есть дополнительные вопросы.

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

@Jan: Ах. Да, думаю, вы нашли истину, что нет хорошего инкапсулированного способа сделать это, кроме как писать (дублирующийся) HTML. Я часто сталкивался с такой же проблемой. Может быть, создайте тикет на trac с запросом этого улучшения и пришлите ссылку/номер тикета сюда, чтобы мы могли его поддержать.
