I’ve started building a widget that needs to have an array inside an array. I’m trying to get the widget to have the ability to create multiple elements that will display on the site, but also has multiple fields inside each element…but after a couple days of searching and trying various things, I haven’t had much luck getting it sorted out. I’m hoping someone can help straighten out the errors in my code here. It isn’t saving the field data to the database, so I don’t have the update command finding the values properly. If I adjust “$this->get_field_id(‘inner_elements’)” and “$this->get_field_name(‘inner_elements’)” in the first $fields[‘icontxt’] element and change “inner_elements” to “elements“, it does save, but only the data for the last array element (to all three values, since I haven’t figured out how to specify it in the update function). However, if I change all three from “inner_elements” to “elements”, it doesn’t work at all. In this code, I’m just trying to display 3 fields, then save the results, but I plan on building in a drop down list and other controls once this is working (so they won’t all be the same input tag).

From what I can guess, it seems the majority of my problems are in the way I’m naming the “id” and “name” in the input tags (inside $fields) and I need to save each specifically in the update function.

// Widget Backend 
public function form( $instance ) {
$title = isset ($instance['title']) ? $instance['title'] : 'New Title';

$elements = isset ($instance['elements']) ? $instance['elements'] : array();
$elements_num = count($elements);
$elements[$element_num + 1] = '';
$elements_counter = 0;
$inner_elements_counter = 0;

foreach($elements as $element){
    $inner_elements = isset($instance['elements']['inner_elements']) ? $instance['elements']['inner_elements'] : array(
        'icontxt' => 'The Icon Text',
        'iconlnk' => 'The Icon Link',
        'iconlnktrgt' => 'Link Target'
    );
    $fields = array();

    $fields['icontxt'] = sprintf(
        '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
        $this->get_field_id('inner_elements'),
        $this->get_field_name('inner_elements'),
        $elements_counter,
        esc_attr($inner_elements['icontxt'])
    );
    $fields['iconlnk'] = sprintf(
        '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
        $this->get_field_id('inner_elements'),
        $this->get_field_name('inner_elements'),
        $elements_counter,
        esc_attr($inner_elements['iconlnk'])
    );
    $fields['iconlnktrgt'] = sprintf(
        '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
        $this->get_field_id('inner_elements'),
        $this->get_field_name('inner_elements'),
        $elements_counter,
        esc_attr($inner_elements['iconlnktrgt'])
    );

    $elements_counter += 1;
}

//displays the widget in admin
print 'Elements<br />' . join('<br />', $fields);
}

// Updating widget replacing old instances with new
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = esc_html($new_instance['title']);
$instance['elements'] = array();
$instance['elements']['inner_elements'] = array();
if(isset($new_instance['elements'])){
    foreach($new_instance['elements'] as $element){
        if('' !== trim($element)){
            //$instance['inner_elements'] = array();
            foreach($new_instance['inner_elements'] as $inner_element){
                //$instance['elements']['inner_elements'][] = $inner_element;
                $instance['elements']['inner_elements']['icontxt'] = $inner_element;
                $instance['elements']['inner_elements']['iconlnk'] = $inner_element;
                $instance['elements']['inner_elements']['iconlnktrgt'] = $inner_element;
            }
        }
    }
}
return $instance;
}

Here’s a general idea of where I’m headed:

enter image description here

The button will allow someone to select an image/icon, then the other fields will be additional values to that image/icon. Clicking the plus symbol will allow the user to create a new image/icon (array) and related fields (inner array).

1 Answer
1

get_field_id prepare prefix for field id, within one widget instance it’s always the same. Your form fields probably look similar to these:

// -- $element[0] --
  // $field[icontxt]
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][0]" value="%4$s">
  // $field[iconlnk]
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][0]" value="%4$s">
  //$field[iconlnktrgt]
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][0]" value="%4$s">

// -- $element[1] --
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][1]" value="%4$s">
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][1]" value="%4$s">
<input class="widefat" id="widget-baseid-instance-inner_elements" name="widget-baseid[instance][inner_elements][1]" value="%4$s">

You need use diffrent id/name for each field.
Additionally, number the group of fields (txt, lnk and lnktrgt) and add this number to the end of the field ID. The same should be done after clicking on “+” button.

$fields['icontxt'] = sprintf(
    '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
    $this->get_field_id('icontxt' . $elements_counter),
    $this->get_field_name('icontxt'),
    $elements_counter,
    esc_attr($inner_elements['icontxt'])
);
$fields['iconlnk'] = sprintf(
    '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
    $this->get_field_id('iconlnk' . $elements_counter),
    $this->get_field_name('iconlnk'),
    $elements_counter,
    esc_attr($inner_elements['iconlnk'])
);
$fields['iconlnktrgt'] = sprintf(
    '<input class="widefat" id="%1$s" name="%2$s[%3$s]" value="%4$s">',
    $this->get_field_id('iconlnktrgt' . $elements_counter),
    $this->get_field_name('iconlnktrgt'),
    $elements_counter,
    esc_attr($inner_elements['iconlnktrgt'])
);

<input class="widefat" id="widget-baseid-instance-icontxt0" name="widget-baseid[instance][icontxt][0]" value="%4$s">
<input class="widefat" id="widget-baseid-instance-iconlnk0" name="widget-baseid[instance][iconlnk][0]" value="%4$s">
<input class="widefat" id="widget-baseid-instance-iconlnktrgt0" name="widget-baseid[instance][iconlnktrgt][0]" value="%4$s">

Should not $elements[$element_num] be here? You leave one index.

$elements_num = count($elements);
$elements[$element_num + 1] = '';

Update
In update() function, $new_instance parameres contains keys used in form() – ‘icontxt’, ‘iconlnk’, ‘iconlnktrgt’:

$new_instance[icontxt][ 
      0 => "some value from field no. 1", 
      1 => "value from field no. 2", 
];
$new_instance[iconlnk][ 
      0 => "link from first field ", 
      1 => "second link", 
]; 

To read data from field

//name= $this->get_field_name('icontxt')" ...> 
<input class="widefat" id="..." name="" ...>  

inside update() function use $value = $new_instance['icontxt'][0];.

You can try code like this to collect form data into array $elements['inner_elements']:

// arrays initialization
$tmp_elements = [ 'inner_elements' => [] ];
if ( isset($new_instance['icontxt']) ) {
    for($i = 0; $i < count( $new_instance['icontxt'] ); ++$i)
        $tmp_elements['inner_elements' . $i]['icontxt'] = $new_instance['icontxt'][$i];
    unset($new_instance['icontxt']);
}
// ...
$new_instance['elements'] = $tmp_elements;

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *