upload images

DBには写真の名前(パス)のみを保存
写真はローカルのstorageに保存していく

**laravel側
Post_Imageの場合
Reactの方でファイルをアップロードすると、サーバー側の指定したフォルダにファイルが移動します。
また、「ファイル名」はデータベースのテーブルに保存されます。

モデル、マイグレーション、コントローラーファイルを作成します。
以下のコマンドを実行

```
sail php artisan make:model Post_Image -mc
```

このコマンドは、モデル、マイグレーション、コントローラーをそれぞれ作成します。

マイグレーションファイルを作成したら、そのファイルを開き、ファイル名を格納するフィールドをテーブルに追加しましょう。

/post__images_table.phpの中を編集

```php
public function up()
    {
        Schema::create('post__images', function (Blueprint $table) {
            $table->id();
            $table->string('image_name')->nullable();
            $table->timestamps();
        });
    }
```

マイグレーションファイルをデータベースに移行する必要があるため、以下のコマンドを実行
```
sail php artisan migrate
```
コマンド実行後、指定されたテーブルがデータベースに生成されます。


次のステップでは、作成したモデルファイルの中にfillableプロパティを追加する必要があります。

Eloquentを使ってデータをテーブルに保存する前に、属性を指定する必要があります。プロジェクトのapp/ディレクトリ内にあるPost_Image.phpに移動してください。そして、以下のコードを入力します。

```php
class Image extends Model
{
    use HasFactory;

    protected $fillable = [
        "image_name"
    ];
}
```

次にコントローラーの記述をしていく

PostImageController.phpというコントローラを作成したから、あとはそれを開いて、サーバーサイドでの画像アップロードの機能を書きましょう。

(とりあえず下のコードをコピペして、post_imageとなっているこを各自書き直す、、、)
PostImageController.php

```

<?php

namespace App\Http\Controllers;

use App\Post_Image;
use App\Http\Requests\PostImageRequest;
use Illuminate\Support\Facades\Validator;



class PostImageController extends Controller
{
     /**
     * Fetch images
     * @param NA
     * @return JSON response
     */
    public function index() {
        $post_images = Post_Image::all();
        return response()->json(["status" => "success", "count" => count($post_images), "data" => $post_images]);
        
    }

    /**
     * Upload Image
     * @param $request
     * @return JSON response
     */
    public function upload(PostImageRequest $request) {
        $imagesName = [];
        $response = [];
        
        $validator = Validator::make($request->all(),
            [
                'post_images' => 'required',
                'post_images.*' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
            ]
        );

        if($validator->fails()) {
            return response()->json(["status" => "failed", "message" => "Validation error", "errors" => $validator->errors()]);
        }

        if($request->has('post_images')) {
            foreach($request->file('post_images') as $post_image) { 
                $filename = time().rand(1,4). '.'.$post_image->getClientOriginalExtension();
                $post_image->move('post_images/', $filename);

                Post_Image::create([
                    'image_name' => $filename
                ]);
            }

            $response["status"] = "successs";
            $response["message"] = "Success! post_image(s) uploaded";
        }

        else {
            $response["status"] = "failed";
            $response["message"] = "Failed! post_image(s) not uploaded";
        }
        return response()->json($response);   
    }

    public function destroy(Post_Image $post_image){
        return $post_image->delete()
            ? response()->json($post_image)
            : response()->json([] . 500);
    }
}


```

上のコントローラでは、2つの関数を作成しました。
1つは、アップロードされた画像をテーブルからすべて取得するためのものです。
2つ目の関数は、画像をアップロードするためのものです。mime type validation ruleを使って画像を検証しました。


次に、これら2つの関数に対応するルートを用意します。

```php
use App\Http\Controllers\PostImageController; //忘れずに
Route::get('post-images' , [PostImageController::class , 'index'])->name('post_images');
    Route::post('post-images' , [PostImageController::class , 'upload'])->name('post_images');
```

現段階のrouteのリストを確認すると

```
sail php artisan route:list
+--------+----------+-----------------------+-------------+-------------------------------------------------+------------+
| Domain | Method   | URI                   | Name        | Action                                          | Middleware |
+--------+----------+-----------------------+-------------+-------------------------------------------------+------------+
|        | GET|HEAD | api/posts             |             | App\Http\Controllers\PostController@index       | api        |
|        | POST     | api/posts             |             | App\Http\Controllers\PostController@store       | api        |
|        | GET|HEAD | api/posts/post-images | post_images | App\Http\Controllers\PostImageController@index  | api        |
|        | POST     | api/posts/post-images | post_images | App\Http\Controllers\PostImageController@upload | api        |
|        | GET|HEAD | api/posts/{post}      |             | App\Http\Controllers\PostController@show        | api        |
|        | PATCH    | api/posts/{post}      |             | App\Http\Controllers\PostController@update      | api        |
|        | DELETE   | api/posts/{post}      |             | App\Http\Controllers\PostController@destroy     | api        |
|        | GET|HEAD | {fallbackPlaceholder} |             | App\Http\Controllers\IndexController            | web        |
+--------+----------+-----------------------+-------------+-------------------------------------------------+------------+
```

このようになっている

ルートを作成したら、APIをPostmanにチェック。
例
・GET
![スクリーンショット 2021-07-02 10.46.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674805/7ada957b-8544-be19-2629-f302635ff8bf.png)


まだ何もアップロードしていないので、dataには何も入っていない


・POST
![スクリーンショット 2021-07-02 10.47.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674805/22a82f65-12fa-16b6-0327-684cb08f4cfc.png)


画像が選択されていないため、エラーがでる(validation)

