3

I have an edit form for a model and a section is dedicated to that specific record's child records called "shipment_details".

Editing a record will always show at least one existing shipment_detail record. At the moment my HTML of the "shipment_details" is as such from the Blade:

<tbody>
    @foreach($shipment_details as $sd)
    <tr style="height:40px">
        <td style="width:8%;text-align:center;">{{Form::text('shipment_details['.$sd->id.'][piecesNumber]', $sd->pieces_number, array('class' => 'form-control','placeholder'=>'No. Pieces','required','id'=>'piecesNumber'))}}
        </td>
        <td style="width:16%;text-align:center;">
            {!! Form::select('shipment_details['.$sd->id.'][piecesType]', $piecetypes, $sd->pieces_type, ['id' => 'pieces_type', 'class' => 'form-control full-width','required']) !!}    
        </td>
        <td>
            {!! Form::select('shipment_details['.$sd->id.'][rateType]', $ratetypes, $sd->rate_type, ['id' => 'rateType', 'class' => 'form-control full-width','required']) !!}
        </td>
        <td style="width:16.5%;text-align:center;">
            {{Form::text('shipment_details['.$sd->id.'][weight]', $sd->weight, array('class' => 'form-control','placeholder'=>'Weight','required','id'=>'weight'))}}    
        </td>
        <td style="width:16.5%;text-align:center;">
            {{Form::select('shipment_details['.$sd->id.'][hazmat]',array(
                                    '0'=>'No',
                                    '1'=>'Yes',
                                ), $sd->hazmat, array('class' => 'form-control','id'=>'hazmat'))}}   
        </td>
        <td style="width:16.5%;text-align:center;">
                            {{Form::text('shipment_details['.$sd->id.'][description]', $sd->description, array('class' => 'form-control','placeholder'=>'Description','required','id'=>'description'))}} 
        </td>
        <td style="width:16.5%;text-align:center;">
            {{Form::text('shipment_details['.$sd->id.'][charge]', $sd->charge, array('class' => 'form-control','placeholder'=>'Charge','required','id'=>'charge'))}} 
        </td>
        <td><button type="button" name="add" id="add" class="btn btn-success">Add More</button></td>
    </tr>

    @endforeach
</tbody>

But if there is a need to add additional shipment_details, I have a script which adds additional record rows formatted below:

<script>
 $(document).ready(function(){  
      var i=1;  
      $('#add').click(function(){  
            i++;  
           $('#freight_bill_items').append('<tr id="row'+i+'"> <td style="width:8%;text-align:center;">{{Form::text('shipment_details[piecesNumber][]', null, array('class' => 'form-control','placeholder'=>'No. Pieces','required','id'=>'piecesNumber'))}}</td><td style="width:16%;text-align:center;">{!! Form::select('shipment_details[piecesType][]', $piecetypes, 'null', ['id' => 'pieces_type', 'class' => 'form-control full-width','required']) !!} </td><td>{!! Form::select('shipment_details[rateType][]', $ratetypes, null, ['id' => 'rateType', 'class' => 'form-control full-width','required']) !!}</td><td style="width:16.5%;text-align:center;">{{Form::text('shipment_details[weight][]', null, array('class' => 'form-control','placeholder'=>'Weight','required','id'=>'weight'))}}</td><td style="width:16.5%;text-align:center;">{{Form::select('shipment_details[hazmat][]',array('No'=>'No','Yes'=>'Yes',), null, array('class' => 'form-control','id'=>'hazmat'))}}</td><td style="width:16.5%;text-align:center;">{{Form::text('shipment_details[description][]', null, array('class' => 'form-control','placeholder'=>'Description','required','id'=>'description'))}}</td><td style="width:16.5%;text-align:center;">{{Form::text('shipment_details[charge][]', null, array('class' => 'form-control','placeholder'=>'Charge','required','id'=>'charge'))}}</td><td><button type="button" name="remove" id="'+i+'" class="btn btn-danger btn_remove">X</button></td></tr>');   
      });  
      $(document).on('click', '.btn_remove', function(){  
           var button_id = $(this).attr("id");   
           $('#row'+button_id+'').remove();
           $( 'input[name^="shipment_details[charge][]"]' ).trigger( "change" );
      });

 });
