Добавьте столбец «Последний отредактирован» в таблицу списка типов сообщений

В настоящее время я использую плагин Admin Columns Pro для WordPress для изменения некоторых столбцов в бэкэнд. Плагин содержит функциональные возможности для отображения автора сообщения (или в моем случае, продукта) в качестве столбца.

Тем не менее, я нахожусь в положении, когда мне нужно выяснить, кто самый последний редактор продукта. В основном то, что у нас есть сейчас, – это один человек, создающий черновик продукта, затем кто-то заканчивает его, и мне нужно увидеть оба этих имени (один создатель и один the_modified_author).

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

Есть предположения?

Solutions Collecting From Web of "Добавьте столбец «Последний отредактирован» в таблицу списка типов сообщений"

Изменение столбцов администратора относится к плагину , а не к файлу темы, потому что темы никогда не должны изменять ничего, кроме внешнего вывода. Вы можете получить полный плагин здесь: Column Editor редактора плагинов .

Посмотрев на документы, с которыми вы связались, я вижу, что разработчику плагина требуется дочерний класс, который смешивает несколько отдельных задач. Это не хорошо. Через минуту мы увидим.

С другой стороны, мы все равно не в бизнесе поддержки плагинов . Мы находимся в поиске собственного решения . Поэтому давайте проигнорируем этот плагин и рассмотрим основные функции.

введите описание изображения здесь

  • Заголовки столбцов для типов сообщений регистрируются в фильтре "manage_{$post_type}_posts_columns" . Для product типа post это будет "manage_product_posts_columns" .
  • Содержимое столбца может быть напечатано в действии "manage_{$post_type}_posts_custom_column" .

Это очень бедные имена: они действительно не говорят нам, что здесь происходит.

Оба крючка вызываются при загрузке файла wp-admin/edit.php , поэтому мы ждем, пока не появится действие 'load-edit.php' .

На странице редактирования мы рассмотрим объект, возвращаемый get_current_screen() : если его свойство id соответствует "edit-$post_type" , мы регистрируем наши обратные вызовы.

"manage_{$post_type}_posts_columns" дает нам массив существующих заголовков столбцов. Мы просто добавляем запись и возвращаем массив обратно:

 function add_column( Array $columns ) { $columns[ 'modified_author' ] = 'Last modified by'; return $columns; } 

Действие "manage_{$post_type}_posts_custom_column" дает нам $column_name и $post_id . Мы сравниваем имя столбца с нашим ранее зарегистрированным именем 'modified_author' , потому что могут быть другие пользовательские столбцы, и мы не хотим их трогать. Если имя данного столбца наш, мы используем $post_id чтобы получить идентификатор последнего автора, который изменил сообщение:

 $last_id = get_post_meta( $post_id, '_edit_last', TRUE ); $last_user = get_userdata( $last_id ); print esc_html( $last_user->display_name ); 

Краткая версия нашего кода может выглядеть так:

 add_action( 'load-edit.php', function() { $post_type = 'product'; $col_name = 'modified_author'; $screen = get_current_screen(); if ( ! isset ( $screen->id ) ) return; if ( "edit-$post_type" !== $screen->id ) return; add_filter( "manage_{$post_type}_posts_columns", function( $posts_columns ) use ( $col_name ) { $posts_columns[ $col_name ] = 'Last modified by'; return $posts_columns; } ); add_action( "manage_{$post_type}_posts_custom_column", function( $column_name, $post_id ) use ( $col_name ) { if ( $col_name !== $column_name ) return; $last_id = get_post_meta( $post_id, '_edit_last', TRUE ); if ( ! $last_id ) { print '<i>Unknown</i>'; return; } $last_user = get_userdata( $last_id ); print esc_html( $last_user->display_name ); }, 10, 2 ); }); 

Это работает, но это не хорошо.

  • Мы не можем повторно использовать этот код для добавления того же столбца к сообщениям или страницам или к чему-то еще.
  • Мы не можем проверить код, потому что все происходит в один звонок.
  • Разделения проблем не происходит. Мы смешиваем слишком много разных задач:
    • Регистрация обратных вызовов
    • Проверка имени столбца
    • Получение значения из таблицы метаданных
    • Печать экранированного вывода столбца

