react×Laravel環境構築×Todoリスト(1)
react×Laravel環境構築忘れないために
やること
- プロジェクトのセットアップ
- データベース設定
- APIエンドポイントの作成
- Reactコンポーネントの作成
- 画面の表示
プロジェクトのセットアップ
vsCodeを開いてctrl+@キーでターミナルを開く
C:\workspace何に作成したので
cdコマンドを使って自分の今いる位置からworkspaceに飛ぶ
composer create-project --prefer-dist laravel/laravel todoList
todoListの部分がプロジェクト名になります
ファイル作成が完了したらReactとLaravel Breezeのインストールします
cd todoList
composer require laravel/breeze --dev
php artisan breeze:install react --typescript
npm install react-router-dom @mui/material @emotion/react @emotion/styled axios
npm install
これでプロジェクトのセットアップは完了です。
データベース関連設定
apacheとMySQLをスタートさせて
xampp開いてMySQLのadminを押すと専用Webに飛ぶので、sqlをタブを選択して
以下を入力して実行
CREATE DATABASE tododb CHARACTER SET utf8;
vsCodeに戻って.envファイルの編集をします
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database_name(tododb)
DB_USERNAME=your_database_username
DB_PASSWORD=your_database_password
your_database_name・your_database_username・your_database_passwordは自分のを入れてください
ターミナルでマイグレーションファイルの作成
以下はモデルとマイグレーションファイル同時に作成するコマンド
php artisan make:model Todo -m
マイグレーションファイルを編集
database/migrations/xxxx_xx_xx_create_todos_table.phpを編集
public function up()
{
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->string('content');
$table->timestamps();
});
}
保存して以下を実行
php artisan migrate
APIエンドポイントの作成
app/Models/Todo.phpを編集
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Todo extends Model
{
use HasFactory;
// $fillable に定義されたフィールドは、ユーザーがリクエストを通してデータをモデルに一括で割り当てる際に、保存できる属性のリストです。
// $fillable を使うことで、セキュリティの確保を目的としています。リクエストから予期しないデータが送信されても、許可されたフィールドのみをデータベースに保存できるように制限することで、マスアサインメント脆弱性を防ぎます。
protected $fillable = ['content'];
}
``
ターミナルでコントローラの作成
```js
php artisan make:controller TodoController
app/Http/Controllers/TodoController.phpを編集
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Todo;
class TodoController extends Controller
{
// 新しいTodoを追加するメソッド
public function setTodo(Request $request)
{
// リクエストのバリデーション: 'content' フィールドが必須であり、文字列で、最大255文字までであることを検証
$request->validate([
'content' => 'required|string|max:255'
]);
// Todoを作成してデータベースに保存
$todo = Todo::create([
'content' => $request->content
]);
// 作成したTodoをJSON形式で返す
return response()->json($todo, 201); // ステータスコード201で返す
}
// 全てのTodoを取得して返すメソッド
public function index()
{
// Todoのすべてのレコードを取得して変数に格納
$todos = Todo::all();
// 取得したTodoのリストをJSON形式で返す
return response()->json($todos);
}
}
ルートの設定
routes/web.phpの内容を変更
<?php
use App\Http\Controllers\ProfileController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TodoController;
// Todo APIエンドポイント
Route::post('/api/todo', [TodoController::class, 'setTodo']);
Route::get('/api/todo', [TodoController::class, 'index']);
// フロントエンドのすべてのリクエストをキャッチするルート
Route::get('{any}', function () {
return view('app');
})->where('any','.*');
Reactコンポーネントの作成
登録用
import React, { useState } from "react";
import axios from "axios";
import { Box, Button, TextField, Typography } from "@mui/material";
import { useNavigate } from "react-router-dom";
const TodoForm: React.FC = () => {
const [content, setContent] = useState("");
const [message, setMessage] = useState("");
const navigate = useNavigate();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await axios.post("/api/todo", { content });
setMessage("Item added successfully");
setContent("");
navigate("/show");
} catch (error) {
setMessage("Failed to add item");
}
};
return (
<Box sx={{ maxWidth: 400, mx: "auto", mt: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
lifiGoalTodo
</Typography>
<form onSubmit={handleSubmit}>
<TextField
label="Name"
value={content}
onChange={(e) => setContent(e.target.value)}
fullWidth
margin="normal"
required
/>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
>
目標を送信
</Button>
</form>
{message && (
<Typography variant="body2" color="textSecondary">
{message}
</Typography>
)}
</Box>
);
};
export default TodoForm;
表示用
import React, { useEffect, useState } from "react";
import axios from "axios";
import { Box, List, ListItem, ListItemText, Typography } from "@mui/material";
const ShowTodoList: React.FC = () => {
const [todos, setTodos] = useState<{ id: number; content: string }[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchTodos = async () => {
try {
const response = await axios.get("/api/todo");
console.log(response.data);
setTodos(response.data);
} catch (error) {
console.error("Error fetching items:", error);
} finally {
setLoading(false);
}
};
fetchTodos();
}, []);
if (loading) {
return <Typography>Loading...</Typography>;
}
return (
<Box sx={{ maxWidth: 400, mx: "auto", mt: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Item List
</Typography>
<List>
{todos.map((todo) => (
<ListItem key={todo.id}>
<ListItemText primary={todo.content} />
</ListItem>
))}
</List>
</Box>
);
};
export default ShowTodoList;
resources/js/app.tsxを編集
import "../css/app.css";
import React from "react";
import TodoForm from "./Components/TodoForm";
import ShowTodoList from "./Components/ShowTodoList";
import { createRoot } from "react-dom/client";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
const App: React.FC = () => {
return (
<Router>
<div>
<Routes>
<Route path="/" element={<TodoForm />} />
<Route path="/show" element={<ShowTodoList />} />
</Routes>
</div>
</Router>
);
};
const container = document.getElementById("app");
if (container) {
const root = createRoot(container);
root.render(<App />);
}
app.blade.phpを変更
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Scripts -->
@routes
@viteReactRefresh
@vite('resources/js/app.tsx')
</head>
<body class="font-sans antialiased">
<div id="app"></div>
</body>
</html>
XAMPPの設定
xamppを開いてApacheのConfigボタンを押してhttpd.configを選択して
ctrl+fでDocumentRootを検索して以下のように変更
DocumentRoot "C:\workspace\laravel-react\public"
<Directory "C:\workspace\laravel-react\public">
これはxamppの方で開くための設定(必要に応じて)
後はターミナルで
npm run dev
を打ってターミナルの方の
APP_URL:http://localhostをctrl+クリックで起動できる
ありがとうございました。
Discussion