Tworzenie przyjaznego dla użytkownika filtra URL

Ten poradnik pokazuje, jak utworzyć przyjazny dla użytkownika filtr URL dla dowolnego typu wpisów w WordPressie. Postępuj zgodnie ze szczegółową instrukcją, aby usprawnić filtrowanie za pomocą uporządkowanych i czytelnych adresów URL.

Tworzenie przyjaznego dla użytkownika filtra URL

Tworzymy filtr oparty na taksonomiach i polach meta dla dowolnego typu wpisu. Ten kod i podejście są uniwersalne i można je zastosować do wszystkich typów wpisów oraz taksonomii — wystarczy podstawić własne dane.

Przykład tworzenia adresu URL:
/{post_type_name}/filter/{taxonomy_name}-in-{term_slug}-or-{second_term_slug}-and-{meta_name}-in-{meta_value}

Separators:

  • -and- — separator pierwszego poziomu
  • -in- — separator drugiego poziomu
  • -or- — separator trzeciego poziomu

Najpierw musimy nadpisać reguły permalinków. W tym celu używamy add_action(‘init’, ‘rewrite_rules’), gdzie bots to mój dedykowany typ wpisu.

/**
 * Rewrite rules filter.
 *
 * @return void
 */
function rewrite_rules(): void {
add_rewrite_rule(
'bots/filter/([-_a-zA-Z0-9+%.:]+)/page/(\d+)/?$',
'index.php?post_type=bots&filter=$matches[1]&paged=$matches[2]',
'top'
); // rule for pagination support

add_rewrite_rule(
    'bots/filter/([-_a-zA-Z0-9+%.:]+)/?$',
    'index.php?post_type=bots&filter=$matches[1]',
    'top'
); // main rule for overriding

}
add_action('init', 'rewrite_rules');

Teraz musimy dodać naszą zmienną do query_vars. Jest to konieczne, aby przechowywać nasze dane w zmiennej globalnej i umożliwić dostęp do nich.

/**
 * Add Query vars filter
 *
 * @param array $vars Vars Query.
 *
 * @return array
 */
function add_filter_vars( array $vars ): array {
    $vars[] = 'filter';

    return $vars;
}

add_filter( 'query_vars', 'add_filter_vars'  );

Następnie tworzymy formularz filtra. Poniżej znajduje się kod, którego użyłem w jednym z moich projektów.

<form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="post" class="filter-form">
    <!-- Your HTML form code here -->
    <?php wp_nonce_field(TBL_FILTER_ACTION_NAME, 'filter_nonce'); ?>
    <input type="hidden" name="action" value="<?php echo esc_attr(TBL_FILTER_ACTION_NAME); ?>">
    <input type="hidden" id="sort_by" name="tbl_filter[sort_by]" value="">
    <input type="hidden" id="price_sort" name="tbl_filter[price_sort]" value="">
    <input class="button" type="submit" value="<?php esc_html_e('Filtered', 'telegram-bots-listing'); ?>">
</form>

Do przetwarzania danych z formularza używam admin-post, który pozwala przetwarzać dane formularza i generować na ich podstawie adres URL, działając na podobnej zasadzie jak standardowy handler żądań AJAX.

/**
 * Filter bots handler.
 *
 * @return void
 */
public function filter_bot_handler(): void {
    $filter_nonce = !empty($_POST['filter_nonce']) ? filter_var(wp_unslash($_POST['filter_nonce']), FILTER_SANITIZE_SPECIAL_CHARS) : null; // retrieve and clean data with the nonce code
    if (!wp_verify_nonce($filter_nonce, Main::TBL_FILTER_ACTION_NAME)) {
        wp_safe_redirect(filter_input(INPUT_POST, '_wp_http_referer', FILTER_SANITIZE_STRING));
        die();
    } // check the nonce, and if it's not valid, redirect the user back

    $filter_args = [
        'category'   => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FORCE_ARRAY],
        'language'   => ['filter' => FILTER_SANITIZE_SPECIAL_CHARS, 'flags' => FILTER_FORCE_ARRAY],
        'price'      => FILTER_SANITIZE_SPECIAL_CHARS,
        'sort_by'    => FILTER_SANITIZE_SPECIAL_CHARS,
        'price_sort' => FILTER_SANITIZE_SPECIAL_CHARS,
    ]; // array for cleaning and validating form data

    // phpcs:disable
    $filter_request = !empty($_POST['tbl_filter']) ? filter_var_array(wp_unslash($_POST['tbl_filter']), $filter_args) : [];
    $rating = !empty($_POST['review_rating']) ? filter_var(wp_unslash($_POST['review_rating']), FILTER_SANITIZE_NUMBER_INT) : null;
    // phpcs:enable

    if (!empty($rating)) {
        $filter_request['rating'] = $rating;
    }

    $param_url = generate_filter_url($filter_request); // pass the array to a function that will generate our URL
    wp_safe_redirect($param_url . '/', 301); // redirect the user to our URL
    die();
}

add_action('admin_post_nopriv_' . YOUR_ACTION_NAME, 'filter_bot_handler');
add_action('admin_post_' . YOUR_ACTION_NAME, 'filter_bot_handler');

