1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ tích hợp Firebase Data Connect với cơ sở dữ liệu Cloud SQL để tạo một ứng dụng web đánh giá phim. Ứng dụng hoàn chỉnh sẽ cho thấy cách Firebase Data Connect đơn giản hoá quy trình tạo ứng dụng dựa trên SQL. Gói này bao gồm các tính năng sau:
- Xác thực: Triển khai quy trình xác thực tuỳ chỉnh cho các truy vấn và đột biến của ứng dụng, đảm bảo chỉ những người dùng được uỷ quyền mới có thể tương tác với dữ liệu của bạn.
- Giản đồ GraphQL: Tạo và quản lý cấu trúc dữ liệu bằng cách sử dụng giản đồ GraphQL linh hoạt, phù hợp với nhu cầu của ứng dụng web đánh giá phim.
- Truy vấn và đột biến SQL: Truy xuất, cập nhật và quản lý dữ liệu trong Cloud SQL bằng các truy vấn và đột biến do GraphQL cung cấp.
- Tìm kiếm nâng cao bằng kiểu khớp một phần chuỗi: Sử dụng bộ lọc và lựa chọn tìm kiếm để tìm phim dựa trên các trường như tiêu đề, nội dung mô tả hoặc thẻ.
- (Không bắt buộc) Tích hợp tính năng tìm kiếm vectơ: Thêm chức năng tìm kiếm nội dung bằng cách sử dụng tính năng tìm kiếm vectơ của Firebase Data Connect để mang lại trải nghiệm phong phú cho người dùng dựa trên dữ liệu đầu vào và lựa chọn ưu tiên.
Điều kiện tiên quyết
Bạn cần có kiến thức cơ bản về JavaScript.
Kiến thức bạn sẽ học được
- Thiết lập Firebase Data Connect bằng trình mô phỏng cục bộ.
- Thiết kế giản đồ dữ liệu bằng Data Connect và GraphQL.
- Viết và kiểm thử nhiều truy vấn và đột biến cho một ứng dụng đánh giá phim.
- Tìm hiểu cách Firebase Data Connect tạo và sử dụng SDK trong ứng dụng.
- Triển khai giản đồ và quản lý cơ sở dữ liệu một cách hiệu quả.
Những gì bạn cần
- Git
- Visual Studio Code
- Cài đặt Node.js bằng nvm-windows (Windows) hoặc nvm (macOS/Linux)
- Nếu chưa có, hãy tạo một dự án Firebase trong bảng điều khiển của Firebase
- (Không bắt buộc) Để tìm kiếm vectơ, hãy nâng cấp dự án của bạn lên gói giá linh hoạt (trả tiền theo mức dùng)
2. Thiết lập môi trường phát triển
Giai đoạn này của lớp học lập trình sẽ hướng dẫn bạn thiết lập môi trường để bắt đầu xây dựng ứng dụng đánh giá phim bằng Firebase Data Connect.
- Nhân bản kho lưu trữ dự án và cài đặt các phần phụ thuộc bắt buộc:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- Sau khi chạy các lệnh này, hãy mở http://localhost:5173 trong trình duyệt để xem ứng dụng web đang chạy cục bộ. Đây là giao diện người dùng để tạo ứng dụng đánh giá phim và tương tác với các tính năng của ứng dụng.
- Mở thư mục
codelab-dataconnect-web
được sao chép bằng Visual Studio Code. Đây là nơi bạn sẽ xác định giản đồ, ghi truy vấn và kiểm thử chức năng của ứng dụng. - Để sử dụng các tính năng của Data Connect, hãy cài đặt Tiện ích Visual Studio cho Firebase Data Connect.
Ngoài ra, bạn có thể cài đặt tiện ích này từ Visual Studio Code Marketplace hoặc tìm kiếm tiện ích đó trong VS Code. - Mở hoặc tạo một dự án Firebase mới trong bảng điều khiển của Firebase.
- Kết nối dự án Firebase với tiện ích Firebase Data Connect VSCode. Trong tiện ích, hãy làm như sau:
- Nhấp vào nút Đăng nhập.
- Nhấp vào Kết nối dự án Firebase rồi chọn dự án Firebase của bạn.
- Khởi động trình mô phỏng Firebase bằng tiện ích Firebase Data Connect VS Code:
Nhấp vào Start Emulators (Khởi động trình mô phỏng), sau đó xác nhận rằng trình mô phỏng đang chạy trong thiết bị đầu cuối.
3. Xem lại cơ sở mã khởi động
Trong phần này, bạn sẽ khám phá các phần chính của cơ sở mã ban đầu của ứng dụng. Mặc dù ứng dụng này thiếu một số chức năng, nhưng bạn vẫn nên tìm hiểu cấu trúc tổng thể của ứng dụng.
Cấu trúc thư mục và tệp
Các tiểu mục sau đây cung cấp thông tin tổng quan về thư mục và cấu trúc tệp của ứng dụng.
Thư mục dataconnect/
Chứa các cấu hình Firebase Data Connect, trình kết nối (xác định truy vấn và đột biến) và tệp giản đồ.
schema/schema.gql
: Xác định giản đồ GraphQLconnector/queries.gql
: Các truy vấn cần thiết trong ứng dụngconnector/mutations.gql
: Các đột biến cần thiết trong ứng dụngconnector/connector.yaml
: Tệp cấu hình để tạo SDK
Thư mục app/src/
Chứa logic ứng dụng và hoạt động tương tác với Firebase Data Connect.
firebase.ts
: Cấu hình để kết nối với một Ứng dụng Firebase trong dự án Firebase.lib/dataconnect-sdk/
: Chứa SDK đã tạo. Bạn có thể chỉnh sửa vị trí tạo SDK trong tệpconnector/connector.yaml
và SDK sẽ được tạo tự động bất cứ khi nào bạn xác định một truy vấn hoặc đột biến.
4. Xác định giản đồ cho bài đánh giá phim
Trong phần này, bạn sẽ xác định cấu trúc và mối quan hệ giữa các thực thể chính trong ứng dụng phim trong một giản đồ. Các thực thể như Movie
, User
, Actor
và Review
được liên kết với các bảng cơ sở dữ liệu, với các mối quan hệ được thiết lập bằng cách sử dụng lệnh của giản đồ Firebase Data Connect và GraphQL. Sau khi triển khai, ứng dụng của bạn sẽ sẵn sàng xử lý mọi thứ, từ việc tìm kiếm phim có điểm xếp hạng cao nhất và lọc theo thể loại cho đến việc cho phép người dùng để lại bài đánh giá, đánh dấu phim yêu thích, khám phá các phim tương tự hoặc tìm phim được đề xuất dựa trên nội dung nhập bằng văn bản thông qua tính năng tìm kiếm vectơ.
Các thực thể và mối quan hệ cốt lõi
Loại Movie
chứa các thông tin chi tiết chính như tiêu đề, thể loại và thẻ mà ứng dụng sử dụng cho nội dung tìm kiếm và hồ sơ phim. Loại User
theo dõi các lượt tương tác của người dùng, chẳng hạn như bài đánh giá và mục yêu thích. Reviews
kết nối người dùng với phim, cho phép ứng dụng hiển thị điểm xếp hạng và ý kiến phản hồi do người dùng tạo.
Mối quan hệ giữa phim, diễn viên và người dùng giúp ứng dụng trở nên linh động hơn. Bảng nối MovieActor
giúp hiển thị thông tin chi tiết về dàn diễn viên và danh sách phim của diễn viên. Loại FavoriteMovie
cho phép người dùng đánh dấu phim yêu thích để ứng dụng có thể hiển thị danh sách yêu thích được cá nhân hoá và làm nổi bật các lựa chọn phổ biến.
Thiết lập bảng Movie
Loại Movie
xác định cấu trúc chính cho một thực thể phim, bao gồm các trường như title
, genre
, releaseYear
và rating
.
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type Movie
@table {
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
}
Điểm chính cần ghi nhớ:
- id: Một UUID duy nhất cho mỗi bộ phim, được tạo bằng
@default(expr: "uuidV4()")
.
Thiết lập bảng MovieMetadata
Loại MovieMetadata
thiết lập mối quan hệ một với một với loại Movie
. Dữ liệu này bao gồm các dữ liệu bổ sung như đạo diễn của phim.
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type MovieMetadata
@table {
# @ref creates a field in the current table (MovieMetadata)
# It is a reference that holds the primary key of the referenced type
# In this case, @ref(fields: "movieId", references: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String
}
Điểm chính cần ghi nhớ:
- Phim! @ref: Tham chiếu loại
Movie
, thiết lập mối quan hệ khoá ngoại.
Thiết lập bảng Actor
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
Loại Actor
đại diện cho một diễn viên trong cơ sở dữ liệu phim, trong đó mỗi diễn viên có thể tham gia nhiều bộ phim, tạo thành mối quan hệ nhiều với nhiều.
Thiết lập bảng MovieActor
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie!
# movieId: UUID! <- this is created by the implied @ref, see: implicit.gql
actor: Actor!
# actorId: UUID! <- this is created by the implied @ref, see: implicit.gql
role: String! # "main" or "supporting"
}
Điểm chính cần ghi nhớ:
- movie: Tham chiếu đến loại Movie (Phim), ngầm tạo một khoá ngoại movieId: UUID!.
- actor: Tham chiếu đến loại Actor, ngầm tạo một khoá ngoại actorId: UUID!.
- role: Xác định vai trò của diễn viên trong phim (ví dụ: "chính" hoặc "hỗ trợ").
Thiết lập bảng User
Loại User
xác định một thực thể người dùng tương tác với phim bằng cách để lại bài đánh giá hoặc thêm phim vào danh sách yêu thích.
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type User
@table {
id: String! @col(name: "auth_uid")
username: String! @col(dataType: "varchar(50)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
Thiết lập bảng FavoriteMovie
Loại FavoriteMovie
là một bảng nối xử lý mối quan hệ nhiều với nhiều giữa người dùng và những bộ phim mà họ yêu thích. Mỗi bảng liên kết một User
với một Movie
.
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type FavoriteMovie
@table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
# @ref is implicit
user: User!
movie: Movie!
}
Điểm chính cần ghi nhớ:
- movie: Tham chiếu đến loại Movie (Phim), ngầm tạo một khoá ngoại
movieId: UUID!
. - user: Tham chiếu đến loại người dùng, ngầm tạo một khoá ngoại
userId: UUID!
.
Thiết lập bảng Review
Loại Review
đại diện cho thực thể bài đánh giá và liên kết các loại User
và Movie
theo mối quan hệ nhiều với nhiều (một người dùng có thể để lại nhiều bài đánh giá và mỗi bộ phim có thể có nhiều bài đánh giá).
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @default(expr: "uuidV4()")
user: User!
movie: Movie!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
Điểm chính cần ghi nhớ:
- user: Tham chiếu đến người dùng đã để lại bài đánh giá.
- movie: Tham chiếu đến bộ phim đang được đánh giá.
- reviewDate: Tự động đặt thành thời gian tạo bài đánh giá bằng
@default(expr: "request.time")
.
Các trường được tạo tự động và mặc định
Giản đồ này sử dụng các biểu thức như @default(expr: "uuidV4()")
để tự động tạo mã nhận dạng và dấu thời gian duy nhất. Ví dụ: trường id
trong các loại Movie
và Review
sẽ tự động được điền bằng UUID khi tạo một bản ghi mới.
Giờ đây, khi đã xác định được giản đồ, ứng dụng phim của bạn đã có nền tảng vững chắc cho cấu trúc và mối quan hệ dữ liệu!
5. Truy xuất các bộ phim hàng đầu và mới nhất
Trong phần này, bạn sẽ chèn dữ liệu phim mô phỏng vào trình mô phỏng cục bộ, sau đó triển khai các trình kết nối (truy vấn) và mã TypeScript để gọi các trình kết nối này trong ứng dụng web. Cuối cùng, ứng dụng của bạn sẽ có thể tự động tìm nạp và hiển thị các bộ phim mới nhất và được đánh giá cao nhất ngay trong cơ sở dữ liệu.
Chèn dữ liệu giả lập về phim, diễn viên và bài đánh giá
- Trong VSCode, hãy mở
dataconnect/moviedata_insert.gql
. Đảm bảo trình mô phỏng trong tiện ích Firebase Data Connect đang chạy. - Bạn sẽ thấy nút Run (local) (Chạy (trên máy)) ở đầu tệp. Nhấp vào đây để chèn dữ liệu phim mô phỏng vào cơ sở dữ liệu.
- Kiểm tra thiết bị đầu cuối Data Connect Execution (Thực thi kết nối dữ liệu) để xác nhận rằng dữ liệu đã được thêm thành công.
Triển khai trình kết nối
- Mở
dataconnect/movie-connector/queries.gql
. Bạn sẽ thấy một truy vấnListMovies
cơ bản trong phần nhận xét: Truy vấn này tìm nạp tất cả phim và thông tin chi tiết về phim (ví dụ:query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }
id
,title
,releaseYear
). Tuy nhiên, truy vấn này không sắp xếp các phim. - Thay thế truy vấn
ListMovies
hiện có bằng truy vấn sau để thêm các tuỳ chọn sắp xếp và giới hạn:# List subset of fields for movies query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) { movies( orderBy: [ { rating: $orderByRating }, { releaseYear: $orderByReleaseYear } ] limit: $limit ) { id title imageUrl releaseYear genre rating tags description } }
- Nhấp vào nút Run (local) (Chạy (cục bộ)) để thực thi truy vấn trên cơ sở dữ liệu cục bộ. Bạn cũng có thể nhập các biến truy vấn trong ngăn cấu hình trước khi chạy.
Điểm chính cần ghi nhớ:
movies()
: Trường truy vấn GraphQL để tìm nạp dữ liệu phim từ cơ sở dữ liệu.orderByRating
: Tham số để sắp xếp phim theo điểm xếp hạng (tăng dần/giảm dần).orderByReleaseYear
: Tham số để sắp xếp phim theo năm phát hành (tăng dần/giảm dần).limit
: Hạn chế số lượng phim được trả về.
Tích hợp truy vấn trong ứng dụng web
Trong phần này của lớp học lập trình, bạn sẽ sử dụng các truy vấn được xác định trong phần trước trong ứng dụng web của mình. Trình mô phỏng Firebase Data Connect tạo SDK dựa trên thông tin trong tệp .gql
(cụ thể là schema.gql
, queries.gql
, mutations.gql
) và tệp connector.yaml
. Bạn có thể gọi trực tiếp các SDK này trong ứng dụng.
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú câu lệnh nhập ở trên cùng: Hàmimport { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
, loại phản hồiListMoviesData
và enumOrderDirection
đều là các SDK do trình mô phỏng Firebase Data Connect tạo ra dựa trên giản đồ và các truy vấn mà bạn đã xác định trước đó . - Thay thế các hàm
handleGetTopMovies
vàhandleGetLatestMovies
bằng mã sau:// Fetch top-rated movies export const handleGetTopMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByRating: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching top movies:", error); return null; } }; // Fetch latest movies export const handleGetLatestMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByReleaseYear: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching latest movies:", error); return null; } };
Điểm chính cần ghi nhớ:
listMovies
: Hàm được tạo tự động gọi truy vấnlistMovies
để truy xuất danh sách phim. Trang này có các tuỳ chọn sắp xếp theo điểm xếp hạng hoặc năm phát hành và giới hạn số lượng kết quả.ListMoviesData
: Loại kết quả dùng để hiển thị 10 phim hàng đầu và mới nhất trên trang chủ của ứng dụng.
Xem ví dụ thực tế
Tải lại ứng dụng web để xem truy vấn hoạt động. Trang chủ hiện hiển thị linh động danh sách phim, tìm nạp dữ liệu trực tiếp từ cơ sở dữ liệu cục bộ. Bạn sẽ thấy các bộ phim mới nhất và được đánh giá cao nhất xuất hiện liền mạch, phản ánh dữ liệu bạn vừa thiết lập.
6. Hiển thị thông tin chi tiết về phim và diễn viên
Trong phần này, bạn sẽ triển khai chức năng truy xuất thông tin chi tiết về một bộ phim hoặc một diễn viên bằng mã nhận dạng duy nhất của họ. Việc này không chỉ liên quan đến việc tìm nạp dữ liệu từ các bảng tương ứng mà còn liên kết các bảng có liên quan để hiển thị thông tin chi tiết toàn diện, chẳng hạn như bài đánh giá phim và danh sách phim của diễn viên.
Triển khai trình kết nối
- Mở
dataconnect/movie-connector/queries.gql
trong dự án. - Thêm các truy vấn sau để truy xuất thông tin chi tiết về phim và diễn viên:
# Get movie by id query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl releaseYear genre rating description tags metadata: movieMetadatas_on_movie { director } mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) { id name imageUrl } supportingActors: actors_via_MovieActor( where: { role: { eq: "supporting" } } ) { id name imageUrl } reviews: reviews_on_movie { id reviewText reviewDate rating user { id username } } } } # Get actor by id query GetActorById($id: UUID!) @auth(level: PUBLIC) { actor(id: $id) { id name imageUrl mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) { id title genre tags imageUrl } supportingActors: movies_via_MovieActor( where: { role: { eq: "supporting" } } ) { id title genre tags imageUrl } } }
- Lưu các thay đổi và xem lại các truy vấn.
Điểm chính cần ghi nhớ:
movie()
/actor()
: Các trường truy vấn GraphQL để tìm nạp một bộ phim hoặc diễn viên từ bảngMovies
hoặcActors
._on_
: Cho phép truy cập trực tiếp vào các trường từ một loại được liên kết có mối quan hệ khoá ngoại. Ví dụ:reviews_on_movie
tìm nạp tất cả bài đánh giá liên quan đến một bộ phim cụ thể._via_
: Dùng để điều hướng các mối quan hệ nhiều với nhiều thông qua bảng nối. Ví dụ:actors_via_MovieActor
truy cập vào loạiActor
thông qua bảng nốiMovieActor
và điều kiệnwhere
lọc diễn viên dựa trên vai trò của họ (ví dụ: "chính" hoặc "hỗ trợ").
Kiểm thử truy vấn bằng cách nhập dữ liệu mô phỏng
- Trong ngăn thực thi Data Connect, bạn có thể kiểm thử truy vấn bằng cách nhập mã nhận dạng mô phỏng, chẳng hạn như:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- Nhấp vào Run (local) (Chạy (trên máy)) cho
GetMovieById
để truy xuất thông tin chi tiết về "Quantum Paradox" (phim mô phỏng liên quan đến mã nhận dạng ở trên).
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- Thay thế các hàm
handleGetMovieById
vàhandleGetActorById
bằng mã sau:// Fetch movie details by ID export const handleGetMovieById = async ( movieId: string ) => { try { const response = await getMovieById({ id: movieId }); if (response.data.movie) { return response.data.movie; } return null; } catch (error) { console.error("Error fetching movie:", error); return null; } }; // Calling generated SDK for GetActorById export const handleGetActorById = async ( actorId: string ): Promise<GetActorByIdData["actor"] | null> => { try { const response = await getActorById({ id: actorId }); if (response.data.actor) { return response.data.actor; } return null; } catch (error) { console.error("Error fetching actor:", error); return null; } };
Điểm chính cần ghi nhớ:
getMovieById
/getActorById
: Đây là các hàm được tạo tự động gọi các truy vấn mà bạn đã xác định, truy xuất thông tin chi tiết về một bộ phim hoặc diễn viên cụ thể.GetMovieByIdData
/GetActorByIdData
: Đây là các loại kết quả, dùng để hiển thị thông tin chi tiết về phim và diễn viên trong ứng dụng.
Xem ví dụ thực tế
Bây giờ, hãy chuyển đến trang chủ của ứng dụng web. Nhấp vào một bộ phim để xem tất cả thông tin chi tiết về bộ phim đó, bao gồm cả diễn viên và bài đánh giá. Đây là thông tin được lấy từ các bảng liên quan. Tương tự, khi bạn nhấp vào một diễn viên, những bộ phim mà họ tham gia sẽ xuất hiện.
7. Xử lý việc xác thực người dùng
Trong phần này, bạn sẽ triển khai chức năng đăng nhập và đăng xuất của người dùng bằng tính năng Xác thực Firebase. Bạn cũng sẽ sử dụng dữ liệu Xác thực Firebase để truy xuất hoặc chèn/cập nhật dữ liệu người dùng trong Firebase DataConnect, đảm bảo việc quản lý người dùng an toàn trong ứng dụng.
Triển khai trình kết nối
- Mở
mutations.gql
trongdataconnect/movie-connector/
. - Thêm đột biến sau để tạo hoặc cập nhật người dùng đã xác thực hiện tại:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
Điểm chính cần ghi nhớ:
id_expr: "auth.uid"
: Phương thức này sử dụngauth.uid
do Xác thực Firebase trực tiếp cung cấp, chứ không phải do người dùng hoặc ứng dụng cung cấp, giúp tăng cường thêm một lớp bảo mật bằng cách đảm bảo mã nhận dạng người dùng được xử lý một cách an toàn và tự động.
Tìm nạp người dùng hiện tại
- Mở
queries.gql
trongdataconnect/movie-connector/
. - Thêm truy vấn sau để tìm nạp người dùng hiện tại:
# Get user by ID query GetCurrentUser @auth(level: USER) { user(key: { id_expr: "auth.uid" }) { id username reviews: reviews_on_user { id rating reviewDate reviewText movie { id title } } favoriteMovies: favorite_movies_on_user { movie { id title genre imageUrl releaseYear rating description tags metadata: movieMetadatas_on_movie { director } } } } }
Điểm chính cần ghi nhớ:
auth.uid
: Mã này được truy xuất trực tiếp từ tính năng Xác thực Firebase, đảm bảo quyền truy cập an toàn vào dữ liệu dành riêng cho người dùng.- Các trường
_on_
: Các trường này đại diện cho các bảng nối:reviews_on_user
: Tìm nạp tất cả bài đánh giá liên quan đến người dùng, bao gồm cảid
vàtitle
của phim.favorite_movies_on_user
: Truy xuất tất cả phim mà người dùng đánh dấu là phim yêu thích, bao gồm cả thông tin chi tiết nhưgenre
,releaseYear
,rating
vàmetadata
.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- Thay thế các hàm
handleAuthStateChange
vàhandleGetCurrentUser
bằng mã sau:// Handle user authentication state changes and upsert user export const handleAuthStateChange = ( auth: any, setUser: (user: User | null) => void ) => { return onAuthStateChanged(auth, async (user) => { if (user) { setUser(user); const username = user.email?.split("@")[0] || "anon"; await upsertUser({ username }); } else { setUser(null); } }); }; // Fetch current user profile export const handleGetCurrentUser = async (): Promise< GetCurrentUserData["user"] | null > => { try { const response = await getCurrentUser(); return response.data.user; } catch (error) { console.error("Error fetching user profile:", error); return null; } };
Điểm chính cần ghi nhớ:
handleAuthStateChange
: Hàm này theo dõi các thay đổi về trạng thái xác thực. Khi người dùng đăng nhập, ứng dụng sẽ đặt dữ liệu của người dùng và gọi đột biếnupsertUser
để tạo hoặc cập nhật thông tin của người dùng trong cơ sở dữ liệu.handleGetCurrentUser
: Tìm nạp hồ sơ của người dùng hiện tại bằng cách sử dụng truy vấngetCurrentUser
. Truy vấn này sẽ truy xuất bài đánh giá và phim yêu thích của người dùng.
Xem ví dụ thực tế
Bây giờ, hãy nhấp vào nút "Đăng nhập bằng Google" trong thanh điều hướng. Bạn có thể đăng nhập bằng trình mô phỏng Xác thực Firebase. Sau khi đăng nhập, hãy nhấp vào "Hồ sơ của tôi". Hiện tại, phần này sẽ trống, nhưng bạn đã thiết lập nền tảng để xử lý dữ liệu dành riêng cho người dùng trong ứng dụng.
8. Triển khai hoạt động tương tác của người dùng
Trong phần này của lớp học lập trình, bạn sẽ triển khai các hoạt động tương tác của người dùng trong ứng dụng đánh giá phim, cụ thể là cho phép người dùng quản lý các bộ phim mà họ yêu thích, đồng thời để lại hoặc xoá bài đánh giá.
Cho phép người dùng thêm phim vào danh sách yêu thích
Trong phần này, bạn sẽ thiết lập cơ sở dữ liệu để cho phép người dùng thêm phim vào danh sách yêu thích.
Triển khai trình kết nối
- Mở
mutations.gql
trongdataconnect/movie-connector/
. - Thêm các đột biến sau để xử lý việc thêm phim vào danh sách yêu thích:
# Add a movie to the user's favorites list mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId }) } # Remove a movie from the user's favorites list mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) }
Điểm chính cần ghi nhớ:
userId_expr: "auth.uid"
: Sử dụngauth.uid
do Xác thực Firebase trực tiếp cung cấp, đảm bảo rằng chỉ truy cập hoặc sửa đổi dữ liệu của người dùng đã xác thực.
Kiểm tra xem một bộ phim có được thêm vào danh sách yêu thích hay không
- Mở
queries.gql
trongdataconnect/movie-connector/
. - Thêm truy vấn sau để kiểm tra xem một bộ phim có được thêm vào danh sách yêu thích hay không:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
Điểm chính cần ghi nhớ:
auth.uid
: Đảm bảo quyền truy cập an toàn vào dữ liệu dành riêng cho người dùng bằng tính năng Xác thực Firebase.favorite_movie
: Kiểm tra bảng nốifavorite_movies
để xem người dùng hiện tại có đánh dấu một bộ phim cụ thể là phim yêu thích hay không.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Thay thế các hàm
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
vàhandleGetIfFavoritedMovie
bằng mã sau:// Add a movie to user's favorites export const handleAddFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await addFavoritedMovie({ movieId }); } catch (error) { console.error("Error adding movie to favorites:", error); throw error; } }; // Remove a movie from user's favorites export const handleDeleteFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await deleteFavoritedMovie({ movieId }); } catch (error) { console.error("Error removing movie from favorites:", error); throw error; } }; // Check if the movie is favorited by the user export const handleGetIfFavoritedMovie = async ( movieId: string ): Promise<boolean> => { try { const response = await getIfFavoritedMovie({ movieId }); return !!response.data.favorite_movie; } catch (error) { console.error("Error checking if movie is favorited:", error); return false; } };
Điểm chính cần ghi nhớ:
handleAddFavoritedMovie
vàhandleDeleteFavoritedMovie
: Sử dụng các đột biến để thêm hoặc xoá một bộ phim khỏi danh sách yêu thích của người dùng một cách an toàn.handleGetIfFavoritedMovie
: Sử dụng truy vấngetIfFavoritedMovie
để kiểm tra xem người dùng có đánh dấu một bộ phim là yêu thích hay không.
Xem ví dụ thực tế
Giờ đây, bạn có thể thêm phim vào danh sách yêu thích hoặc xoá phim khỏi danh sách yêu thích bằng cách nhấp vào biểu tượng trái tim trên thẻ phim và trang chi tiết về phim. Ngoài ra, bạn có thể xem các bộ phim yêu thích trên trang hồ sơ của mình.
Cho phép người dùng để lại hoặc xoá bài đánh giá
Tiếp theo, bạn sẽ triển khai phần quản lý bài đánh giá của người dùng trong ứng dụng.
Triển khai trình kết nối
Trong mutations.gql
(dataconnect/movie-connector/mutations.gql
): Thêm các đột biến sau:
# Add a review for a movie
mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
@auth(level: USER) {
review_insert(
data: {
userId_expr: "auth.uid"
movieId: $movieId
rating: $rating
reviewText: $reviewText
reviewDate_date: { today: true }
}
)
}
# Delete a user's review for a movie
mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
Điểm chính cần ghi nhớ:
userId_expr: "auth.uid"
: Đảm bảo rằng bài đánh giá được liên kết với người dùng đã xác thực.reviewDate_date: { today: true }
: Tự động tạo ngày hiện tại cho bài đánh giá bằng DataConnect, giúp bạn không cần phải nhập theo cách thủ công.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:import { addReview, deleteReview } from "@movie/dataconnect";
- Thay thế các hàm
handleAddReview
vàhandleDeleteReview
bằng mã sau:// Add a review to a movie export const handleAddReview = async ( movieId: string, rating: number, reviewText: string ): Promise<void> => { try { await addReview({ movieId, rating, reviewText }); } catch (error) { console.error("Error adding review:", error); throw error; } }; // Delete a review from a movie export const handleDeleteReview = async (movieId: string): Promise<void> => { try { await deleteReview({ movieId }); } catch (error) { console.error("Error deleting review:", error); throw error; } };
Điểm chính cần ghi nhớ:
handleAddReview
: Gọi đột biếnaddReview
để thêm bài đánh giá cho bộ phim đã chỉ định, liên kết bài đánh giá đó một cách an toàn với người dùng đã xác thực.handleDeleteReview
: Sử dụng đột biếndeleteReview
để xoá bài đánh giá về một bộ phim do người dùng đã xác thực thực hiện.
Xem ví dụ thực tế
Giờ đây, người dùng có thể để lại bài đánh giá về phim trên trang chi tiết về phim. Người dùng cũng có thể xem và xoá bài đánh giá của mình trên trang hồ sơ, nhờ đó có toàn quyền kiểm soát các hoạt động tương tác với ứng dụng.
9. Bộ lọc nâng cao và tính năng so khớp văn bản một phần
Trong phần này, bạn sẽ triển khai các tính năng tìm kiếm nâng cao, cho phép người dùng tìm kiếm phim dựa trên một loạt điểm xếp hạng và năm phát hành, lọc theo thể loại và thẻ, so khớp một phần văn bản trong tiêu đề hoặc nội dung mô tả, thậm chí kết hợp nhiều bộ lọc để có kết quả chính xác hơn.
Triển khai trình kết nối
- Mở
queries.gql
trongdataconnect/movie-connector/
. - Thêm truy vấn sau để hỗ trợ nhiều tính năng tìm kiếm:
# Search for movies, actors, and reviews query SearchAll( $input: String $minYear: Int! $maxYear: Int! $minRating: Float! $maxRating: Float! $genre: String! ) @auth(level: PUBLIC) { moviesMatchingTitle: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { title: { contains: $input } } ] } ) { id title genre rating imageUrl } moviesMatchingDescription: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { description: { contains: $input } } ] } ) { id title genre rating imageUrl } actorsMatchingName: actors(where: { name: { contains: $input } }) { id name imageUrl } reviewsMatchingText: reviews(where: { reviewText: { contains: $input } }) { id rating reviewText reviewDate movie { id title } user { id username } } }
Điểm chính cần ghi nhớ:
- Toán tử
_and
: Kết hợp nhiều điều kiện trong một truy vấn, cho phép lọc kết quả tìm kiếm theo một số trường nhưreleaseYear
,rating
vàgenre
. - Toán tử
contains
: Tìm kiếm các kết quả khớp một phần văn bản trong các trường. Trong truy vấn này, hàm này tìm kiếm các kết quả trùng khớp trongtitle
,description
,name
hoặcreviewText
. - Mệnh đề
where
: Chỉ định các điều kiện để lọc dữ liệu. Mỗi phần (phim, diễn viên, bài đánh giá) sử dụng một mệnh đềwhere
để xác định tiêu chí cụ thể cho nội dung tìm kiếm.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:import { searchAll, SearchAllData } from "@movie/dataconnect";
- Thay thế hàm
handleSearchAll
bằng mã sau:// Function to perform the search using the query and filters export const handleSearchAll = async ( searchQuery: string, minYear: number, maxYear: number, minRating: number, maxRating: number, genre: string ): Promise<SearchAllData | null> => { try { const response = await searchAll({ input: searchQuery, minYear, maxYear, minRating, maxRating, genre, }); return response.data; } catch (error) { console.error("Error performing search:", error); return null; } };
Điểm chính cần ghi nhớ:
handleSearchAll
: Hàm này sử dụng truy vấnsearchAll
để tìm kiếm dựa trên dữ liệu đầu vào của người dùng, lọc kết quả theo các tham số như năm, điểm xếp hạng, thể loại và kết quả trùng khớp một phần văn bản.
Xem ví dụ thực tế
Chuyển đến trang "Tìm kiếm nâng cao" trên thanh điều hướng trong ứng dụng web. Giờ đây, bạn có thể tìm kiếm phim, diễn viên và bài đánh giá bằng nhiều bộ lọc và thông tin đầu vào, nhận kết quả tìm kiếm chi tiết và phù hợp.
10. Không bắt buộc: Triển khai lên Google Cloud (bắt buộc phải có thông tin thanh toán)
Giờ đây, khi bạn đã hoàn tất quá trình lặp lại phát triển cục bộ, đã đến lúc triển khai giản đồ, dữ liệu và truy vấn cho máy chủ. Bạn có thể thực hiện việc này bằng cách sử dụng tiện ích Firebase Data Connect VS Code hoặc Firebase CLI.
Nâng cấp gói giá Firebase
Để tích hợp Firebase Data Connect với Cloud SQL cho PostgreSQL, dự án Firebase của bạn cần sử dụng gói thanh toán theo mức sử dụng (Blaze), tức là dự án được liên kết với một tài khoản Google Cloud Billing.
- Tài khoản Cloud Billing cần có một phương thức thanh toán, chẳng hạn như thẻ tín dụng.
- Nếu bạn mới sử dụng Firebase và Google Cloud, hãy kiểm tra xem bạn có đủ điều kiện nhận khoản tín dụng trị giá 300 USD và tài khoản Cloud Billing dùng thử miễn phí hay không.
- Nếu bạn tham gia lớp học lập trình này trong một sự kiện, hãy hỏi người tổ chức xem có khoản tín dụng Google Cloud nào không.
Để nâng cấp dự án lên gói Blaze, hãy làm theo các bước sau:
- Trong bảng điều khiển của Firebase, hãy chọn nâng cấp gói.
- Chọn gói Blaze. Làm theo hướng dẫn trên màn hình để liên kết tài khoản Cloud Billing với dự án của bạn.
Nếu cần tạo tài khoản Cloud Billing trong quá trình nâng cấp này, bạn có thể cần quay lại quy trình nâng cấp trong bảng điều khiển Firebase để hoàn tất quá trình nâng cấp.
Kết nối ứng dụng web với dự án Firebase
- Đăng ký ứng dụng web trong dự án Firebase bằng bảng điều khiển của Firebase:
- Mở dự án của bạn, rồi nhấp vào Thêm ứng dụng.
- Bỏ qua phần thiết lập SDK và cấu hình hiện tại, nhưng hãy nhớ sao chép đối tượng
firebaseConfig
đã tạo.
- Thay thế
firebaseConfig
hiện có trongapp/src/lib/firebase.tsx
bằng cấu hình bạn vừa sao chép từ bảng điều khiển Firebase.const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Tạo ứng dụng web: Quay lại VS Code, trong thư mục
app
, hãy sử dụng Vite để tạo ứng dụng web nhằm lưu trữ quá trình triển khai:cd app npm run build
Thiết lập tính năng Xác thực Firebase trong dự án Firebase
- Thiết lập Xác thực Firebase bằng tính năng Đăng nhập bằng Google.
- (Không bắt buộc) Cho phép các miền cho Xác thực Firebase bằng bảng điều khiển Firebase (ví dụ:
http://127.0.0.1
).- Trong phần cài đặt Xác thực, hãy chuyển đến Miền được uỷ quyền.
- Nhấp vào "Thêm miền" rồi thêm miền địa phương của bạn vào danh sách.
Triển khai bằng Giao diện dòng lệnh (CLI) của Firebase
- Trong
dataconnect/dataconnect.yaml
, hãy đảm bảo rằng mã nhận dạng thực thể, cơ sở dữ liệu và mã nhận dạng dịch vụ khớp với dự án của bạn:specVersion: "v1alpha" serviceId: "your-service-id" location: "us-central1" schema: source: "./schema" datasource: postgresql: database: "your-database-id" cloudSql: instanceId: "your-instance-id" connectorDirs: ["./movie-connector"]
- Đảm bảo bạn đã thiết lập Firebase CLI với dự án:
npm i -g firebase-tools firebase login --reauth firebase use --add
- Trong dòng lệnh, hãy chạy lệnh sau để triển khai:
firebase deploy --only dataconnect,hosting
- Chạy lệnh sau để so sánh các thay đổi về giản đồ:
firebase dataconnect:sql:diff
- Nếu bạn chấp nhận các thay đổi, hãy áp dụng bằng:
firebase dataconnect:sql:migrate
Phiên bản Cloud SQL cho PostgreSQL sẽ được cập nhật bằng giản đồ và dữ liệu đã triển khai cuối cùng. Bạn có thể theo dõi trạng thái trong bảng điều khiển của Firebase.
Bây giờ, bạn có thể xem ứng dụng của mình trực tiếp tại your-project.web.app/
. Ngoài ra, bạn có thể nhấp vào Run (Production) (Chạy (Bản chính thức)) trong bảng điều khiển Firebase Data Connect (Kết nối dữ liệu Firebase), giống như cách bạn đã làm với trình mô phỏng cục bộ, để thêm dữ liệu vào môi trường chính thức.
11. Không bắt buộc: Tìm kiếm vectơ bằng Firebase Data Connect (yêu cầu thanh toán)
Trong phần này, bạn sẽ bật tính năng tìm kiếm vectơ trong ứng dụng đánh giá phim bằng Firebase Data Connect. Tính năng này cho phép tìm kiếm dựa trên nội dung, chẳng hạn như tìm phim có nội dung mô tả tương tự bằng cách sử dụng các vectơ nhúng.
Bước này yêu cầu bạn phải hoàn tất bước cuối cùng của lớp học lập trình này để triển khai lên Google Cloud.
Cập nhật giản đồ để thêm các phần nhúng cho một trường
Trong dataconnect/schema/schema.gql
, hãy thêm trường descriptionEmbedding
vào bảng Movie
:
type Movie
# The below parameter values are generated by default with @table, and can be edited manually.
@table {
# implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
descriptionEmbedding: Vector @col(size:768) # Enables vector search
}
Điểm chính cần ghi nhớ:
descriptionEmbedding: Vector @col(size:768)
: Trường này lưu trữ các nội dung nhúng ngữ nghĩa của nội dung mô tả phim, cho phép tìm kiếm nội dung dựa trên vectơ trong ứng dụng.
Kích hoạt Vertex AI
- Làm theo hướng dẫn về điều kiện tiên quyết để thiết lập API Vertex AI từ Google Cloud. Bước này rất quan trọng để hỗ trợ chức năng tạo tính năng nhúng và tìm kiếm vectơ.
- Triển khai lại giản đồ để kích hoạt
pgvector
và tìm kiếm vectơ bằng cách nhấp vào "Triển khai công khai" bằng tiện ích Firebase Data Connect VS Code.
Điền nội dung nhúng vào cơ sở dữ liệu
- Mở thư mục
dataconnect
trong VS Code. - Nhấp vào Run(local) (Chạy (tại máy)) trong
optional_vector_embed.gql
để điền các nội dung nhúng cho phim vào cơ sở dữ liệu.
Thêm truy vấn tìm kiếm vectơ
Trong dataconnect/movie-connector/queries.gql
, hãy thêm truy vấn sau để thực hiện tìm kiếm vectơ:
# Search movie descriptions using L2 similarity with Vertex AI
query SearchMovieDescriptionUsingL2Similarity($query: String!)
@auth(level: PUBLIC) {
movies_descriptionEmbedding_similarity(
compare_embed: { model: "textembedding-gecko@003", text: $query }
method: L2
within: 2
limit: 5
) {
id
title
description
tags
rating
imageUrl
}
}
Điểm chính cần ghi nhớ:
compare_embed
: Chỉ định mô hình nhúng (textembedding-gecko@003
) và văn bản đầu vào ($query
) để so sánh.method
: Chỉ định phương thức tương đồng (L2
) đại diện cho khoảng cách Euclide.within
: Giới hạn nội dung tìm kiếm ở những bộ phim có khoảng cách L2 từ 2 trở xuống, tập trung vào những nội dung trùng khớp gần.limit
: Giới hạn số lượng kết quả trả về ở mức 5.
Triển khai hàm tìm kiếm vectơ trong ứng dụng
Giờ đây, khi đã thiết lập giản đồ và truy vấn, hãy tích hợp tính năng tìm kiếm vectơ vào lớp dịch vụ của ứng dụng. Bước này cho phép bạn gọi cụm từ tìm kiếm từ ứng dụng web.
- Trong
app/src/lib/
MovieService.ts
, hãy bỏ ghi chú các lệnh nhập sau đây từ SDK, lệnh này sẽ hoạt động như mọi truy vấn khác.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- Thêm hàm sau để tích hợp tính năng tìm kiếm dựa trên vectơ vào ứng dụng:
// Perform vector-based search for movies based on description export const searchMoviesByDescription = async ( query: string ): Promise< | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"] | null > => { try { const response = await searchMovieDescriptionUsingL2similarity({ query }); return response.data.movies_descriptionEmbedding_similarity; } catch (error) { console.error("Error fetching movie descriptions:", error); return null; } };
Điểm chính cần ghi nhớ:
searchMoviesByDescription
: Hàm này gọi truy vấnsearchMovieDescriptionUsingL2similarity
, truyền văn bản đầu vào để thực hiện tìm kiếm nội dung dựa trên vectơ.
Xem ví dụ thực tế
Chuyển đến phần "Tìm kiếm vectơ" trong thanh điều hướng rồi nhập các cụm từ như "lãng mạn và hiện đại". Bạn sẽ thấy danh sách phim phù hợp với nội dung bạn đang tìm kiếm. Ngoài ra, bạn có thể truy cập vào trang chi tiết của bất kỳ phim nào rồi xem phần phim tương tự ở cuối trang.
12. Kết luận
Xin chúc mừng! Bạn có thể sử dụng ứng dụng web! Nếu bạn muốn chơi với dữ liệu phim của riêng mình, đừng lo, hãy chèn dữ liệu của riêng bạn bằng tiện ích Firebase Data Connect bằng cách mô phỏng các tệp _insert.gql
hoặc thêm các tệp đó thông qua ngăn thực thi Data Connect trong VS Code.