admin管理员组

文章数量:1122846

I have two custom taxonomies on the site - Countries and Cities.

The Country taxonomy contains over 200 terms, and the Cities taxonomy contains over 47,000 terms.

Naturally, editing content with reference to such a large taxonomy is very resource-intensive.

So the task arose to link these two taxonomies and load the list of cities only for the selected country on Post Edit (Add) screen.

Now Cities taxonomy terms have a meta field linked to Country.

I have two metaboxes (post_category_meta_box type) on the custom post type (ex. Event) editing page.

How can I implement the following features:

  • by default for new content, do not display a list of cities, but replace it with the (zero) value - "First select a country"
  • when selecting a Country (post_category_meta_box) - update the Cities metabox, indicating only the cities of the selected country

A similar task was discussed on wordpress, but I don’t know how it was implemented. Post Edit – Filter Terms on Meta-box 1 based on selected terms of Meta-box 2

I will be glad to any help. Note: content can be linked to different countries and several cities.

Here's what I did:

  • added a script to the post editor window for selecting countries

`

/* Set linked process with country taxonomy */
            function cities_add_admin_scripts( $hook ) {
                global $post;
                if ( $hook == 'post-new.php' || $hook == 'post.php' ) {
                    if ( in_array($post->post_type, ['hotel','event','sight','webcamera'])) {
                        wp_add_inline_script(  "jquery",
            "(function($){ 
                function city_doing_ajax( countries ){
                    $.ajax({
                        url: ajaxurl,
                        method: 'post',
                        dataType: 'json',
                        data: {action: 'city_filter', countries: countries, nonce: '" . wp_create_nonce('myajax-nonce') . "'} 
                    }).done(function(data){
                        console.log(data);
                    });             
                }
                $(document).ready(function() {
                /* set starting values */
                var countries = $('#countrychecklist input[type=\"checkbox\"]:checked').map(function(){ return $(this).val();}).get();
                console.log('countries Ids:', countries);
                if(countries.length > 0){
                    city_doing_ajax(countries);
                }
                $('#countrychecklist input[type=\"checkbox\"]').change(function() {
                    countries = $('#countrychecklist input[type=\"checkbox\"]:checked').map(function(){ return $(this).val();}).get();
                    console.log('renew countries Ids:', countries);
                    city_doing_ajax(countries);
                });
            })})(jQuery);" );
                    }
                }
            }

` I do this twice. Immediately after loading the form, to have a list of already marked countries, and for each select (unselect) of the form

Next, I hang the handler on the Ajax request

`

add_action( 'wp_ajax_city_filter', array(
    $this, 'city_ajax_action_callback'
) );

`

and the code of the Ajax processing function itself

`

function city_ajax_action_callback(){       
            $countries_ids = array_map( 'intval', $_POST['countries'] );
            if(!empty( $countries_ids ) ) {             
                $country_abbr = get_terms([
                    'taxonomy' => 'country',
                    'hide_empty' => false,
                    'fields' => 'all', /* slug not exists as parameter*/
                    'include' => $countries_ids
                ]);
                if( $country_abbr && ! is_wp_error( $country_abbr ) ){
                    $countr_codes = [];
foreach ($country_abbr as $term) $countr_codes[] = $term->slug;
                    **$this->country_codes** = $countr_codes;
                    add_action('parse_term_query', array($this, 'city_filter_term_query'));
                } else {
                    remove_action('parse_term_query', array($this, 'city_filter_term_query'));
                }
                wp_send_json_success();
            } else {
                /* remove_action('parse_term_query', array($this, 'city_filter_term_query')); */
                wp_send_json_error();
            }
        }

`

and the last function adds our filter at the stage of preparing the request to the table of terms

`

function city_filter_term_query( $wp_term_query ){
                $taxonomy = $wp_term_query->query_vars['taxonomy'];
                if( is_admin() && $taxonomy == 'city' && (! empty($this->country_codes))){
                    $meta_query = [
                        'relation' => 'AND',
                    ];  
                    $meta_query[] = [
                        'key' => 'country_code',
                        'value' => **$this->country_codes**,
                        'compare' => 'IN',
                    ];
                    $wp_term_query->query_vars['meta_query'] = $meta_query; 
                    error_log( print_r(['city_filter_term_query' => $wp_term_query], true ) );              
                }
    }

`

I note that all this is implemented in the CityTaxonomy class, where there is a field (highlighted in bold) private $country_codes = [];

I have two custom taxonomies on the site - Countries and Cities.

The Country taxonomy contains over 200 terms, and the Cities taxonomy contains over 47,000 terms.

Naturally, editing content with reference to such a large taxonomy is very resource-intensive.

So the task arose to link these two taxonomies and load the list of cities only for the selected country on Post Edit (Add) screen.

