Chuyển đến nội dung chính

Umbraco: Hiểu Content Tree trong Umbraco 13 qua website bán sách lịch sử và triết học - Day 2

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:

flowchart TD A["Bookstore - Trang chủ"] A --> B["Lịch sử"] A --> C["Triết học"] A --> D["Giới thiệu"] B --> E["Lịch sử Việt Nam"] B --> F["Lịch sử thế giới"] C --> G["Triết học phương Đông"] C --> H["Triết học phương Tây"] E --> I["Đại Việt sử ký toàn thư"] E --> J["Việt Nam sử lược"] G --> K["Luận Ngữ"] H --> L["Cộng hòa - Plato"]

Đâ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ụ:

  • Bookstore là 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:

flowchart TD A["Media Type: Image"] --> B["Media item: luan-ngu-cover.jpg"] B --> C["Media Picker: coverImage"] C --> D["Content item: Luận Ngữ"] D --> E["API trả dữ liệu sách"] E --> F["Frontend hiển thị ảnh bìa"]

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.

  1. Tìm khu vực Document Types.
  2. Tạo một Document Type mới có tên Bookstore Home.
  3. Đặt alias là bookstoreHome nếu cần chỉnh thủ công.
  4. Thêm các property cần thiết.
  5. Cho phép loại nội dung này xuất hiện ở root của Content Tree.
  6. 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:

flowchart TD A["Bookstore"] --> B["Lịch sử"] A --> C["Triết học"] B --> D["Lịch sử Việt Nam"] B --> E["Lịch sử thế giới"] C --> F["Triết học phương Đông"] C --> G["Triết học phương Tây"] D --> H["Đại Việt sử ký toàn thư"] D --> I["Việt Nam sử lược"] F --> J["Luận Ngữ"] G --> K["Cộng hòa - Plato"]

Đâ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:

  1. Vào Settings và tạo Document Type with Template.
  2. Umbraco tạo Document Type và Template tương ứng.
  3. Template dùng Razor để render HTML.
  4. Vào Content và tạo node sử dụng Document Type đó.
  5. 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

flowchart TD A["Settings: Document Type"] --> B["Định nghĩa Properties"] B --> C["Content Editor tạo Content item"] C --> D["Nhập dữ liệu và Media"] D --> E["Save and Publish"] E --> F["Content Tree và dữ liệu đã xuất bản"] F --> G["Razor Template hoặc Custom API"] G --> H["Next.js / React nếu dùng Headless"] H --> I["Giao diện website"]

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 Book với các property title, author, price và coverImage.
  • [ ] Tạo Document Type Book Category vớ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ế.

Tài liệu tham khảo

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.