Как избежать дублирования экрана управления сообщениями, когда два плагина используют одну и ту же библиотеку


У меня есть два пользовательских плагина, которые используют класс WP Logging. Он создает пользовательский тип записи, называемый журналами, который (по крайней мере, в моем случае) виден только внутри администратора, например, в /wp-admin/edit.php?s&post_status=все&post_type=wp_log.

Чтобы было проще определить, какой плагин создает журналы, я добавил несколько крючков. Оба плагина используют их одинаково. То, что они делают, это:

  1. Добавьте столбец сортируемого типа на экран "Управление публикациями" и добавьте поле тип (которое добавляет библиотека журналов WP) на этот экран.
  2. Добавьте раскрывающийся список в верхней части экрана (рядом с фильтром даты) и разрешите фильтровать сообщения по используемому значению типа.

Вот код, который я использую:

// add a sortable Type column to the posts admin
add_filter( 'manage_edit-wp_log_columns', array( $this, 'type_column' ), 10, 1 );
add_filter( 'manage_edit-wp_log_sortable_columns', array( $this, 'sortable_columns' ), 10, 1 );
add_action( 'manage_wp_log_posts_custom_column', array( $this, 'type_column_content' ), 10, 2 );

// filter the log posts admin by log type
add_filter( 'parse_query', array( $this, 'posts_filter' ), 10, 1 );
add_action( 'restrict_manage_posts', array( $this, 'restrict_log_posts' ) );

/**
 * Add a Type column to the posts admin for this post type
 *
 * @param array $columns
 * @return array $columns
 */
public function type_column( $columns ) {
    $columns['type'] = __( 'Type', 'form-processor-mailchimp' );
    return $columns;
}

/**
 * Make the Type column in the posts admin for this post type sortable
 *
 * @param array $columns
 * @return array $columns
 */
public function sortable_columns( $columns ) {
    $columns['type'] = 'type';
    return $columns;
}

/**
 * Add the content for the Type column in the posts admin for this post type
 *
 * @param string $column_name
 * @param int $post_id
 */
public function type_column_content( $column_name, $post_id ) {
    if ( 'type' != $column_name ) {
        return;
    }
    // get wp_log_type
    $terms = wp_get_post_terms(
        $post_id,
        'wp_log_type',
        array(
            'fields' => 'names',
        )
    );
    if ( is_array( $terms ) ) {
        echo esc_attr( $terms[0] );
    }
}

/**
 * Filter log posts by the taxonomy from the dropdown when a value is present
 *
 * @param object $query
 * @return object $query
 */
public function posts_filter( $query ) {
    global $pagenow;
    $type     = 'wp_log';
    $taxonomy = 'wp_log_type';
    if ( is_admin() && 'edit.php' === $pagenow ) {
        if ( isset( $_GET['post_type'] ) && esc_attr( $_GET['post_type'] ) === $type ) {
            if ( isset( $_GET[ $taxonomy ] ) && '' !== $_GET[ $taxonomy ] ) {
                $query->post_type = $type;
                $query->tax_query = array(
                    array(
                        'taxonomy' => $taxonomy,
                        'field'    => 'slug',
                        'terms'    => esc_attr( $_GET[ $taxonomy ] ),
                    ),
                );
            }
        }
    }
}

/**
 * Add a filter form for the log admin so we can filter by wp_log_type taxonomy values
 *
 * @param object $query
 * @return object $query
 */
public function restrict_log_posts() {
    $type     = 'wp_log';
    $taxonomy = 'wp_log_type';
    // only add filter to post type you want
    if ( isset( $_GET['post_type'] ) && esc_attr( $_GET['post_type'] ) === $type ) {
        // get wp_log_type
        $terms = get_terms(
            [
                'taxonomy'   => $taxonomy,
                'hide_empty' => true,
            ]
        );
        ?>
        <select name="wp_log_type">
            <option value=""><?php _e( 'All log types ', 'form-processor-mailchimp' ); ?></option>
            <?php
            $current_log_type = isset( $_GET[ $taxonomy ] ) ? esc_attr( $_GET[ $taxonomy ] ) : '';
            foreach ( $terms as $key => $term ) {
                printf(
                    '<option value="%s"%s>%s</option>',
                    $term->slug,
                    $term->slug == $current_log_type ? ' selected="selected"' : '',
                    $term->name
                );
            }
            ?>
        </select>
        <?php
    }
}

В этом случае код между двумя плагинами идентичен. Конечно, проблема, если она включена в обоих плагинах, заключается в том, что она дублирует отображение.

posts manage screen with two identical type dropdowns, and the type displayed twice in the column

Могу ли я как-нибудь проверить чтобы увидеть, были ли эти дополнения уже сделаны, прежде чем делать их?

Я подумал, что это стоит попробовать (для всех фильтров и использования doing_action для всех действий):

if ( ! doing_filter( 'manage_edit-wp_log_columns' ) ) {
    add_filter( 'manage_edit-wp_log_columns', array( $this, 'type_column' ), 10, 1 );
}

Но это ничего не изменило. Есть ли что-нибудь еще, что я могу попробовать на этом?