Tablica zamienników znaków specjalnych:

const TBL_CODE_MATCH = [
    '"', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', '|', ':', '"', '<', '>', '?', '[', ']', ';', "'", '', '.', '/', '', '~', '`', '='
];

Funkcja generująca adres URL filtra:

/**
 * Generate filter URL.
 *
 * @param array $params Params.
 * @return string
 */
function generate_filter_url(array $params): string {
    $params_array = [];
    $redirect = get_bloginfo('url') . '/bots/filter/';
    foreach ($params as $key => $item) {
        $params_array[$key] = [];
        if (is_array($item)) {
            foreach ($item as $value) {
                $params_array[$key][] = rawurlencode(str_replace(self::TBL_CODE_MATCH, '-', $value)); // clean special characters in the data and replace them with '-'
            }
        } else {
            $params_array[$key][] = rawurlencode(str_replace(self::TBL_CODE_MATCH, '-', $item)); // clean special characters in the data and replace them with '-'
        }
    }
    $i = 0;
    foreach ($params_array as $key => $param) {
        if (0 === $i && !empty($param[0])) {
            $redirect .= $key . '-in-' . implode('-or-', (array) $param);
            $i++;
            continue;
        }
        foreach ($param as $index => $item) {
            if (!empty($item)) {
                $addon = (0 === $index) ? '-and-' . $key . '-in-' : '-or-';
                $redirect .= $addon . $item;
            }
        }
    }
    return $redirect;
}

Ostatnim krokiem jest sparsowanie tego adresu URL i zmodyfikowanie głównego zapytania za pomocą filtra z wykorzystaniem następującej akcji: add_action(‘pre_get_posts’, ‘add_query_filter’).

/**
 * Filter query.
 *
 * @param WP_Query $query WP Query.
 * @return WP_Query
 */
function add_query_filter(WP_Query $query) {
    if (
        !is_admin() && $query->is_main_query() &&
        is_post_type_archive('bots') &&
        !empty($query->query_vars['filter'])
    ) {
        // check for main_query and if data is present in query_vars['filter']
        $args = [];
        $filter_params = parse_url_query($query->query_vars['filter']); // parse our URL
        if (empty($filter_params)) {
            return $query;
        }
        foreach ($filter_params as $key => $value) {
            switch ($key) {
                case 'category':
                    $args['tax_query'][] = [
                        'taxonomy' => 'bot-category',
                        'field'    => 'slug',
                        'terms'    => $value,
                        'operator' => 'IN'
                    ];
                    break;
                case 'language':
                    $args['tax_query']['relation'] = 'AND';
                    $args['tax_query'][] = [
                        'taxonomy' => 'bot-languages',
                        'field'    => 'slug',
                        'terms'    => $value,
                        'operator' => 'IN'
                    ];
                    break;
                // additional cases...
            }
        }
        foreach ($args as $key => $arg) {
            if (!empty($arg)) {
                $query->set($key, $arg);
            }
        }
        return $query;
    }
    return $query;
}

add_action('pre_get_posts', 'add_query_filter');

Funkcja do parsowania adresu URL:

/**
 * Parse string URL
 *
 * @param string $url_query URL Query.
 * @return array|false
 */
function parse_url_query(string $url_query) {
    $param_string = $url_query;
    $query_arg = [];
    if (!empty($param_string)) {
        $params = explode('-and-', $param_string);
        foreach ($params as $param) {
            $items = explode('-in-', urldecode($param));
            $param_name = $items[0];
            unset($items[0]);
            if (preg_match('-or-', $items[1])) {
                $query_arg[$param_name] = explode('-or-', $items[1]);
            } else {
                $query_arg[$param_name] = $items;
            }
        }
        return array_filter($query_arg);
    } else {
        return false;
    }
}

Po dodaniu tego kodu filtr powinien działać w szablonie archive_{post_type}, a przefiltrowane dane będą dostępne w standardowej pętli wyświetlającej wyniki.

Powiązane wpisy

Wskazówki i spostrzeżenia z mojej drogi zawodowej

Krytyczne podatności PHP2Shell (CVE-2026-109618 i CVE-2026-109619): Analiza i instrukcja naprawy dla VPS

Krytyczne podatności PHP2Shell (CVE-2026-109618 i CVE-2026-109619): Analiza i instrukcja naprawy dla VPS

  • 23.10.2026
  • 16
Jak korzystać z autoloadingu Composera w WordPressie

Jak korzystać z autoloadingu Composera w WordPressie

  • 02.10.2026
  • 122
Przeniesienie strony ze środowiska lokalnego na serwer za pomocą dostępu SSH

Przeniesienie strony ze środowiska lokalnego na serwer za pomocą dostępu SSH

  • 02.10.2026
  • 119
Wszystkie wpisy

Gotowy, aby wynieść swój projekt
na wyższy poziom?

Wprowadźmy Twoją wizję w życie dzięki profesjonalnemu tworzeniu stron i dedykowanym rozwiązaniom.
Skontaktuj się z nami już teraz i rozpocznijmy pracę nad przekształceniem Twoich pomysłów w rzeczywistość!