如果用 Route::resource 建立 API 的話,編輯單一資料的路由會是
請求方法 | 路由 | 控制器函式 | 路由名稱標籤 |
---|---|---|---|
PUT/PATCH | /todos/{id} | update | photos.update |
在 TodoController 編輯 update 函式
public function update(Request $request, Todo $todo)
{
- //
+ $data = $request->all(); // 取得請求中的 body 資料
+
+ $todo->name = $data['name'];
+
+ $todo->save();
}
update 函式中宣告了 Todo $todo 這個參數,這樣 Laravel 的 Service Container 就會自動根據路由中的 id 找出該筆資料,建立 Todo Model 的實例。
有了實例後剩下的就跟新增資料時一樣了,從 Request 中取出 name 值,更新實例的欄位後呼叫 save() 更新資料。
如果更新資料的邏輯不複雜的話,也可以用快捷式的 update 方法
public function update(Request $request, Todo $todo)
{
- //
+ $data = $request->all(); // 取得請求中的 body 資料
+
+ $todo->update([
+ 'name' => $data['name']
+ ]);
}
跟 create 一樣, 要將想寫入的欄位在 Model 中加入 $fillable 才能夠更新。
另外 update 可以用於一次性更新多筆資料
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
用 where 搜尋出來的多筆資料會被一併更新 delayed 欄位。
就在寫文的期間 Material UI 更新到 5.0.1 版了,套件名稱都不同,通通重裝一遍。
sail yarn remove @material-ui/core
sail yarn add @mui/material @emotion/react @emotion/styled
接下來要加上修改之前建立好的 Todo 資料功能,會用到圖案按鈕所以先來加上圖案套件:
sail yarn add @mui/icons-material
Dashboard 畫面整個翻新
import React from "react";
import Authenticated from "@/Layouts/Authenticated";
import {
Container,
Button,
List,
ListItem,
ListItemText,
TextField,
Grid,
IconButton,
Box,
} from "@mui/material";
import { Head, Link, useForm } from "@inertiajs/inertia-react";
import { Inertia } from "@inertiajs/inertia";
import DeleteIcon from "@mui/icons-material/Delete";
import CreateIcon from "@mui/icons-material/Create";
export default function Dashboard(props) {
const { todos } = props; // 取出 todos
const { data, setData, post, processing, errors, reset, transform } = useForm(
{
todo: "",
}
);
const handleChange = (event) => {
setData(
event.target.name,
event.target.type === "checkbox"
? event.target.checked
: event.target.value
);
};
const submit = (e) => {
e.preventDefault();
// 轉換欄位名稱
transform((data) => ({
name: data.todo,
}));
// 發送請求
post(route("todo.store"), {
// 新增資料結束後重載 todos
onFinish: (visit) => {
Inertia.reload({ only: ["todos"] });
reset(); //另外順便清空輸入框
},
});
};
return (
<Authenticated
auth={props.auth}
errors={props.errors}
header={
<h2 className='font-semibold text-xl text-gray-800 leading-tight'>
Dashboard
</h2>
}>
<Head title='Dashboard' />
<div className='py-12'>
<div className='max-w-7xl mx-auto sm:px-6 lg:px-8'>
<div className='bg-white overflow-hidden shadow-sm sm:rounded-lg'>
<div className='p-6 bg-white border-b border-gray-200'>
You logged in!
</div>
</div>
</div>
</div>
<Container>
<form onSubmit={submit}>
<Grid
container
direction='row'
justifyContent='flex-start'
alignItems='flex-end'>
<TextField
id='todo'
label='Todo'
name='todo'
value={data.todo}
onChange={handleChange}
variant='standard'
style={{ marginRight: 4 }}
/>
<Button
type='submit'
variant='contained'
color='primary'
disabled={processing}>
Add
</Button>
</Grid>
</form>
<Box sx={{ width: "100%", maxWidth: 360 }}>
<List>
{todos.map((item, index) => (
<ListItem
button
key={index}
secondaryAction={
<IconButton edge='end' aria-label='edit'>
<CreateIcon />
</IconButton>
}>
<ListItemText primary={item.name} />
</ListItem>
))}
</List>
</Box>
</Container>
</Authenticated>
);
}
稍微修改一下畫面的功能,在點擊編輯按鈕後開始編輯 Todo ,按下 Enter 的話就準備送出更新請求。
@@ -1,6 +1,5 @@
-import React from "react";
+import React, { useState } from "react";
import Authenticated from "@/Layouts/Authenticated";
-import clsx from "clsx";
import {
Container,
Button,
@@ -26,6 +25,10 @@ export default function Dashboard(props) {
}
);
+ const [todoList, setTodoList] = useState(
+ todos.map((todo) => ({ ...todo, editing: false }))
+ );
+
const handleChange = (event) => {
setData(
event.target.name,
@@ -55,6 +58,17 @@ export default function Dashboard(props) {
});
};
+ const edit = (todoId) => {
+ console.log(`edit ${todoId}`);
+
+ const newList = todoList.map((todo) =>
+ todo.id === todoId
+ ? { ...todo, editing: true }
+ : { ...todo, editing: false }
+ );
+ setTodoList(newList);
+ };
+
return (
<Authenticated
auth={props.auth}
@@ -102,16 +116,41 @@ export default function Dashboard(props) {
</form>
<Box sx={{ width: "100%", maxWidth: 360 }}>
<List>
- {todos.map((item, index) => (
+ {todoList.map((item) => (
<ListItem
button
- key={index}
+ key={item.id}
secondaryAction={
- <IconButton edge='end' aria-label='edit'>
+ <IconButton
+ edge='end'
+ aria-label='edit'
+ onClick={() => {
+ edit(item.id);
+ }}>
<CreateIcon />
</IconButton>
}>
- <ListItemText primary={item.name} />
+ {item.editing ? (
+ <TextField
+ autoFocus
+ placeholder={item.name}
+ variant='standard'
+ onBlur={() => {
+ setTodoList(
+ todos.map((todo) => ({ ...todo, editing: false }))
+ );
+ }}
+ onKeyDown={(e) => {
+ if (e.key == "Enter") {
+ console.log("value", e.target.value);
+
+ // Update todo
+ }
+ }}
+ />
+ ) : (
+ <ListItemText primary={item.name} />
+ )}
</ListItem>
))}
</List>
用 Inertia.put 發送 put 請求給 todo.update 路由,記得指定路由中的 id 值。
然後一樣在請求完成後重新拉一次 todos 資料。
@@ -69,6 +69,20 @@ export default function Dashboard(props) {
setTodoList(newList);
};
+ const confirmEdit = (todo) => {
+ Inertia.put(
+ route("todo.update", { id: todo.id }),
+ {
+ name: todo.name,
+ },
+ {
+ onFinish: (visit) => {
+ Inertia.reload({ only: ["todos"] });
+ },
+ }
+ );
+ };
+
return (
<Authenticated
auth={props.auth}
@@ -145,6 +159,7 @@ export default function Dashboard(props) {
console.log("value", e.target.value);
// Update todo
+ confirmEdit({ id: item.id, name: e.target.value });
}
}}
/>