YelpCamp4-Review

增加评论模块

  • Review Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const reviewSchema = new Schema({
    body: String,
    rating: Number
});

module.exports = mongoose.model('Review',reviewSchema);

与campground连接(在campgroundSchema加入)

reviews:[{
        type:Schema.Types.ObjectId,
        ref:'Review'
    }]
  • show.ejs 增加review 模块(有client-side validation)
  • (记得这个validation在boilerplate里还有验证(script),且form-class名相同)
<form action="/campgrounds/<%=campground._id%>/reviews" method="post" class="mb-3 validated-form" novalidate>
            <div class="mb-3">
                <label class="form-label" for="rating">Rating</label>
                <input class="form-range" type="range" name="review[rating]" id="rating" min="1" max="5">
            </div>
            <div class="mb-3">
                <label class="form-label" for="body">Review</label>
                <textarea class="form-control" name="review[body]" id="body" cols="30" rows="3" required></textarea>
            </div>
            <button class="btn btn-success">Submit</button>
        </form>

+Joi验证

  • Schema.js:
module.exports.reviewSchema = Joi.object({
    review: Joi.object({
        rating: Joi.number().required().min(1).max(5),
        body: Joi.string().required()
    }).required()
})
  • app.js
const { campgroundSchema, reviewSchema } = require('./schemas.js');//joi

const validateReview = (req,res,next) => {
    const {error} = reviewSchema.validate(req.body);
    if (error) {
        const msg = error.details.map(el => el.message).join(',');
        throw new ExpressError(msg, 400);
    } else {
        next();
    }
}

app.post('/campgrounds/:id/reviews', validateReview , async (req,res) => {
    const campground = await Campground.findById(req.params.id);
    const review = new Review(req.body.review);
    campground.reviews.push(review);
    await review.save();
    await campground.save();
    res.redirect(`/campgrounds/${campground._id}`);
})

Show Reviews

app.js 在show details里的 findById 加populate(‘reviews’),才能传入所有数据

//Show Detail
app.get('/campgrounds/:id', catchAsync( async (req,res) => {
    const campground = await Campground.findById(req.params.id).populate('reviews');
    res.render('campgrounds/show',{campground});
}));

show.ejs:
(两个‘div class=“col-6”’ 可以把布局分两半)

<div class="col-6">
    <h2>Leave a review</h2>
    <form action="/campgrounds/<%=campground._id%>/reviews" method="post" class="mb-3 validated-form" novalidate>
        <div class="mb-3">
            <label class="form-label" for="rating">Rating</label>
            <input class="form-range" type="range" name="review[rating]" id="rating" min="1" max="5">
        </div>
        <div class="mb-3">
            <label class="form-label" for="body">Review</label>
            <textarea class="form-control" name="review[body]" id="body" cols="30" rows="3" required></textarea>
        </div>
        <button class="btn btn-success">Submit</button>
    </form>
    
    <% for (let review of campground.reviews) {%>
        <div class="card mb-3">
            <div class="card-body">
                <h5 class="card-title">
                    Rating: <%= review.rating %>
                </h5>
                <p class="card-text">
                    Review: <%= review.body %>
                </p>
            </div>
        </div>
    
        <% } %>
    </div>

删除Review

show.ejs:

 <form action="/campgrounds/<%=campground._id%>/reviews/<%=review._id%>?_method=DELETE" method="post">
                    <button class="btn btn-sm btn-danger">DELETE</button>
                </form>

app.js:
这样删除review时 campground中的review索引也会被删除

//Delete Review
app.delete('/campgrounds/:id/reviews/:reviewId', catchAsync(async (req,res) => {
    //res.send('Deleting!!')
    const { id, reviewId } = req.params;
    await Campground.findByIdAndUpdate(id, { $pull: { reviews: reviewId } }); 
    //$pull:Mongo操作删除(先从campground中删除索引)
    await Review.findByIdAndDelete(reviewId);//再从review中删除整个
    res.redirect(`/campgrounds/${id}`);

}));
  • 但是现在如果直接删除campgroud, 与之相关的review不会被删除
  • 所以加一个中间件,当findByIdAndDelete被触发时,中间件会删除相对应的review
  • 与findByIdAndDelete向对应的中间件为findOneAndDelete()(mongoose文档找到):
CampgroundSchema.post('findOneAndDelete', async (doc) => {
    if(doc){
        await review.deleteMany({
            _id:{
                $in: doc.reviews
            }
        })
    }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值