Добавляем колонки у постов в админке (сортируемые)
Поговорим о создании новых колонок в таблице записей (постов) в админке. Также рассмотрим как сделать такие колонки сортируемыми, как например, сортируется колонка даты. Так можно будет создать колонку с данными произвольного поля и затем сортировать записи, выбирая нужные. Делается все это просто.
В этой статье, в качестве примера, создадим колонку "Визиты", с данными произвольного поля views, куда записываются посещения. Колонка получится сортируемая.
Хуки для создания колонок
- manage_(screen_id)_columns
Позволяет добавить колонки в таблицу записей на указанном экране (у нас edit-post).
Передает массив с данными колонок, который мы можем изменить, добавив свою колонку (views) или удалить существующую через unset(). Название нашего фильтра будет:
manage_edit-post_columns
.- manage_(post_type)_posts_columns
- Аналогичный предыдущему - добавляет колонки. Только тут указывается тип записи а не ID экрана... Название нашего фильтра будет:
manage_post_posts_columns
. С версии 3.1. рекомендуется использовать этот хук. - manage_(post_type)_posts_custom_column
Отвечает за заполнение данных колонки на странице постов. В нашем случае:
manage_post_posts_custom_column
.Передает название колонки и ID поста.
- manage_(screen_id)_sortable_columns
Аналогичный первому — регистрирует сортируемую колонку, где мы указываем имя запроса orderby.
Также передает массив с зарегистрированными сортируемыми колонками. В нашем случае фильтр выглядит так:
manage_edit-post_sortable_columns
.- pre_get_posts (wp-includes/query.php)
Этот фильтр-действие срабатывает в самом начале метода get_posts() класса WP_query.
Хук передает весь класс по ссылке (&$this). Используя этот фильтр, мы можем задать параметры главного запроса WP ($wp_query), по которому потом строится вывод.
Чтобы узнать screen_id используем функцию get_current_screen(). Её можно повесить на хук in_admin_header:
add_action( 'in_admin_header', function(){ echo '<pre>'. print_r( get_current_screen(), 1 ) .'</pre>'; } );
В нашем случае screen_id = edit-post
- это страница редактирования постов в админке.
Создаем колонку
Вставляем такой код в файл темы function.php:
// создаем новую колонку add_filter( 'manage_' . 'post' . '_posts_columns', 'add_views_column', 4 ); function add_views_column( $columns ) { $num = 2; // после какой по счету колонки вставлять новые $new_columns = [ 'views' => 'Визиты', ]; return array_slice( $columns, 0, $num ) + $new_columns + array_slice( $columns, $num ); } // заполняем колонку данными // wp-admin/includes/class-wp-posts-list-table.php add_action( 'manage_' . 'post' . '_posts_custom_column', 'fill_views_column', 5, 2 ); function fill_views_column( $colname, $post_id ) { if( $colname === 'views' ){ echo get_post_meta( $post_id, 'views', 1 ); } }
На этом можно остановится, если нам не нужно сортировать колонку — будет просто колонка с данными.
Делаем колонку сортируемой
// добавляем возможность сортировать колонку add_filter( 'manage_edit-' . 'post' . '_sortable_columns', 'add_views_sortable_column' ); function add_views_sortable_column( $sortable_columns ) { $sortable_columns['views'] = [ 'views_views', false ]; // false = asc (по умолчанию) // true = desc return $sortable_columns; }
Здесь ключ views
должен совпадать с ключом при регистрации колонки: $out['views']
и $sortable_columns['views']
. Значение: views_views будет значением параметра запроса "orderby", который WordPress добавит автоматически (&orderby=views_views). Это же значение будет добавлено в параметры запрос WP_query и если оно будет совпадать со значениями известными WP ('title', 'date', 'modified', 'comment_count' и т.д.), то WP сам отсортирует колонку как нужно и тут можно остановится. Полный список значений известных WP, исключения составляют: meta_value и meta_value_num.
Если мы укажем meta_value вместо views_views, WP не сможет автоматически выполнить правильную сортировку. Поэтому, если в параметре orderby указывается наше значение views_views (&orderby=views_views), то мы создадим кастомный, нужный нам запрос.
Для сортировки по произвольному полю, проще всего изменить аргументы базового запроса, использовав хук pre_get_posts
. Но важно понимать, что этот хук глобальный и срабатывает всякий раз, когда генерируется страница и не только в админке, но и на фронте. Поэтому мы должны точно указать в каком случае изменять запрос, этим случаем у нас будет значение аргумента orderby = views_views. Во всех остальных случаях запрос не трогаем.
Вариант 1:
// изменяем запрос при сортировке колонки add_action( 'pre_get_posts', 'add_column_views_request' ); function add_column_views_request( $query ) { if( ! is_admin() || ! $query->is_main_query() || $query->get( 'orderby' ) !== 'views_views' || get_current_screen()->id !== 'edit-post' ){ return; } $query->set( 'meta_key', 'views' ); $query->set( 'orderby', 'meta_value_num' ); }
Вариант 2: принцип точно такой же, только используется хук request:
// изменяем запрос при сортировке колонки add_filter( 'request', 'add_column_views_request' ); function add_column_views_request( $vars ) { if( isset( $vars['orderby'] ) && $vars['orderby'] === 'views_views' ){ $vars['meta_key'] = 'views'; $vars['orderby'] = 'meta_value_num'; } return $vars; }
Вариант 3: принцип взял из статьи Sortable Taxonomy Columns.
Тут мы изменяем SQL запрос, а не параметры передаваемые WP_query. Пригодится, если нужно создать какую-нибудь уникальную сортировку. Переделал под наш случай:
// изменяем запрос при сортировке колонки add_filter( 'posts_clauses', 'add_column_views_request', 10, 2 ); function add_column_views_request( $clauses, $wp_query ) { if( 'views_views' != $wp_query->query['orderby'] ){ return $clauses; } global $wpdb; $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID={$wpdb->postmeta}.post_id"; //$clauses['where'] .= " AND {$wpdb->postmeta}.meta_key='views'"; $clauses['orderby'] = " {$wpdb->postmeta}.meta_value+0 "; $clauses['orderby'] .= ( 'ASC' == strtoupper( $wp_query->get( 'order' ) ) ) ? 'ASC' : 'DESC'; // еще изменяемые элементы //$clauses['groupby'] //$clauses['distinct'] //$clauses['fields'] // wp_posts.* //$clauses['limits'] // LIMIT 0, 20 return $clauses; }
Посты с незаполненными произвольными полями (его не будет существовать у поста), не попадут в выборку.
Заметка
Варианты выше показывают при сортировке только те записи, у которых указанное метаполе существует. Отсортировать так, чтобы были и записи у которых поля нет, сложно! Для этого нужно писать отдельный сложный запрос. Гораздо проще и логичнее обойтись тем что получается...
Например, так (или аналогичным способом) сделать не получится:
Ширина колонки
Еще может пригодится отредактировать ширину колонки, а то бывает растягивается без надобности. Ширину указываем так:
// подправим ширину колонки через css add_action('admin_head', 'add_views_column_css'); function add_views_column_css(){ echo '<style type="text/css">.column-views{ width:10%; }</style>'; }
Код целиком (в виде класса)
/** * Дополнительные сортируемые колонки для постов в админке */ final class My_Sortable_Post_Columns { public static function init() { // создаем новую колонку add_filter( 'manage_post_posts_columns', [ __CLASS__, 'add_columns' ], 4 ); // заполняем колонку данными - wp-admin/includes/class-wp-posts-list-table.php add_filter( 'manage_post_posts_custom_column', [ __CLASS__, 'fill_columns' ], 5, 2 ); // подправим ширину колонки через css add_action( 'admin_head', [ __CLASS__, '_css' ] ); // добавляем возможность сортировать колонку add_filter( 'manage_edit-post_sortable_columns', [ __CLASS__, 'add_sortable_columns' ] ); // изменяем запрос при сортировке колонки add_filter( 'pre_get_posts', [ __CLASS__, 'handle_sort_request' ] ); } public static function add_columns( $columns ) { // вставляем в нужное место - 3 - 3-я колонка $out = []; foreach( $columns as $col => $name ){ if( ++$i == 3 ){ $out['views'] = 'Визиты'; } $out[ $col ] = $name; } return $out; } public static function add_sortable_columns( $sortable_columns ) { $sortable_columns['views'] = 'views_views'; return $sortable_columns; } public static function fill_columns( $colname, $post_id ) { if( $colname === 'views' ){ echo get_post_meta( $post_id, 'views', 1 ); } } public static function _css() { if( 'edit' === get_current_screen()->base ){ echo '<style>.column-views{ width:10%; }</style>'; } } public static function handle_sort_request( $object ) { if( $object->get( 'orderby' ) != 'views_views' ){ return; } $object->set( 'meta_key', 'views' ); $object->set( 'orderby', 'meta_value_num' ); } }