業務でReact×Laravelのアプリ作成に関わっているので勉強しています。
今回は学習用のアウトプットです。
Laravel側
環境構築
主にReactとLaravelの連携部分のアウトプットなので環境構築について詳細の説明は省きます。
今回はこちらを記事を参考にさせていただきました。
API作成
手順はこちらの記事を参考にさせていただきました。
モデルとマイグレーションの作成
$ php artisan make:model Book --migration
作成されたBookモデルに追記
app/Models/Book.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
protected $fillable = [
'title',
'author',
];
}
マイグレーションファイル記述
database/migrations/xxxx_xx_xx_xxxxxx_create_books_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('author');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('books');
}
};
マイグレーション実行
$ php artisan migrate
コントローラーの作成
$ php artisan make:controller BookController --api
ルーティング設定
routes/api.php
<?php
use App\Http\Controllers\BookController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::apiResource('/books', BookController::class);
ルーティング確認
php artisan route:list
結果
GET|HEAD / ....................................................................................................................................
POST _ignition/execute-solution ............................. ignition.executeSolution › Spatie\LaravelIgnition › ExecuteSolutionController
GET|HEAD _ignition/health-check ......................................... ignition.healthCheck › Spatie\LaravelIgnition › HealthCheckController
POST _ignition/update-config ...................................... ignition.updateConfig › Spatie\LaravelIgnition › UpdateConfigController
GET|HEAD api/books ......................................................................................... books.index › BookController@index
POST api/books ......................................................................................... books.store › BookController@store
GET|HEAD api/books/{book} .................................................................................... books.show › BookController@show
PUT|PATCH api/books/{book} ................................................................................ books.update › BookController@update
DELETE api/books/{book} .............................................................................. books.destroy › BookController@destroy
GET|HEAD api/user .............................................................................................................................
GET|HEAD sanctum/csrf-cookie ...................................................................... Laravel\Sanctum › CsrfCookieController@show
コントローラーにメソッド追加
app/Http/Controllers/BookController.php
<?php
namespace App\Http\Controllers;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$books = Book::all();
return response()->json(
$books, 200
);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$book = Book::create($request->all());
return response()->json(
$book, 201
);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$update = [
'title' => $request->title,
'author' => $request->author
];
$book = Book::where('id', $id)->update($update);
$books = Book::all();
if ($book) {
return response()->json(
$books
, 200);
} else {
return response()->json([
'message' => 'Book not found',
], 404);
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$book = Book::where('id', $id)->delete();
if ($book) {
return response()->json([
'message' => 'Book deleted successfully',
], 200);
} else {
return response()->json([
'message' => 'Book not found',
], 404);
}
}
}
フォームリクエストバリデーション作成
$ php artisan make:request StoreBook
app/Http/Requests/StoreBook.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Validation\ValidationException;
class StoreBook extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required',
'author' => 'required',
];
}
public function messages()
{
return [
'title.required' => 'タイトルが未入力です',
'author.required' => '著者が未入力です',
];
}
protected function failedValidation(Validator $validator)
{
$errors = (new ValidationException($validator))->errors();
throw new HttpResponseException(response()->json([
'message' => 'Failed validation',
'errors' => $errors,
], 422, [], JSON_UNESCAPED_UNICODE));
}
}
curlコマンドでAPIを叩いてみる
新規作成
$ curl -X POST http://localhost:80/api/books -d 'title=羅生門&author=芥川龍之介'
すべてのレコードを確認
$ curl http://localhost:80/api/books/
React側
参考記事
新規プロジェクト作成
$ npx create-react-app {プロジェクト名} --template typescript
Appコンポーネントを以下の記述に変更
import axios from "axios";
import { useEffect, useState } from "react";
import "./App.css";
type Book = {
id: number;
title: string;
author: string;
};
export const App = () => {
const [books, setBooks] = useState<Book[]>([
{
id: 0,
title: "",
author: "",
},
]);
useEffect(() => {
axios
.get("http://localhost:80/api/books/")
.then((response) => setBooks(response.data))
.catch((error) => console.log(error));
}, []);
const [title, setTitle] = useState<string>("");
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
};
const [author, setAuthor] = useState<string>("");
const handleAuthorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setAuthor(e.target.value);
};
const createNewBook = (): void => {
axios
.post("http://localhost:80/api/books/", {
title: title,
author: author,
})
.then((response) => {
setBooks([...books, response.data]);
})
.then(() => {
setAuthor("");
setTitle("");
})
.catch((error) => {
console.log(error);
});
};
const deleteBook = (id: number) => {
axios
.delete(`http://localhost:80/api/books/${id}`)
.then((response) => {
console.log(response);
setBooks(books.filter((book) => book.id !== id));
})
.catch((error) => console.log(error));
};
const modifyBook = (id: number) => {
axios
.patch(`http://localhost:80/api/books/${id}`, {
title: title,
author: author,
})
.then((response) => {
setBooks(response.data);
})
.then(() => {
setAuthor("");
setTitle("");
})
.catch((error) => console.log(error));
};
return (
<>
<ul>
{books.map((book) => (
<>
<li key={book.id}>
タイトル:{book.title} 作者:{book.author}
<button onClick={() => deleteBook(book.id)}>削除</button>
<button onClick={() => modifyBook(book.id)}>更新</button>
</li>
</>
))}
</ul>
<label>
タイトル:
<input value={title} onChange={handleTitleChange} />
</label>
<label>
作者:
<input value={author} onChange={handleAuthorChange} />
</label>
<br />
<button onClick={createNewBook}>作成</button>
</>
);
};
exportを変更したので他のファイルでエラーが発生した場合は適宜修正。
以上になります。
型の定義方法等はまだまだ勉強中ですので修正点ありましたら教えていただきたく思います。