Reactのシンプルなサンプルをまとめているサイトはたくさんあるのですが、自分の環境に合っていない部分もあり、覚書のために本記事にまとめました。
本記事をまとめるにあたって、こちらの記事を参考にさせていただきました。
たいへん分かりやすくまとまっていて、こちらのサイトの情報だけでも完全に環境を構築することが出来ると思います。
Laravelで REST API を実装し Reactと連携したCRUDアプリ作成
https://qiita.com/masakichi_eng/items/af50d9a3e6a975b601e6
Laravel側
API作成
モデルとマイグレーションの作成
1 |
$ php artisan make:model Product --migration |
1 2 |
INFO Model [app/Models/Product.php] created successfully. INFO Migration [database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php] created successfully. |
作成されたProductモデルに追記します。
app/Models/Product.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasFactory; protected $fillable = [ 'code', 'name', 'price', ]; } |
マイグレーションファイルを追記します。
database/migrations/xxxx_xx_xx_xxxxxx_create_products_table.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('code'); $table->string('name'); $table->integer('price'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('products'); } }; |
マイグレーション実行
1 |
$ php artisan migrate |
1 2 |
INFO Running migrations. xxxx_xx_xx_xxxxxx_create_products_table ........................................................................................ 2,335ms DONE |
コントローラーの作成
1 |
$ php artisan make:controller ProductController --api |
いったん、コントローラーを作成し、実装は後ほど追記していきます。
1 |
INFO Controller [app/Http/Controllers/ProductController.php] created successfully. |
ルーティング設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; use App\Http\Controllers\ProductController; /* |-------------------------------------------------------------------------- | API Routes |-------------------------------------------------------------------------- | | Here is where you can register API routes for your application. These | routes are loaded by the RouteServiceProvider and all of them will | be assigned to the "api" middleware group. Make something great! | */ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); Route::apiResource('/products', ProductController::class); |
ルーティング確認
1 |
$ php artisan route:list |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/var/www/html # 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/products ....................................................................... products.index › ProductController@index POST api/products ....................................................................... products.store › ProductController@store GET|HEAD api/products/{product} ............................................................... products.show › ProductController@show PUT|PATCH api/products/{product} ........................................................... products.update › ProductController@update DELETE api/products/{product} ......................................................... products.destroy › ProductController@destroy GET|HEAD api/user .................................................................................................................... GET|HEAD confirm-password ................................................. password.confirm › Auth\ConfirmablePasswordController@show POST confirm-password ................................................................... Auth\ConfirmablePasswordController@store GET|HEAD dashboard ......................................................................................................... dashboard GET|HEAD demo ............................................................................................ demo › DemoController@react POST email/verification-notification ...................... verification.send › Auth\EmailVerificationNotificationController@store GET|HEAD forgot-password .................................................. password.request › Auth\PasswordResetLinkController@create POST forgot-password ..................................................... password.email › Auth\PasswordResetLinkController@store GET|HEAD login .................................................................... login › Auth\AuthenticatedSessionController@create POST login ............................................................................. Auth\AuthenticatedSessionController@store POST logout ................................................................. logout › Auth\AuthenticatedSessionController@destroy PUT password ................................................................... password.update › Auth\PasswordController@update GET|HEAD profile ............................................................................... profile.edit › ProfileController@edit PATCH profile ........................................................................... profile.update › ProfileController@update DELETE profile ......................................................................... profile.destroy › ProfileController@destroy GET|HEAD register .................................................................... register › Auth\RegisteredUserController@create POST register ................................................................................ Auth\RegisteredUserController@store POST reset-password ............................................................ password.store › Auth\NewPasswordController@store GET|HEAD reset-password/{token} ................................................... password.reset › Auth\NewPasswordController@create GET|HEAD sanctum/csrf-cookie ....................................... sanctum.csrf-cookie › Laravel\Sanctum › CsrfCookieController@show GET|HEAD stock ......................................................................................... stock › StockController@index GET|HEAD verify-email ................................................... verification.notice › Auth\EmailVerificationPromptController GET|HEAD verify-email/{id}/{hash} ................................................... verification.verify › Auth\VerifyEmailController |
コントローラーの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Product; class ProductController extends Controller { /** * Display a listing of the resource. */ public function index() { $products = Product::all(); return response()->json( $products, 200 ); } /** * Store a newly created resource in storage. */ public function store(Request $request) { $product = Product::create($request->all()); return response()->json( $product, 201 ); } /** * Update the specified resource in storage. */ public function update(Request $request, string $id) { $update = [ 'code' => $request->code, 'name' => $request->name, 'quantity' => $request->quantity, ]; $product = Product::where('id', $id)->update($update); $products = Product::all(); if ($product) { return response()->json( $products , 200); } else { return response()->json([ 'message' => 'Product not found', ], 404); } } /** * Remove the specified resource from storage. */ public function destroy(string $id) { $product = Product::where('id', $id)->delete(); if ($product) { return response()->json([ 'message' => 'Product deleted successfully', ], 200); } else { return response()->json([ 'message' => 'Product not found', ], 404); } } } |
フォームリクエストバリデーション作成
1 |
$ php artisan make:request StoreProduct |
1 |
INFO Request [app/Http/Requests/StoreProduct.php] created successfully. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?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 StoreProduct extends FormRequest { /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; } /** * Get the validation rules that apply to the request. * * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string> */ public function rules(): array { return [ 'code' => 'required', 'name' => 'required', 'quantity' => 'required', ]; } /** * */ public function messages() { return [ 'code.required' => 'コードが未入力です', 'name.required' => '名称が未入力です', 'quantity.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を確認してみます。
新規作成
1 |
$ curl -X POST http://localhost:8000/api/books -d 'code=TEST00001&name=テスト商品1&price=1000' |
すべてのレコードを確認
1 |
$ curl http://localhost:8000/api/books/ |
ブラウザで確認した場合には、このようにデータが表示されます。
React側
1 |
$ npx create-react-app {プロジェクト名} --template typescript |
React
Appコンポーネントを以下の記述に変更
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
import '../../../css/app.css'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; import { Head } from '@inertiajs/react'; import { useEffect, useState } from "react"; import axios from "axios"; export default function Stock(props) { const [products, setProducts] = useState([ { id: 0, code: "", name: "", price: 0. }, ]); useEffect(() => { axios .get("http://localhost:8000/api/products/") .then((response) => setProducts(response.data)) .catch((error) => console.log(error)); }, []); const [code, setCode] = useState(""); const handleCodeChange = (e) => { setCode(e.target.value); }; const [name, setName] = useState(""); const handleNameChange = (e) => { setName(e.target.value); }; const [price, setPrice] = useState(""); const handlePriceChange = (e) => { setPrice(e.target.value); }; const createNewProduct = () => { axios .post("http://localhost:8000/api/products/", { code: code, name: name, price: price, }) .then((response) => { setProducts([...products, response.data]); }) .then(() => { setCode(""); setName(""); setPrice(""); }) .catch((error) => { console.log(error); }); }; const deleteBook = (id) => { axios .delete(`http://localhost:8000/api/products/${id}`) .then((response) => { console.log(response); setBooks(books.filter((book) => book.id !== id)); }) .catch((error) => console.log(error)); }; const modifyBook = (id) => { axios .patch(`http://localhost:8000/api/products/${id}`, { code: code, name: name, price: price, }) .then((response) => { setProducts(response.data); }) .then(() => { setCode(""); setName(""); setPrice(""); }) .catch((error) => console.log(error)); }; return ( <AuthenticatedLayout auth={props.auth} errors={props.errors} header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Stock</h2>} > <Head title="Stock" /> <table> <thead> <tr> <th>コード</th> <th>商品名</th> <th>単価</th> </tr> </thead> <tbody> {products.map((product) => ( <> <tr key={product.id}> <td>{product.code}</td> <td>{product.name}</td> <td>{product.price}</td> </tr> </> ))} </tbody> </table> <label> code <input value={code} onChange={handleCodeChange} /> </label> <label> name <input value={name} onChange={handleNameChange} /> </label> <label> price <input value={price} onChange={handlePriceChange} /> </label> <br /> <button onClick={createNewProduct}>作成</button> </AuthenticatedLayout> ); } |
実行結果
下記の画面のように、一覧表示、登録ができる画面が表示されます。
参考サイト
手順はこちらの記事を参考にさせていただきました。
Laravelで REST API を実装し Reactと連携したCRUDアプリ作成
https://qiita.com/masakichi_eng/items/af50d9a3e6a975b601e6