</script>

My problem arises when I go to post the data, the existing data is "arrayed" correctly, but the new data isn't and at the moment gives me an undefined index record.

Here is the current POST data output for shipment_details for a test record:

shipment_details

array:8 [▼
  13149 => array:7 [▼
    "piecesNumber" => "1"
    "piecesType" => "1"
    "rateType" => "1"
    "weight" => "12"
    "hazmat" => "0"
    "description" => "124"
    "charge" => "12.00"
  ]
  "piecesNumber" => array:1 [▼
    0 => "3"
  ]
  "piecesType" => array:1 [▼
    0 => "2"
  ]
  "rateType" => array:1 [▼
    0 => "2"
  ]
  "weight" => array:1 [▼
    0 => "1200"
  ]
  "hazmat" => array:1 [▼
    0 => "Yes"
  ]
  "description" => array:1 [▼
    0 => "desc2"
  ]
  "charge" => array:1 [▼
    0 => "40.00"
  ]
]

So as you can see, the inputs are all separate except for the existing record, so my problem must be originating in my form. Does anyone have any suggestions?

Update

Using the above output:

This is how it should be sent (or in a similar format):

  13149 => array:7 [▼
    "piecesNumber" => "1"
    "piecesType" => "1"
    "rateType" => "1"
    "weight" => "12"
    "hazmat" => "0"
    "description" => "124"
    "charge" => "12.00"
  ]

Since the new records don't have a record ID assigned yet, I don't believe the top row would be the same for the new records but this should explain my request.

Update

Portion of Controller with Section about shipment_detail

foreach ($request->shipment_details as $id => $details) {
            $shipdetail = Shipment_Detail::updateOrCreate(['id' => $id], [
            'pieces_type' => $details['piecesType'][0],
            'pieces_number' => $details['piecesNumber'][0],
            'rate_type' => $details['rateType'][0],
            'weight' => $details['weight'][0],
            'charge' => $details['charge'][0],
            'description' => $details['description'][0],
            'hazmat' => $details['hazmat'][0]
                ]);
        }
7
  • So how do you expect the data to be grouped? Commented Jan 26, 2018 at 20:47
  • I updated the question to include the correct (or close to correct) format of the array for the new records using the output array of an already existing record. Commented Jan 26, 2018 at 20:50
  • Even with your current format you can iterate over all fields and prepare required arrays. Commented Jan 26, 2018 at 20:55
  • My issue is that likely because it's not bundling itself together, I get an error because of it (titled Undefined index: piecesType) and this is likely because all my controller sees is the "piecesNumber" in the first array and then it stops and starts anew. Do you have any suggestions based on this portion? Commented Jan 26, 2018 at 21:01
  • I don't know what is in your controller, but once again - you can iterate over piecesNumber as key => value for example and collect values from other sub-arrays under the same key. Commented Jan 26, 2018 at 21:07

1 Answer 1

2

There's no obvious and simple solution to your question.

First, from your current html markup you can do the following:

// Regroup post fields to required arrays' structure:

$new_items = [];
foreach ($request->shipment_details['piecesTtype'] as $key => $value) {
    $new_items[] = [
        'pieces_type' => $value,
        'pieces_number' => $request->shipment_details['piecesNumber'][$key],
        'rate_type' => $request->shipment_details['rateType'][$key],
        'weight' => $request->shipment_details['weight'][$key],
        'charge' => $request->shipment_details['charge'][$key],
        'description' => $request->shipment_details['description'][$key],
        'hazmat' => $request->shipment_details['hazmat'][$key],
    ];
}

// so, as these are new items - you can only create them.
// To distinguish existing items from new ones you can:

foreach ($request->shipment_details as $id => $details) {
    if (is_numeric($id)) {  // or any other function that checks if array key is a numeric value like 13149
        // update with $details
    }
}

Another approach is you can rewrite your html markup to something like:

<!-- I put only excerpt of fields -->
<input type="text" name="shipment_details[13149][piecesNumber]" value="" />