Author: Jonathan Stegall, 2019-08-28

2 answers

В своем коде вы добавляете фильтр следующим образом:

add_action( 'restrict_manage_posts', array( $this, 'restrict_log_posts' ) );

, который добавляет функцию, которая начинается так:

public function restrict_log_posts() {
    $type     = 'wp_log';
    $taxonomy = 'wp_log_type';
    // only add filter to post type you want
    if ( isset( $_GET['post_type'] ) && esc_attr( $_GET['post_type'] ) === $type ) {
        // get wp_log_type
        $terms = get_terms(

Во-первых, это действие передает тип сообщения, поэтому его можно упростить, настроив add_action и используя первый передаваемый параметр:

public function restrict_log_posts( $post_type ) {
    $type     = 'wp_log';
    $taxonomy = 'wp_log_type';
    // only add filter to post type you want
    if ( 'wp_log' === $post_type ) {
        // get wp_log_type
        $terms = get_terms(

Тогда мы могли бы использовать did_action, чтобы запустить его только в первый раз:

public function restrict_log_posts( $post_type ) {
    if ( did_action( 'restrict_manage_posts' ) ) {
        return;
    }
....

Но это может не дать желаемого эффекта, и это грубо. Вместо этого это действие вообще не относится к этому классу и должно быть самостоятельное действие. Тогда мы можем определить функцию, только если она еще не определена:


if ( !function_exists('restrict_logs_by_type') ) {
    add_action( 'restrict_manage_posts', 'restrict_logs_by_type', 10, 1 );
    /**
     * Add a filter form for the log admin so we can filter by wp_log_type taxonomy values
     *
     */
    function restrict_logs_by_type( $post_type ) {
        $type     = 'wp_log';
        $taxonomy = 'wp_log_type';
        // only add filter to post type you want
        if ( $type !== $post_type ) {
            return;
        }
        // get wp_log_type
        $terms = get_terms([
                'taxonomy'   => $taxonomy,
                'hide_empty' => true,
        ]);
        if ( is_wp_error( $terms ) || empty( $terms ) ) {
            // no terms, or the taxonomy doesn't exist, skip
            return;
        }

        ?>
        <select name="wp_log_type">
            <option value=""><?php esc_html_e( 'All log types ', 'form-processor-mailchimp' ); ?></option>
            <?php
            $current_log_type = isset( $_GET[ $taxonomy ] ) ? esc_attr( $_GET[ $taxonomy ] ) : '';
            foreach ( $terms as $key => $term ) {
                printf(
                    '<option value="%s"%s>%s</option>',
                    esc_attr( $term->slug ),
                    selected( $term->slug, $current_log_type, false ),
                    esc_html( $term->name )
                );
            }
            ?>
        </select>
        <?php
    }
}

Обратите внимание, что я внес ряд значительных улучшений:

  • Я исправил серьезную дыру в безопасности, добавив экранирование в ваши теги опций
  • Я заменил уродливый тернарный оператор функцией selected, предоставленной WP
  • Я заменил проверку if на !==, сделав ее охранником. Теперь вся функция может быть разделена на 1 и более удобочитаема. Кроме того, была уменьшена как цикломатическая, так и NPath сложность
  • Функция теперь автономна, никогда не было необходимости, чтобы она была частью объекта или класса
  • Я переименовал функцию, чтобы описать, что она делает
  • Если термины не найдены или таксономия не существует, эта версия полностью пропускает отображение раскрывающегося списка. Нет смысла иметь выпадающий список, если единственным пунктом является "Все". Без этого экран журнала выйдет из строя, если не будет типов журналов или записи
  • Весь фрагмент заключен в вызов function_exists, если функция загружена первым плагином, второй плагин не будет беспокоиться о ее определении и добавлении
 1
Author: Tom J Nowell, 2019-08-29 16:43:48

Теперь, когда я посмотрел на это, я подумал о возможном решении.

// add a filter to check for other plugins that might be filtering the log screen
$are_logs_filtered = apply_filters( 'wp_logging_manage_logs_filtered', false );
add_filter( 'wp_logging_manage_logs_filtered', '__return_true' );

if ( false === $are_logs_filtered ) {
    // add a sortable Type column to the posts admin
    add_filter( 'manage_edit-wp_log_columns', array( $this, 'type_column' ), 10, 1 );
    add_filter( 'manage_edit-wp_log_sortable_columns', array( $this, 'sortable_columns' ), 10, 1 );
    add_action( 'manage_wp_log_posts_custom_column', array( $this, 'type_column_content' ), 10, 2 );

    // filter the log posts admin by log type
    add_filter( 'parse_query', array( $this, 'posts_filter' ), 10, 1 );
    add_action( 'restrict_manage_posts', array( $this, 'restrict_log_posts' ) );
}

Добавление wp_logging_manage_logs_filtered в оба плагина и установка его значения true для обоих плагинов приводит к появлению поля фильтрации, отсортированного столбца и содержимого в столбце, и все это происходит только один раз.

Я склонен думать, что это лучшее, что можно сделать, но, конечно, я мог что-то упустить.

 0
Author: Jonathan Stegall, 2019-08-28 14:21:33