maharzan's avatar

Uploading multiple images multiple times with same input

I am trying to make a create/edit form that handles multiple image uploads (Livewire/Laravel). This part is done.

I want to take this to another level. For example, in the edit mode, I have say 5 images uploaded previously and I have a "Upload more images" input. I want to be able to click on it and select more images and upload them along with the previously uploaded images. The input field isn't hidden so I want to be able to upload more images with every single click and retaining the earlier selected uploads.

I have searched everywhere and cannot find any solution to this. One idea I could think of is to rename the input field to different one each time but it won't be practical since we will have to define them in laravel end as well.

Any hint/solution would be highly appreciated.

Thanks.

0 likes
21 replies
Snapey's avatar

I dont understand the confusion.

You need an area where you show existing images.

You have an input form where you select additional images (s)

You accept the new images, store them and add the new images to a list of existing images.

Where is the confusion?

1 like
maharzan's avatar

@Snapey Using same wire:model="photos" is not letting me upload new images to existing ones. It looks like the name is conflicting. The only way I can do is to change the wire model name. But since it can be unlimited (for now), I am not sure how to do it.

So, here is a snippet of my code for adding new images.. (create)

  @if ($photos)
        	@foreach ($photos as $photo)
                	<img src="{{ $photo->temporaryUrl() }}" />
		  	@endforeach
    @endif
      <input id="gallery-images" type="file" class="hidden" wire:model="photos" multiple />

on edit, I have similar code but with the existing photos like

	 @if ($photos)
        	@foreach ($photos as $photo)
					<img src="{{ asset($photo->image) }}"  />
			@endforeach
    @endif
     <input id="gallery-images" type="file" class="hidden" wire:model="photos" multiple />

This is conflicting and I cannot add more photos to existing ones.

My component has this

	public $photos = [];
	public function mount(Gallery $gallery)
	{
    	$this->photos = $gallery->images;
	 }

I am also trying to figure out if we can do it like this: wire:model="photos.{{ $count }}.image"

but I have no idea how to show that on preview..

Snapey's avatar

@maharzan just call your existing photos and the new ones something different !

maharzan's avatar

@Snapey The issue is I want to make the file upload button upload images multiple times.. like I select few images once and then maybe I forgot and want to upload 2 more images.. and this can go forever. If I rename, it works for once only.. any other uploads will replace the just uploaded ones. So, to make this work, I will have to have different names each time and this is not practical as we will need to define that in the component as well. Thats why I am seeking help.

Snapey's avatar

@maharzan what?

have an array of existing photos. Push new photos to the array of existing. Why on earth would you need to change the form?

Tray2's avatar

@maharzan am I getting this straight?

You upload the same images over and over or......?

The display component should differ from the upload component. As for the naming, just let the temp name that the sever generates be the name of the stored file, very low chance of duplicates, and overwrites.

maharzan's avatar

@Tray2 Not the same image.. different ones.. I have a display part where it shows the existing images. I have a upload form where it uploads images. When I upload images, the preview images are shown. I want to use the same file input to upload more photos.. not sure what is confusing .. I am doing this all in the same page (edit) and using livewire.

maharzan's avatar

@Snapey This is my page layout Edit page...

Show existing photos Input field to upload photos.

When I upload more photos (without saving), here is what I want it to do

Show existing photos | Show uploaded photo preview 1 | Input field to upload photos |

Upload again..

Show existing photos. | Show uploaded photo preview 1. | Show uploaded photo preview 2. | Input field to upload photos. |

and so on..

The problem now is (with different name), 2nd time I try to upload, the first one gets replaced. Note, I am using livewire and the page is not refreshing.

Tray2's avatar

@maharzan Because you are like @martinbean says. overcomplicating things.

<app-display-uploaded-images>
<app-upload-more-images>
<article>
  <app-selected-images-preview>
  <app-fileupload>
</article>

Of course you could/should use the same form to upload more images. If that overwrites your already uploaded files, well then you need to fix that in your code, it's not that hard to get unique file names. The ones you get from the server will most likely suffice.

1 like
maharzan's avatar

@Tray2 filenames are unique.. I am talking about the field name (wire:model)..

