Trường hợp sử dụng của insert
là khi chúng ta tạo một danh mục mới trên giao diện quản lý blog. Lúc này biểu mẫu nhập liệu <form>
có chứa các thành phần như ô nhập tên name
, ô nhập các từ khóa liên quan keywords
, khung soạn thảo nội dung markdown
. Các dữ liệu thu được ở đây sẽ được gửi tới một endpoint
đặc định để yêu cầu thêm bản ghi mới vào database
.
Code xử lý của route
tương ứng sẽ sử dụng class Category
để tạo ra một object
mô tả bản ghi mới và gọi thủ tục insert-category
qua manager
. Như vậy là chúng ta chỉ còn thiếu một trị số id
mới – được quản lý bởi code database
là hoàn thiện đủ nội dung của một bản ghi. Tham số out_
ở đây là tùy chọn để code bên ngoài có thể gắn một object Category
bất kỳ và theo dõi kết quả thực hiện insert
. Như vậy chúng ta sẽ có các tác vụ chia nhỏ cần xử lý là –
/* generate new record's id */
-tạo ra một giá trịid
mới/* populate output */
– ghi dữ liệu hoàn chỉnh của bản ghi mới vàoout_inserted
/* write to data folder */
– ghi dữ liệu của bản ghi mới vào thư mụcdata
const Category =require("../../type/Category");const generateNewRecordId =require("./sub-procedure/generate-new-record-id--async-throw");const writeRecordToDataFolder =require("./sub-procedure/write-record-to-data-folder--async-throw");
module.exports =async(
in_submitted =newCategory(),
out_inserted =newCategory())=>{try{/* --- generate new record's id */var generated ={ recordId:null};awaitgenerateNewRecordId(generated);/* --- populate output */
Category.clone(in_submitted, out_inserted);
out_inserted.set("@id", generated.recordId);/* --- write to data folder */awaitwriteRecordToDataFolder(out_inserted);}catch(error){throw error;}};// module.exports
Đối với tác vụ đầu tiên, việc tạo ra một id
mới sẽ được thực hiện bằng cách tách lấy giá trị id
của bản ghi cuối cùng và tăng lên một đơn vị. Kết quả được trả về qua một object
bất kỳ có thuộc tính recordId
.
const readAllRecordFolderNames =require("./read-all-record-folder-names--async-throw");
module.exports=async(out_generated ={ recordId:null})=>{try{/* --- collect all record folder names */var allRecordFolderNames =[];awaitreadAllRecordFolderNames(allRecordFolderNames);/* --- get the latest id */var latestRecordFolderName = allRecordFolderNames.pop();var[ _, __, latestRecordId ]= latestRecordFolderName.match(/(id-)(d+)/);/* --- generate new id */var newRecordIdNumber = Number.parstInt(latestRecordId)+1;
out_generated.recordId =String(newRecordIdNumber).padStart(2,"0");}catch(error){throw error;}};// module.exports
Do chúng ta đã sử dụng các trị số id
để làm tên thư mục dữ liệu của các bản ghi, vì vậy nên thao tác tách lấy các giá trị id
có thể dừng ở bước đọc tên của các thư mục và chưa cần mở các tệp header.json
bên trong. Ở đây chúng ta chỉ cần lưu ý về dạng của trị số id
khi ghi vào database
hoặc gửi cho trình duyệt web cần phải đồng nhất là dạng chuỗi string
có hai chữ số.
Thao tác đọc và thu thập tên thư mục của tất cả các bản ghi được tách thành một sub-procedure
để có thể sử dụng lại cho các procedure
khác – và được hỗ trợ xử lý bởi module File System
.
const fsPromises =require("fs/promises");const path =require("path");
module.exports=async(out_allFolderNames =[])=>{try{var pathToCategoryDataFolder = path.join(__dirname,"../../../data/Category");var dir =await fsPromises.opendir(pathToCategoryDataFolder);forawait(var dirEnt of dir){
out_allFolderNames.push(dirEnt.name);}// for await}catch(error){throw error;}};// module.exports
Bây giờ thì chúng ta đã có thể viết một vài dòng trong test.js
để kiểm tra hoạt động của các sub-procedure
hỗ trợ tạo id
mới. Đầu tiên chúng ta sẽ kiểm tra thao tác đọc tên của tất cả các thư mục bản ghi category
.
const readAllRecordFolderNames=require("./database/procedure/Category/sub-procedure/read-all-record-folder-names--async-throw");voidasyncfunction(){var allFolderNames =[];awaitreadAllRecordFolderNames(allFolderNames);for(var folderName of allFolderNames){
console.log(folderName);}}()// void
npm test
id-00
id-01
id-02
Vậy giá trị id
tiếp theo sẽ được tạo ra là "03"
.
const generateNewRecordId =require("./database/procedure/category/sub-procedure/generate-new-record-id--async-throw");voidasyncfunction(){var newId ={ recordId:null};awaitgenerateNewRecordId(newId);
console.log(newId);}();// void
npm test
{ recordId: '03' }
Sau khi đã có được id
mới thì thao tác tiếp theo mà chúng ta cần xử lý là tạo ra một object
mô tả một bản ghi category
hoàn chỉnh để ghi vào thư mục data
và đồng thời là để dùng làm kết quả trả về cho code gọi thủ tục insert
.
/* requires ... */
module.exports =async(
in_submitted =newCategory(),
out_inserted =newCategory())=>{try{/* generate new record's id ... (done) *//* populate output */
Category.clone(in_submitted, out_inserted);
out_inserted.set("@id", generated.recordId);/* write to data folder ... */}catch(error){throw error;}};// module.exports
Công việc cần thực hiện ở thao tác này thì về cơ bản chỉ là sao chép nội dung từ object
được gửi đến in_submitted
vào object
kết quả out_inserted
và sau đó gắn thêm trị số id
vừa mới được tạo ra ở bước trước đó. Ở đây chúng ta sẽ ủy thác việc sao chép nội dung từ in_submitted
vào out_inserted
cho một phương thức static
của class Category
.
const Category =classextends Map {/* ... */staticclone(
in_source =newCategory(),
out_target =newCategory()){var categoryEntries =[...in_source ];for(var entry of categoryEntries){var[key, value]= entry;
out_target.set(key, value);}return Category;}};// Category
Sau đó viết code kiểm tra kết quả vận hành của phương thức Category.copy()
.
const Category =require("./database/type/Category");var source =newCategory();
source.set("@id","Infinity").set("name","webdev").set("keywords",["tutorial","web"]).set("markdown","Looonggg... content...");var target =newCategory();
Category.clone(source, target);
console.log(target);
npm test
Category(4) [Map] {
'@id' => 'Infinity',
'name' => 'webdev',
'keywords' => [ 'tutorial', 'web' ],
'markdown' => 'Looonggg... content...'
}
Tiếp theo là tiến hành ghi dữ liệu của bản ghi mới vào thư mục data
.
/* requires ... */
module.exports =async(
in_submitted =newCategory(),
out_inserted =newCategory())=>{try{/* generate new record's id ... (done) *//* populate output ... (done) *//* write to data folder */awaitwriteRecordToDataFolder(out_inserted);}catch(error){throw error;}};// module.exports
Nội dung chính của thao tác này gồm các bước là – tạo ra một thư mục cho bản ghi mới, và ghi dữ liệu vào các tệp header.json
và content.md
.
const path =require("path");const fsPromises =require("fs/promises");const Category =require("../../../type/Category");const writeRecordHeaderToFile =require("./write-record-header-to-file--async-throw");const writeRecordContentToFile =require("./write-record-content-to-file--async-throw");
module.exports =async(
in_record =newCategory())=>{try{/* prepare path to record's data folder */var categoryFolderPath = path.join(__dirname,"../../../data/Category");var recordFolderName ="id-"+ in_record.get("@id");var recordFolderPath = path.join(categoryFolderPath, recordFolderName);/* create folder for new record */await fsPromises.mkdir(recordFolderPath);/* write record's data to files */awaitwriteRecordHeaderToFile(in_record, recordFolderPath);awaitwriteRecordContentToFile(in_record, recordFolderPath);}catch(error){throw error;}};// module.exports
Thao tác ghi dữ liệu vào các tệp header.json
và content.md
sẽ cần thêm một chút sự chuẩn bị và có thể khiến code của sub-procedure
này bị rối; Do đó chúng ta sẽ ủy thác tới các sub-procedure
tương ứng với mỗi kiểu tệp cần ghi dữ liệu.
const path =require("path");const fsPromises =require("fs/promises");const Category =require("../../../type/Category");
module.exports =async(
in_record =newCategory(),
in_recordFolderPath ="...")=>{try{var headerFilePath = path.join(in_recordFolderPath,"header.json");var headerJSON ={};
Category.populateHeaderJSON(in_record, headerJSON);var headerText =JSON.stringify(headerJSON);await fsPromises.writeFile(headerFilePath, headerText);}catch(error){throw error;}};// module.exports
const path =require("path");const fsPromises =require("fs/promises");const Category =require("../../../type/Category");
module.exports =async(
in_record =newCategory(),
in_recordFolderPath ="./")=>{try{var contentFilePath = path.join(in_recordFolderPath,"content.md");var markdownText = in_record.get("markdown");await fsPromises.writeFile(contentFilePath, markdownText);}catch(error){throw error;}};// module.exports
Thao tác ghi nội dung cho tệp content.md
thực sự không có gì đặc biệt ngoài việc chuẩn bị đường dẫn tới tệp cần ghi. Tuy nhiên thao tác ghi nội dung cho header.json
thì chúng ta cần chuyển phần header
trong object
bản ghi thành một chuỗi JSON. Và ở bước này thì chúng ta sẽ lại ủy thác cho một phương thức static
của class Category
.
const Category =classextends Map {/* ... */staticpopulateHeaderJSON(
in_source =newCategory(),
out_headerJSON ={}){var allEntries =[...in_source ];var headerEntries = allEntries.slice(0,-1);for(var entry of headerEntries){var[key, value]= entry;
out_headerJSON[key]= value;}return Category;}};// Category
module.exports = Category;
Bây giờ thì chúng ta đã có thể viết code để chạy thử các sub-procedure
và thủ tục chính insert
. Chúng ta sẽ xuất phát từ phương thức Category.populateHeaderJSON()
–
const Category =require("./database/type/Category");var record =newCategory()
record.set("@id","Infinity").set("name","webdev").set("keywords",["tutorial","web"]).set("markdown","Looonggg... content...");var headerJSON ={};
Category.populateHeaderJSON(record, headerJSON);
console.log(headerJSON);
npm test
{ '@id': 'Infinity', name: 'webdev', keywords: [ 'tutorial', 'web' ] }
Trong kết quả in ra chúng ta đã thấy trường dữ liệu markdown
không được sao chép sang object headerJSON
. Như vậy là kết quả ghi vào tệp header.json
cũng đã được đảm bảo. Bây giờ chúng ta sẽ thử insert
luôn một bản ghi category
mới.
const Category =require("./database/type/Category");const databaseManager =require("./database/manager");voidasyncfunction(){var newRecord =newCategory();
newRecord.set("@id","Infinity").set("name","webdev").set("keywords",["tutorial","web"]).set("markdown","Looonggg... content...");var inserted =newCategory();await databaseManager.execute("insert-category", newRecord, inserted);
console.log(inserted);}();// void
npm test
Category(4) [Map] {
'@id' => '03',
'name' => 'webdev',
'keywords' => [ 'tutorial', 'web' ],
'markdown' => 'Looonggg... content...'
}
Phù… như vậy là chúng ta đã thực hiện được thủ tục insert
một bản ghi category
mới vào database
. Và trong bài viết tiếp theo, chúng ta sẽ tiếp tục viết code cho thủ tục select
.
Nguồn: viblo.asia