Intereting Posts
Автоматическое обновление WordPress Старая дата публикации и установка Как отображать только две категории Вместо отображения всех категорий Файл темы для всех страниц, являющихся дочерним элементом определенной страницы Получение постоянной связи внутри цикла Не удается увидеть выпадающий ярлык на панели управления сайтом в сети WordPress, почему? Настроить начало и конец выдержки Различные шрифты для заголовков Как я могу скрыть, что я использую WordPress (с общим кэшем W3) Галерея изображений и postmeta почты Настройка многосайтовых настроек при создании нового сайта https to https problem – 404 и не может войти Вкладки и виджет категории Загрузите несколько медиафайлов с сообщением Решите конфигурацию Metabox для всех пользователей Как добавить Ajax в календарь виджета по умолчанию для поддержки загрузки следующего / предыдущего месяца?

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

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

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

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

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

Изменение столбцов администратора относится к плагину , а не к файлу темы, потому что темы никогда не должны изменять ничего, кроме внешнего вывода. Вы можете получить полный плагин здесь: 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 – без каких-либо изменений в контроллере или представлении.

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

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

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