I've made the store data part in this question, and it works well using the first answer from user N69S. But now I tried to implement the same code in the update data part (to add new data when updating the dynamic form), like this:
I attempted to update one of the existing rows and add two new rows (the last two rows are the new ones). It was updated successfully, but the barcode and image for the newly created rows were not saved.
What I need to achieve is still the same as the store data part, just need to add new data when updating the form. I've also added the validation rule to my code.
The View
@foreach($packs as $x => $pack)
<div class="form-group row justify-content-center product-size-row pb-3">
<input type="hidden" name="packs[{{$x}}][id]" value="{{$pack->id}}">
<div class="col-xl-3">
<label class="control-label">Size</label>
<input type="text" placeholder="Pack Size" class="form-control form-control-modern" value="{{$pack->size}}" name="packs[{{$x}}][size]" />
</div>
<div class="col-xl-3">
<label class="control-label">Barcode</label>
<input type="text" class="form-control form-control-modern" placeholder="Barcode" name="packs[{{$x}}][barcode]" value="{{$pack->barcode}}" />
</div>
<div class="col-xl-6">
<a href="javascript:deletePack(this, {{ $pack->id }});" class="product-size-remove text-color-danger float-end">Remove</a>
<label class="control-label">Image</label>
<div class="fileupload fileupload-new" data-provides="fileupload">
<div class="input-append">
<div class="uneditable-input">
<span class="fileupload-preview"></span>
</div>
<span class="btn btn-default btn-file">
<span class="fileupload-exists">Change</span>
<span class="fileupload-new">Select file</span>
<input type="file" name="packs_{{$pack->id}}_image" />
</span>
<a class="btn btn-default fileupload-exists" data-dismiss="fileupload">Remove</a>
</div>
</div>
</div>
</div>
@endforeach
<script>
var x = 0;
$(document).on('click', '.product-size-add-new', function(e) {
e.preventDefault();
++x;
var html = '' +
'<div class="form-group row justify-content-center product-size-row pb-3">' +
'<div class="col-xl-3">' +
'<label class="control-label">Size</label>' +
'<input type="text" placeholder="Pack Size" class="form-control form-control-modern" name="packs[' + x + '][size]" required />' +
'</div>' +
'<div class="col-xl-3">' +
'<label class="control-label">Barcode</label>' +
'<input type="text" placeholder="Barcode" class="form-control form-control-modern" name="packs[' + x + '][barcode]" />' +
'</div>' +
'<div class="col-xl-6">' +
'<a href="#" class="product-size-remove text-color-danger float-end">Remove</a>' +
'<label class="control-label">Image</label>' +
' <div class="fileupload fileupload-new" data-provides="fileupload">' +
'<div class="input-append">' +
'<div class="uneditable-input">' +
'<span class="fileupload-preview"></span>' +
'</div>' +
'<span class="btn btn-default btn-file">' +
'<span class="fileupload-exists">Change</span>' +
'<span class="fileupload-new">Select file</span>' +
'<input type="file" name="packs_image" />' +
'</span>' +
'<a class="btn btn-default fileupload-exists" data-dismiss="fileupload">Remove</a>' +
'</div>' +
'<input type="hidden" name="packs[' + x + '][id]" value="" />' +
'</div>' +
'</div>' +
'</div>' +
'';
$('.product-size-wrapper').append(html);
});
//Product Pack - Remove
$(document).on('click', '.product-size-remove', function(e) {
//e.preventDefault();
$(this).closest('.product-size-row').remove();
});
function deletePack(el, id) {
if (Number(id) > 0) {
$('.product-size-wrapper').append('<input type="hidden" name="delete_pack[]" value="' + id + '" />');
}
}
Controller
if ($request->has('delete_pack')) {
foreach ($request->delete_pack as $id) {
ProductSize::find($id)->forceDelete();
}
}
//update pack size
foreach ($request->packs as $key => $pack) {
// Update
if (isset($pack['id']) && $pack['id']) {
if ($request->has('packs_' . $pack['id'] . '_image')) {
$pack_image = $request->file('packs_' . $pack['id'] . '_image');
$pack_image->storeAs('products', $pack_image->hashName());
//delete old image
// Storage::delete('products/' . $pack['image']);
$packdata = ProductSize::where('id', $pack['id'])->first();
$packdata->product_id = $products->id;
$packdata->size = $pack['size'];
$packdata->barcode = $pack['barcode'];
$packdata->image = $pack_image->hashName();
$packdata->updated_by = Auth::user()->id;
} else {
$packdata = ProductSize::where('id', $pack['id'])->first();
$packdata->product_id = $products->id;
$packdata->size = $pack['size'];
$packdata->barcode = $pack['barcode'];
$packdata->updated_by = Auth::user()->id;
}
// Create
} else {
$fileNames = [];
foreach ($request->file('packs_image') as $new_image) {
$fileName = $new_image->hashName();
$new_image->storeAs('products', $fileName);
$fileNames[] = $fileName;
}
$packdata = new ProductSize();
$packdata->product_id = $products->id;
$packdata->size = $pack['size'];
$packdata->created_by = Auth::user()->id;
//now check for image and barcode separately and add them to the array if present
if (isset($request->barcode) && $request->barcode != '') {
$packdata->barcode = $pack['barcode'];
}
if (isset($request->packs_image) && $request->packs_image != '' && isset($new_image)) {
$packdata->image = $new_image->hashName();
}
}
$packdata->save();
}
Please help because I'm still a beginner. Thank you.
Your existing rows work, but new rows fail because:
You are mixing two different formats:
name="packs[0][barcode]"
name="packs_5_image"
name="packs[1][barcode]"
name="packs_image"
So Laravel receives:
packs[x][barcode]packs_image (not linked to the same pack)Laravel cannot know which image belongs to which pack
If data belongs to a pack, its image must also belong to that same pack index
That means:
packs[0][size]
packs[0][barcode]
packs[0][image]
packs[1][size]
packs[1][barcode]
packs[1][image]
In case you want to add more than 1 image to same pack then you can also add it by changing image to an array i.e. :
packs[0][image][0]
packs[0][image][1]
packs[0][image][2]
Wrong
<input type="file" name="foo_bar" />
Correct
<input type="file" name="foo[' + x + '][bar]" />
var html = `
<div class="form-group row product-size-row">
<div class="col-xl-3">
<label>Size</label>
<input type="text" name="foo[${x}][size]" class="form-control" required>
</div>
<div class="col-xl-3">
<label>Barcode</label>
<input type="text" name="foo[${x}][barcode]" class="form-control">
</div>
<div class="col-xl-6">
<label>Image</label>
<input type="file" name="foo[${x}][image]">
<input type="hidden" name="packs[${x}][id]" value="">
<a href="#" class="product-size-remove text-danger">Remove</a>
</div>
</div>`;
$request->file('packs_image')
This breaks because images are now inside packs. So this changes to following:
foreach ($request->packs as $pack) {
// UPDATE
if (!empty($pack['id'])) {
$packdata = Model::find($pack['id']);
// CREATE
} else {
$packdata = new Model();
$packdata->product_id = $products->id;
$packdata->created_by = Auth::id();
}
// Common fields
$packdata->size = $pack['size'];
$packdata->barcode = $pack['barcode'] ?? null;
// Image handling
if (isset($pack['image'])) {
$image = $pack['image'];
$filename = $image->hashName();
$image->storeAs('products', $filename);
$packdata->image = $filename;
}
$packdata->save();
}