iT邦幫忙

2022 iThome 鐵人賽

DAY 15
2

Day15 自己做一個價值幾十萬的動態網站

第十五課:完成需要授權前的hotel room user Api part1

前一天我們完成了CRUD 讀取、更新、修改、刪除資料的基本瞭解,也了解了next()如何搭配應用客製化我們想要resopnse的訊息,今天我們將繼續完成hotel與room的Api創建。

完成CRUD應用在住宿與房間

房間Api為創建在住宿之後,為各個hotel住宿地點的房間,提供給住客選擇房型,所以為附屬在hotel資料庫的子資料,所以要來實作Room子資料api練習,所以首先一樣先完成好hotel Api中這個階段的最後一個Api,抓取所有hotel資料與id來看我們有哪些hotel,之後room可以串接在之下。

完成hotels Api,抓取目前所有住宿資料實作

Get all hotels Api Method抓取所有資料實作
所以現在流程會是在routesControllder中寫好export 的函數並在ApiRoutes中導入,最後在insomnia啊測試看看有沒有建立成功

export const getAllHotels = async(req,res,next)=>{
    try{
        const hotelsList = await Hotel.find()
        res.status(200).json(hotelsList)
    }catch(error){
        next(errorMessage(500,"無法抓取所有飯店資料",error)) 
    }
}



這邊我先準備了之前的data.js資料可以從這邊抓取,讓你可以省時間先多上傳幾筆資料,然後查看是否都上傳成功,下列Url可以直接複製貼上瀏覽器上,或是使用insomnia一樣的get method來抓取(伺服器連結將在2022/年11月結束,heroku將不支援免費伺服器專案,非常的難過)
https://samkochallenge.herokuapp.com/api/v1/hotels

url連接 可以一個一個複製並貼上在insomnia創建這些飯店,那這邊就簡單完成了

room Api的CURD應用 NoSql類創建資料並串接

接下來就是住宿房間的所有資料創建,所以一樣我們先從創建第一筆資料開始,所以這邊跟之前簡單的創建hotel不同的是,我們這次要創建在hotel資料底下,所以我們許多操作都先拿到想要創建的那個hotel_id,並有像是前面get method,先抓取到資料後再進行其他操作。

app.use("/api/v1/hotels",hotelsApiRoute)
app.use("/api/v1/rooms",roomsApiRoute) <---現在要操作的
app.use("/api/v1/users",usersApiRoute)
app.use("/api/v1/auth",authApiRoute)

所以先一樣進到ApiRoute中 我們將操作room.js內容

先一樣在ApiRoute room.js導入下方。

//前面的url是/api/v1/rooms
//創建第一個room 
router.post("/:hotelid",createRoom);
//更改room updatedRoom
router.put("/:id",updatedRoom)
//刪除room
router.delete("/:hotelid/:id",deleteRoom)
//讀取單筆room 資料 不用hotelid
//是因為會多此一舉roomid來抓
router.get("/:id",getRoom)
//抓取rooms所有資料
router.get("/",getAllRooms)
//抓取一個hotel 的rooms所有資料
router.get("/findHotel/:hotelid/",getHotelRooms)

雙重try{}catch{}與$push方式


先從最難的開始 我們將創建第一筆Room資料,順便複習我們一開始有創立好的room models,一樣要依照它的格式上傳創建

export const createRoom = async (req, res, next) => {
    const hotelId = req.params.hotelid;
    const newRoom = new Room(req.body)
    try {
        const saveRoom = await newRoom.save()
        try{
            await Hotel.findByIdAndUpdate(hotelId,
                {$push: { rooms: saveRoom._id}})
        }catch(error) {
            next(errorMessage(500,"找不到hotel id 無法上傳room更新",error)) 
        }
        res.status(200).json(saveRoom)
    } catch (error) {
        next(errorMessage(500,"room的上傳失敗,可能為格式錯誤",error)) 
    }
}


這邊try&catch的上下層關係,可以花時間探究一下,有牽扯到兩個資料庫,與Api的前後關係,所以使用try&catch加上await的用法,等於是用兩層類同步函數,讓他展現不同步函數的特性,但又有前後的關係。

MongoDB的資料串接法 $push $pull



把room.id推進去hotel資料裡面,與把room.id從hotel資料裡面拉出來

使用insomnia Api創建房間資料測試

這邊我們打好上述Api以實際操作將會更明白整個流程,在room Api 中我們知道我們到時候都需要hotelid 跟roomId,所以在insomnia實作前,我們也先打好原本的get room資料的Api,因為他跟gethotel一樣,所以相對簡單,並先做的原因是因為我們都要靠他來抓roomId,到時候才能更新、刪除等等的操作,所以順序是先create後get。
抓取單筆房間資料,長相基本上跟前面的gethotel一樣。
router.get("/:id",getRoom)

export const getRoom = async (req, res, next) => {
    try{
        const getRoom= await Room.findById(req.params.id)
        res.status(200).json(getRoom)
    } catch (error) {
        next(errorMessage(500,"搜尋失敗,找不到其ID",error)) 
    }
}

好了後我們創建insomnia Api folder裡面的roomApi
如圖:create room needed hotelid 等等的


測資範例如下:

{
	"title":"家庭套房 七季公寓酒店",
	"desc":"兩張雙人大床 獨立衛浴",
	"price":3600,
	"maxPeople":4,
	"roomNumbers":[{"number":401},{"number":402}]
}