Now Cities taxonomy terms have a meta field linked to Country.

I have two metaboxes (post_category_meta_box type) on the custom post type (ex. Event) editing page.

How can I implement the following features:

  • by default for new content, do not display a list of cities, but replace it with the (zero) value - "First select a country"
  • when selecting a Country (post_category_meta_box) - update the Cities metabox, indicating only the cities of the selected country

A similar task was discussed on wordpress.org, but I don’t know how it was implemented. Post Edit – Filter Terms on Meta-box 1 based on selected terms of Meta-box 2

I will be glad to any help. Note: content can be linked to different countries and several cities.

Here's what I did:

  • added a script to the post editor window for selecting countries

`

/* Set linked process with country taxonomy */
            function cities_add_admin_scripts( $hook ) {
                global $post;
                if ( $hook == 'post-new.php' || $hook == 'post.php' ) {
                    if ( in_array($post->post_type, ['hotel','event','sight','webcamera'])) {
                        wp_add_inline_script(  "jquery",
            "(function($){ 
                function city_doing_ajax( countries ){
                    $.ajax({
                        url: ajaxurl,
                        method: 'post',
                        dataType: 'json',
                        data: {action: 'city_filter', countries: countries, nonce: '" . wp_create_nonce('myajax-nonce') . "'} 
                    }).done(function(data){
                        console.log(data);
                    });             
                }
                $(document).ready(function() {
                /* set starting values */
                var countries = $('#countrychecklist input[type=\"checkbox\"]:checked').map(function(){ return $(this).val();}).get();
                console.log('countries Ids:', countries);
                if(countries.length > 0){
                    city_doing_ajax(countries);
                }
                $('#countrychecklist input[type=\"checkbox\"]').change(function() {
                    countries = $('#countrychecklist input[type=\"checkbox\"]:checked').map(function(){ return $(this).val();}).get();
                    console.log('renew countries Ids:', countries);
                    city_doing_ajax(countries);
                });
            })})(jQuery);" );
                    }
                }
            }

` I do this twice. Immediately after loading the form, to have a list of already marked countries, and for each select (unselect) of the form

Next, I hang the handler on the Ajax request

`

add_action( 'wp_ajax_city_filter', array(
    $this, 'city_ajax_action_callback'
) );

`

and the code of the Ajax processing function itself

`

function city_ajax_action_callback(){       
            $countries_ids = array_map( 'intval', $_POST['countries'] );
            if(!empty( $countries_ids ) ) {             
                $country_abbr = get_terms([
                    'taxonomy' => 'country',
                    'hide_empty' => false,
                    'fields' => 'all', /* slug not exists as parameter*/
                    'include' => $countries_ids
                ]);
                if( $country_abbr && ! is_wp_error( $country_abbr ) ){
                    $countr_codes = [];
foreach ($country_abbr as $term) $countr_codes[] = $term->slug;
                    **$this->country_codes** = $countr_codes;
                    add_action('parse_term_query', array($this, 'city_filter_term_query'));
                } else {
                    remove_action('parse_term_query', array($this, 'city_filter_term_query'));
                }
                wp_send_json_success();
            } else {
                /* remove_action('parse_term_query', array($this, 'city_filter_term_query')); */
                wp_send_json_error();
            }
        }

`

and the last function adds our filter at the stage of preparing the request to the table of terms

`

function city_filter_term_query( $wp_term_query ){
                $taxonomy = $wp_term_query->query_vars['taxonomy'];
                if( is_admin() && $taxonomy == 'city' && (! empty($this->country_codes))){
                    $meta_query = [
                        'relation' => 'AND',
                    ];  
                    $meta_query[] = [
                        'key' => 'country_code',
                        'value' => **$this->country_codes**,
                        'compare' => 'IN',
                    ];
                    $wp_term_query->query_vars['meta_query'] = $meta_query; 
                    error_log( print_r(['city_filter_term_query' => $wp_term_query], true ) );              
                }
    }

`

I note that all this is implemented in the CityTaxonomy class, where there is a field (highlighted in bold) private $country_codes = [];

Share Improve this question edited Oct 10, 2024 at 18:47 Toologic asked Oct 3, 2024 at 3:41 ToologicToologic 12 bronze badges 1
  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. – Community Bot Commented Oct 3, 2024 at 3:51
Add a comment  | 

1 Answer 1

Reset to default 0

My approach would be:

  1. Hide city edition metabox, by setting public to false.
  2. I would override how metabox for country should render by using meta_box_cb parameter. You should provide callback for the function that will do the render.
  3. In this function, you first render the field for country, and then on choose you make an AJAX request to your backend to gather a list of the cities for the provided country.

本文标签: