2

Hoping someone can help me. I'm used to procedural code, and I'm currently refactoring an older PHP app into Laravel. A page in the old app had a table layout with each td having an input. These inputs were all an array, and with jquery, you could add a new row to the table to create a new record. Here's a jsfiddle: https://jsfiddle.net/5tjk7jza/2/. It looks a bit like this, to give you an idea, but the Fiddle will help a lot.:

<tr>
  <td><input type="text" id="name[]" size="15" name="name[]"></td>
  <td><input type="text" id="heartrate[]" size="1" name="heartrate[]"></td>
  <td><input type="text" id="intensity[]" size="1" name="intensity[]"></td>
  <td><textarea rows="4" cols="50" id="description[]" name="description[]"></textarea></td>
</tr>

When it saves, it runs through the loop updating each of the old records based on its id in the array up to a max specified by a hidden input at the bottom. Then it inserts the new records.

I'd like to do this using Laravel and form model binding, and not have separate views. I really like this add/edit in the same table/view. For the life of me, despite reading docs and google searching, I can't find a solution. I've looked at syncing, but that is only for many-to-many relationships/pivot tables, an attach won't work, and a save won't work. Can anyone help me out? Thanks!! Loving Laravel so far!

EDIT: What if I just ditched form-model binding and did something like this guy: How to validate multiple records insertion in laravel?. Is that 'wrong'? I could loop through all the "old" records and then insert all the new ones, but that feels a lot like not using the framework and procedural code.

EDIT 2: Having some trouble with updateorcreate method... I get an error "preg_replace(): Parameter mismatch, pattern is a string while replacement is an array." Can anyone look at this and help me? I've been looking at the docs for a few hours now...

Here is my form/view:

{!! Form::model($workoutcategories,['method' => 'PATCH', 'url' => 'workoutcategories/update']) !!}

@foreach($workoutcategories as $workoutcategory)
<tr>
    <td>{!! Form::text('name[]', $workoutcategory->name, ['class' => 'form-control', 'size' => '15']) !!}</td>
    <td>{!! Form::text('heartrate[]', $workoutcategory->heartrate, ['class' => 'form-control', 'size' => '1']) !!}</td>
    <td>{!! Form::text('intensity[]', $workoutcategory->intensity, ['class' => 'form-control', 'size' => '1']) !!}</td>
    <td>{!! Form::textarea('description[]', $workoutcategory->description,['class' => 'form-control', 'rows' => '4', 'cols'=>'100']) !!}</td>
</tr>
    @endforeach

</tbody>
</table>

Here is my controller:

public function CreateOrUpdateCategory ()
{
    $workoutcategories = WorkoutCategory::all();
    return view('workouts.categoryform', compact('workoutcategories'));
}

public function update(WorkoutCategory $workoutCategory, WorkoutCategoryRequest $request) {
    $cats = WorkoutCategory::all();
    $workoutCategory->updateorcreate(['name' => $request->name],['name' => $request->name, 'intensity' => $request->intensity, 'heartrate' => $request->heartrate,'description' => $request->description]);
    return redirect('workoutcategoies');
}
7
  • 1
    May be it can help: laravel.com/docs/5.3/eloquent#other-creation-methods Commented Dec 5, 2016 at 14:17
  • assumissing each row(tr) is a separate record in db then you can update/add only that field by the primary key. Commented Dec 5, 2016 at 14:33
  • Would updateOrCreate be like your add/edit? Commented Dec 5, 2016 at 15:08
  • Yes, @RaunakGupta, each row is a separate record in the DB. Commented Dec 5, 2016 at 15:15
  • @MahfuzulAlam, I think this may be the solution. I'll try it and update this if it works. Thanks! Commented Dec 5, 2016 at 15:17

1 Answer 1

0

After a lot of help from other questions on Stack and mainly the tips/pointers from @Wistar + @MahfuzulAlam, I found the solution. I needed to use createOrUpdate, but using it with the name field like in the columns above doesn't work, since if you change the name on one of the fields, it will create a new field. You need a hidden id field in each input array to check to see if you need to update or create.

Here is my view. When building your view, MAKE SURE your FORM close and FORM open are OUTSIDE of any other elements (specifically the table)

@section('content')
    <script type='text/javascript'>
        $(window).load(function(){
            var count = 2;

            $("#Add").click(function() {

                var $clone = $("#tbl tbody #clone").clone();
                $clone.attr({
                    id: "cloned",
                    style: "" // remove "display:none",
                });

                $("#tbl tbody").append($clone);
                $("#count").val(count);
                count++;
            });
        });
    </script>

    <h1>Workout Zones</h1>
    @include('errors/list')

    <hr>
    {!! Form::model($workoutcategories,['method' => 'POST', 'url' => 'workoutcategories/update']) !!}
    <table class='table table-bordered' id='tbl'>
        <tbody>
        <tr>
            <td>Name</td>
            <td>Heart Rate (Optional)</td>
            <td>Intensity (Optional)</td>
            <td>Description</td>
        </tr>

        @foreach($workoutcategories as $workoutcategory)
            <tr>
                <td>{!! Form::text('name[]', $workoutcategory->name, ['class' => 'form-control', 'size' => '15']) !!}</td>
                <td>{!! Form::text('heartrate[]', $workoutcategory->heartrate, ['class' => 'form-control', 'size' => '1']) !!}</td>
                <td>{!! Form::text('intensity[]', $workoutcategory->intensity, ['class' => 'form-control', 'size' => '1']) !!}</td>
                <td>{!! Form::textarea('description[]', $workoutcategory->description,['class' => 'form-control', 'rows' => '4', 'cols'=>'100']) !!}{!! Form::hidden('id[]', $workoutcategory->id,['class' => 'form-control', 'rows' => '4', 'cols'=>'100']) !!}</td>

            </tr>
        @endforeach

        <tr id="clone" style="display:none;">
            <td>{!! Form::text('name[]', null, ['class' => 'form-control', 'size' => '15']) !!}</td>
            <td>{!! Form::text('heartrate[]', null, ['class' => 'form-control', 'size' => '1']) !!}</td>
            <td>{!! Form::text('intensity[]', null, ['class' => 'form-control', 'size' => '1']) !!}</td>
            <td>{!! Form::textarea('description[]', null,['class' => 'form-control', 'rows' => '4', 'cols'=>'100']) !!}{!! Form::hidden('id[]', null,['class' => 'form-control', 'rows' => '4', 'cols'=>'100']) !!}</td>
        </tr>


        </tbody>
    </table>

    <input type='button' id='Add' value='Add'>

    <div class="form-group">
        {!! Form::submit('Update Workout Zones', ['class' => 'btn btn-primary form-control']) !!}
        </form>
    </div>

Here is the controller. It is fairly simple. First loop through each field, make sure the name isn't empty (I'm still having some issues with validation, but there are lots of documents for me to read on that) and then updateOrCreate. The logic there is check if the id on any of the categories matches the id on the request. If so, update the fields in the array (name, HR, intensity, desc). Otherwise, create a new record (Laravel does the create automatically, no need to call save). Good luck to anyone else who needs to solve this issue!

public function update(WorkoutCategory $workoutCategory, WorkoutCategoryRequest $request) {

        foreach($request->input('name') as $key => $value) {
            if(!empty($request->input('name.' . $key))) {
            $workoutCategory->updateOrCreate(['id' => $request->input('id.' . $key)], ['name' => $request->input('name.' . $key), 'heartrate' => $request->input('heartrate.' . $key), 'intensity' => $request->input('intensity.' . $key), 'description' => $request->input('description.' . $key)]);
            }
        }

        flash()->overlay('Your workout zones have been updated!', 'Good Job!');
        return redirect('workoutcategories')
            ;
    }
Sign up to request clarification or add additional context in comments.

Comments

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.