並我們會在這邊先多創建好更多room資料,方便到時候測試,並上述可以看到我們有預留一欄是unavailableDates是到時候要放預訂時間的。
測資資料連接,房間在創建時可以對應到自己各自飯店,並創建完後我們可以使用get rooms全部 來觀看全部創建好的房間。
與所有住宿的房間資料,抓了全部房間資料只是看一下創建的情況如合,所以下方當然後在特別介紹抓取特定hotel的全部房間最重要的讀取api功能

export const getAllRooms = async (req, res, next) => {
    try{
        const getRooms= await Room.find()
        res.status(200).json(getRooms)
    } catch (error) {
        next(errorMessage(500,"搜尋失敗,為資料庫變動問題",error)) 
    }
}

用getHotelRooms的roomList來認識Promise.all()的用法

抓取特定hotel住宿的所有房間,需要hotelId 並有filter的感覺

export const getHotelRooms = async (req, res, next) => {
    const gethotel = req.params.hotelid;
    try{
        const hoteldata = await Hotel.findById(gethotel)
        try{
            const roomsList = await Promise.all(hoteldata.rooms.map(roomId=>{
            return Room.findById(roomId)})) //是利用Room資料庫裡面所有的rooms裡面
            res.status(200).json(roomsList)  //找尋現在在hoteldata的那些符合id的room 全部資料叫出來
        } catch(error){
            next(errorMessage(500,"發生錯誤,找尋房間時發生錯誤,可能為Room資料庫問題",error)) 
        }
    } catch (error) {
        next(errorMessage(500,"找不到hotel id 無法查看rooms",error)) 
    }
}

這邊我們之前已經講解了async 非同步函數的意思,我們是配搭await讓他變成同步函數,而這邊突然出現的promise其實就是原本非同步函數的用法,這邊可以不用搞的太複雜,白話文解釋就是這裡面Promise出來的意義是,希望能我們這邊return的一堆東西能夠最後組成一個團結array,然後再讓await去表示說全部人都要先等Promise.all的組成,最後在宣告這個array為roomsList。所以這邊Promise.all很常會跟map一起使用,因為map專門把array的東西排列出來,而Promise.all又會把它重組起來,就很適合我們要filter一些特定分類或是產品。

並完成最後的房間資料更新,不需要hotelid,基本上也跟hotel.js的更新法一樣

export const updatedRoom = async (req, res, next) => {
    const roomId = req.params.id;
    try{
        const updatedRoom = await Room.findByIdAndUpdate(roomId,{$set:req.body}
            ,{new:true})
        res.status(200).json(updatedRoom)
    } catch (error) {
        next(errorMessage(500,"room的更新失敗,可能為格式錯誤或是找不到其ID",error)) 
    }
}

最後需要特別講的就是房間資料刪除,需要hotelid因為也要刪除在hotel資料裡面的rooms Array,用到的也會是上面提到的$pull函數,所以首先會先把Room資料庫裡面該房型的完整資料先刪除,再將hotel裡面的rooms有我們要的roomid資料刪除即可。

export const deleteRoom = async (req, res, next) => {
   const hotelId = req.params.hotelid;
    try{
        await Room.findByIdAndDelete(req.params.id)
        try{//updated hotel
            await Hotel.findByIdAndUpdate(hotelId,
                {$pull: { room: req.params.id}})
        }catch(error) {
            next(error)
        }
        res.status(200).json("成功刪除房間資訊")
    } catch (error) {
        next(errorMessage(500,"刪除失敗,找不到其ID",error)) 
    }
}

所以這邊就跟創建Room一樣,創建Room創建了兩個地方,一個Room的資料庫,一個Hotel資料裡面記錄的房型id,所以刪除也要刪除這兩個地方即可。

這邊就簡單地完成全部的roomApi了,就容易出現問題的可能是insomnia的創建與輸入有時候要搭配著看,並確認都有更新與mongoDB保持連線
最後附上github.15day挑戰連結

結論

這邊開始都會有版本控制的連結,測試資料在上傳時,大家可能要比較累一點手訂一個一個的上傳,要先測資完hotel在測試room的部分,與今天是鐵人挑戰的一半,也感謝目前願意看到第15天的你們,以圖文去表示程式是有些難度,表達上與理解上很容易詞不達意,也常常因為時間的關係,與每篇的強度,在設想如何表達會比較好,但我後來的心得是要先說服自己能看得下去跟看得懂,這邊以後忘記了,我也可以感謝自己願意花那麼多時間,來整理這份專案,並也很完整的自己完成了一份心得紀錄與軟體的每一步都變得有意義,對我來說這樣在開發時,也會不斷地幫助自己記憶與釐清成長,所以如果之後還有時間,我也會想保留這樣的學習心得,可能往更大的yt平台前進,或是以部落格的方式來開創新的一個專題,而下一個目前最有興趣並想實作出來的就是Dcard,想挑戰能不能復刻與致敬Dcard這個軟體。


上一篇
「全端挑戰」 (req, res, next) next()等多種用法 ,Api串接CRUD的練習與熟悉 part2
下一篇
「全端挑戰」User Api CRUD 與條件子句設置,bcrypt.js加密使用者密碼過程
系列文
自己做一個價值幾十萬的動態網站,學會Mern開發、前台UI設計各式觀念與各式Lib、typescript你該學會的前端技術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言