1

Currently I have a structure where the top level categories are acting like landing pages, so they should have no posts directly assigned to them. What I would like to do is disable the top level terms and only allow the user to check sub-terms (children). I've found a walker which transforms checkboxes to radio buttons here but I don't quite understand it enough to only change the top level parent, it keeps cascading. Was hoping somebody could break down what is going on in this walker with the main question being "How do I disable top level terms in the admin panel term metabox"

/**
 * Use radio inputs instead of checkboxes for term checklists in specified taxonomies.
 * https://wordpress.stackexchange.com/questions/139269/wordpress-taxonomy-radio-buttons
 *
 * @param   array   $args
 * @return  array
 */
function wpse_139269_term_radio_checklist( $args ) {
    if ( ! empty( $args['taxonomy'] ) && ($args['taxonomy'] === 'product_cat') ) {
        if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) {
            if ( ! class_exists( 'WPSE_139269_Walker_Category_Radio_Checklist' ) ) {
                class WPSE_139269_Walker_Category_Radio_Checklist extends Walker_Category_Checklist {
                    function walk( $elements, $max_depth, $args = array() ) {
                        $output = parent::walk( $elements, $max_depth, $args );

                        foreach($elements as $element){
                            if($element->parent == 0){
                                $output = str_replace(
                                    array( 'type="checkbox"', "type='checkbox'" ),
                                    array( 'type="checkbox"', "type='checkbox' disabled='disabled'" ),
                                    $output
                                );
                            }
                        }

                        return $output;
                    }
                }
            }

            $args['walker'] = new WPSE_139269_Walker_Category_Radio_Checklist;
        }
    }

    return $args;
}
add_filter( 'wp_terms_checklist_args', 'wpse_139269_term_radio_checklist' );

elements holds all the terms in an array so I use that to test whether that element has a parent in my foreach then I try to replace some output and append my disabled flag (much like the linked question turns checkboxes into radio buttons).

2

Edited because I had completely misread question.


To know if term is a parent or not, we need access to term post parent, so is better to replace more specialized methods istead of walk().

That said, I understand that code in OP uses class definition inside the function to avoid fatal error because of Walker_Category_Checklist class not defined, however code becomes hardly readable in that way.

So I think is better write a custom function that return an instance of the custom walker:

add_filter( 'wp_terms_checklist_args', 'wpse_149328_set_walker' );  

function wpse_149328_set_walker( $args ) {
  if (
    ! empty( $args['taxonomy'] )
    && ( $args['taxonomy'] === 'product_cat' ) // only for 'product_cat' taxonomy
    && ( ! isset( $args['walker'] ) || ! $args['walker'] instanceof Walker )
  ) {
    $args['checked_ontop'] = FALSE;
    $args['walker'] = get_Walker_Category_No_Parent();
  }
  return $args;
}

now let's code the get_Walker_Category_No_Parent() and the custom walker:

function get_Walker_Category_No_Parent() {

  class Walker_Category_No_Parent extends Walker_Category_Checklist {

   function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
      if ( (int) $category->parent === 0 ) {
         $this->doing_parent = esc_html( $category->name );
      } else {
        parent::start_el( $output, $category, $depth, $args, $id );
      }
    }

    function end_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
      if ( ! isset( $this->doing_parent ) || empty( $this->doing_parent ) ) {
        parent::end_el( $output, $category, $depth, $args, $id );
      }
    }

    function start_lvl( &$output, $depth = 0, $args = array() ) {
      if ( isset( $this->doing_parent ) && ! empty( $this->doing_parent ) ) {
        $output .= '<li><strong>' . $this->doing_parent . '</strong></li>';
        $this->doing_parent = FALSE;
      }
      parent::start_lvl( $output, $depth = 0, $args );
    }

  }
  return new Walker_Category_No_Parent;
}

This walker output parent categories as text and only if they have some children.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.