3

I have a file upload method for a single image working properly, using the following code:

$file = $request->file('file');

if ($file && $file->isValid()) {
    $photo['size'] = $request->file('file')->getClientSize();
    $path = $request->file('file')->store($request->subdomain);
    $path = explode('/', $path);
    $photo['file'] = $path[1];
    $photo['cover'] = 1;
    $photo['gallery'] = $newGallery->id;
    $photo['uploaded_by'] = $user->id;
    Photo::create($photo);
}

$file is an instance of UploadedFile here, and the store() method works perfectly.

However, I needed to change the method to allow multiple files to be uploaded. The following adapted code nets the following error:

$photos = $request->files->all();

    foreach($photos as $photo) {
        foreach($photo as $p) {

            if($p->isValid()) {
                $path = $p->store($request->subdomain);
                $path = explode('/', $path);

                $newPhoto = [
                    'uploaded_by' => $user->id,
                    'file' => $path[1],
                    'size' => $p->getClientSize(),
                    'gallery' => $request->gallery,
                    'subdomain' => $request->subdomain,
                ];

                Photo::create($requestData);
            }
        }
    }

Call to undefined method Symfony\Component\HttpFoundation\File\UploadedFile::store()

$p's dd output:

UploadedFile {#28 ▼
  -test: false
  -originalName: "Untitled.png"
  -mimeType: "image/png"
  -size: 18030
  -error: 0
  path: "/tmp"
  filename: "phpBDSWCR"
  basename: "phpBDSWCR"
  pathname: "/tmp/phpBDSWCR"
  extension: ""
  realPath: "/tmp/phpBDSWCR"
  aTime: 2017-09-19 20:19:57
  mTime: 2017-09-19 20:19:57
  cTime: 2017-09-19 20:19:57
  inode: 3014878
  size: 18030
  perms: 0100600
  owner: 1000
  group: 1000
  type: "file"
  writable: true
  readable: true
  executable: false
  file: true
  dir: false
  link: false
}

Which is pretty strange, as Laravel should be using Illuminate\Http\UploadedFile that has the proper store() method (docs) against Symfony's class that doesn't have that method (docs)

Also, using Symfony's move() is way worse, as store() already saves the file with a generated filename and the extension, so I don't have to bother generating a random filename and guessing the extension, as it's not very reliable.

Bottom line is: why is it behaving like that, and how can I make it use the proper class instance?

jacques mouette
  • 363
  • 3
  • 8
  • What does `dd(get_class($p));` output? – Jonathon Sep 19 '17 at 23:38
  • `"Symfony\Component\HttpFoundation\File\UploadedFile"` – jacques mouette Sep 20 '17 at 00:05
  • Output of `dd(get_class($request->file('file')` from the previous working single upload: `"Illuminate\Http\UploadedFile"` – jacques mouette Sep 20 '17 at 00:07
  • That seems to be your issue then, you're ending up with an underlying Symfony `UploadedFile` which doesn't seem to have a `store` method. Out of interest, why are you doing the inner `foreach` loop? – Jonathon Sep 20 '17 at 00:27
  • `$request->files` gives me a `FileBag`, which in return gives me an array of Files. I thought it was pretty strange too. And yes, that is the underlying issue, as my question stated. The point is why is this happening. – jacques mouette Sep 20 '17 at 02:04
  • Yes, sorry, I managed to miss that part reading your question. If `$request->files` gives you a `FileBag`, and `$request->files->all()` gives you an array, you can still iterate it with one loop, without the inner loop. By adding the inner loop you're iterating through each file and then through something internally on each `Illuminate\Http\UploadedFile` instance. Removing the inner loop and changing occurrences of `$p` to `$photo` should help. – Jonathon Sep 20 '17 at 09:27

3 Answers3

6

You can only use the store method you're using on a request instance.

Maybe you could try doing something like this

foreach($photo as $index => $p) {
    $request->file('files')[$index]->store();
}
Jerico Pulvera
  • 1,032
  • 7
  • 15
  • `$request->file('files')` throws an exception IIRC, I tried doing something similar before. I'll try this approach to confirm. – jacques mouette Sep 20 '17 at 02:06
  • `$photos = $request->file('files'); foreach($photos as $photo) { if($photo->isValid()) { $path = $photo->store($request->subdomain); {...} ` worked! Now I'm having another problem where it's not saving all the photos I upload but that's a whole other business. Thanks! – jacques mouette Sep 20 '17 at 02:22
  • I am glad to be of help. – Jerico Pulvera Sep 20 '17 at 02:24
0

I was also struggling with the same error and the above solution worked: here is a more detailed one:

$result = [];
//$photos = $request->file('files'); // just saving the line
foreach($request->file('files') as $k => $v) {
        $path = Storage::disk('public')->putFile('editor/uploads/1', $request->file('files')[$k]);
        $filename = basename($path);
        $result['url'] = $filename;
}

return $result;
MR_AMDEV
  • 1,712
  • 2
  • 21
  • 38
0

If you validate the $request params beforehand, save them as a variable and access them from it, the file from the array is correctly inferred as Illuminate\Http\UploadedFile:


$fields = $request->validate([
    'files.*' => 'file|mimes:pdf,png,jpg'
]);

foreach($fields['files'] as $photo) {
    foreach($photo as $index => $p) {
         $path = $fields['files'][$index]->store($fields['subdomain']);
    }
};

Note - if validation fails for a traditional HTTP request, a redirect response to the previous URL will be generated. If the incoming request is an XHR request, a JSON response containing the validation error messages will be returned. See more explanation at the official docs.

askepott
  • 250
  • 3
  • 13