Support

Account

Home Forums General Issues Disable Options for Select Field

Solved

Disable Options for Select Field

  • Hi We are using ACF Pro for some plugin we developed.

    We have a frontend form where anonymous users could submit and draft entries of some custum post type is generated.

    We have now the problem that we wanted to replace some options for some select field.
    Were some options shouldn’t be not available any more for new entries.

    But of course still should be selectable for admins (e.g. update old entries), frontend update by the entry owners (link to some form with update mode via email).
    And for the frontend search form and item listing we also need old options to be selectable.

    
    function filter_choices($field, $all_choices = false)
    {
        if (is_admin()) {
            $all_choices = true;
        }
    
        if ($all_choices) {
    	
    		$field['choices'] = array(
    		 'key1' => 'value 1',
    		 'key2' => 'value 2'
    		 'key3' => 'value 3'
    		);
    
    	} else {
    		$field['choices'] = array(
    		 'key3' => 'value 3'
    		);
    		
    	
    	}
    return $field;
    }
    add_filter('acf/load_field/name=select_field_name', 'filter_choices', 10 , 2);
    

    with that we could have
    – all options available for admins
    – only the new options available for new inserts via the frontend form
    – have the possibility to apply the filter with all_choices= true for the search form.

    But we have the problem when the user edits an existing entry.

    Everything would be easy if the “post_id” is given to the filter functions
    because for new entries it would be something like $post_id= ‘new_post’;
    but for existing entries a real post_id.

    We also tried to just disable some options by adding the following to the filter.
    which was mentioned here:
    https://support.advancedcustomfields.com/forums/topic/field-object-shows-load_field-as-working-but-it-doesnt-render-that-way/

    
         $disabled_options = array('key1', 'key2');
    	foreach ($disabled_options as $value) {
            $disabled[$value] = true;
        }
        $field['disabled'] = $disabled;
    

    but sadly this only disables the complete select field.
    From the look into the source code i also couldn’t find a possibility how single options could be disabled.

  • try changing your priority to 20

    
    add_filter('acf/load_field/name=select_field_name', 'filter_choices', 20 , 2);
    
  • I take that back, it will not help. The ACF select field does not have the ability to disable individual options. You might want to contact them and ask to have this feature added https://www.advancedcustomfields.com/contact/

    In the mean time the only solution I can think of it to do it with JavaScript.

  • What should increasing the priority to 20 change?

    From the “class-acf-field-select.php” i see that there isn’t an possibility to “disable” options.

    The select itself use the “disable” in line 318.
    if( !empty($field['disabled']) ) $select['disabled'] = 'disabled';

    the select itself is rendered via
    acf_select_input( $select );

    which is in acf-input-functions.php

    acf_get_select_input calls acf_walk_select_input($choices, $value)

    where the “option tags are rended with “value”, “selected” and “data-i” attributes.
    So no “disabled” html attribute.

    
    // single (option)	
    			} else {
    				$attrs = array(
    					'value' => $value
    				);
    				
    				// If is selected.
    				$pos = array_search( esc_attr($value), $values );
    				if( $pos !== false ) {
    					$attrs['selected'] = 'selected';
    					$attrs['data-i'] = $pos;
    				}
    				$html .= sprintf( '<option %s>%s</option>', acf_esc_attr($attrs), esc_html($label) );
    			}
    

    If there would be a way to access the form “post_id” to filter function of the field i could delete the “not wanted options” from the array when post_id is e.g. “new_post”.

  • Sorry, increasing the priority was a quick answer before I actually looked into the ACF code and posted my second answer.

    Increasing the priority is my first step whenever I’m debugging some issue in ACF. Why? because ACF initializes the field on the same hook that you use. There are filters in ACF itself for acf/load_field... and all of the ACF filters run with a priority of 10. ACF’s filters do not look any changes that may have already been made and loads the field with all default settings, meaning that if you’re filter runs before ACF’s filter that anything you change is ignored and replaced with the original values. Changing the priority to > 10 ensures that you filter runs only after ACF is done an eliminates that as a cause. When I tested your issue myself, this is the first thing I tried before looking at the ACF code.

    At any rate, that is not the solution. There are other fields that do allow disabling of individual options. The checkbox and radio field have this ability by passing the array you have in the OP. More than likely it is an oversight that it is not also include for the select field, which is why I suggested contacting support.

  • Thx John for the explanation of the priority.
    I already thought that this is the intention.

    My Problem also isn’t that I don’t able to change the options (remove some) on specific conditions.
    My problem is that i couldn’t fulfil all the requirements we have.

    The missing option to disable individual options entries (weird sentence ^^) is an additional point.

    We have already contacted the support but still waiting for some answer.

    have a nice weekend 🙂

  • Looking at the OP.

    You want to remove some values for new entries, but allow them on current entries, or when an admin is editing. I would simply remove them from the front end of the site or based on other things. Removing an choice in a select field will not remove that value where it has already been selected.

    
    add_filter('acf/prepare_field/name=select_field_name', 'filter_choices', 20);
    function filter_choices($field) {
      if (is_user_logged_in() && current_user_can('administrator')) {
        // not for admins
        return $field;
      }
      // values to remove
      $remove = array(1, 2, 3);
      // get the current value of the field
      $value = acf_get_array($field['value']);
      // get all field choices
      $choices = field['choices'];
      // clear the field choices
      $field['choices'] = array();
      foreach ($choices as $v => $l) {
        if (!in_array($v, $remove) || in_array($v, $value)) {
          // this value should not be removed
          // or it might have been previously selected
          // so keep it
          $field['choices'][$v] = $l;
        } // end if keep it
      } // end foreach choices
      return $field;
    }
    
  • The problem is we also have an “edit” possibility for not logined users by some “hidden key value” which they receive through email.

    The post_id is then selected and given to the acf_form object

    
     acf_form(array(
         'id' => 'acf-form',
         /* (int|string) The post ID to load data from and save data to. Defaults to the current post ID.
                Can also be set to 'new_post' to create a new post on submit */
         'post_id' => $post_id,
    ...
    

    Sadly in the called filters function for select field the value is always “null” even When the priority is set to 1 / 10 / 20 / 1000.

    A quick look into the source code even shows me that even in the filter ‘acf/pre_render_fields’ the values aren’t set….

    from acf_field_functions.php

    
    /**
     * acf_render_fields
     *
     * Renders an array of fields. Also loads the field's value.
     *
     * @date	8/10/13
     * @since	5.0.0
     * @since	5.6.9 Changed parameter order.
     *
     * @param	array $fields An array of fields.
     * @param	(int|string) $post_id The post ID to load values from.
     * @param	string $element The wrapping element type.
     * @param	string $instruction The instruction render position (label|field).
     * @return	void
     */
    function acf_render_fields( $fields, $post_id = 0, $el = 'div', $instruction = 'label' ) {
    
    	// Parameter order changed in ACF 5.6.9.
    	if( is_array($post_id) ) {
    		$args = func_get_args();
    		$fields = $args[1];
    		$post_id = $args[0];
    	}
    
    	/**
    	 * Filters the $fields array before they are rendered.
    	 *
    	 * @date	12/02/2014
    	 * @since	5.0.0
    	 *
    	 * @param	array $fields An array of fields.
    	 * @param	(int|string) $post_id The post ID to load values from.
    	 */
    	$fields = apply_filters( 'acf/pre_render_fields', $fields, $post_id );
    
    	
    	// Filter our false results.
    	$fields = array_filter( $fields );
    
    	// Loop over and render fields.
    	if( $fields ) {
    		foreach( $fields as $field ) {
    
    			// Load value if not already loaded.
    			if( $field['value'] === null ) {
    				$field['value'] = acf_get_value( $post_id, $field );
    			}
    
    			// Render wrap.
    			acf_render_field_wrap( $field, $el, $instruction );
    		}
    	}
    
    	/**
    	*  Fires after fields have been rendered.
    	*
    	*  @date	12/02/2014
    	*  @since	5.0.0
    	*
    	* @param	array $fields An array of fields.
    	* @param	(int|string) $post_id The post ID to load values from.
    	*/
    	do_action( 'acf/render_fields', $fields, $post_id );
    }
    

    the filter add_filter('acf/load_field/name=myselect'.. is called by far before the actual render of the select field and sadly also way before the value is set.
    So your code doesn’t work.

  • Ups i have just seen that you have changed the filter function from acf/load_field to acf/prepare_field.

    With prepare_field the value is indeed available.

    I will check if with this filter all requirements could be set.

  • With the acf/prepare_field it would work to remove the options for all my requirements.
    So thanks @hube2 for pointing me at that direction.

    I used some different code because i also have optgroups.

    
    function filter_choices($field)
    {
        if (is_user_logged_in() && current_user_can('administrator')) {
            // don't filter for admins
            return $field;
        }
    
        $value = acf_get_array($field['value']);
        $choices = acf_get_array($field['choices']);
    
        // keys which will be removed
        $disabled_options = array(1, 2, 3);
    
        // value should be kept as selectable option for edit therefore remove it from disabled options
        if (!is_null($value)) {
            if (is_array($value)) {
                $disabled_options = array_diff($disabled_options, $value);
            } else {
                $disabled_options = array_diff($disabled_options, array($value));
            }
        }
    
        // delete keys in in normal options
        $choices = array_diff_key($choices, array_flip((array) $disabled_options));
        foreach ($choices as $choice_key => $choice_value) {
            // delete keys in option groups
            if (is_array($choice_value)) {
                $choices[$choice_key] = array_diff_key($choice_value, array_flip((array) $disabled_options));
                //remove empty groups from choices
                if (sizeof($choices[$choice_key])==0) {
                    unset($choices[$choice_key]);
                }
            }
        }
        $field['choices'] = $choices;
        return $field;
    }
    add_filter('acf/prepare_field/name=select_field_name', 'filter_choices', 20);
    

    Anyhow a possibility to disable single options would also be good if this would be implemented in future acf versions.

Viewing 10 posts - 1 through 10 (of 10 total)

The topic ‘Disable Options for Select Field’ is closed to new replies.