プロフィール画像

ブログ投稿、サムネあり

PostController.php

```
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use App\Http\Requests\PostRequest;
use Illuminate\Support\Facades\Validator;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     * Post一覧
     * @return Post[]|\Illuminate\Database\Eloquent\Collection
     */
    public function index()
    {
        $posts = Post::query()->with(['tags'])->get();
        return $posts;
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  　PostRequest  $request
     * @return \Illuminate\Http\Json 
     */
    public function store(PostRequest $request ){   
        $response = [];

        $validator = Validator::make($request->all(),
            [
                'top_image' => 'required',
                'top_image.*' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
            ]
        );

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

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

            $post = Post::create([
                'top_image' => $filename,
                'title' => $request->title,
                'body' => $request->body,
                'user_id' => $request->user_id,
                'category_id' => $request->category_id,
                'breed_id' => $request->breed_id,
                'is_publish' => $request->is_publish
            ]);

            return $post
            ? response()->json($post , 201)
            : response()->json([] , 500);
        }
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        // return Post::find($post);
        return Post::find($post)
        ? response()->json($post)
        : response()->json([] , 500);
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(post $post)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\PostRequest  $request
     * @param  \App\Models\Post  $post
     * @return \Illuminate\Http\JsonResponse
     */
    public function update(PostRequest $request , Post $post ){
        $validator = Validator::make($request->all(),
            [
                'top_image' => 'required',
                'top_image.*' => '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('top_image')){
            foreach($request->file('top_image') as $top_image){
                $filename = time().rand(1,4). '.'.$top_image->getClientOriginalExtension();
                $top_image->move('top_images/', $filename);
            }

            $newPost = Post::find($post->id)->update([
                'top_image' => $filename,
                'title' => $request->title,
                'body' => $request->body,
                'category_id' => $request->category_id,
                'breed_id' => $request->breed_id,
                'is_publish' => $request->is_publish,
                'user_id' => $request->user_id
            ]);

        }

        return $newPost
            ? response()->json($newPost)
            : response()->json([] , 500);

    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\post  $post
     * @return \Illuminate\Http\JsonResponse
     */
    public function destroy(Post $post)
    {
        return $post->delete()
            ? response()->json($post)
            : response()->json([] , 500);
    }
}
```

api.php

```

Route::prefix('posts')->group(function () {
    Route::get('',[PostController::class, 'index'] );
    Route::post('',[PostController::class, 'store'] );
    Route::get('post-images' , [PostImageController::class , 'index'])->name('post-images');
    Route::post('post-images' , [PostImageController::class , 'upload'])->name('post-images');
    Route::delete('post-images/{post_image}' , [PostImageController::class , 'destroy'])->name('post-images');
    Route::prefix('{post}')->group(function() {
        Route::get('' , [PostController::class, 'show']);
        Route::post('',[PostController::class,'update'] );
        Route::delete('',[PostController::class,'destroy'] );
    });
});

```


Create.tsx

```

import React, { useState } from "react"
import { Post, ResMsg } from "../../types/Blog"
import * as api from "../../API/BlogAPI"
import { useHistory } from "react-router-dom"

const Create = () => {
  const history = useHistory()
  const initialPost = {
    top_image: "",
    title: "",
    body: "",
    category_id: 0,
    breed_id: 0,
    user_id: 0,
    is_publish: 1,
  }

  const initialResMsg = {
    status: 0,
    message: "",
    error: "",
  }
  const [post, setPost] = useState<Post>(initialPost)
  const [responseMsg, setResponseMsg] = useState<ResMsg>(initialResMsg)

  const inputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target
    setPost({ ...post, [name]: value })
  }

  const inputImageChange = (e: any) => {
    setPost({ ...post, top_image: e.target.files[0] })
  }

  const changeIsPublish = () => {
    console.log(post)
    setPost({ ...post, is_publish: post.is_publish === 1 ? 0 : 1 })
  }

  const setRes = (response: any) => {
    if (response.status === "failed") {
      setResponseMsg({
        status: response.status,
        message: response.message,
        error: "",
      })
    } else {
      history.push("/home")
    }
  }

  const inputReset = () => {
    setTimeout(() => {
      setPost(initialPost)
      setResponseMsg(initialResMsg)
    }, 3000)
  }

//タイトルとか画像とか全てのデータをFormDataとして送る
  const save = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    const postData = new FormData()
    Object.entries(post).forEach(([key, value]) => {
      if (typeof value === "object") {
        postData.append(key + "[]", value)
      } else {
        postData.append(key, value)
      }
    })
    await api
      .create(postData)
      .then((response) => {
        setRes(response)
        inputReset()
      })
      .catch((error) => {
        alert(error)
      })
  }

  return (
    <div>
      {responseMsg.status === "failed" && <div>{responseMsg.message}</div>}
      <h3>Create Post</h3>
      <form onSubmit={save} id="postBlogForm" encType="multipart/form-data">
        <label htmlFor="top_image">Top Image</label>
        <input type="file" name="top_image" onChange={inputImageChange} />
        <input value={post.title} type="text" placeholder="title" name="title" onChange={inputChange} />
        <input value={post.body} type="text" placeholder="body" name="body" onChange={inputChange} />
        <input
          value={post.category_id}
          type="number"
          placeholder="category"
          name="category_id"
          onChange={inputChange}
        />
        <input value={post.breed_id} type="number" placeholder="breed" name="breed_id" onChange={inputChange} />
        <p>{post.is_publish ? "publish" : "private"}</p>
        <button type="submit">投稿</button>
      </form>
      <button onClick={changeIsPublish}>publish setting</button>
    </div>
  )
}

export default Create
```