Давайте улучшим наш код!

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

 class Controller { /** * @var Column_Data */ private $data; /** * @var Column_View */ private $view; /** * @param Column_Data $data * @param Column_View $view */ public function __construct( Column_Data $data, Column_View $view ) { $this->data = $data; $this->view = $view; } /** * @return void */ public function setup() { $screen = get_current_screen(); $post_type = $this->data->get_post_type(); if ( ! isset ( $screen->id ) ) return; if ( "edit-$post_type" !== $screen->id ) return; add_filter( "manage_{$post_type}_posts_columns", [ $this->data, 'add_column' ] ); add_action( "manage_{$post_type}_posts_custom_column", [ $this->view, 'render_column' ], 10, 2 ); } } 

Column_Data и Column_View – это интерфейсы , а не конкретные классы, поэтому мы можем повторно использовать этот контроллер с разными поставщиками данных или обработчиками вывода. Давайте построим эти интерфейсы.

Интерфейс для данных требует методов для

  • добавить заголовки столбцов
  • получить контент (например, отображаемое имя автора)
  • сообщите контроллеру, какой тип сообщения он обрабатывает и
  • представление, если оно работает в правильном столбце, когда оно вызывается
 interface Column_Data { /** * @param array $columns * @return array */ public function add_column( Array $columns ); /** * @param int $post_id * @return string */ public function get_column_content( $post_id ); /** * @return string */ public function get_post_type(); /** * @param $column_name * @return bool */ public function is_valid_column( $column_name ); } 

Объекту output / view нужен только один метод:

 interface Column_View { /** * @param string $column_name * @param int $post_id * @return void */ public function render_column( $column_name, $post_id ); } 

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

 class Last_Mod_Author_Column_Output implements Column_View { /** * @var Column_Data */ private $data; /** * @param Column_Data $data */ public function __construct( Column_Data $data ) { $this->data = $data; } /** * @param string $column_name * @param int $post_id * @return void */ public function render_column( $column_name, $post_id ) { if ( ! $this->data->is_valid_column( $column_name ) ) return; $content = $this->data->get_column_content( $post_id ); if ( '' === $content ) print '<i>Unknown</i>'; else print esc_html( $content ); } } 

Модель данных должна знать тип сообщения, поэтому мы передаем это конструктору конкретного класса:

 class Last_Mod_Author_Column_Data implements Column_Data { /** * @var string */ private $post_type; /** * @var string */ private $column_name = 'modified_author'; /** * @param string $post_type */ public function __construct( $post_type ) { $this->post_type = $post_type; } /** * @param array $columns * @return array */ public function add_column( Array $columns ) { $columns[ $this->column_name ] = 'Last modified by'; return $columns; } /** * @param int $post_id * @return string */ public function get_column_content( $post_id ) { $last_id = get_post_meta( $post_id, '_edit_last', TRUE ); if ( ! $last_id ) { return ''; } $last_user = get_userdata( $last_id ); return $last_user->display_name; } /** * @return string */ public function get_post_type() { return $this->post_type; } /** * @param $column_name * @return bool */ public function is_valid_column( $column_name ) { return $this->column_name === $column_name; } } 

И теперь мы можем использовать эти классы:

 add_action( 'load-edit.php', function() { $model = new Last_Mod_Author_Column_Data( 'product' ); $view = new Last_Mod_Author_Column_Output( $model ); $controller = new Controller( $model, $view ); $controller->setup(); }); 

Мы можем использовать один и тот же код для страниц с небольшим изменением:

 add_action( 'load-edit.php', function() { $model = new Last_Mod_Author_Column_Data( 'page' ); $view = new Last_Mod_Author_Column_Output( $model ); $controller = new Controller( $model, $view ); $controller->setup(); }); 

Почему этот довольно длинный код лучше?

Мы могли бы реализовать интерфейс данных в классе, который извлекает значение из другой таблицы или текстового файла или внешнего API – без каких-либо изменений в контроллере или представлении.

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

Мы можем протестировать все общедоступные методы всех классов, предоставив фиктивные объекты для зависимостей (заглушек).

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