Khi delete dữ liệu, thường dùng kỹ thuật Soft Delete, bao gồm:
- Delete (soft): xóa mềm
- Restore: khôi phục
- Force delete: xóa vĩnh viễn
1. Sử dụng thư viện plugin delete cho mongoose
-- mongoose-delete: https://www.npmjs.com/package/mongoose-delete
1.1. Cài đặt:
1 |
npm install mongoose-delete |
1.2. Cấu hình Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
const mongoose = require('mongoose'); const slug = require('mongoose-slug-generator'); const mongooseDelete = require('mongoose-delete'); const Schema = mongoose.Schema; const Course = new Schema( { name: {type: String,require: true,default: '',minlength: 1,maxlength: 255,}, description: { type: String }, image: { type: String }, videoID: { type: String, require: true }, level: { type: String }, slug: { type: String, slug: 'name', unique: true }, }, { timestamps: true, }, ); //Add plugins mongoose.plugin(slug); Course.plugin(mongooseDelete, { deletedAt: true, overrideMethods: 'all', }); // module.exports = mongoose.model('ModelName', mySchema) module.exports = mongoose.model('Course', Course); |
2. Router
-- Tại ./src/routes/courses.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
const express = require('express'); const router = express.Router(); const courseController = require('../app/controllers/CourseController'); router.get('/create', courseController.create); router.post('/store', courseController.store); router.get('/:id/edit', courseController.edit); router.put('/:id', courseController.update); //URL xóa mềm, khôi phục,và xóa vĩnh viễn khóa học: xxx123.com/course/:id router.delete('/:id', courseController.destroy); router.patch('/:id/restore', courseController.restore); router.delete('/:id/force', courseController.forceDestroy); router.get('/trash', courseController.trash); router.get('/:slug', courseController.show); router.get('/', courseController.index); module.exports = router; |
3. Controller
-- Tại .src/app/controllers/CourseController.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
const Course = require('../models/Course'); class CourseController { //--[GET]-- /courses/course-list -> Gửi thêm biến "deletedCount" courseList(req,res,next){ Promise.all([Course.find({}), Course.countDocumentsDeleted()]) .then(([courses, deletedCount]) => res.render('courses/course-list', { deletedCount, courses: multipleMongooseToObject(courses), }) ) .catch(next); } show(req,res,next){} create(req, res, next) {} store(req, res, next) { edit(req, res, next) {} update(req, res, next){} //--[DELETE]-- /courses/:id destroy(req, res, next) { Course.delete({ _id: req.params.id }) .then(() => res.redirect('back')) .catch(next); } //--[DELETE]-- /courses/:id/force forceDestroy(req, res, next) { Course.deleteOne({ _id: req.params.id }) .then(() => res.redirect('back')) .catch(next); } //--[POST]-- /courses/handle-form-action -> delete nhiều item handleFormAction(req, res, next) { switch (req.body.action) { case 'delete': Course.delete({ _id: {$in: req.body.courseIds} }) .then(() => res.redirect('back')) .catch(next); break; default: res.json({ message: 'Action is invalid' }); } } //--[PATCH]-- /courses/:id/restore restore(req, res, next) { Course.restore({ _id: req.params.id }) .then(() => res.redirect('back')) .catch(next); } //--[GET]-- /courses/trash trash(req, res, next) { Course.findDeleted({}) .then((courses) => res.render('courses/course-list-trash', { courses: multipleMongooseToObject(courses), }), ) .catch(next); } } |
3. View
-- Các view có sử dụng: Boostrap 5 (Modal, Table), Jquery
3.1Tại .src/resources/views/courses/course-list.hbs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<div class="mt4"> <h2>Danh sách khóa học</h2> <a href="/courses/create" type="button" class="btn btn-primary">Đăng khóa học mới</a> <a href="/courses/trash" type="button" class="btn btn-secondary">Thùng rác ({{deletedCount}})</a> </div> <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Tên khóa học</th> <th scope="col">Trình độ</th> <th scope="col">Thời gian tạo</th> <th scope="col">Action</th> </tr> </thead> <tbody> {{#each courses}} <tr> <th scope="row">{{sum @index 1}}</th> <td>{{this.name}}</td> <td>{{this.level}}</td> <td>{{this.createdAt}}</td> <td> <a href="/courses/{{this._id}}/edit" class="btn btn-link">Sửa</a> <a href="" class="btn btn-link" data-bs-toggle="modal" data-id="{{this._id}}" data-bs-target="#delete-course-modal">Xóa</a> </td> </tr> {{else}} <tr> <td colspan="5" class="text-center"> Danh sách trống <a href="/courses/create" type="button" class="btn btn-primary">Đăng khóa học mới</a> </td> </tr> {{/each}} </tbody> </table> {{!-- confirm delete course --}} <div class="modal" tabindex="-1" id="delete-course-modal"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Xóa khóa học ?</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p>Bạn chắc chắn muốn xóa khóa học này ?</p> </div> <div class="modal-footer"> <button id="btn-delete-course" type="button" class="btn btn-danger">Xóa bỏ</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Hủy</button> </div> </div> </div> </div> {{!-- Delete hiden form --}} <form method="POST" name="action-course-form"></form> <script> let courseDeleteID; let modal = document.getElementById('delete-course-modal'); let actionForm = document.forms['action-course-form']; modal.addEventListener('show.bs.modal', function (event) { let button = event.relatedTarget; courseDeleteID = button.getAttribute('data-id'); }) $("#btn-delete-course").click(function () { actionForm.action = '/courses/' + courseDeleteID + '?_method=DELETE'; actionForm.submit(); }) </script> |
3.1Tại .src/resources/views/courses/course-list-trash.hbs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
<div class="mt-4"> <div> <a href="/courses">Danh sách khóa học</a> <h3>Khóa học đã xóa</h3> </div> <table class="table"> <thead> <tr> <th scope="col">#</th> <th scope="col">Tên khóa học</th> <th scope="col">Trình độ</th> <th scope="col">Thời gian xóa</th> <th scope="col">Action</th> </tr> </thead> <tbody> {{#each courses}} <tr> <th scope="row">{{sum @index 1}}</th> <td>{{this.name}}</td> <td>{{this.level}}</td> <td>{{this.deletedAt}}</td> <td> <a href="" class="btn btn-link btn-restore" data-id="{{this._id}}">Khôi phục</a> <a href="" class="btn btn-link" data-bs-toggle="modal" data-id="{{this._id}}" data-bs-target="#delete-course-modal">Xóa vĩnh viễn</a> </td> </tr> {{else}} <tr> <td colspan="5" class="text-center"> Thùng rác trống <a href="/me/stored/courses">Danh sách khóa học</a> </td> </tr> {{/each}} </tbody> </table> </div> {{!-- confirm delete course --}} <div class="modal" tabindex="-1" id="delete-course-modal"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">Xóa khóa học ?</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p>Hành động này không thể khôi phục. Bạn vẫn muốn xóa khóa học này ?</p> </div> <div class="modal-footer"> <button id="btn-delete-course" type="button" class="btn btn-danger">Xóa vĩnh viễn</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Hủy</button> </div> </div> </div> </div> <form method="POST" name="action-course-form"></form> <script> let courseDeleteID; let modal = document.getElementById('delete-course-modal'); let actionForm = document.forms['action-course-form']; modal.addEventListener('show.bs.modal', function (event) { let button = event.relatedTarget; courseDeleteID = button.getAttribute('data-id'); }) $('#btn-delete-course').click(function (e) { e.preventDefault(); actionForm.action = '/courses/' + courseDeleteID + '/force?_method=DELETE'; actionForm.submit(); }) $('.btn-restore').click(function (e) { e.preventDefault(); let id = $(this).data('id') actionForm.action = '/courses/' + id + '/restore?_method=PATCH'; actionForm.submit(); }) </script> |