This allows you to select multiple files using multiple="true":
<input type="file" class="form-control" name="image[]" placeholder="Upload Image" multiple="true">
You'll also need to loop through them in your controller to save them all
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
Good morning
right now i have this part of code in my website im wondering how can i upload multiple image?
view
<input type="file" class="form-control" name="image[]" placeholder="Upload Image">
controller
$file = new Images;
$file->filename = $request['image[]'];
$file->save();
This allows you to select multiple files using multiple="true":
<input type="file" class="form-control" name="image[]" placeholder="Upload Image" multiple="true">
You'll also need to loop through them in your controller to save them all
@code_chris what i did is that i made some javascript to append a new input to limit the image upload my problem is that i tried to add loop on the controller but im getting error
Show your loop code
Also, are you saying you are adding more image input fields using javascript? So you submit multiple individual inputs?
When you are adding more input fields with javascript to your form, make sure they have the name attribute e.g. "files[]". In your Controller you can then access the files with $files = Input::file('files'); (note the missing []).
You can then loop through the $files array with foreach.
yes im going to submit multiple individual inputs for image
this is the error message Invalid argument supplied for foreach()
$imagelist = [];
foreach( Input::file('image[]') as $file) {
$name = str_random(12).'.'.$file->getClientOriginalExtension();
$imagelist[] = $name;
}
$file = new Images;
$file->filename = $imagelist[];
$file->save();
As deadlock said, take off the square brackets from image[], just use Input::file('image')
I would also call it images in your form and controller so it makes more sense, but that doesn't affect how it works
i tried this one but still i got Invalid argument supplied for foreach()
$imagelist = [];
foreach( Input::file('image') as $file) {
$name = str_random(12).'.'.$file->getClientOriginalExtension();
$imagelist[] = $name;
}
$file = new Images;
$file->filename = $imagelist;
$file->save();
Try replacing Input::file('image') with:
$request->file('image')
If you are on Laravel 5 use
$files = \Input::file('image');
foreach ($files as $file) {
...
}
Btw: the multiple => true needs to be set on the Form, not on the input element
<form enctype="multipart/form-data" accept-charset="UTF-8" action="/route-to-post"" method="POST">
So your Form should look like this:
<form enctype="multipart/form-data" accept-charset="UTF-8" action="/route-to-post" method="POST">
<input type="file" name="image[]" />
<input type="file" name="image[]" />
</form>
and yout Route + Controller logic like that:
Route::post('route-to-post', function()
{
$files = \Input::file('files');
$output = "";
foreach ($files as $file) {
$output .= $file->getClientOriginalName();
}
return $output;
});
@deadlockgB True, you need that to post files. Seeing only the input in the first one I assumed he meant selecting multiple files :(
@deadlockgB here is what im trying to do from the input when i click add more image a new input line will be added
<input type="file" class="form-control" name="image[]" placeholder="Upload Image" multiple="true">
<input type="file" class="form-control" name="image[]" placeholder="Upload Image" multiple="true">
<input type="file" class="form-control" name="image[]" placeholder="Upload Image" multiple="true">
@Ci You need to use
<form enctype="multipart/form-data" accept-charset="UTF-8" action="/route-to-post" method="POST">
in your form definition. the multiple => true sets the enctype="multipart/form-data" attribute and value. If you set the multiple="true" on your input fields it will not have any impact (apart from being able to select multiple files per input field),
Edit. wrong. the multiple="true" on the input element allows you to choose multiple files. The behavior is the same.
In that case if you only add one file in each input then you don't want to use multiple="true", that is only used to upload multiple from the same input field, although personally I'd probably use that and limit it in some other way.
multiple="true" is valid, i allows multiple files to be selected in a single input field
If you use the Form builder then you add 'files' => 'true' in the Form::open array
@code_chris you are absolutly right.
Although you don't actually need the true part, you can simple use multiple by itself
@deadlockgB @code_chris thanks guys, i think im doing it wrong with key value for caption
I think you shouldn't loop through the files as a key value pair but instead as objects:
....
First Picture <input type="file" name="files[]" />
Caption for first Picture: <input type="text" name="captions[]" />
Second Picture <input type="file" name="files[]" />
Caption for second picture: <input type="text" name="captions[]" />
.....
Route::post('route-to-post', function()
{
$files = \Input::file('files');
$captions = \Input::get('captions');
$captionIndex = 0;
foreach ($files as $file) {
$file->....
$captionForFile = $captions[$captionIndex];
$captionIndex++;
}
....
});
Thank you @code_chris & @deadlockgB
When I use one input field and allow multiple upload, how am I supposed to store all files within the database attached to the correct product?
E.g. while creating a product as an Authenticated Business. So, I edit the content such as Title, Brand and Price and want to add 5 pictures to it.
Products table:
public function up()
{
Schema :: create ('products', function(Blueprint $table){
$table->increments('id');
$table->unsignedInteger('business_id');
$table->string ('title')->nullable();
$table->string ('brand')->nullable();
$table->decimal ('price')->nullable();
$table->string('product_image')->default('productdefault.jpg');
$table->timestamps();
});
ProductUploadController.php:
public function postProductEdit(Request $request){
$this->validate($request, [
'title'=> 'max:140',
'brand'=> 'max:140',
'price'=> 'max:10',
'product_image'=> 'max:140',
]);
$update = Auth::user()->products()->create([
'business_id' => auth()->user()->id,
'title' => $request->title,
'brand' => $request->brand,
'price' => $request->price,
'product_image' => $request->product_image,
]);
if($request->hasFile('product_image')){
$business = Auth::user();
$image = $request->file('product_image');
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
$business->product_image = $filename;
$business->save();
return redirect()
->route('products.create')
->with('info', 'Your product has been created.');
}
At the moment, it won't work. Would be happy about your expertise. Thank you
@splendidkeen You should probably make a new thread as this was 2 years old.
Anyway as I am here... From your code it looks like it was designed to have a single image per product. If you allow multiple on the form then you should name the input with brackets such as:
<input type="file" name="images[]" multiple="true" />
When this is posted to your Controller you can get all of the images by using:
$request->file('images')
Then you'll need to loop through them and save them. You may still want to keep your default product image in the product table but if you're going to have multiple images per product you should add a new model and table for them such as "images" or "pictures".
If you only have products then you can have a one to many relationship, if you have other resources with images you can use polymorphic relationships instead.
I usually have a "pictures" table and with the right relationship you can then do:
$product->pictures()->save($picture);
Where $picture is the info about each picture in your loop.
And you can use:
$product->pictures
to loop through existing pictures and output them in blade or something
Hope that helps?
@splendidkeen You got this working yet?
I see a few issues with what you have:
Picture.php
public function products(){
return $this->belongsTo(Product::class, 'product_id');
}
I'd name the function product() without the s because ti should be just 1 product right? That way i don't think you need to specify product_id as it will look for that by default but it also makes more sense when coding.
Product.php
public function pictures(){
return $this->hasMany(Picture::class, 'product_image');
}
Not sure why you have 'product_image' here as that's not the key, you shouldn't need to specify anything unless your method does not match the column name in database.
Pictures Table:
Schema :: create ('pictures', function(Blueprint $table){
$table->increments('id');
$table->unsignedInteger('product_id'); //
$table->string('product_image')->default('productdefault.jpg');
$table->timestamps();
});
Looks good
Controller:
if ($request->hasFile('images')) {
foreach ($images as $image) {
$image = $request->file('product_image');
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
}
$product->pictures()->save($picture);
}
You need to load the images into $images before the loop, so instead of:
if ($request->hasFile('images')) {
foreach ($images as $image) {
$image = $request->file('product_image');
use:
if ($request->hasFile('images')) {
$images = $request->file('images')
foreach ($images as $image) {
This is assuming your file inputs are named with name="images[]" You need the square brackets to pass multiple fields as an array but if you wanted to name it "product_images[]" then just look it up with $request->hasFile('product_images') and $request->file('product_images')
You also need to make sure your $picture variable is filled with the correct data for your pictures table each time you loop through.
Hey @code_chris Thank you again for your reply. It doesn't work yet.
My ProductUploadController.php changed a bit:
foreach ($request->images as $image) {
$business = Auth::user();
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
}
$product->pictures()->save();
which results in:
Type error: Too few arguments to function Illuminate\Database\Eloquent\Relations\HasOneOrMany::save(), 0 passed in /home/vagrant/app/Http/Controllers/ProductUploadController.php on line 81 and exactly 1 expected
Line 81:
$product->pictures()->save();
How to fill the $picture variable? I can't see the connection to my product_image row in the pictures table.
@splendidkeen You need to pass in the data for your columns.
Based on your pictures table Schema above you should only need to pass in the 'product_image' data:
$product->pictures()->save(['product_image' => $filename]);
That should work, you'll obviously need to code your page to look in the right directory depending on where you saved the file. product_id will be entered automatically as you are using the relationship to save it.
There seems to be a problem with the pivot table (pictures). @code_chris
Type error: Argument 1 passed to Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be an instance of Illuminate\Database\Eloquent\Model, array given, called in /home/vagrant/app/Http/Controllers/ProductUploadController.php on line 81
Picture.php
class Picture extends Model
{
use Authenticatable;
protected $guard = 'partner';
protected $table = 'pictures';
protected $fillable = [
'id',
'product_id',
'product_image',
];
public function product()
{
return $this->belongsTo(Product::class);
}
}
Controller:
$this->validate($request, [
'title'=> 'max:140',
'brand'=> 'max:140',
'sku'=> 'max:140',
'description'=> 'max:140',
'price'=> 'max:140',
'availability'=> 'max:140',
'product_image'=> 'max:3000',
]);
$product = Auth::user()->products()->create([
'business_id' => auth()->user()->id,
'title' => $request->title,
'brand' => $request->brand,
'sku' => $request->sku,
'description' => $request->description,
'price' => $request->price,
'availability' => $request->availability,
'product_image' => $request->product_image,
]);
foreach ($request->images as $image) {
$business = Auth::user();
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
}
$product->pictures()->save(['product_image' => $filename]);
Anything wrong here?
When I try to upload 2 files for instance, it stores only one picture within my uploads folder, then spits the error, but still no activity within pictures folder.
@splendidkeen My mistake, I guess you need to pass an instance of the model.
You also need to create each model instance in the loop for each image. Rather than saving each image in the loop you can build it up and use saveMany() instead, something like this:
$pictures = []
foreach ($request->images as $image) {
$business = Auth::user();
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
$pictures[] = new App\Picture(['product_image' => $filename])
}
$product->pictures()->saveMany($pictures);
The saveMany() means only 1 query after the loop instead of saving each picture in the loop with it's own query. Hope that works for you now.
Thank you so much @code_chris It worked.
foreach ($request->images as $image) {
$business = Auth::user();
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('uploads/business/products/'. $filename );
Image::make($image)->resize(812, null, function ($constraint){$constraint->aspectRatio();})->save($location);
$pictures[] = new Picture(['product_image' => $filename]);
}
$product->pictures()->saveMany($pictures);
@splendidkeen I think you also need to adjust this line when creating your product:
'product_image' => $request->product_image,
Unless you have a textfield with the name 'product_image' you're probably not gonna get anything in that column. You probably want to send it the filename for one of your images?
You could pass it a filename in your loop or something if you wanted to, or have separate functionality after uploading to select your primary image for a product.
You are right @code_chris
How do I get the product images for display now? Is there something like getAll() for this product or the first one?
"/uploads/business/products/{{ $business->$product->product_image }}"
If you have this method for the relationship on your product model:
public function pictures(){
return $this->hasMany(Picture::class);
}
You can get a collection of all related pictures using:
$product->pictures
The first picture found using:
$product->pictures->first()
If you want to get the product_image column for a picture:
$product->pictures->first()->product_image
Or if you want to save the primary image to your products table for a product you just need to get an instance of the Product and update it:
$product->product_image = $picture->product_image
Then you can just look up the main image directly from the product model, which is good for initial thumbnail or something.
I want to parse in the product data of the businesses into display blocks, like so:
<div>
<div class="row">
<div class="col-sm-2">
<img src="/uploads/business/products/{{ Auth::user()->$product->pictures->first() }}">
</div>
<div>
<h2>Product Title</h2>
<p>Product Description</p>
</div>
<div class="col-sm-2">
<h3 class="editp">Edit</h3>
</div>
<div class="col-sm-2">
<h3 class="deletep">Delete</h3>
</div>
</div>
</div>
But it won't get the $product variable, yet. It's a bit difficult, because a business can setup many products and I want to loop through all and list them, with the data parsed into the display block. @code_chris
Please or to participate in this conversation.