画像をアップロード(POSTMANはWEB版とアプリ版があるが、Web版だと画像のアップロードが出来ないため、POSTMANのアプリのインストールが必要した上で、POSTのテストを行う)

![スクリーンショット 2021-07-02 10.48.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674805/fd9e2c9c-318f-06c5-86fd-ea40f712083f.png)



```
http://localhost/api/posts/post-images
```

GETを実行し、データが取得できているか確認

![スクリーンショット 2021-07-02 10.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674805/81191d5f-796e-5b25-2c64-501444bb205c.png)



**React側

ここでは2つのコンポーネントを作成します。

PostImageUpload.js - 画像やファイルをアップロードするためのコンポーネントです。
PostImages.js - アップロードされた画像をAPIから取得するためのコンポーネント
今回の記事では、単一画像と複数画像のアップロードについて見ていきます。そこで、コンポーネントを作成した後、まず単一ファイルのアップロードの機能を実装

![スクリーンショット 2021-07-02 10.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1674805/bd4c504d-7bd0-bc82-d90b-d410d92a1319.png)


PostImageUpload.jsコンポーネントを開き、以下のコードを配置します。

```javascript
import React, { useState } from "react"
import axios from "axios"
import { useHistory } from "react-router-dom"
import { Link } from "react-router-dom"

const PostImageUpload = () => {
  const history = useHistory()

  const initialResMsg = {
    status: "",
    message: "",
    error: "",
  }

  const [postImage, setPostImage] = useState<Blob | string>("")
  const [responseMsg, setResponseMsg] = useState<any>(initialResMsg)

  const handleChange = (e: any) => {
    setPostImage(e.target.files[0])
  }

  const submitHandler: any = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
    e.preventDefault()
    const data = new FormData()
    data.append("post_images[]", postImage)
    console.log("data : ", data)
    axios
      .post("api/posts/post-images", data)
      .then((response) => {
        if (response.status === 200) {
          console.log(response)
          setResponseMsg({
            status: response.data.status,
            message: response.data.message,
          })
          setTimeout(() => {
            setPostImage("")
            setResponseMsg({
              status: "",
              message: "",
              error: "",
            })
          }, 4000)

          document.querySelector<any>("#postImageForm").reset()
        }
        // history.push("/post-images")
      })
      .catch((error) => {
        console.error(error)
      })
  }

  return (
    <div className="container py-5">
      <div>
        <div>
          <form
            onSubmit={submitHandler}
            encType="multipart/form-data"
            id="postImageForm"
          >
            <div className="card shadow">
              {responseMsg.status === "successs" ? (
                <div className="alert alert-success">{responseMsg.message}</div>
              ) : responseMsg.status === "failed" ? (
                <div className="alert alert-danger">{responseMsg.message}</div>
              ) : (
                ""
              )}
              <div>
                <h4>Upload Image</h4>
              </div>

              <div className="card-body">
                <div>
                  <label htmlFor="post_images">Images</label>
                  <input
                    type="file"
                    name="post_images"
                    onChange={handleChange}
                    className="form-control"
                  />
                  <span className="text-danger">{responseMsg.error}</span>
                </div>
              </div>

              <div>
                <button type="submit" className="btn btn-success">
                  Upload
                </button>
              </div>
            </div>
          </form>
        </div>
      </div>
      <Link to="post-images">post images list</Link>
    </div>
  )
}

export default PostImageUpload
```

・上記のコードでは、入力タイプのファイルを持つフォームを作成しています。入力ファイルのために、onChangeイベントを作成しました。
・0インデックスで利用可能なファイルを読み込み、これをステートに設定しています。
・その後、フォーム送信ハンドラを作成し、この中で、ファイルアップロードのためのAPIコールをReact with Axiosで作成しました。
・ファイルの送信には、FormData()を使用しました。そして、ファイルをフォームデータオブジェクトに追加し、それをAPIに渡しました。

次に、Router.tsxにルートの設定をする
以下のコードを追加


```tsx
    <Route path="/post-images">
            <PostImageUpload />
          </Route>
```

PostImages.tsxに以下のコードを配置

```tsx
import React, { useState, useEffect } from "react"
import axios from "axios"

const PostImages: React.FC<any> = () => {
  const [postImages, setPostImages] = useState<any>([])

  const getImages = () => {
    axios
      .get("api/posts/post-images")
      .then((response) => {
        console.log("images list", response)
        if (response.status === 200) {
          setPostImages(response.data.data)
        }
      })
      .catch((error) => {
        console.error(error)
      })
  }

  useEffect(() => {
    getImages()
  }, [])

  return (
    <div>
      <div className="row">
        <div className="col-xl-6 col-lg-8 col-md-8 col-sm-12 m-auto">
          <div className="card shadow">
            <div className="card-header">
              <button onClick={getImages}>get Images List</button>
              <h4 className="card-title fw-bold"> Images List </h4>
            </div>
            <div className="card-body">
              <div className="row">
                {/* {postImages.images.length > 0 ? ( */}
                {postImages ? (
                  postImages.map((image: any) => (
                    <div
                      className="col-xl-6 col-lg-8 col-sm-12 col-12 mt-3"
                      key={image.id}
                    >
                      <img
                        src={"http://localhost/uploads/" + image.image_name}
                        className="img-fluid img-bordered"
                        width="200px"
                      />
                    </div>
                  ))
                ) : (
                  <h6 className="text-danger text-center">No Image Found </h6>
                )}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export default PostImages
```

参考

```
https://programmingfields.com/file-upload-in-react-js-using-laravel-8-restful-api/
```