Tray2's avatar

@maharzan You probably need to connect the model on a higher lever in your DOM.

<div id="photo-wrapper" wire:model="photos">
	<Gallery>
   <Previews>
</div>
maharzan's avatar

@Tray2 Ok, I found out that if I refresh the page, the same code works but since I am using livewire, it is only replacing the just uploaded images. So, what do I need to do? maybe reset something in the code after update? Looks like I am nearly there..

maharzan's avatar

It seems the images are being saved in the backend.. its only that its replacing on the temporaryURL() call on the frontend.

	@if ($inputFields)
        @foreach ($inputFields as $photo)
            <img src="{{ $photo->temporaryUrl() }}"  />
        @endforeach
    @endif

I have this in mount

		public function mount(Gallery $gallery)
{
    $this->gallery = $gallery;
    $this->galleryTitle = $gallery->title;
    $this->galleryDescription = $gallery->description;
    $this->photos = $gallery->images;
}

do we need to call mount() after each update? $this->mount($this->gallery);

martinbean's avatar

@maharzan Stop over-complicating the issue. Show the already-uploaded images in some sort of gallery. Then have an input that allows the user to upload additional images if they want to.

maharzan's avatar

@martinbean It is a simple thing I want to do but longer I write, more complicated it has become.

It is exactly what I want to do and I said earlier, I thought this would do the job

			@if ($photos)
    	@foreach ($photos as $photo)
				<img src="{{ asset($photo->image) }}"  />
		@endforeach
@endif
 <input id="gallery-images" type="file" class="hidden" wire:model="photos" multiple />

but its not working.

Snapey's avatar

@maharzan Because as you have been told

@foreach ($photos as $photo)

and

wire:model="photos" 

is never going to work.

Show the code of your livewire component.

maharzan's avatar

@Snapey yup. thanks. I am changing the wire:model to another name and that seems to work if the page is refreshed. I am using livewire so just missing one step perhaps.. do you have any idea?

martinbean's avatar

It is a simple thing I want to do but longer I write, more complicated it has become.

@maharzan So stop and take a breather.

You have a form. It has an input to upload multiple images. On the edit page, you want to show the previously-uploaded images.

<div id="gallery">
    @foreach($existingImage as $image)
        <!--
            TODO: Show thumbnail
            TODO: Add button to remove existing image
        -->
    @endforeach
</div>

<div>
    <label for="images">Images</label>
    <input id="images" multiple name="images[]" type="file">
</div>

When you submit the form, you can simply upload any newly-uploaded images and attach them to the model:

foreach ($request->input('images') as $image) {
    $product->images()->create([
        'path' => $image->store('uploads'),
    ]);
}
maharzan's avatar

@martinbean This is very similar to what I am doing. Its saving on the backend. Only the frontend has issues now. I had add last 2 lines after save().. Do you know if we have to do this? Or if there is a better way? Thanks.

public function update(Gallery $gallery)
		{

    if ($this->photos) {
        $validated = $this->validate(
            [
                'galleryTitle' => 'required|min:2'
            ],
            [
                'galleryTitle.required' => 'Please provide a title for this gallery.',
                'galleryTitle.min' => 'This title is too short.'
            ]
        );
    } else {
        $validated = $this->validated();
    }

    $gallery->title = $this->galleryTitle;
    $gallery->description = $this->galleryDescription;

    foreach ($this->inputFields as $photo) {
        $newImage = new Image();
        $imageID = Str::uuid();
        $validated['file'] = $photo->store('photos', 'public');
        $newImage->caption = $this->imageCaption;
        $newImage->image = $validated['file'];
        $newImage->gallery_id = $gallery->id;
        $newImage->save();
    }
    $gallery->update();
    session()->flash('status', 'Gallery updated.');

	// I had to add these to make it work as expected
    $this->photos = $gallery->images;
    $this->inputFields = [];
}
Snapey's avatar
Snapey
Best Answer
Level 122

@maharzan In your render method, get the existing images into an array (not called photos)

After you save images, you will return to the render method and get existing images again, now including the new images.

1 like

Please or to participate in this conversation.