Khi bắt đầu làm việc với Umbraco, một trong những câu hỏi đầu tiên là: tại sao phải tạo Document Type trước khi tạo nội dung? Content Type khác gì Document Type? Media Type có liên quan gì đến ảnh bìa sách? Và nếu tạo thêm một node trong Backoffice thì giao diện website có tự thay đổi không?
Trong bài viết này, chúng ta sẽ tìm hiểu các khái niệm đó bằng cách xây dựng một ví dụ nhỏ: website bán sách chuyên về lịch sử và triết học.
Thay vì học lý thuyết một cách riêng lẻ, chúng ta sẽ thực hành trực tiếp trên Umbraco 13, tạo Document Type, xây dựng Content Tree, thêm sách, upload ảnh bìa và quan sát sự thay đổi ở frontend.
Bài viết sử dụng các khái niệm trong Umbraco 13 và có hai hướng triển khai:
- Traditional Umbraco: Umbraco sử dụng Razor Template để render HTML.
- Headless Umbraco: Umbraco quản lý nội dung, API cung cấp dữ liệu và Next.js render giao diện.
Hai hướng này có nhiều khái niệm CMS giống nhau, nhưng cách hiển thị frontend khác nhau.
1. Content Tree là gì?
Content Tree là cây nội dung trong khu vực Content của Umbraco Backoffice. Nó giúp người biên tập tổ chức nội dung theo cấu trúc cha và con.
Hãy tưởng tượng chúng ta đang xây dựng website bán sách lịch sử và triết học với cấu trúc như sau:
Đây là một ví dụ về cách tổ chức nội dung. Trong một dự án thực tế, bạn có thể chọn cấu trúc khác tùy cách website quản lý danh mục và sản phẩm.
Mỗi node trong Content Tree đại diện cho một Content item. Node có tên, loại nội dung, các property và trạng thái xuất bản.
Ví dụ:
Bookstorelà node trang chủ.Lịch sửlà node danh mục sách lịch sử.Đại Việt sử ký toàn thưlà node chứa thông tin một cuốn sách.
Quan hệ cha/con trong Content Tree giúp tổ chức nội dung và có thể tham gia vào việc tạo URL. Tuy nhiên, không nên mặc định URL luôn giống đường dẫn cây nội dung. URL cuối cùng còn phụ thuộc vào cấu hình và cách ứng dụng triển khai.
2. Document Type là gì?
Hãy liên hệ với kiến thức C# mà bạn đã biết.
Trong một ứng dụng ASP.NET Core, nếu muốn lưu thông tin sách, chúng ta có thể tạo class như sau:
public class Book
{
public string Title { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public decimal Price { get; set; }
public string Description { get; set; } = string.Empty;
}
Class Book định nghĩa các trường dữ liệu mà một đối tượng sách có thể có.
Trong Umbraco, Document Type cũng có vai trò tương tự về mặt khái niệm: nó định nghĩa cấu trúc của một loại nội dung và các property mà editor có thể nhập trong Backoffice.
Ví dụ, chúng ta tạo Document Type có tên Book với các property sau:
| Property name | Alias | Data Type | Ý nghĩa |
|---|---|---|---|
| Tiêu đề | title |
Textstring | Tên sách |
| Tác giả | author |
Textstring | Tác giả sách |
| Giá bán | price |
Decimal Number | Giá bán |
| Mô tả | description |
Textarea | Mô tả ngắn về sách |
| Ảnh bìa | coverImage |
Media Picker | Chọn ảnh bìa từ Media Library |
Khi tạo một Content item sử dụng Document Type Book, Umbraco sẽ cung cấp form tương ứng để editor nhập thông tin.
Ví dụ, một cuốn sách có dữ liệu:
{
"title": "Đại Việt sử ký toàn thư",
"author": "Ngô Sĩ Liên và các sử thần",
"price": 180000,
"description": "Bộ sử quan trọng về lịch sử Việt Nam."
}
Đây là JSON minh họa các giá trị nội dung, không phải định dạng response bắt buộc của Umbraco API.
Điểm quan trọng nhất cần nhớ là: Document Type định nghĩa cấu trúc; Content item chứa dữ liệu thực tế.
3. Content Type, Document Type và Element Type khác nhau thế nào?
Trong quá trình học Umbraco, bạn sẽ gặp nhiều thuật ngữ gần giống nhau.
3.1. Content Type
Trong Umbraco, Content Type là khái niệm liên quan đến định nghĩa cấu trúc nội dung. Trong ngữ cảnh Backoffice, Document Type là một loại Content Type.
Khi đọc tài liệu hoặc source code, bạn cũng có thể gặp lớp ContentType trong API .NET. Đây là model dùng để biểu diễn định nghĩa Document Type ở cấp độ lập trình.
3.2. Document Type
Document Type thường được dùng để định nghĩa một loại nội dung có thể tạo thành node trong Content Tree.
Ví dụ:
HomePage: trang chủ.BookCategory: trang danh mục sách.Book: trang chi tiết sách.
Document Type có thể được tạo với Template hoặc không có Template. Việc lựa chọn phụ thuộc vào cách bạn sử dụng nội dung.
3.3. Element Type
Element Type cũng định nghĩa cấu trúc và property, nhưng được thiết kế cho các phần tử nội dung được nhúng vào nội dung khác, chẳng hạn các block trong Block List hoặc Block Grid.
Ví dụ, nếu một trang chủ có nhiều thẻ sách, chúng ta có thể tạo Element Type BookCard để định nghĩa các trường:
- Tiêu đề.
- Ảnh bìa.
- Mô tả ngắn.
- Liên kết đến trang chi tiết.
Element Type không được tạo thành node độc lập trong Content Tree như một Document Type thông thường. Nó thường được sử dụng bên trong một property có Data Type hỗ trợ các element.
Trong bài thực hành đầu tiên, bạn chưa cần tạo Element Type. Hãy bắt đầu bằng các Document Type đơn giản để hiểu mối quan hệ giữa định nghĩa và dữ liệu.
4. Media Type, Media item và Media Picker
Website bán sách cần có ảnh bìa. Trong Umbraco, phần quản lý tài nguyên này nằm trong Media Library, tách biệt với Content Tree.
Có ba khái niệm cần phân biệt:
| Khái niệm | Ý nghĩa | Ví dụ |
|---|---|---|
| Media Type | Định nghĩa cấu trúc metadata của một loại Media | Image |
| Media item | Tài nguyên thực tế trong Media Library | luan-ngu-cover.jpg |
| Media Picker | Data Type cho phép chọn một Media item | Property coverImage của Book |
Ví dụ, chúng ta upload ảnh bìa cuốn Luận Ngữ vào Media Library. Sau đó, khi chỉnh sửa Content item của cuốn sách, editor chọn ảnh này trong property coverImage.
Luồng dữ liệu có thể được mô tả như sau:
Trong Headless, API có thể trả URL ảnh, thông tin Media hoặc một object chứa các thuộc tính khác tùy cách custom API được viết. Không nên mặc định tất cả API đều trả cùng một cấu trúc JSON.
5. Thực hành: Tạo Document Type cho website bán sách
Bây giờ chúng ta sẽ tạo cấu trúc nội dung trên Umbraco 13.
5.1. Tạo Document Type Bookstore Home
Đăng nhập Backoffice và mở Settings.
- Tìm khu vực Document Types.
- Tạo một Document Type mới có tên
Bookstore Home. - Đặt alias là
bookstoreHomenếu cần chỉnh thủ công. - Thêm các property cần thiết.
- Cho phép loại nội dung này xuất hiện ở root của Content Tree.
- Lưu Document Type.
Nếu muốn website render bằng Razor, bạn có thể tạo Document Type with Template. Nếu đang thực hành Headless, hãy xác định rõ việc tạo Template có cần thiết đối với luồng render của dự án hay không.
Thêm các property sau:
| Property | Alias | Data Type |
|---|---|---|
| Tiêu đề trang | pageTitle |
Textstring |
| Mô tả | intro |
Textarea |
| Ảnh banner | bannerImage |
Media Picker |
Document Type này định nghĩa cấu trúc cho trang chủ của website.
5.2. Tạo Document Type Book Category
Tạo một Document Type khác tên Book Category, alias bookCategory.
Thêm các property:
| Property | Alias | Data Type |
|---|---|---|
| Tên danh mục | categoryTitle |
Textstring |
| Mô tả | categoryDescription |
Textarea |
Trong phần Structure hoặc Permissions của Document Type, cấu hình cho phép tạo các node Book Category dưới trang chủ nếu đó là cấu trúc bạn muốn sử dụng.
5.3. Tạo Document Type Book
Tạo Document Type Book và thêm các property đã mô tả ở phần 2.
Tiếp theo, cấu hình quy tắc tạo node con để cho phép tạo sách dưới một danh mục. Trong Umbraco, quy tắc này được cấu hình ở phần Structure hoặc Permissions tùy giao diện phiên bản.
Ví dụ, bạn muốn tạo các cuốn sách trong danh mục Lịch sử Việt Nam. Khi tạo node con dưới danh mục đó, Umbraco cần cho phép Document Type Book được sử dụng tại vị trí này.
Đừng quên lưu các Document Type sau khi chỉnh sửa.
6. Tạo Content Tree và các node cha/con
Sau khi đã định nghĩa cấu trúc, chúng ta mới tạo dữ liệu thực tế.
Trong khu vực Content, tạo node trang chủ sử dụng Document Type Bookstore Home. Đặt tên node là Bookstore, nhập tiêu đề và mô tả, rồi Save and Publish.
Tiếp theo, tạo các node danh mục:
Đây là sơ đồ nội dung mong muốn. Nếu bạn muốn tạo node theo đúng cấu trúc trên, cần định nghĩa các Document Type phù hợp và cho phép chúng xuất hiện ở vị trí tương ứng trong Content Tree.
Ví dụ, nếu node Lịch sử là một trang danh mục và Lịch sử Việt Nam là danh mục con, bạn có thể dùng cùng Document Type Book Category cho cả hai cấp. Khi đó, cần cấu hình cho phép Book Category làm node con của chính nó nếu muốn tạo cấu trúc lồng nhau.
Đối với các cuốn sách, tạo Content item sử dụng Document Type Book bên dưới danh mục phù hợp.
Nhập thông tin cho một cuốn sách:
- Node name: Đại Việt sử ký toàn thư.
- Title: Đại Việt sử ký toàn thư.
- Author: Ngô Sĩ Liên và các sử thần.
- Price: 180000.
- Description: Bộ sử quan trọng về lịch sử Việt Nam.
- Cover Image: chọn ảnh bìa trong Media Library.
Nhấn Save and Publish để xuất bản nội dung.
7. Tạo node con có làm frontend thay đổi không?
Câu trả lời phụ thuộc vào cách frontend được xây dựng.
Trường hợp 1: Website render bằng Razor Template
Nếu Umbraco dùng Razor để render website, việc tạo một node mới có thể làm xuất hiện một URL mới, miễn là Document Type có Template phù hợp, node được xuất bản và routing được cấu hình đúng.
Tuy nhiên, tạo node không tự động tạo ra một giao diện tùy ý. Template phải có logic render dữ liệu. Nếu Template chỉ hiển thị tiêu đề và mô tả, giao diện sẽ chỉ có các thành phần đó.
Trường hợp 2: Umbraco Headless với Next.js
Trong kiến trúc Headless, Content Tree chỉ là nơi quản lý nội dung. Next.js phải có logic lấy dữ liệu và render nó.
Ví dụ, API có thể trả dữ liệu sách cho frontend:
{
"title": "Đại Việt sử ký toàn thư",
"author": "Ngô Sĩ Liên và các sử thần",
"price": 180000,
"description": "Bộ sử quan trọng về lịch sử Việt Nam.",
"coverImage": {
"url": "/media/example-book-cover.jpg"
}
}
JSON trên chỉ minh họa cấu trúc dữ liệu. API thực tế của dự án có thể trả object ảnh hoặc các property khác theo một định dạng riêng.
Frontend cần nhận dữ liệu và hiển thị chúng. Ví dụ với React:
type Book = {
title: string;
author: string;
price: number;
description: string;
coverImage?: {
url: string;
};
};
type BookCardProps = {
book: Book;
};
export function BookCard({ book }: BookCardProps) {
return (
<article>
{book.coverImage && (
<img
src={book.coverImage.url}
alt={book.title}
/>
)}
<h2>{book.title}</h2>
<p>Tác giả: {book.author}</p>
<p>{book.description}</p>
<p>Giá: {book.price.toLocaleString("vi-VN")} đồng</p>
</article>
);
}
Component này chỉ là ví dụ React độc lập, chưa bao gồm cách gọi API và ánh xạ response thực tế của Umbraco.
Điều cần nhớ là: tạo node mới trong Umbraco không tự động tạo React component hoặc route trong Next.js. Frontend phải có cơ chế lấy nội dung, xác định loại nội dung và render giao diện tương ứng.
8. Có cần tạo Template để tạo một trang About Us không?
Hướng dẫn truyền thống của Umbraco thường sử dụng quy trình:
- Vào Settings và tạo Document Type with Template.
- Umbraco tạo Document Type và Template tương ứng.
- Template dùng Razor để render HTML.
- Vào Content và tạo node sử dụng Document Type đó.
- Nhập nội dung và Publish.
Nếu Document Type đã tồn tại mà chưa có Template, bạn cũng có thể vào Settings, mở Templates trong khu vực Templating, nhấp phải vào Templates và chọn Create để tạo Template mới. Sau đó, cần cho phép Template đó trên Document Type phù hợp trong phần Templates của Document Type.
Vì vậy, hướng dẫn tạo Template tên About Us là hợp lý trong mô hình website render bằng Razor. Nhưng tạo Template không đồng nghĩa với tạo Content node. Bạn vẫn cần tạo node trong Content và xuất bản nó.
Với dự án Headless sử dụng Custom API và Next.js, Template Razor không nhất thiết tham gia vào quá trình render frontend. Nếu API chỉ trả dữ liệu nội dung, giao diện được quyết định bởi code Next.js.
Tài liệu chính thức:
9. Content Tree có nhất thiết phải giống URL website không?
Không. Content Tree và URL có liên quan nhưng không bắt buộc phải giống nhau.
Ví dụ, cấu trúc Content Tree có thể là:
Bookstore
├── Lịch sử
│ ├── Lịch sử Việt Nam
│ │ ├── Đại Việt sử ký toàn thư
│ │ └── Việt Nam sử lược
└── Triết học
├── Triết học phương Đông
│ └── Luận Ngữ
Trong một website render theo URL phân cấp, một cuốn sách có thể có URL:
/lich-su/lich-su-viet-nam/dai-viet-su-ky-toan-thu/
Nhưng trong một ứng dụng Headless, frontend có thể thiết kế URL ngắn hơn:
/books/dai-viet-su-ky-toan-thu
Next.js có thể dùng dynamic route để xử lý URL đó, sau đó gọi API để lấy dữ liệu sách. Việc này cần được triển khai trong frontend hoặc routing của hệ thống; nó không tự phát sinh chỉ vì bạn tạo node trong Umbraco.
10. Sơ đồ tổng thể: từ Document Type đến frontend
Trong mô hình Razor, Umbraco render HTML bằng Template. Trong mô hình Headless, API cung cấp dữ liệu và Next.js render HTML. Sơ đồ trên mô tả cả hai nhánh ở mức khái quát; một dự án cụ thể có thể chỉ sử dụng một nhánh.
11. Bài tập thực hành
Để chắc chắn đã hiểu bài, hãy thử hoàn thành các nhiệm vụ sau trong project Umbraco 13 của bạn:
- [ ] Tạo Document Type
Bookvới các property title, author, price và coverImage. - [ ] Tạo Document Type
Book Categoryvới các property tên danh mục và mô tả. - [ ] Cấu hình cho phép tạo danh mục con và sách ở vị trí mong muốn.
- [ ] Tạo Media item chứa ảnh bìa sách.
- [ ] Tạo ít nhất hai danh mục: Lịch sử và Triết học.
- [ ] Tạo hai cuốn sách và Publish.
- [ ] Kiểm tra API hiện có để xem dữ liệu sách được trả về như thế nào.
- [ ] Nếu frontend là Next.js, xác định component nào sẽ hiển thị sách và route nào sẽ dùng để xem chi tiết.
Sau khi hoàn thành, hãy thử đổi tiêu đề một cuốn sách trong Backoffice, Publish lại và quan sát frontend. Nếu giao diện không đổi, hãy kiểm tra API response, cache, mapping và logic render trước khi kết luận rằng Umbraco không lưu dữ liệu.
12. Tổng kết
Qua ví dụ website bán sách lịch sử và triết học, chúng ta đã phân biệt được các khái niệm cốt lõi:
- Document Type: định nghĩa cấu trúc của nội dung.
- Content item: dữ liệu thực tế được tạo từ Document Type.
- Content Type: thuật ngữ liên quan đến định nghĩa cấu trúc nội dung; Document Type là một loại Content Type.
- Element Type: định nghĩa cấu trúc cho các phần tử nội dung được nhúng vào nội dung khác.
- Media Type: định nghĩa cấu trúc metadata của Media.
- Media item: tài nguyên cụ thể trong Media Library.
- Data Type: xác định editor được sử dụng để nhập dữ liệu của một property.
- Template: dùng để render giao diện server-side trong mô hình Razor.
- Content Tree: tổ chức các node nội dung theo quan hệ cha/con.
Nếu bạn đang maintain dự án Umbraco Headless, bước tiếp theo nên là tìm một node thực tế trong Content Tree, xác định Document Type và các property, sau đó trace dữ liệu từ Custom API sang component Next.js. Đó là cách biến kiến thức Umbraco thành kỹ năng debug và phát triển tính năng thực tế.
Nhận xét