Welcome Guest, Not a member yet? Register   Sign In
Inserting multiple form fields with same name
#1

[eluser]Dede[/eluser]
Hi

This is my first post here, so please if I missed the correct forum thread, I'm sorry in advance.

I'm starting CI, and really like it, but have some trouble that cannot solve for few days.
I have a form with 4 fields in view:

Code:
<?php
  echo form_open('room_type/insert');
?>
<p>
  &lt;input type="button"  value="Add another room type" enabled="enabled" /&gt;
</p>
    <p><label for="room_types">Room type</label>&lt;input type="text" name="room_types[]" /&gt;&lt;/p>
    <p><label for="room_types_mk">Room type SR</label>&lt;input type="text" name="room_types_sr[]" /&gt;&lt;/p>
    <p><label for="max_occupancy">Max Occuancy</label>&lt;input type="text" name="max_occupancy[]" /&gt;&lt;/p>
    <p><label for="extra_bedding">Extra Bedding</label>&lt;input type="text" name="extra_bedding[]" /&gt;&lt;/p>
  


</div>
&lt;?php

    $submit_attr = array(
       'type' =>'submit',
       'name'=>'insert_room_types'
       );
    
    echo form_submit($submit_attr, 'Insert');

           echo form_close();
        ?&gt;


As you can see, there's an "Add another room type" button on top of the form which lets me add the same form again under the first one, and the process can be repeated up to 5 forms, meaning I can insert (well, not exactly, since I haven't found a solution :redSmile 5 different room types with different occupancy, etc.
The other form fields are added with jQuery, and have the same names as the above posted.

In short, when I submit the form with e.g 3 sets of room types, I got the following multidimensional array:

Code:
Array
(
    [room_types] => Array
        (
            [1] => Single
            [2] => Double
            [3] => Three beds
        )

    [room_types_sr] => Array
        (
            [1] => Single SR
            [2] => Double SR
            [3] => Three beds SR
        )

    [max_occupancy] => Array
        (
            [1] => 1
            [2] => 2
            [3] => 3
        )

    [extra_bedding] => Array
        (
            [1] => 1
            [2] => 1
            [3] => 0
        )

    [insert_room_types] => Insert
)

I cannot find a way to sort the array in following format:
Code:
$insert_array = array(
  array('room_types' => 'Single', 'room_types_sr '=> 'Single SR' 'max_occupancy' => '1', 'extra_bedding' => '1'),
  array('room_types' => 'Double', 'room_types_sr '=> 'Double SR' 'max_occupancy' => '2', 'extra_bedding' => '1'),
            // etc....
);

);

Also, I'm using info from http://ellislab.com/codeigniter/user-gui...ysasfields and cannot validate appropriately.

What is the best practice to make DB inserts using AC for this type of form?

Thanks in advance
#2

[eluser]skunkbad[/eluser]
Since you are already using jQuery, why not just create a hidden form field that holds the number of sets of fields. For instance, on intial page load, the field would have a value of 1. If you add a set, jQuery adds more fields with a -2 extension, and updates the value of the hidden field. Upon submission, you use the value of the hidden form field to dynamically create validation rules for however many sets of fields you need to check. It's pretty easy to do. You could even submit the form via ajax and return a response that you display in a modal dialog box.
#3

[eluser]Dede[/eluser]
Thanks for fast reply skunkbad.

I've already tried that approach, and so far seems the best.
The only problem is that I'm using Jamie Rumbelow's CRUD model https://github.com/jamierumbelow/codeigniter-base-model which is implementing the following validation pattern: http://ellislab.com/codeigniter/user-gui...lesasarray.
And since I want to have dynamic number of room types, e.g. 5 sets for free user, 10 sets of room types for paid user, the validation rules should be defined dynamically as well.

For example, if the user has opened and filled 3 sets of room types, the validation rules for first set in Rumbelow's MY_Model should be:

Code:
protected $validate = array(
   array(
    'field' => 'room_types_1',
    'label' => 'room_types',
    'rules' => 'trim|required|xss_clean|min_lenght[3]'  
   ),
   array(
    'field' => 'room_types_mk_1',
    'label' => 'room_types',
    'rules' => 'trim|required|xss_clean|min_lenght[3]'
   ),
   array(
    'field' => 'max_occupancy_1',
    'label' => 'room_types',
    'rules' =>'trim|required|xss_clean|min_lenght[1]|max_length[2]'  
   ),
   array(
    'field' => 'extra_bedding_1',
    'label' => 'room_types',
    'rules' => 'trim|xss_clean|min_lenght[1]|max_length[2]'
   )  
);

