Давайте углубимся в обзор наших буткемпов
давайте сначала начнем, собрав все обзоры и обзоры по буткемпам
создайте models/Review.js
const mongoose = require('mongoose');
const ReviewSchema = new mongoose.Schema({
title: {
type: String,
trim: true,
required: [true, 'Please add a title for the review'],
maxlength: 100
},
text: {
type: String,
required: [true, 'Please add some text']
},
rating: {
type: Number,
min: 1,
max: 10,
required: [true, 'Please add a rating between 1 and 10']
},
createdAt: {
type: Date,
default: Date.now
},
bootcamp: {
type: mongoose.Schema.ObjectId,
ref: 'Bootcamp',
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
}
});
module.exports = mongoose.model('Review', ReviewSchema);
и controllers/reviews.js
const ErrorResponse = require('../utils/errorResponse');
const asyncHandler = require('../middleware/async');
const Review = require('../models/Review');
const Bootcamp = require('../models/Bootcamp');
// @desc Get reviews
// @route GET /api/v1/reviews
// @route GET /api/v1/bootcamps/:bootcampId/reviews
// @access Public
exports.getReviews = asyncHandler(async (req, res, next) => {
if (req.params.bootcampId) {
const reviews = await Review.find({ bootcamp: req.params.bootcampId });
return res.status(200).json({
success: true,
count: reviews.length,
data: reviews
});
} else {
res.status(200).json(res.advancedResults);
}
});
и в routes/reviews.js
const express = require('express');
const {
getReviews
} = require('../controllers/reviews');
const Review = require('../models/Review');
const router = express.Router({ mergeParams: true });
const advancedResults = require('../middleware/advancedResults');
const { protect, authorize } = require('../middleware/auth');
router
.route('/')
.get(
advancedResults(Review, {
path: 'bootcamp',
select: 'name description'
}),
getReviews
);
module.exports = router;
и обновите весь routes/bootcamps.js этим
const express = require('express');
const {
getBootcamps,
getBootcamp,
createBootcamp,
updateBootcamp,
deleteBootcamp,
getBootcampsInRadius,
bootcampPhotoUpload,
} = require('../controllers/bootcamps');
const Bootcamp = require('../models/Bootcamp');
const advancedResults = require('../middleware/advancedResults');
//include other resourse router
const courseRouter = require('./courses');
const reviewRouter = require('./reviews');
const router = express.Router();
const { protect,authorize } = require('../middleware/auth');
router.use('/:bootcampId/courses', courseRouter);
router.use('/:bootcampId/reviews', reviewRouter);
router.route('/radius/:zipcode/:distance').get(getBootcampsInRadius);
router
.route('/')
.get(advancedResults(Bootcamp, 'courses'), getBootcamps)
.post(protect,authorize('publisher','admin'), createBootcamp);
router.route('/:id/photo').put(protect, authorize('publisher','admin'),bootcampPhotoUpload);
router
.route('/:id')
.get(getBootcamp)
.put(protect, authorize('publisher','admin'),updateBootcamp)
.delete(protect, authorize('publisher','admin'),deleteBootcamp);
module.exports = router;
и включите маршруты в server.js.
получить единый обзор и обновить сеялку
включите это как reviews.json в seeder.js для заполнения базы данных
[
{
"_id": "5d7a514b5d2c12c7449be020",
"title": "Learned a ton!",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "8",
"bootcamp": "5d713995b721c3bb38c1f5d0",
"user": "5c8a1d5b0190b214360dc033"
},
{
"_id": "5d7a514b5d2c12c7449be021",
"title": "Great bootcamp",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "10",
"bootcamp": "5d713995b721c3bb38c1f5d0",
"user": "5c8a1d5b0190b214360dc034"
},
{
"_id": "5d7a514b5d2c12c7449be022",
"title": "Got me a developer job",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "7",
"bootcamp": "5d713a66ec8f2b88b8f830b8",
"user": "5c8a1d5b0190b214360dc035"
},
{
"_id": "5d7a514b5d2c12c7449be023",
"title": "Not that great",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "4",
"bootcamp": "5d713a66ec8f2b88b8f830b8",
"user": "5c8a1d5b0190b214360dc036"
},
{
"_id": "5d7a514b5d2c12c7449be024",
"title": "Great overall experience",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "7",
"bootcamp": "5d725a037b292f5f8ceff787",
"user": "5c8a1d5b0190b214360dc037"
},
{
"_id": "5d7a514b5d2c12c7449be025",
"title": "Not worth the money",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "5",
"bootcamp": "5d725a037b292f5f8ceff787",
"user": "5c8a1d5b0190b214360dc038"
},
{
"_id": "5d7a514b5d2c12c7449be026",
"title": "Best instructors",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "10",
"bootcamp": "5d725a1b7b292f5f8ceff788",
"user": "5c8a1d5b0190b214360dc039"
},
{
"_id": "5d7a514b5d2c12c7449be027",
"title": "Was worth the investment",
"text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec viverra feugiat mauris id viverra. Duis luctus ex sed facilisis ultrices. Curabitur scelerisque bibendum ligula, quis condimentum libero fermentum in. Aenean erat erat, aliquam in purus a, rhoncus hendrerit tellus. Donec accumsan justo in felis consequat sollicitudin. Fusce luctus mattis nunc vitae maximus. Curabitur semper felis eu magna laoreet scelerisque",
"rating": "7",
"bootcamp": "5d725a1b7b292f5f8ceff788",
"user": "5c8a1d5b0190b214360dc040"
}
]
добавьте этот фрагмент кода в controllers/reviews.js
// @desc Get single review // @route GET /api/v1/reviews/:id // @access Public exports.getReview = asyncHandler(async (req, res, next) => { const review = await Review.findById(req.params.id).populate({ path: 'bootcamp', select: 'name description' }); if (!review) { return next( new ErrorResponse(`No review found with the id of ${req.params.id}`, 404) ); } res.status(200).json({ success: true, data: review }); });
а также включить маршруты в routes/reviews.js.
router
.route('/:id')
.get(getReview);
теперь давайте напишем функцию для добавления отзыва
поскольку пользователь может оставить только 1 отзыв на любой буткемп, мы добавляем его в models/Review.js
// Prevent user from submitting more than one review per bootcamp
ReviewSchema.index({ bootcamp: 1, user: 1 }, { unique: true });
и добавьте этот фрагмент кода в conrollers/reviews.js.
// @desc Add review // @route POST /api/v1/bootcamps/:bootcampId/reviews // @access Private exports.addReview = asyncHandler(async (req, res, next) => { req.body.bootcamp = req.params.bootcampId; req.body.user = req.user.id; const bootcamp = await Bootcamp.findById(req.params.bootcampId); if (!bootcamp) { return next( new ErrorResponse( `No bootcamp with the id of ${req.params.bootcampId}`, 404 ) ); } const review = await Review.create(req.body); res.status(201).json({ success: true, data: review }); });
и обновите маршруты в routes/reviews.js.
router
.route('/')
.get(
advancedResults(Review, {
path: 'bootcamp',
select: 'name description'
}),
getReviews
)
.post(protect, authorize('user', 'admin'), addReview);
теперь нам нужно рассчитать средний рейтинг, данный буткемпу
перейдите в models/Review.js и добавьте этот код над module.exports = ….
// Call getAverageCost after save
ReviewSchema.post('save', async function() {
await this.constructor.getAverageRating(this.bootcamp);
});
// Call getAverageCost before remove
ReviewSchema.post('remove', async function() {
await this.constructor.getAverageRating(this.bootcamp);
});
теперь давайте напишем код для обновления и удаления отзывов
перейдите к controllers/reviews.js
// @desc Update review // @route PUT /api/v1/reviews/:id // @access Private exports.updateReview = asyncHandler(async (req, res, next) => { let review = await Review.findById(req.params.id); if (!review) { return next( new ErrorResponse(`No review with the id of ${req.params.id}`, 404) ); } // Make sure review belongs to user or user is admin if (review.user.toString() !== req.user.id && req.user.role !== 'admin') { return next(new ErrorResponse(`Not authorized to update review`, 401)); } review = await Review.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); res.status(200).json({ success: true, data: review }); }); // @desc Delete review // @route DELETE /api/v1/reviews/:id // @access Private exports.deleteReview = asyncHandler(async (req, res, next) => { const review = await Review.findById(req.params.id); if (!review) { return next( new ErrorResponse(`No review with the id of ${req.params.id}`, 404) ); } // Make sure review belongs to user or user is admin if (review.user.toString() !== req.user.id && req.user.role !== 'admin') { return next(new ErrorResponse(`Not authorized to update review`, 401)); } await review.remove(); res.status(200).json({ success: true, data: {} }); });
и обновите весь код routes/reviews.js этим
const express = require('express');
const {
getReviews,
getReview,
addReview,
updateReview,
deleteReview
} = require('../controllers/reviews');
const Review = require('../models/Review');
const router = express.Router({ mergeParams: true });
const advancedResults = require('../middleware/advancedResults');
const { protect, authorize } = require('../middleware/auth');
router
.route('/')
.get(
advancedResults(Review, {
path: 'bootcamp',
select: 'name description'
}),
getReviews
)
.post(protect, authorize('user', 'admin'), addReview);
router
.route('/:id')
.get(getReview)
.put(protect, authorize('user', 'admin'), updateReview)
.delete(protect, authorize('user', 'admin'), deleteReview);;
module.exports = router;
формат отправки почтового запроса - переходить по таким маршрутам
{{URL}}/api/v1/bootcamps/5f7cb8d814a9866e474b4940/reviews
и сделайте почтовый запрос в таком формате
{
"title":"great bcdootcamp",
"text":"i learnexcxcd a lot",
"rating":10
}
так что да, это мы сделали crud для обзоров