Home > Mobile >  Cannot change file name in laravel request
Cannot change file name in laravel request

Time:07-14

I'm sending some data from a form, one field is type file like this(simplified version) that is an avatar image:

<form id="userform" method="POST" action="/users/store" autocomplete="off" enctype="multipart/form-data">
    <div >
        <input  id="avatar" type="file" name="avatar" accept="image/*">
        <input  id="name" type="text" name="name" >
        <input  id="email" type="text" name="email" >
        <input  id="password" type="password" name="password">
        <input type="submit" id="submit" value="submit">
    </div>
</form>

in the related controller I have something like this (simplified version) that checks if there is a file in the request and stores it in a dedicated folder with a unique name, then (and here is the problem) it will change the value of the avatar field in the request, so I can store it later in the db with the new unique name

// Update User
public function update(Request $request){

    // Upload avatar
    if( $request->hasFile('avatar') ) {

        // Unique file name
        $avatarName = $request['name'].'-'.uniqid().'-avatar.jpg';

        // I save the file in the avatar folder with its new name
        $request->file('avatar')->storeAs(
            'avatars', $avatarName, 'public'
        );
        
        $request['avatar'] = $avatarName;
        
    } else {
        $request['avatar'] = config('options.default_avatar');
    }

...

}

no matter how much I tried, I cannot change the name in the request, I tried in so many ways

$request['avatar'] = $avatarName;
$request->avatar = $avatarName;
$request->merge(['avatar'=>'$avatarName'])

if I try debugging

dd($request->all())

I always obtain this Illuminate\Http\UploadedFile, where the value of avatar doesn't changes

array:7 [
  "action" => "update"
  "id" => "50"
  "_token" => "d0WSL69fEVEb71HDnXoirQJ2fsfe9hes3ZmCuyff"
  "name" => "Lauras"
  "password" => "$2y$10$g.4dGPWfEA2LjhZBcXFUiOTqwjM8PCHuooaaHnUWyPFoJFQihlDkC"
  "password_confirmation" => "nt1access"
  "avatar" => Illuminate\Http\UploadedFile {#313
    -test: false
    -originalName: "52F.jpg"
    -mimeType: "image/jpeg"
    -error: 0
    #hashName: null
    path: "/tmp"
    filename: "phpWbVial"
    basename: "phpWbVial"
    pathname: "/tmp/phpWbVial"
    extension: ""
    realPath: "/tmp/phpWbVial"
    aTime: 2022-07-13 22:08:15
    mTime: 2022-07-13 22:08:15
    cTime: 2022-07-13 22:08:15
    inode: 406627
    size: 200630
    perms: 0100600
    owner: 0
    group: 0
    type: "file"
    writable: true
    readable: true
    executable: false
    file: true
    dir: false
    link: false
  }
]

I can change any value in the request before I update by simply doing

$request['field'] = $newFieldName;

but I cannot change the avatar field. something really strange is that if I do this

$request->avatar = $avatarName;
dd($request->avatar);

it will output the $avatarName that I want, but if I do

$request->avatar = $avatarName;
dd($request);

it will output again this Illuminate\Http\UploadedFile stuffs.

For now the only way to fix this, is to store the entire request in another variable and then manipulate the new variable, before I update the db, in this way

$data = $request->all();

this is not so bad ok, but I cannot understand why this is happening, is like everytime I call $request it will reset only the avatar field to the original value.

CodePudding user response:

I did some digging and I think I found out the issue.

In Symfony's Request class, there's a property $request which is where $_POST is stored, and a property $files which is where $_FILES is stored.

Laravel's Request class extends ArrayAccess, and its OffsetSet method looks like this:

public function offsetSet($offset, $value): void 
{ 
    $this->getInputSource()->set($offset, $value); 
}

And the getInputSource method looks like this:

protected function getInputSource()
{
    if ($this->isJson()) {
        return $this->json();
    }

    return in_array($this->getRealMethod(), ['GET', 'HEAD']) ? $this->query : $this->request;
}

In your case, your request isn't JSON, and it's not using the GET or HEAD methods, so the input source you're modifying is $this->request.

This means that when you set $request['field'] = $newFieldName; you're setting $this->request['field'] to $newFieldName as you expect.

It also means that when you set $request['avatarName'] = $avatarName;, you're also setting $this->request['avatarName'] to $avatarName as you expect.

So what's the problem?

When you convert a Laravel Request object to an array, it uses the toArray() method on that class:

public function toArray(): array
{
    return $this->all();
}

And in the all() method (inherited from InteractsWithInput) we find the issue:

public function all($keys = null)
{
    $input = array_replace_recursive($this->input(), $this->allFiles());
    ...

$this->input() is getting values from the result of getInputSource(), which is to say Symfony's $request property containing the values of $_POST, and overwriting it with the values from $this->allFiles(), which is getting values from $files, which contains the values of $_FILES.


So what does this all mean?

Long story short, this is a weird quirk of Laravel's implementation of ArrayAccess on the Request class. When you read/write to a Request object with array notation, you're really just reading/writing the variables which were $_POSTed (or the JSON-encoded body or $_GET variables if your request was json-encoded or a GET/HEAD request).

If you want to continue down this path, you'll need to modify your $request->files property. It's a Symfony FileBag, which you can change with the set() method:

$request->files->set('avatarName', /*Your modified UploadedFile object*/);

CodePudding user response:

The $request is an object of class \Illuminate\Http\Request. Its not a simple array. You can verify this if you do dd(get_class($request));

Setting it as:

$request['avatar'] = $avatarName;
$request->avatar = $avatarName;

Will not change set the value. So doing it this way:

$data = $request->all();

Will convert it to array and you can manipulate the $data in any way you want

  • Related