But real problem occurs when the user, for example, has opened 5 sets of room types but filled only 3.
Then, instead of inserting only 3 sets, there will be validation error on other 2 since they are empty.

I guess I should switch to normal AC (without using MY_Model CRUD) for this form to avoid this problem and use you solution.
#4

[eluser]sarcastron[/eluser]
You may want to consider using a multidimensional array for the inputs on the HTML side. Then you could use a foreach loop to insert them or build the an array to sort them. The HTML for the form (be it generated via PHP or jQuery) would look sort of like this:

Code:
<h3>First Room</h3>
<p><label for="room_types">Room type</label>&lt;input type="text" name="rooms[1][room_type]" /&gt;&lt;/p>
<p><label for="room_types_mk">Room type SR</label>&lt;input type="text" name="rooms[1][room_types_sr]" /&gt;&lt;/p>
<p><label for="max_occupancy">Max Occuancy</label>&lt;input type="text" name="rooms[1][max_occupancy]" /&gt;&lt;/p>
<p><label for="extra_bedding">Extra Bedding</label>&lt;input type="text" name="rooms[1][extra_bedding]" /&gt;&lt;/p>

<h3>Second Room</h3>
<p><label for="room_types">Room type</label>&lt;input type="text" name="rooms[2][room_type]" /&gt;&lt;/p>
<p><label for="room_types_mk">Room type SR</label>&lt;input type="text" name="rooms[2][room_types_sr]" /&gt;&lt;/p>
<p><label for="max_occupancy">Max Occuancy</label>&lt;input type="text" name="rooms[2][max_occupancy]" /&gt;&lt;/p>
<p><label for="extra_bedding">Extra Bedding</label>&lt;input type="text" name="rooms[2][extra_bedding]" /&gt;&lt;/p>

<h3>Third Room</h3>
<p><label for="room_types">Room type</label>&lt;input type="text" name="rooms[3][room_type]" /&gt;&lt;/p>
<p><label for="room_types_mk">Room type SR</label>&lt;input type="text" name="rooms[3][room_types_sr]" /&gt;&lt;/p>
<p><label for="max_occupancy">Max Occuancy</label>&lt;input type="text" name="rooms[3][max_occupancy]" /&gt;&lt;/p>
<p><label for="extra_bedding">Extra Bedding</label>&lt;input type="text" name="rooms[3][extra_bedding]" /&gt;&lt;/p>

You want to adjust the FOR attribute in your labels and give your inputs unique ID attributes as well, but that's beside the point.

Then you could use something like this in the controller:

Code:
$thePost = $this->input->post();
$insert_array = $thePost['rooms'];

This should give you a multidimensional array that you could use and you wouldn't have to worry about sorting the array since the sorting was done before the POST was sent. I hope this is helpful.
#5

[eluser]CroNiX[/eluser]
If your array looks exactly what you stated, you could do this, where $t is your original array:
Code:
// get the keys for room_types.  It should be the same as all other fields
// since they will be equal in number for each array of form values
$keys = array_keys($t['room_types']);

// for storing newly formatted data
$insert_data = array();  

// loop through the keys (happens to be 3 with your sample data)
// and assign data to a new array based on the key
foreach($keys as $key)
{
  $insert_data[] = array(
    'room_types'    => $t['room_types'][$key],
    'room_types_sr' => $t['room_types_sr'][$key],
    'max_occupancy' => $t['max_occupancy'][$key],
    'extra_bedding' => $t['extra_bedding'][$key]
  );  
}

// get the action the form
$action = $t['insert_room_types'];  //will be 'Insert' from your example
This will create an array like you showed you wanted no matter how many additions to your form there are, assuming each new section is comprised of the 'room_types', 'room_types_sr', 'max_occupancy' and 'extra_bedding' fields.
#6

[eluser]Dede[/eluser]
Guys, thank you for replies.

I've finally solved this with your help (I love this community).
The solution is kind of combination of all your proposed codes above.

Here's the controller method:
Code:
&lt;?php
        protected $validate;

