0

I have a form setup like this:

<form action="/clients/{{ $client->id }}/create-invoice" method="post">
    <div class="form-group">
        <label for="due_date" class="sr-only">Due Date</label>
        <input type="date" name="due_date" class="form-control" placeholder="Due Date">
    </div>
    <hr />
    <p class="clearfix">
        <strong class="pull-left">Invoice Items</strong>
        <a id="add_invoice_item" class="pull-right"><span class="fa fa-plus"></span></a>
    </p>
    <div class="form-group invoice_item">
        <div class="row">
            <div class="col-ms-6 col-sm-8">
                <input type="text" name="item_description[]" class="form-control" placeholder="Description">
            </div>
            <div class="col-ms-3 col-sm-2">
                <input type="number" class="form-control" name="item_quantity[]" placeholder="Quantity">
            </div>
            <div class="col-ms-3 col-sm-2">
                <input type="text" class="form-control" name="item_price[]" placeholder="Price">
            </div>
        </div>
    </div>
    <div class="form-group">
        <input type="hidden" name="_token" value={{ csrf_token() }}>
        <button class="btn-primary btn btn-block">Create Invoice</button>
    </div>
</form>

I am using Laravel 5.2.

I have the following jQuery to allow the user to add more invoice items:

$("#add_invoice_item").click(function(){
    $(".invoice_item").first().clone().insertAfter("div.invoice_item:last").find("input[type='text']").val("");
});

The problem is when I am looking at the request:

public function store(Request $request, Client $client)
{
    return $request->all();
}

It returns this:

{
  "due_date": "2016-11-19",
  "item_description": [
    "Website Development",
    "SEO",
    "Server Setup"
  ],
  "item_quantity": [
    "1",
    "1",
    "1"
  ],
  "item_price": [
    "450.00",
    "300.00",
    "100.00"
  ],
  "_token": "7tUMdXX9FBU4a7cug51oBlrRyeBi9H8ucUNltQgM"
}

And whilst that is the expected result I am not sure how to get it to return in a way which it is easy to use.

For example I need instead of 3 different array lists I need one array list with each object with each field for that row object.

So:

That it would be returning this:

{
  "due_date": "2016-11-19",
  "items": [
    {"description": "Server Setup", "price": "100.00", "quantity": "1"},
    {"description": "SEO", "price": "300.00", "quantity": "1"},
    {"description": "Website Development", "price": "450.00", "quantity": "1"}
  ],
  "_token": "7tUMdXX9FBU4a7cug51oBlrRyeBi9H8ucUNltQgM"
}

I assume I need to change the HTML and the jQuery but I am unsure how to get it working so that jQuery adds the new fields properly and into the array of items

Thanks!

2
  • 1
    I would suggest that you leave the request data as it is. As it's much simpler to loop through either array and use the index of the current iteration to match up items from all arrays, than it is to hack around the contents of the form before submitting. Commented Nov 16, 2016 at 7:47
  • in html group the checkboxes with an unique id, whether it be the id in the database or an id you're only using on the html page, like: <input type="checkbox" name="item_price[0]"> <input type="checkbox" name="item_description[0]"> etc Commented Nov 16, 2016 at 7:50

2 Answers 2

3

Change naming of your array to product[i][field]

<input type="text" name="product[0][description]"/>
<input type="text" name="product[0][quantity]"/>
<input type="text" name="product[0][price]"/>

<input type="text" name="product[1][description]"/>
<input type="text" name="product[1][quantity]"/>
<input type="text" name="product[1][price]"/>

And in PHP you will receive this:

var_dump($_POST['product']);

[
    [
        'description': 'Website development',
        'quantity': '1',
        'price': '450.00'
    ],
    [
        'description': 'SEO',
        'quantity': '1',
        'price': '300.00'
    ],
]

Only disadvantage is that you need to manually set array index.


Using jQuery and template:

var index = 0;

function add() {
  $('.form').append(template('#my-template', {
    i: index
  }));
  index++;
}

function template(selector, params) {
  if (typeof params === 'undefined') {
    params = [];
  }

  var tplEl = $(selector);

  if (tplEl.length) {
    var tpl = $(selector).html();

    $.each(params, function(i, n) {
      tpl = tpl.replace(new RegExp("\\{" + i + "\\}", "g"), function() {
        if (typeof n === 'object') {
          return n.get(0).outerHTML;
        } else {
          return n;
        }
      });
    });

    return $(tpl);
  } else {
    console.error('Template "' + selector + '" not found!');
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onClick="add()">Add more</button>
<hr/>
<div class="form">

</div>
<script type="text/jQuery-tpl" id="my-template">
  <div class="row">
    {i}: 
    <input type="text" name="product[{i}][description]" />
    <input type="text" name="product[{i}][quantity]" />
    <input type="text" name="product[{i}][price]" />
  </div>
</script>

Sign up to request clarification or add additional context in comments.

5 Comments

Yeah I can't set the array index (it needs to be done with jQuery as I said in my question).
@Shiv Actually you can. I have done it few times. Instead of cloning your inputs, just use some template HTML where you pass in index var newInput = $('<input>', {name: 'product['+i+'][description]'});. Also I think when cloning your items, inputted values are also cloned (may be disadvantage)
@Shiv For what?
Templates in jquery
0

Ah, it has to be done in jQuery. Well, it's also quite easy. Do something like this:

$('.invoice_item').each(function(i, el) {
    // i holds the index. Use it to alter the checkboxes
    $(this)
        .find('input[name="item_description[]"]')
        .attr('name', 'item_description[' + i + ']');

    // Do the same for price and quantity
});

When you post the form, the item properties will be grouped by each row.

3 Comments

Why use two functions if you can directly set formatted name?
Because this serves his purposes just right. If OP can't think of this, using regex and functions that parse field names is overkill.
ah btw I didn't see the part that he's adding more invoice item rows with jquery, I was too fast. Anyway; yes in that case incorporating adding indices in that function is more sane

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.