<!-- New fields will be added with an explicit indexing: -->
<input type="text" name="shipment_details[1][piecesNumber]" value="" />
<input type="text" name="shipment_details[2][piecesNumber]" value="" />
<input type="text" name="shipment_details[3][piecesNumber]" value="" />
<!-- and so on -->

In this case you will get items grouped as you need, but the case is that you will have indexes 1,2,3 and more which will be considered as ids in your loop:

foreach ($request->shipment_details as $id => $details) {

    // as you have id = 1 for example, this record
    // will be updated if it exists, and as a result
    // wrong data will be in a database.
    $shipdetail = Shipment_Detail::updateOrCreate(['id' => $id], [
        ....
    ]);
}

Also in this case you need to modify your js so as to count number of fields somewhere and output correct index in shipment_details[%INDEX%][piecesNumber].

The third solution is the following. You rebuild your html layout adding special field with item id.

Example, again only part of fields:

<!-- Group 1, an existing record-->
<input type="hidden" name="shipment_details[itemId][]" value="13149" />
<input type="text" name="shipment_details[piecesNumber][]" value="Some existsing value" />
<input type="text" name="shipment_details[weight][]" value="Another existsing value" />

<!-- Group 2, new record-->
<input type="hidden" name="shipment_details[itemId][]" value="0" />
<input type="text" name="shipment_details[piecesNumber][]" value="Value will be input by user" />
<input type="text" name="shipment_details[weight][]" value="Value will be input by user" />

<!-- Group 3, new record-->
<input type="hidden" name="shipment_details[itemId][]" value="0" />
<input type="text" name="shipment_details[piecesNumber][]" value="Value will be input by user-2" />
<input type="text" name="shipment_details[weight][]" value="Value will be input by user-2" />

In this case your array will be structured like you have now, but there won't be any numeric indexes. Again you iterate over it and create required arrays:

$new_items = $existsing_items = [];
foreach ($request->shipment_details['itemId'] as $key => $value) {
    if ($value != 0) {
        $existing_items[] = [
            'id' => $value,
            'pieces_number' => $request->shipment_details['piecesNumber'][$key],
            'rate_type' => $request->shipment_details['rateType'][$key],
            // other values
        ];
        // or update immediately
    } else {
        $new_items[] = [
            'pieces_type' => $value,
            'pieces_number' => $request->shipment_details['piecesNumber'][$key],
            'rate_type' => $request->shipment_details['rateType'][$key],
            // other values
        ];
        // or insert immediately
    }
}

But in this case some bad guys can modify values of shipment_details[itemId] so that instead of 0 values there will be real values, and again you will update wrong data in a database.

So, the first solution is still the most secure. Of course, someone can change the value of an existing id field, but I suppose you can check if such shipment details is a property of a current user or any other condition.

Maybe you can split names of fields to existing ones and new, for example:

<!-- Group 1, an existing record-->
<input type="text" name="existsing_details[13149][piecesNumber]" value="Some existsing value" />
<input type="text" name="existsing_details[13149][weight]" value="Another existsing value" />

<!-- Group 2, another existing record-->
<input type="text" name="existsing_details[13150][piecesNumber]" value="Some existsing value" />
<input type="text" name="existsing_details[13150][weight]" value="Another existsing value" />

<!-- Group 3, new record-->
<input type="text" name="new_shipment_details[piecesNumber][]" value="Value will be input by user" />
<input type="text" name="new_shipment_details[weight][]" value="Value will be input by user" />

<!-- Group 4, new record-->
<input type="text" name="new_shipment_details[piecesNumber][]" value="Value will be input by user" />
<input type="text" name="new_shipment_details[weight][]" value="Value will be input by user" />

In this case you can iterate over existsing_details and update values, as they are existing ones. For new items you use code above:

// Regroup post fields to required arrays' structure:

$new_items = [];
foreach ($request->new_shipment_details['piecesType'] as $key => $value) {
    $new_items[] = [
        'pieces_type' => $value,
        'pieces_number' => $request->new_shipment_details['piecesNumber'][$key],
        'rate_type' => $request->new_shipment_details['rateType'][$key],
        // other fields
    ];
    // or insert immediately
}

Which way to choose is up to you.

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.