5

I'm using jQuery to generate two dynamic fields. Each pair of fields is displayed on the page and can have multiple instances. On submit (not ajax), each pair of fields is saved into the their own table row along with Auth::id().

There are two forms in the HTML code, both values are entered, user clicks 'Add link' then jQuery creates two hidden fields (these are the ones that get submitted) and data entered appears (appended) visually to #link-list. The original fields become empty and the process can repeat...

I'm struggling to create an array that is recognised by eloquent to save the data multiple times.

I get the error 'Undefined index: link' or whichever the second input row is in jQuery.

Blade/HTML:

{!! Form::open(['route' => ['multiple.store'], 'method' => 'post', 'role'=> 'form', 'class' => 'form']) !!}

    <ul id="link-list">
        <!-- append new rows -->
    </ul>

    <div id="newlink" class="form-inline">
        <div class="form-group">
            {!! Form::text('prestore', null, ['placeholder' => 'Store name', 'class' => 'form-control']) !!}
        </div>
        <div class="form-group">
            {!! Form::text('prelink', null, ['placeholder' => 'Link / URL', 'class' => 'form-control']) !!}
        </div>
        <div class="form-group">
            <button class="btn btn-primary submit new-row" type="button">Add store link</button>
        </div>
    </div>

    <br/><br/>

    {!! Form::submit('Submit rows', ['class' => 'btn btn-success submit']) !!}

{!! Form::close() !!}

jQuery/JavaScript

$(document).on('click', '.new-row', function() {
    var store = $('#newlink input[name=prestore]').val();
    var link = $('#newlink input[name=prelink]').val();
    console.log(store, link);
    $('<li class="not-saved">' +
            '<a href="'+link+'">'+store+'</a>' +
            '<input type="hidden" name="rows[][link]" value="' + link + '">' +
            '<input type="hidden" name="rows[][store]" value="' + store + '">' +
        '</li>').appendTo('#link-list').hide().fadeIn(280);
    $('input[name=prestore]').val('');
    $('input[name=prelink]').val('');
});

Controller:

public function store()
{
    $input = Input::all();

    foreach ($input['rows'] as $row) {
        $items = new Multiple([
            'user_id' => Auth::id(),
            'store' => $row['store'],
            'link' => $row['link'],
        ]);
        $items->save();
    }
}
Jack Barham
  • 3,197
  • 10
  • 41
  • 62
  • What class type is `Multiple`? Laravel's `Model` class doesn't have a `saveMany()` method. – Ben Claar Jul 25 '15 at 17:25
  • Thats my own class, this is just a testing repo (testing multiple row saves, hence the name). saveMany() is my mistake, but I can't even get to that part – Jack Barham Jul 25 '15 at 17:26
  • With regards to saveMany(), whats this? http://laravel.com/docs/5.1/eloquent-relationships#inserting-related-models – Jack Barham Jul 25 '15 at 17:34
  • 1
    That's the `saveMany()` method on the `HasOneOrMany` or `BelongsToMany` classes, not on the model itself. If you've setup a `HasMany` or `BelongsToMany` relationship on your model, you can use it like `$myModel->myRelationship()->saveMany($arrayOfRelatedModels)`. – Ben Claar Jul 25 '15 at 17:46
  • Cool - I'll bear that in mind. However I still can't get my original question working :/ – Jack Barham Jul 25 '15 at 17:51

3 Answers3

9

One problem is in your JavaScript element names:

<input type="hidden" name="rows[][link]" value="' + link + '">
<input type="hidden" name="rows[][store]" value="' + store + '">

This will generate $rows like:

[
    0 => ["link" => "foo"], 
    1 => ["store" => "bar"]
]

But your PHP code expects $rows to be like:

[
    0 => [
        "link" => "foo",
        "store" => "bar"
    ], 
    1 => [
        "link" => "foo",
        "store" => "bar"
    ]
]

One way to generate the expected values is to specify the row keys in your elements:

<input type="hidden" name="rows[0][link]" value="' + link + '">
<input type="hidden" name="rows[0][store]" value="' + store + '">
<input type="hidden" name="rows[1][link]" value="' + link + '">
<input type="hidden" name="rows[1][store]" value="' + store + '">

Obviously this is a bit tricky given the code you've provided, so let me know if you need assistance with that.

Ben Claar
  • 3,285
  • 18
  • 33
  • Thanks, I'll check when I'm back at my computer tomorrow. That does make sense. It sounds like I need a count in jquery for each (pair) input. I'll let you know either way. – Jack Barham Jul 25 '15 at 18:44
  • This has worked (although I added the count numbers in manually in Dev Tools), but it did save all entries successfully. Now I just need to figure out how to build in a count :) – Jack Barham Jul 26 '15 at 11:43
3

If this helps anyone else, this is the jQuery required to work with Ben's correct answer:

var count = 0;

$(document).on('click', '.new-row', function() {

    count++;

    var store = $('#newlink input[name=prestore]').val();
    var link = $('#newlink input[name=prelink]').val();

    if ($('input[name=prestore]').val().length > 2 && $('input[name=prelink]').val().length > 2) {

        $('<li class="not-saved">' +
            '<a href="' + link + '">' + store + '</a>' +
            '<input type="hidden" name="rows[' + count + '][store]" value="' + store + '">' +
            '<input type="hidden" name="rows[' + count + '][link]" value="' + link + '">' +
            '</li>').appendTo('#link-list').hide().fadeIn(280);

        $('input[name=prestore]').val('');
        $('input[name=prelink]').val('');

    } else {

        console.log('At least 3 characters for each field required!');

    }

});

I also added a tiny bit of validation so it wont append empty fields

Jack Barham
  • 3,197
  • 10
  • 41
  • 62
0

I agree that this is a problem with your element names, but disagree with Ben's solution. You can keep your JavaScript as-is and handle this in PHP:

public function store()
{
    $rows = Input::get('rows');

    $userId = Auth::id();
    for ($i = 0; $i < (count($rows) - 1); $i += 2) {
        $item = new Multiple([
            'user_id' => $userId,
            'link'    => $rows[$i]['link'],
            'store'   => $rows[$i + 1]['store'],
        ]);
        $item->save();
    }
}
Ben Claar
  • 3,285
  • 18
  • 33
  • Thanks for your response, I was hoping this would work (as it does seem cleaner) over the jQuery fix, but I get the same error "Undefined index: link" and the top answer has worked. – Jack Barham Jul 26 '15 at 11:44