# 小組學習筆記_淺談Express_基本MVC架構和ORM工具介紹
撰寫人: 組長 許哲誠/terry xu
# 介紹MVC架構:
* Model :
* 定義資料庫料結構和利用CRUD的方式和資料庫進行溝通 ( 有使用ORM工具的情況。
* 撰寫SQL語法並與資料庫進行溝通 ( 使用SQL模組。
* Controller
* Controller 在過程中扮演 Model 和 View 中間的協調者,當不同路由(route)接收到 request 時,會呼叫 Controller 執行相對應的 Method。
* View
* 負責處理畫面的部分(template),也就是我們看到的網頁內容( 就是上課使用到ejs的部分
* 由於我們是用React,所以就沒有view的部分。
# 介紹ORM工具
* 大專打算用prisma,但市面上也很多種ORM工具,個人只用過Sequelize,但Prisma看起來強一點。
* ORM 在網站開發的架構中,通常是在 **資料庫** 和 **Model** 兩者之間,簡單來說,他就是讓使用者用更簡便、安全去操作資料庫。
## ORM工具優缺點
* 優點
* 安全性 :
* ORM工具通常會自動處理SQL注入問題,因為使用參數化查詢。
* ORM工具會過濾掉可能有害的SQL語句,如未經授權的DELETE操作。
* 提高了可讀性和可維護性 : ORM允許開發者使用面向對象的方式與資料庫交流,無需直接編寫SQL語句。
* 生產力提升 : 現在很多ORM工具都有提供許多現成的功能,如緩存、延遲加載等。
* 版本控制 : 比較好管理資料庫結構的變更。
* 缺點
* 效能問題 : 畢竟他在後面有做轉換成SQL語句等等,所以多多少少會比直接用SQL語句來的慢很多。
* 複雜查詢 : 對於非常複雜的查詢,使用ORM可能會變得繁瑣,有時直接編寫SQL會更高效和清晰,例如有用到跨好幾個表查詢時。
* 容易忘本 : 畢竟ORM都幫你做完了,反而SQL語句忘了就不太OK了。
* 現在國外感覺都比較推崇直接寫SQL,畢竟速度比較快,但我覺得可以視情況而定,像是購物網站這種會大量CRUD需求的的,也許就不太適合ORM。
## ORM工具 VS SQL語法
時空背景 : 假設我們有一個 'Product' 模型,包含 id, name, price 字段。
### ORM建構 VS SQL 建構
```javascript
datasource db {
provider = "mysql" // MariaDB 使用 MySQL
url = env("DATABASE_URL")
//記得所有重要的apikey 是帳號都要寫在.env檔喔
//甚麼是env檔,是用來存儲環境變量,很適合拿來放api key之類的個人隱私資料,
}
// 定義 Prisma 客戶端生成器
generator client {
provider = "prisma-client-js"
}
// 定義 Product 模型
model Product {
id Int @id @default(autoincrement())
name String
price Decimal @db.Decimal(10, 2)
}
// 用於原生 SQL MariaDB 客戶端
const mariadb = require('mariadb')
const pool = mariadb.createPool({
host: process.env.DB_HOST,//記得所有重要的api key 或是 帳號都要寫在.env檔喔
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
connectionLimit: 5
})
```
### Prisma create VS SQL create
```javascript
// 使用 Prisma create產品
async function createProductPrisma(name, price) {
const product = await prisma.product.create({//記得要寫async await用法喔
data: { name, price },
})
}
//為什麼要async await 這個坑之後再來說~~
//用SQL create 產品
async function createProductSQL(name, price) {
let conn;
try {
conn = await pool.getConnection()
const result = await conn.query(
'INSERT INTO Product (name, price) VALUES (?, ?)',
[name, price]
)
} finally {
if (conn) conn.release()
}
//Database Connection Pool 這是個用來維護的資料庫連接的快取,
//這個可學可不學,個人覺得還好QWQ
//想學可以來找Terry。
```
### Prisma read VS SQL read
```javascript
// 使用 Prisma 讀取產品
async function getProductPrisma(id) {
const product = await prisma.product.findUnique({//有很多種用法
where: { id: id },
}
//findUnique查找特定條件的單個記錄
//findMany查找多個記錄
//有可以配合很多酷酷的用法,ex. where select include 等等等
// 使用原生 SQL 讀取產品
async function getProductSQL(id) {
let conn;
try {
conn = await pool.getConnection()
const rows = await conn.query('SELECT * FROM Product WHERE id = ?', [id])
} finally {
if (conn) conn.release()
}
}
```
其他作者懶得打了,自已去看酷酷的官方文件🦥。
https://www.prisma.io/docs
# 大專前後端分離

流程如下 :
1. View ( React ) 會利用Client ( axios )** 發送一個request給到Route。
2. Route透過使用者輸入特定路徑時要指定呼叫 controller 檔案中哪個指定的函式。
3. Controller呼叫Model去資料庫做CRUD。
4. Controller拿到資料後再response回去Client端。
5. Client ( axios ) 端再把拿到的資料 ( JSON ) 做完處理後傳給View (React)。
* axios會再生一份簡易教材出來~~
## 後端方面的範例程式
( 用MVC架構寫的,所以裏頭的view請自動忽略它 )
### App.JS (主要用來掛載路由跟啟動酷酷的express
```javascript
// 掛上todo 路由
const express = require('express');
const bodyParser = require('body-parser');
const todoRoutes = require('./routes/todoRoutes'); // routes路由import
const app = express();
const port = 8080;
app.use('/todo', todoRoutes);
app.use('/member', mRoutes);
// 根路由重定向到 /todo
app.get('/', (req, res) => res.redirect('/todo'));
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
```
routes.js
❗❗❗❗❗請記得寫成restful API ( 不熟的人請洽組長
```javascript
const express = require('express');
const router = express.Router();
const {todoController} = require('../controllers/todoController');
// get所有 todos
router.get('/', todoController.showTodos);
// post一個新的 todo
router.post('/', todoController.createTodo);
// get一個特定的 todo 以便編輯
router.get('/:id/edit', todoController.editTodo);
// update一個特定的 todo
router.put('/:id', todoController.updateTodo);
// delete一個特定的 todo
router.delete('/:id', todoController.deleteTodo);
module.exports = router;//要記得export出去喔
```
Controller.js
```javascript
const express = require('express');
const {todoModel}= require('../models/todo');//把model require近來
const e = require('express');
const router = express.Router();
const todoController = {
showTodos:async (req, res) => {//記得要寫async await !!!!!
var todoItem = await todoModel.read();//這裡去呼叫model裡的read用法
todoItem=todoItem.map(item=>item.dataValues);;
res.render('index', {todoItem: todoItem});
//這裡由於本來是寫成MVC架構,這裡傳給EJS,正常這裡是要response回去Client端
},
//....後面方法我懶得放了(好多東西
};
exports.todoController = todoController;//要記得export出去喔
```
Model.js
```javascript
const { PrismaClient } = require('@prisma/client');//Prisma模型定義~~~
const prisma = new PrismaClient()
const todoModel = {//定義CRUD的用法~~~
//請記得用CRUD的用法,不會的話請洽Terry
create: async (title, isComplete) => {
return await prisma.todoitem.create({
});
},
read: async () => {
return await prisma.todoitem.findMany();
},
update: async (todoTableId, title, isComplete) => {
return await prisma.todoitem.update({
});
},
delete: async (todoTableId) => {
return await prisma.todoitem.delete({,
});
},
};
exports.todoModel = todoModel;//要記得export出去喔
```
# 結語
以上資訊為TerryXu的經驗,有寫錯誤的話請不吝嗇找我討論。
還沒補齊的知識點 :
* async await ( 🌟🌟🌟
* Prisma ( 🌟🌟
* axios (🌟🌟
* CRUD ( 🌟🌟