PHP/Laravel/ Docker php-pmの画像投稿でハマった

###背景
vue/phpを使って画像投稿するサイトを作っていました。

### 問題
ローカルでのビルトインサーバでは画像投稿できるが、
'''request->file```実行時
Docker起動のビルトインサーバで画像投稿　
```validation:update```
と表示されて、画像投稿できない。

###結論
php-pmでは```/private/var/```にtmpファイル保存されていないことが原因だったので、php-fpmを使うことにしました。

### 前提
怪しいと思われるLaravel側でログ出力画像を投稿するstoreメソッドに
Logを仕込んで確認しました。

php-pm

```Docker:Dockerfile
FROM phppm/nginx:2.0.3
RUN apk update && apk add --no-cache \
      tzdata curl \
      && apk del tzdata \
      && rm -rf /var/cache/apk/*
COPY . /var/www
CMD ["--bootstrap=laravel","--static-directory=public/"]
```
Laravel側

```php:Create.php
public function store(Request $request)
    {
        Log::debug('API storeメソッドの実行');
        Log::debug($request);
        Log::debug(request());

        $this->validate($request, [
            'file' => 'required|image',
            'gear_name' => 'required',
            'gear_category' => 'required',
            'maker_name' => 'required',
            'content' => ['required', 'min:2'],

        ], [
            'file.required' => '画像が選択されていません',
            'file.image' => '画像ファイルではありません',
            'gear_category.required' => '登録するギアのカテゴリを選択してください',

            'gear_name.required' => '登録するギアの名前を入力してください',
            'maker_name.required' => '登録するギアのメーカ名を入力してください',
            'content.min' => 'ギアのお気に入りポイントを2文字以上入力してください',
            'content.required' => 'ギアのお気に入りポイントを入力してください',
        ]);
        Log::debug('$image = $request->file');
        Log::debug($image = $request->file('file'));
        Log::debug('=====');
        if ($request->hasFile('file')) {
            Log::debug('requstにfileがある時の処理');
            Log::debug(request()->file);

            $image = $request->file('file');
            $image_url = Storage::disk('s3')->put('/myprefix', $image, 'public');
            $gear = new Gear();
            $gear->image_url = Storage::disk('s3')->url($image_url);
            $gear->gear_name = $request->gear_name;
            $gear->maker_name = $request->maker_name;
            $gear->content = $request->content;
            $gear->ref_url = self::replaceUrl($request->ref_url);
            $gear->gear_category = $request->gear_category;
            $gear->updated_at = date('Y/m/d H:i:s');
            $gear->gear_category = $request->gear_category;

            $gear->user_id = $request->user_id;
            $gear->save();

            return ['success' => '登録しました!'];
        }
    }
```

Vue側

```vue:js
<script>
        //画像、コメント投稿用のvue側のaxios通信
        uploadImage() {
            let data = new FormData();
            data.append("file", this.file);
            data.append("maker_name", this.maker_name);
            data.append("gear_name", this.gear_name);
            data.append("gear_category", this.gear_category);
            data.append("content", this.content);
            //Vuexのstoreからauth_user情報を呼び出す
            data.append("user_id", this.$store.state.auth_user.id);
            console.log('dataの表示')
            console.log(data);
            for (let value of data.entries()) {
                console.log(value);

            }
            NProgress.start();
            axios.post("/api/gears", data)
            // axios.post("/gears", data)
                .then(response => {
                    setTimeout(()=>{
                    // this.getGears();
                    console.log('responseの表示')
                    console.log(response);
    /***
       responseは表示されない
     ***/
                    this.message = response.data.success;
                    this.confirmedImage = "";
                    this.maker_name = "";
                    this.gear_name = "";
                    this.gear_category = "";
                    this.content = "";
                    this.file = "";
                    // this.user_id = $route.params;
                    this.message = 'success'
                        //ファイルを選択のクリア
                    this.view = false;
                    this.is_post_success = true;
                    this.$nextTick(function () {
                        this.view = true;
                    });
                    NProgress.done();
                },3000);
                })
                .catch(err => {
                    console.log('errorの表示')
                    console.log(err)
                    console.log(err.response)
　　　/***
　　　　Error: Request failed with status code 422
 　　　   at createError (app.js:699)
   　　　　 at settle (app.js:960)
  　　　  at XMLHttpRequest.handleLoad (app.js:168)
　　　　{data: {…}, status: 422, statusText: "", headers: {…}, 　　　　
　　　　config: {…}, …}
　　　　　config: {url: "/api/gears", method: "post", data: 
   FormData, headers: {…}, transformRequest: Array(1), …}
    data: {message: "The given data was invalid.", errors: {…}}
   headers: {access-control-allow-origin: "*", cache-control: "no- cache, private", content-length: "83", content-type: 
   "application/json", date: "Thu, 21 Jan 2021 10:50:37 GMT", …}
    request: XMLHttpRequest {readyState: 4, timeout: 0, 
    withCredentials: false, upload: XMLHttpRequestUpload, 
    onreadystatechange: ƒ, …}
    status: 422
    statusText: ""
    __proto__: Object
      ***/
                    this.message = err.response.data.errors;
                    this.is_post_success = false;
                });
        }
        //    ここまで
    }
};
</script>

````



###エラー確認

```php:phpの画像投稿　ローカル起動時
[2021-01-22 19:04:39] production.DEBUG: API storeメソッドの実行  
[2021-01-22 19:04:39] production.DEBUG: array (
  'maker_name' => 'da',
  'gear_name' => 'da',
  'gear_category' => 'BackPack',
  'content' => 'da',
  'user_id' => '1',
  'file' => 
  Illuminate\Http\UploadedFile::__set_state(array(
     'test' => false,
     'originalName' => 'テスト.png',
     'mimeType' => 'image/png',
     'error' => 0,
     'hashName' => NULL,
  )),
)  
[2021-01-22 19:04:39] production.DEBUG: requstにfileがある時の処理  
[2021-01-22 19:04:39] production.DEBUG: /private/var/tmp/phpRMqJe0  
```

```requestでは/private/var/tmp/phpRM ```

と表示されている。


```php:laravellogs-error時
[2021-01-22 19:06:35] dev.DEBUG: index                         
[2021-01-22 19:06:42] dev.DEBUG: API store.....................
[2021-01-22 19:06:42] dev.DEBUG: array (          
  'maker_name' => 'da',                           
  'gear_name' => 'da',                            
  'gear_category' => 'BackPack',                                                
  'content' => 'daa',                                                           
  'user_id' => '1',                                                             
  'file' =>                                                                     
  Illuminate\Http\UploadedFile::__set_state(array(                              
     'test' => false,                                                           
     'originalName' => '..........png',                                         
     'mimeType' => 'image/png',                                                 
     'error' => 2190,                                                           
     'hashName' => NULL,                                                        
  )),                                                                           
)                                                                               
   
```

```php:php-fpm下での画像投稿結果
[2021-01-23 20:47:52] production.DEBUG: array (
  'maker_name' => 'ss',
  'gear_name' => 'sss',
  'gear_category' => 'Cutting',
  'content' => 'ss',
  'user_id' => '1',
  'file' =>
  Illuminate\Http\UploadedFile::__set_state(array(
     'test' => false,
     'originalName' => 'テスト.png',
     'mimeType' => 'image/png',
     'error' => 0,
     'hashName' => NULL,
  )),
)
[2021-01-23 20:47:52] production.DEBUG: $image = $request->file
[2021-01-23 20:47:52] production.DEBUG: /tmp/php4eELue
[2021-01-23 20:47:52] production.DEBUG: =====
[2021-01-23 20:47:52] production.DEBUG: requstにfileがある時の処理
[2021-01-23 20:47:52] production.DEBUG: /tmp/php4eELue

```

```正常時では /tmp ```ファイル以下に書き込まれています。

###推測
ｐｈｐ７．３　ローカルでは``` request()->file) ```の返り値あり
php-pm　Docker上では　返り値なし
php−fpm　Docker上では　``` request()->file)```の返り値あり。
でした。

以上から
php-pmでは/tmpディレクトリ以下にファイルが書き込み権限がない、そもそもディレクトリ自体がないと考えられました。

### 解決策

php-fpmでDockerfileを準備しました。

```Dockerfile:php-fpm.Dockerfile
#php 7.3 fpmを仕様
FROM php:7.3-fpm 
＃　設定ファイルphp.iniをコピーする。
＃　Dockerfileにあるディレクトリをコピーして/var/wwwに保存

COPY ./docker/php/php.ini /usr/local/etc/php/
COPY  ./ /var/www/　

＃必要なファイルを保存する。
RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client \
  && apt-get install -y libzip-dev \
  && docker-php-ext-install zip pdo_mysql

#作業ディレクトリを/var/wwwに設定する。
WORKDIR /var/www

```

###　結論
特別な理由がない限り仕様がわからないphp-pmは触らず、php-fpmを使用しましょう。


以上