Blog.API

```

import axios from "axios"
import { Blog, Post } from "../types/Blog"

const getAll = async () => {
  const { data } = await axios.get<Blog[]>("api/posts")
  return data
}

const create = async (postData: any) => {
  const { data } = await axios.post("api/posts", postData)
  return data
}

const get = async (id: number) => {
  const { data } = await axios.get<Blog>(`/api/posts/${id}`)
  return data
}

const update = async (id: any, updateData: any) => {
  const { data } = await axios.post(`/api/posts/${id}`, updateData)
  return data
}

const deleteBlog = async (blog: Blog) => {
  await axios.delete<Blog>(`/api/posts/${blog.id}`)
}

export { getAll, update, create, get, deleteBlog }

```


編集機能

ArticleEdit.tsx

```

import React, { useState } from "react"
import { Blog, ResMsg } from "../../../types/Blog"
import * as api from "../../../API/BlogAPI"
import { useHistory, useParams } from "react-router-dom"

type Props = {
  detail: Blog
  changeEdit: () => void
}

const ArticleEdit: React.FC<Props> = ({ detail, changeEdit }) => {
  const history = useHistory()
  const param = useParams()
  const initialResMsg = {
    status: 0,
    message: "",
    error: "",
  }
  const [newBlog, setNewBlog] = useState(detail)
  const [responseMsg, setResponseMsg] = useState<ResMsg>(initialResMsg)

  const inputChange = (event: any) => {
    const { name, value } = event.target
    setNewBlog({ ...newBlog, [name]: value })
  }

  const inputImageChange = (e: any) => {
    setNewBlog({ ...newBlog, top_image: e.target.files[0] })
  }

  const changeIsPublish = () => {
    console.log(newBlog)
    setNewBlog({ ...newBlog, is_publish: newBlog.is_publish === 1 ? 0 : 1 })
  }

  const updateBlog = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    let updateData = new FormData()
    Object.entries(newBlog).forEach(([key, value]) => {
      if (typeof value === "object") {
        console.log("[key , value] : ", "[", key, ",", value, "]")
        updateData.append(key + "[]", value)
      } else {
        console.log("[key , value] :", "[", key, ",", value, "]")
        updateData.append(key, value)
      }
    })
    await api
      .update(newBlog.id, updateData)
      .then((response) => {
        console.log(response)
        // setRes(response)
      })
      .catch((error) => {
        console.log(error)
        alert(error)
      })
  }

  // const setRes = (response: any) => {
  //   if (response.status === "failed") {
  //     setResponseMsg({
  //       status: response.status,
  //       message: response.message,
  //       error: "",
  //     })
  //   } else {
  //     changeEdit()
  //   }
  // }

  return (
    <div>
      {responseMsg.status === "failed" && <div>{responseMsg.message}</div>}
      <h3>ブログ編集</h3>
      <form onSubmit={updateBlog} encType="multipart/form-data" method="post">
        <img src={"http://localhost/top_images/" + newBlog.top_image} width="200px" />
        <input type="file" name="top_image" onChange={inputImageChange} />
        <label>title</label>
        <input value={newBlog.title} name="title" onChange={inputChange} />
        <label>body</label>
        <input value={newBlog.body} name="body" onChange={inputChange} />
        <label>category_id</label>
        <input type="number" value={newBlog.category_id} name="category_id" onChange={inputChange} />
        <label>breed_id</label>
        <input type="number" value={newBlog.breed_id} name="breed_id" onChange={inputChange} />
        <p>{newBlog.is_publish ? "publish" : "private"}</p>
        <button type="submit">変更を保存</button>
      </form>
      <button onClick={changeIsPublish}>publish setting</button>
      <button onClick={changeEdit}>戻る</button>
    </div>
  )
}

export default ArticleEdit

```