Support » Developing with WordPress » Custom widget for category filter with custom post types

  • Resolved amillionmiles

    (@amillionmiles)


    I’m trying to create a widget for my sidebar that allows readers to filter for both the regular blog categories and for the categories of my custom post types. I just can’t figure it out, and I would REALLY appreciate any advice you may have.

    It’s a local install, so unfortunately, I don’t have a link to share.

    What I have come up with so far gives me two dropdowns in the admin area to select taxonomies (“Taxonomy 1” and “Taxonomy 2”), but the front end only give me two sets of whatever is selected under “Taxonomy 1.” Here is my code in functions.php, which I’m sure is a hot mess full of mistakes (I’m very new to building widgets!). Also, I think I may have included a nonexistent hook or something like that… I apologize.

    /**
     * Core class custom_categories_widget
     *
     * @since 1.0.0
     */
    class custom_categories_widget extends WP_Widget {
    
        /**
         * Sets up a new widget instance.
         *
         * @since 1.0.0
         *
         * @access public
         */
        public function __construct() {
            $widget_ops = array(
                'classname'                   => 'widget_custom_categories',
                'description'                 => __( 'A list or dropdown of categories.', 'custom_categories_widget' ),
                'customize_selective_refresh' => true,
            );
            parent::__construct( 'custom_categories', __( 'Custom Categories' ), $widget_ops );
        }
    
        /**
         * Outputs the content for the current Custom Categories widget instance.
         *
         * @since 1.0.0
         *
         * @access public
         *
         * @param array $args     Display arguments including 'before_title', 'after_title',
         *                        'before_widget', and 'after_widget'.
         * @param array $instance Settings for the current widget instance.
         */
        public function widget( $args, $instance ) {
    
            $title = ! empty( $instance['title'] ) ? $instance['title'] : __( 'Categories 2', 'custom_categories-widget' );
    
            /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
            $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
    
            $taxonomy = ! empty( $instance['taxonomy'] ) ? $instance['taxonomy'] : 'category';
            $label    = ! empty( $instance['label'] ) ? $instance['label'] : __( 'Select Category', 'custom_categories_widget' );
            $taxonomy2 = ! empty( $instance['taxonomy2'] ) ? $instance['taxonomy2'] : 'category';
            $c        = ! empty( $instance['count'] ) ? (bool) $instance['count'] : false;
            $h        = ! empty( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false;
            $d        = ! empty( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false;
    
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $args['before_widget'];
            if ( $title ) {
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                echo $args['before_title'] . $title . $args['after_title'];
            }
    
            $cat_args = array(
                'orderby'      => 'name',
                'taxonomy'     => $taxonomy,
                'taxonomy2'     => $taxonomy2,
                'show_count'   => $c,
                'hierarchical' => $h,
            );
    
            if ( $d ) {
                $dropdown_id = "{$this->id_base}-dropdown-{$this->number}";
    
                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                echo '<label class="screen-reader-text" for="' . esc_attr( $dropdown_id ) . '">' . $title . '</label>';
    
                $cat_args['show_option_none'] = $label;
                $cat_args['name']             = 'category' === $taxonomy ? 'category_name' : $taxonomy;
                $cat_args['name1']             = 'category1' === $taxonomy2 ? 'category_name1' : $taxonomy2;
                $cat_args['id']               = $dropdown_id;
                $cat_args['value_field']      = 'slug';
                ?>
    
    <form action="<?php echo esc_url( home_url() ); ?>" method="get">
                <?php
                /**
                 * Filters the arguments for the Categories widget drop-down.
                 *
                 *
                 * @since 2.8.0
                 * @since 4.9.0 Added the <code>$instance</code> parameter.
                 *
                 * @see wp_dropdown_categories()
                 *
                 * @param array  $cat_args An array of Categories widget drop-down arguments.
                 * @param array  $instance Array of settings for the current widget.
                 * @param string $this->id Widget id.
                 * @param string $taxonomy Taxonomy.
                 */
                wp_dropdown_categories(
                    apply_filters(
                        'custom_post_type_widgets/categories/widget_categories_dropdown_args',
                        $cat_args,
                        $instance,
                        $this->id,
                        $taxonomy
                    )
                );
                wp_dropdown_categories(
                    apply_filters(
                        'custom_post_type_widgets/categories/widget_categories_dropdown_args',
                        $cat_args,
                        $instance,
                        $this->id,
                        $taxonomy2
                    )
                );
                ?>
    </form>
    <script>
    /* <![CDATA[ */
    (function() {
        var dropdown = document.getElementById( "<?php echo esc_js( $dropdown_id ); ?>" );
        function onCatChange() {
            if ( dropdown.options[dropdown.selectedIndex].value ) {
                return dropdown.form.submit();
            }
        }
        dropdown.onchange = onCatChange;
    })();
    /* ]]> */
    </script>
                <?php
            }
            else {
                ?>
                <ul>
                <?php
                $cat_args['title_li'] = '';
                /**
                 * Filters the arguments for the Categories widget.
                 *
                 * @see wp_list_categories()
                 *
                 * @param array  $cat_args An array of Categories widget arguments.
                 * @param array  $instance Array of settings for the current widget.
                 * @param string $this->id Widget id.
                 * @param string $taxonomy Taxonomy.
                 */
                wp_list_categories(
                    apply_filters(
                        'widget_categories_args',
                        $cat_args,
                        $instance,
                        $this->id,
                        $taxonomy
                    )
                );
                    wp_list_categories(
                    apply_filters(
                        'widget_custom_categories_args',
                        $cat_args,
                        $instance,
                        $this->id,
                        $taxonomy2
                    )
                );
                ?>
                </ul>
                <?php
            }
    
            // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            echo $args['after_widget'];
        }
    
        /**
         * Handles updating settings for the current Archives widget instance.
         *
         * @since 1.0.0
         *
         * @access public
         *
         * @param array $new_instance New settings for this instance as input by the user via form() method.
         * @param array $old_instance Old settings for this instance.
         *
         * @return array Updated settings to save.
         */
        public function update( $new_instance, $old_instance ) {
            $instance                 = $old_instance;
            $instance['title']        = sanitize_text_field( $new_instance['title'] );
            $instance['taxonomy']     = stripslashes( $new_instance['taxonomy'] );
            $instance['taxonomy2']     = stripslashes( $new_instance['taxonomy2'] );
            $instance['label']        = sanitize_text_field( $new_instance['label'] );
            $instance['count']        = ! empty( $new_instance['count'] ) ? (bool) $new_instance['count'] : false;
            $instance['hierarchical'] = ! empty( $new_instance['hierarchical'] ) ? (bool) $new_instance['hierarchical'] : false;
            $instance['dropdown']     = ! empty( $new_instance['dropdown'] ) ? (bool) $new_instance['dropdown'] : false;
    
            return $instance;
        }
    
        /**
         * Outputs the settings form for the widget.
         *
         * @since 1.0.0
         *
         * @access public
         *
         * @param array $instance Current settings.
         */
        public function form( $instance ) {
            $title        = isset( $instance['title'] ) ? $instance['title'] : '';
            $taxonomy     = isset( $instance['taxonomy'] ) ? $instance['taxonomy'] : '';
            $taxonomy2     = isset( $instance['taxonomy2'] ) ? $instance['taxonomy2'] : '';
            $label        = isset( $instance['label'] ) ? $instance['label'] : __( 'Select Category', 'custom_categories_widget' );
            $count        = isset( $instance['count'] ) ? (bool) $instance['count'] : false;
            $hierarchical = isset( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false;
            $dropdown     = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false;
            ?>
            <p><label for="<?php echo $this->get_field_id( 'title' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php esc_html_e( 'Title:', 'custom_categories_widget' ); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p>
    
            <?php
            $taxonomies = get_taxonomies( array(), 'objects' );
    
            if ( $taxonomies ) {
                printf(
                    '<p><label for="%1$s">%2$s</label>' .
                    '<select class="widefat" id="%1$s" name="%3$s">',
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    $this->get_field_id( 'taxonomy' ),
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    __( 'Taxonomy 1:', 'custom_categories_widget' ),
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    $this->get_field_name( 'taxonomy' )             
                );
    
                foreach ( $taxonomies as $taxobjects => $value ) {
                    if ( ! $value->hierarchical ) {
                        continue;
                    }
                    if ( 'nav_menu' === $taxobjects || 'link_category' === $taxobjects || 'post_format' === $taxobjects ) {
                        continue;
                    }
    
                    printf(
                        '<option value="%s"%s>%s</option>',
                        esc_attr( $taxobjects ),
                        selected( $taxobjects, $taxonomy, false ),
                        esc_html__( $value->label, 'custom_categories_widget' ) . ' ' . esc_html( $taxobjects )
                    );
                }
                echo '</select></p>';
                
                printf(
                    
                    '<p><label for="%1$s">%2$s</label>' .
                    '<select class="widefat" id="%1$s" name="%3$s">',
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    $this->get_field_id( 'taxonomy2' ),
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    __( 'Taxonomy 2:', 'custom_categories_widget' ),
                    /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
                    $this->get_field_name( 'taxonomy2' )    
                );
                
                foreach ( $taxonomies as $taxobjects => $value ) {
                    if ( ! $value->hierarchical ) {
                        continue;
                    }
                    if ( 'nav_menu' === $taxobjects || 'link_category' === $taxobjects || 'post_format' === $taxobjects ) {
                        continue;
                    }
    
                    printf(
                        '<option value="%s"%s>%s</option>',
                        esc_attr( $taxobjects ),
                        selected( $taxobjects, $taxonomy2, false ),
                        esc_html__( $value->label, 'custom_categories_widget' ) . ' ' . esc_html( $taxobjects )
                    );
                }
                echo '</select></p>';
            }
            ?>
    
            <p><label for="<?php echo $this->get_field_id( 'label' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php esc_html_e( 'Dropdown label:', 'custom_categories_widget' ); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id( 'label' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" name="<?php echo $this->get_field_name( 'label' ); ?>" type="text" value="<?php echo esc_attr( $label ); ?>" /></p>
    
            <p><input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'dropdown' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" name="<?php echo $this->get_field_name( 'dropdown' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"<?php checked( $dropdown ); ?> />
            <label for="<?php echo $this->get_field_id( 'dropdown' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php esc_html_e( 'Display as dropdown', 'custom_categories_widget' ); ?></label><br />
    
            <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'count' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" name="<?php echo $this->get_field_name( 'count' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"<?php checked( $count ); ?> />
            <label for="<?php echo $this->get_field_id( 'count' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php esc_html_e( 'Show post counts', 'custom_categories_widget' ); ?></label><br />
    
            <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'hierarchical' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" name="<?php echo $this->get_field_name( 'hierarchical' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"<?php checked( $hierarchical ); ?> />
            <label for="<?php echo $this->get_field_id( 'hierarchical' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php esc_html_e( 'Show hierarchy', 'custom_categories_widget' ); ?></label></p>
            <?php
        }
    }
    
    function wpb_load_widget() {
        register_widget( 'custom_categories_widget' );
    }
    add_action( 'widgets_init', 'wpb_load_widget' );
Viewing 5 replies - 1 through 5 (of 5 total)
  • Moderator bcworkz

    (@bcworkz)

    When you apply $cat_args to wp_dropdown_categories(), it’ll only use $cat_args[‘taxonomy’] in both cases. It’ll never use $cat_args[‘taxonomy2’]. For the second call you need to assign $cat_args[‘taxonomy2’] value to $cat_args[‘taxonomy’].

    Thread Starter amillionmiles

    (@amillionmiles)

    Thank you SO MUCH for taking the time to look at my code and reply! I really appreciate it!!!

    So I changed this part:

    wp_dropdown_categories(
    	apply_filters(
    		'custom_post_type_widgets/categories/widget_categories_dropdown_args',
    		$cat_args,
    		$instance,
    		$this->id,
    		$taxonomy
    		)
    );
    wp_dropdown_categories(
    	apply_filters(
    		'custom_post_type_widgets/categories/widget_categories_dropdown_args',
    		$cat_args['taxonomy2'],
    		$instance,
    		$this->id,
    		$taxonomy2
    		)
    );

    Is this what you meant? This doesn’t fix it, so I’m sure I’m overlooking something. Do I need to change something else? Maybe something in wp_list_categories? Or could there be a problem with the ‘custom_post_type_widgets/categories/widget_categories_dropdown_args’ part?

    Moderator bcworkz

    (@bcworkz)

    No, you’ve misunderstood. Between the two calls do $cat_args['taxonomy'] = $cat_args['taxonomy2']; You no longer need the first taxonomy, right? If you do, save it to a different var first. You still pass $cat_args in its entirety to the second call, except now $cat_args[‘taxonomy’] has a different value.

    I’m not sure what filter “custom_post_type_widgets/categories/widget_categories_dropdown_args” is. As long as no other code has hooked into it, it wouldn’t make any difference. $cat_args is still passed to to wp_dropdown_categories() whether it’s a valid filter or not.

    Even if it did not exist until now, someone else could still use it to modify $cat_args at some point in the future if they wanted to.

    Thread Starter amillionmiles

    (@amillionmiles)

    You are amazing! That worked! I had to add it between the two wp_list_categories calls too, and now it works like a charm! Yay!

    Thank you also for your explanation. I’m not sure what filter “custom_post_type_widgets/categories/widget_categories_dropdown_args” is either… I think I may have made it up or had some plans I can no longer remember…

    Seriously, thank you SO MUCH!

    Moderator bcworkz

    (@bcworkz)

    Happy to help 🙂 You’re free to make up your own filter names for others to use down the road to modify your plugin behavior without modifying your code. A lot of existing filters have nothing added to them, so even if it existed, it doesn’t mean anything would be changed through filtering.

Viewing 5 replies - 1 through 5 (of 5 total)
  • You must be logged in to reply to this topic.