Make WordPress Core

Opened 4 years ago

Last modified 18 months ago

#41857 new defect (bug)

Walker_PageDropdown doesn't set correct selected value when using value_field

Reported by: it4life Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 4.8.1
Component: Posts, Post Types Keywords:
Focuses: Cc:

Description

If the value_field is different from ID, the selected value was not set.

Function Walker_PageDropdown::start_el

if ( $page->ID == $args['selected'] )
    $output .= ' selected="selected"';

See also #32330

Change History (5)

#1 follow-ups: @birgire
4 years ago

Welcome to WordPress Trac, @it4life

Can you give an example?

Not all fields have unique values, like ID and post_name.

How should non-unique fields be handled?

If the first match is selected, then that would depend on the order - that might be problematic.

#2 in reply to: ↑ 1 @it4life
4 years ago

Replying to birgire:

Welcome to WordPress Trac, @it4life

Can you give an example?

Not all fields have unique values, like ID and post_name.

How should non-unique fields be handled?

If the first match is selected, then that would depend on the order - that might be problematic.

Yes, an example is using with value_field = post_name and it doesn't show selected value, as you see the code is only checking selected value by ID.

#3 in reply to: ↑ 1 @it4life
4 years ago

Replying to birgire:

Not all fields have unique values, like ID and post_name.

How should non-unique fields be handled?

If the first match is selected, then that would depend on the order - that might be problematic.

About unique field, I have idea to pass selected and value_field as callback:

<?php

class Custom_Walker_PageDropdown extends Walker_PageDropdown {

        /**
         * Starts the element output.
         * @param array|string $args {
         *
         *    @type int|string|callable $selected     Value of the option that should be selected.
         *                                            Default 0.
         *                                            Or a callback that return true/false.
         *    @type string|callable     $value_field  Post field or a callback used to populate
         *                                            the 'value' attribute of the option elements.
         *                                            Default 'ID'.
         *
         * @see Walker_PageDropdown
         */
        public function start_el( &$output, $page, $depth = 0, $args = array(), $id = 0 ) {
                $pad = str_repeat('&nbsp;', $depth * 3);

                $value = $page->ID;
                if ( isset( $args['value_field'] ) ) {
                        $value_field = $args['value_field'];
                        if ( is_callable( $value_field ) ) {
                                $value = call_user_func( $value_field, $page );
                        } elseif ( isset( $page->{$value_field} ) ) {
                                $value = $page->{$value_field};
                        }
                }

                $output .= "\t<option class=\"level-$depth\" value=\"" . esc_attr( $value ) . "\"";

                if ( isset( $args['selected'] ) ) {
                        $selected = $args['selected'];
                        $is_selected = false;
                        if ( is_callable( $selected ) ) {
                                $is_selected = call_user_func( $selected, $page );
                        } else {
                                $is_selected = ( $value == $selected );
                        }

                        if ( $is_selected ) {
                                $output .= ' selected="selected"';
                        }
                }
                $output .= '>';

                $title = $page->post_title;
                if ( '' === $title ) {
                        /* translators: %d: ID of a post */
                        $title = sprintf( __( '#%d (no title)' ), $page->ID );
                }

                /**
                 * Filters the page title when creating an HTML drop-down list of pages.
                 *
                 * @since 3.1.0
                 *
                 * @param string $title Page title.
                 * @param object $page  Page data object.
                 */
                $title = apply_filters( 'list_pages', $title, $page );

                $output .= $pad . esc_html( $title );
                $output .= "</option>\n";
        }
}

Usage:

<?php
wp_dropdown_pages( [
        'value_field' => function ( $page ) {
                return $page->ID . ' - ' . $page->post_name;
        },
        'selected' => function ( $page ) {
                return $page->post_name == 'blog';
        },
        'walker' => new Custom_Walker_PageDropdown,
] );

What do you think?

Last edited 4 years ago by it4life (previous) (diff)

#4 @birgire
4 years ago

Thanks for the example.

What do you think?

I think it's a cool idea, worth exploring ;-)

But with this callback power, comes a great responsibility, like being sure that it's an injective map ;-)

Alternative (smaller step) approach, would be to add a support for a post_name selection.

#5 @soupia18
18 months ago

I arrived here because of this issue, as the wp_dropdown_pages cannot set selected when value_field is other than the ID.

After reading the discussion above, about the possibility of using fields with non-unique values, then there are 2 options here:

Either, the value_field makes no real sense to exist as an option, or since it's there, then it should give the freedom to the developer to decide how to use it: I mean with what field as default value. In that case something like below would do the job:

if ( $page->{$args['value_field']} == $args['selected'] ) {
	$output .= ' selected="selected"';
}
Note: See TracTickets for help on using tickets.