public function insert()
{
  
  $pointer_array = $this->input->post('pointer');
  
  // Grab the last value of the hidden field `pointer`
  $pointer = array_pop($pointer_array);
  
  // Loop and make validation rules on the fly
  for($i=1; $i<=$pointer; $i++){
   $this->validate = array(
    array(
     'field' => 'room_info['.$i.'][room_type]',
     'label' => 'Room type No.'. $i,
     'rules' => 'trim|required|xss_clean|min_length[3]'  
    ),
    array(
     'field' => 'room_info['.$i.'][room_type_mk]',
     'label' => 'Room Type MK No.'. $i,
     'rules' => 'trim|required|xss_clean|min_length[3]'
    ),
    array(
     'field' => 'room_info['.$i.'][max_occupancy]',
     'label' => 'Maximum Occupancy No.'. $i,
     'rules' => 'trim|required|xss_clean|min_length[1]'  
    ),
    array(
     'field' => 'room_info['.$i.'][extra_bedding]',
     'label' => 'Extra Bedding No.'. $i,
     'rules' => 'trim|xss_clean|min_length[1]'
    )  
   );
  }
  
  $this->load->library('form_validation');
  $this->form_validation->set_error_delimiters('<div class="alert-message error">', '</div>');
  $this->form_validation->set_rules($this->validate);
  
  if($this->form_validation->run() == true){

   $room_info = $this->input->post('room_info');
      
   foreach($room_info as &$info){    // Pass by reference
       //Add property ID to every set of the $room_info array
    array_unshift($info,$this->the_property_id);
    
    // Rename the key of "[0] => $this->the_property_id"
    // to  "[property_id] => $this->the_property_id" of the $room_info array
    $info['property_id'] = $info[0];
    unset($info[0]);
   }

  
   // Make bulk insert using Rumbelow’s MY_Model  
   $this->room_info->insert_many($room_info);
  
  
  
  
  }else {
   // TESTING ONLY !!!!!!
   print_r(validation_errors());
  }
          }
?&gt;

The $pointer variable is a hidden field dynamically added to each set of form fields through jQuery.

And the view:
Code:
&lt;?php
  echo form_open('room_type/insert');
?&gt;
<p>
  &lt;input type="button" id="add"  value="Add another room type" enabled="enabled" /&gt;
</p>
    <p><label>Room type</label>&lt;input type="text" name="room_info[1][room_type]" id="room_type" /&gt;&lt;/p>
    <p><label>Room type MK</label>&lt;input type="text" name="room_info[1][room_type_mk]" id="room_type_mk" /&gt;&lt;/p>
    <p><label>Max Occuancy</label>&lt;input type="text" name="room_info[1][max_occupancy]" id="max_occupancy" /&gt;&lt;/p>
    <p><label>Extra Bedding</label>&lt;input type="text" name="room_info[1][extra_bedding]" id="extra_bedding" /&gt;&lt;/p>
    &lt;input  type="hidden" name="pointer[]" value="1" /&gt;


</div>
&lt;?php

    $submit_attr = array(
       'type' =>'submit',
       'name'=>'insert_room_types'
       );
    
    echo form_submit($submit_attr, 'Insert');

           echo form_close();
        ?&gt;

The jQuery part:
Code:
$(function() {
var room_div = '#room_type_form';
$('#add').attr('enabled', 'enabled')

var i = 1;

$('#add').live('click', function() {
  i++;
  var open_fieldset = '<div class="the_parent"><fieldset><legend>Room Type ' + i + '</legend>'
       + '<p><label for="room_types">Room type</label>&lt;input type="text" name="room_info[' + i + '][room_type]" id="room_type" /&gt;&lt;/p>'
        + '<p><label for="room_types_mk">Room type MK</label>&lt;input type="text" name="room_info[' + i + '][room_type_mk]" id="room_type_mk" /&gt;&lt;/p>'
        + '<p><label for="max_occupancy">Max Occuancy</label>&lt;input type="text" name="room_info[' + i + '][max_occupancy]" id="room_type_mk" /&gt;&lt;/p>'
        + '<p><label for="extra_bedding">Extra Bedding</label>&lt;input type="text" name="room_info[' + i + '][extra_bedding]" id="room_type_mk" /&gt;&lt;/p>'
        + '<p><a href="#" id="remove_fieldset" class="btn small danger">Remove</a></p>'
        + '</fieldset>'
        + '</div>'
        + '&lt;input type="hidden" name="pointer[]" value="' + i + '" /&gt;"';

  $(open_fieldset).appendTo(room_div);
  

  if( i >= 5 ){ // max 5 input fields
   $('#add').attr('disabled', 'disabled').val('Maximum No. of rom types reached');
  }
});
$('#remove_fieldset').live('click', function(){
  /*if( i > 2 ) {*/
                $(this).parents('div.the_parent').remove();
                i--;    
  /*}*/
  if( i < 6 ){
     $('#add').removeAttr("disabled").val('Add another Room Type');
  }
  
        return false;
});
});

Note that the JavaScript part above is for 5 room type sets max.

On the other hand, on validation arrays, I just saw this and it is fresh: https://github.com/EllisLab/CodeIgniter/pull/1105. I guess it will be merged into the core after testing.




Theme © iAndrew 2016 - Forum software by © MyBB