Support

Account

Home Forums General Issues How do I get optgroups in the Select field?

Solved

How do I get optgroups in the Select field?

    • chloe

    • August 7, 2013 at 1:20 am

    I’m using ACF 4.2.0.

    I’ve hunted around the old/new support forums and the Web in general, and the only quasi-answer I found was this thread requesting the feature.

    It looks like if choices are “2 levels deep,” optgroup is detected automatically. The problem: I can’t figure out how to make choices 2 levels deep. What’s the syntax for this?

    Thanks!

    • Elliot

    • August 7, 2013 at 12:45 pm

    Hi @chloe

    The interface for entering choices only allows for 1 level of choices.

    If you were to register the field via PHP, you could add in the multi-=level array of choices!

    Hope that helps

  • Hi. Sorry for getting this up. But I have found in the same situation where I need 2 levels for Select field.

    In my case, I wanted to use the interface to do that, so I have made a change in my ACF select field code to permit that. If the line in the interface starts with – (for instance -Europe ) it will assume there are optgroups, so everything that comes after that line will be for that group until next – is found.

    In my case, in the interface I write:

    -Europe
    Spain
    France
    -America
    Argentina
    Peru

    and then I get a select with optgroup where Europe and America are the group headlines and the rest are the selectable options.

    In advanced_custom_fields/core/fields/select.php LINE 68 add

    		// Other way to group is using - in the field
    		if (!$optgroup) {
    		// vars
    		$new_choices = array();		
    			// Determine if there are - to group the field
    			$group = false;
    			$choices_group = array();
    			$optgroup_key = "";
    			foreach ($field['choices'] as $k => $v) {
    				if (strpos($v, "-") !== false)
    				{
    					// Next elements are optgroup;
    					if ($group) {
    						$choices_group [$optgroup_key] = $new_choices;
    						$new_choices = array();
    						$optgroup_key = $v;
    					} else {
    						
    						// Get ungrouped value from begining of the list
    						foreach ($new_choices as $l=>$w) {
    							$choices_group[$l] = $w;
    						}
    						$new_choices= array();
    						
    						$group = true;
    						$optgroup_key = $v;
    
    					}
    				} else {
    					if(strpos($v, ' : ') !== false)
    					{
    						$choice = explode(' : ', $v);
    						$new_choices[ trim($choice[0]) ] = trim($choice[1]);
    					}
    					else
    					{
    						$new_choices[ trim($v) ] = trim($v);
    					}
    					
    				}
    			
    			}
    			if ($group) {
    				// End last group
    				$choices_group [$optgroup_key] = $new_choices;
    				$field['choices'] = $choices_group;
    				$optgroup = true;
    			}
    		}		
    
    
  • Is this something we can expect to see in future versions of the plugin? The optgroup is something that I want to use too. Fiddeling in the plugin code isn’t really an option, since an update of the plugin will nullify the changes made.

  • A possibility is to develop an ACF-plugin that implements the new code and you can use it in your fields.

    Anyway, yesterday I noticed a bug in my proposal regarding the value stored in the field. This is the new code:

    <?php
    
    class acf_field_select extends acf_field
    {
    	/*
    	*  __construct
    	*
    	*  Set name / label needed for actions / filters
    	*
    	*  @since	3.6
    	*  @date	23/01/13
    	*/
    	
    	function __construct()
    	{
    		// vars
    		$this->name = 'select';
    		$this->label = __("Select",'acf');
    		$this->category = __("Choice",'acf');
    		$this->defaults = array(
    			'multiple' 		=>	0,
    			'allow_null' 	=>	0,
    			'choices'		=>	array(),
    			'default_value'	=>	''
    		);
    		
    		
    		// do not delete!
        	parent::__construct();
        	
        	
        	// extra
    		//add_filter('acf/update_field/type=select', array($this, 'update_field'), 5, 2);
    		add_filter('acf/update_field/type=checkbox', array($this, 'update_field'), 5, 2);
    		add_filter('acf/update_field/type=radio', array($this, 'update_field'), 5, 2);
    	}
    
    	
    	/*
    	*  create_field()
    	*
    	*  Create the HTML interface for your field
    	*
    	*  @param	$field - an array holding all the field's data
    	*
    	*  @type	action
    	*  @since	3.6
    	*  @date	23/01/13
    	*/
    	
    	function create_field( $field )
    	{
    		// vars
    		$optgroup = false;
    		
    		
    		// determin if choices are grouped (2 levels of array)
    		if( is_array($field['choices']) )
    		{
    			foreach( $field['choices'] as $k => $v )
    			{
    				if( is_array($v) )
    				{
    					$optgroup = true;
    				}
    			}
    		}
    		
    		
    		// Other way to group is using - in the field
    		if (!$optgroup) {
    		// vars
    		$new_choices = array();		
    			// Determine if there are - to group the field
    			$group = false;
    			$choices_group = array();
    			$optgroup_key = "";
    			error_log(var_export($field['choices'],true));
    			foreach ($field['choices'] as $k => $v) {
    				if (strpos($v, "-") === 0)
    				{
    					// Next elements are optgroup;
    					if ($group) {
    						$choices_group [$optgroup_key] = $new_choices;
    						$new_choices = array();
    						$optgroup_key = $v;
    					} else {
    						
    						// Get ungrouped value from begining of the list
    						foreach ($new_choices as $l=>$w) {
    							$choices_group[$l] = $w;
    						}
    						$new_choices= array();
    						
    						$group = true;
    						$optgroup_key = $v;
    
    					}
    				} else {
    					if(strpos($v, ' : ') !== false)
    					{
    						$choice = explode(' : ', $v);
    						$new_choices[ trim($choice[0]) ] = trim($choice[1]);
    					}
    					else
    					{
    						// TODO: Change this in desdeelagua
    						$new_choices[ $k ] = trim($v);
    					}
    					
    				}
    			
    			}
    			if ($group) {
    				// End last group
    				$choices_group [$optgroup_key] = $new_choices;
    				$field['choices'] = $choices_group;
    				$optgroup = true;
    			}
    		}		
    		
    		// value must be array
    		if( !is_array($field['value']) )
    		{
    			// perhaps this is a default value with new lines in it?
    			if( strpos($field['value'], "\n") !== false )
    			{
    				// found multiple lines, explode it
    				$field['value'] = explode("\n", $field['value']);
    			}
    			else
    			{
    				$field['value'] = array( $field['value'] );
    			}
    		}
    		
    		
    		// trim value
    		$field['value'] = array_map('trim', $field['value']);
    		
    		
    		// multiple select
    		$multiple = '';
    		if( $field['multiple'] )
    		{
    			// create a hidden field to allow for no selections
    			echo '<input type="hidden" name="' . $field['name'] . '" />';
    			
    			$multiple = ' multiple="multiple" size="5" ';
    			$field['name'] .= '[]';
    		} 
    		
    		
    		// html
    		echo '<select id="' . $field['id'] . '" class="' . $field['class'] . '" name="' . $field['name'] . '" ' . $multiple . ' >';	
    		
    		
    		// null
    		if( $field['allow_null'] )
    		{
    			echo '<option value="null">- ' . __("Select",'acf') . ' -</option>';
    		}
    		
    		// loop through values and add them as options
    		if( is_array($field['choices']) )
    		{
    			foreach( $field['choices'] as $key => $value )
    			{
    				if( $optgroup )
    				{
    					// this select is grouped with optgroup
    					if($key != '') echo '<optgroup label="'.$key.'">';
    					
    					if( is_array($value) )
    					{
    						foreach($value as $id => $label)
    						{
    							$selected = in_array($id, $field['value']) ? 'selected="selected"' : '';
    														
    							echo '<option value="'.$id.'" '.$selected.'>'.$label.'</option>';
    						}
    					}
    					
    					if($key != '') echo '</optgroup>';
    				}
    				else
    				{
    					$selected = in_array($key, $field['value']) ? 'selected="selected"' : '';
    					echo '<option value="'.$key.'" '.$selected.'>'.$value.'</option>';
    				}
    			}
    		}
    
    		echo '</select>';
    	}
    	
    	
    	/*
    	*  create_options()
    	*
    	*  Create extra options for your field. This is rendered when editing a field.
    	*  The value of $field['name'] can be used (like bellow) to save extra data to the $field
    	*
    	*  @type	action
    	*  @since	3.6
    	*  @date	23/01/13
    	*
    	*  @param	$field	- an array holding all the field's data
    	*/
    	
    	function create_options( $field )
    	{
    		$key = $field['name'];
    
    		// implode choices so they work in a textarea
    		if( is_array($field['choices']) )
    		{		
    			foreach( $field['choices'] as $k => $v )
    			{
    				$field['choices'][ $k ] = $k . ' : ' . $v;
    			}
    			$field['choices'] = implode("\n", $field['choices']);
    		}
    
    		?>
    <tr class="field_option field_option_<?php echo $this->name; ?>">
    	<td class="label">
    		<label for=""><?php _e("Choices",'acf'); ?></label>
    		<p><?php _e("Enter each choice on a new line.",'acf'); ?></p>
    		<p><?php _e("For more control, you may specify both a value and label like this:",'acf'); ?></p>
    		<p><?php _e("red : Red",'acf'); ?><br /><?php _e("blue : Blue",'acf'); ?></p>
    	</td>
    	<td>
    		<?php
    		
    		do_action('acf/create_field', array(
    			'type'	=>	'textarea',
    			'class' => 	'textarea field_option-choices',
    			'name'	=>	'fields['.$key.'][choices]',
    			'value'	=>	$field['choices'],
    		));
    		
    		?>
    	</td>
    </tr>
    <tr class="field_option field_option_<?php echo $this->name; ?>">
    	<td class="label">
    		<label><?php _e("Default Value",'acf'); ?></label>
    		<p class="description"><?php _e("Enter each default value on a new line",'acf'); ?></p>
    	</td>
    	<td>
    		<?php
    		
    		do_action('acf/create_field', array(
    			'type'	=>	'textarea',
    			'name'	=>	'fields['.$key.'][default_value]',
    			'value'	=>	$field['default_value'],
    		));
    		
    		?>
    	</td>
    </tr>
    <tr class="field_option field_option_<?php echo $this->name; ?>">
    	<td class="label">
    		<label><?php _e("Allow Null?",'acf'); ?></label>
    	</td>
    	<td>
    		<?php 
    		do_action('acf/create_field', array(
    			'type'	=>	'radio',
    			'name'	=>	'fields['.$key.'][allow_null]',
    			'value'	=>	$field['allow_null'],
    			'choices'	=>	array(
    				1	=>	__("Yes",'acf'),
    				0	=>	__("No",'acf'),
    			),
    			'layout'	=>	'horizontal',
    		));
    		?>
    	</td>
    </tr>
    <tr class="field_option field_option_<?php echo $this->name; ?>">
    	<td class="label">
    		<label><?php _e("Select multiple values?",'acf'); ?></label>
    	</td>
    	<td>
    		<?php 
    		do_action('acf/create_field', array(
    			'type'	=>	'radio',
    			'name'	=>	'fields['.$key.'][multiple]',
    			'value'	=>	$field['multiple'],
    			'choices'	=>	array(
    				1	=>	__("Yes",'acf'),
    				0	=>	__("No",'acf'),
    			),
    			'layout'	=>	'horizontal',
    		));
    		?>
    	</td>
    </tr>
    <?php
    		
    	}
    	
    	
    	/*
    	*  format_value_for_api()
    	*
    	*  This filter is appied to the $value after it is loaded from the db and before it is passed back to the api functions such as the_field
    	*
    	*  @type	filter
    	*  @since	3.6
    	*  @date	23/01/13
    	*
    	*  @param	$value	- the value which was loaded from the database
    	*  @param	$post_id - the $post_id from which the value was loaded
    	*  @param	$field	- the field array holding all the field options
    	*
    	*  @return	$value	- the modified value
    	*/
    	
    	function format_value_for_api( $value, $post_id, $field )
    	{
    		if( $value == 'null' )
    		{
    			$value = false;
    		}
    		
    		
    		return $value;
    	}
    	
    	
    	/*
    	*  update_field()
    	*
    	*  This filter is appied to the $field before it is saved to the database
    	*
    	*  @type	filter
    	*  @since	3.6
    	*  @date	23/01/13
    	*
    	*  @param	$field - the field array holding all the field options
    	*  @param	$post_id - the field group ID (post_type = acf)
    	*
    	*  @return	$field - the modified field
    	*/
    
    	function update_field( $field, $post_id )
    	{
    		
    		// check if is array. Normal back end edit posts a textarea, but a user might use update_field from the front end
    		if( is_array( $field['choices'] ))
    		{
    		    return $field;
    		}
    
    		
    		// vars
    		$new_choices = array();
    		
    		
    		// explode choices from each line
    		if( $field['choices'] )
    		{
    			// stripslashes ("")
    			$field['choices'] = stripslashes_deep($field['choices']);
    		
    			if(strpos($field['choices'], "\n") !== false)
    			{
    				// found multiple lines, explode it
    				$field['choices'] = explode("\n", $field['choices']);
    			}
    			else
    			{
    				// no multiple lines! 
    				$field['choices'] = array($field['choices']);
    			}
    			
    			
    			// key => value
    			foreach($field['choices'] as $choice)
    			{
    				if(strpos($choice, ' : ') !== false)
    				{
    					$choice = explode(' : ', $choice);
    					$new_choices[ trim($choice[0]) ] = trim($choice[1]);
    				}
    				else
    				{
    					$new_choices[ trim($choice) ] = trim($choice);
    				}
    			}
    		}
    		
    		
    		// update choices
    		$field['choices'] = $new_choices;
    		
    		
    		return $field;
    	}
    	
    }
    
    new acf_field_select();
    
    ?>
    
  • Ok, cool.

    Can I use this code in a new plugin? I see it extends the ACF class, so this works ‘stand-alone’?

  • This is the direct modification on the ACF core code for select. I think modifications are needed to have it as a plugin.

    I don’t know if I’ll have time soon to make a plugin that uses this code, but of course you feel free to use it if you want

  • Cool, thanks. I’ll try something 🙂

  • Hm, ok, if I do this programmaticly I can already use the optgroup. In that case, it can already do what I need it to 🙂

  • cool. My code was because I needed to do optgroup filling values in the admin dashboard. So I introduced the – before the first work to indicate an optgroup 🙂

  • Ah right. Mine will be filled programmaticly. So if I understand correctly, a multi-dimensional array will do the trick. something like

    array(
      'Optgroup' => array( 'choice1', 'choice2' );
    )

    etc.

  • Indeed, this is what I do in my piece of code.

  • Yep, works like a charm. Thanks.

  • Hi,

    Do you know how can I display this optgroup and all the options set in this topic in my form as a select element? I mean exactly the way it looks like in the WordPress panel.

    Thank you in advance.

  • As an update, this appears to be in the default ACF with a action hook now. For example:

    
    add_filter('acf/load_field/key=field_12345678', 'acf_create_select');
    function acf_create_select( $field ) {
    	$field['choices'] = array();
    	$choices = get_my_options();
    	foreach( $choices as $t ) {
    		if ( !isset( $field['choices'][ $t->optgroup ] ) )
    			$field['choices'][ $t->optgroup ] = array();
    		$field['choices'][ $t->optgroup ][ $t->item_value ] = $t->item_text;
    	}
    	return $field;
    }
    
Viewing 15 posts - 1 through 15 (of 15 total)

You must be logged in to reply to this topic.

We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Cookie Policy. If you continue to use this site, you consent to our use of cookies.