Xây dựng hệ thống điều hướng linh hoạt, phân quyền cho ứng dụng Laravel SaaS đa tenant

06 tháng 4, 2026·6 phút đọc

Bài viết chia sẻ giải pháp xây dựng module điều hướng toàn diện cho ứng dụng SaaS đa tenant sử dụng Laravel, với tính năng tự động lọc menu dựa trên vai trò, công ty và quyền truy cập của người dùng. Hệ thống hỗ trợ điều hướng cả trên desktop lẫn mobile, xử lý thông minh chuyển hướng tránh lỗi 403 và cho phép người dùng tùy chỉnh trang mặc định.

Xây dựng hệ thống điều hướng linh hoạt, phân quyền cho ứng dụng Laravel SaaS đa tenant

Xây dựng hệ thống điều hướng linh hoạt, phân quyền cho ứng dụng Laravel SaaS đa tenant

Trong các ứng dụng SaaS đa tenant, việc thiết kế menu điều hướng không chỉ đơn giản là tạo ra vài liên kết tĩnh, mà cần đáp ứng nhiều yêu cầu phức tạp như thay đổi menu theo người dùng đăng nhập, công ty đang hoạt động và các quyền hạn cụ thể. Bài viết này trình bày một module điều hướng hoàn chỉnh, được phát triển cho hệ thống CRM/ERP thực tế Kohana.io và hiện được tách ra trong LaraFoundry — một framework SaaS mã nguồn mở dành cho Laravel.

Thách thức trong điều hướng SaaS đa tenant

Một hệ thống SaaS đa tenant với phân quyền theo vai trò thường gặp các yêu cầu sau:

  • Admin có giao diện menu hoàn toàn riêng biệt so với người dùng bình thường (bảng điều khiển quản trị vs các module nghiệp vụ).
  • Chủ sở hữu công ty được xem tất cả các module (Đơn hàng, Kho, Kế toán, Sản xuất...).
  • Nhân viên chỉ thấy các module mà họ có quyền truy cập.
  • Mỗi module còn có sub-pages với các yêu cầu phân quyền khác nhau.
  • Menu phải cập nhật lại khi người dùng chuyển đổi giữa các công ty.
  • Dữ liệu menu phải hiển thị đồng nhất trên desktop (header 2 tầng) và mobile (menu hamburger có thể mở rộng).
  • Người dùng có thể chọn trang mặc định khi đăng nhập.
  • Hệ thống không được để xảy ra trang lỗi 403 mà phải chuyển hướng thông minh nếu người dùng truy cập trang bị cấm.

Kiến trúc tổng quan

Từ request, controller LayoutController::getLayout() gọi đến một service duy nhất LayoutDataService đảm nhận toàn bộ xử lý:

  • Lấy menu top-level cho desktop
  • Lấy menu sub-level cho desktop
  • Lấy sidebar quản lý công ty
  • Lấy cây menu cho mobile
  • Tính toán trang chuyển hướng thông minh (First Allowed Route)

Mọi method đều lấy danh sách menu và kiểm tra quyền truy cập người dùng thông qua hàm checkUserAndCompanyPolicy() để đảm bảo các mục menu được lọc chính xác.

Kết quả dữ liệu được truyền đến frontend qua middleware HandleInertiaRequests nhờ lazy loading, không cần API gọi thêm.

Frontend sử dụng Vue.js với 2 chế độ:

  • Desktop: hiển thị menu 2 tầng
  • Mobile: menu hamburger trượt kéo theo cấu trúc cây

Thiết kế backend với LayoutDataService

Toàn bộ điều hướng tập trung trong một class duy nhất giúp tập trung logic và dễ bảo trì.

Cấu trúc item menu

Mỗi mục menu có các thuộc tính như:

  • linkName (label được dịch)
  • linkUrl (đường dẫn)
  • policyName (mã quyền yêu cầu)
  • routeName (tên route để đối chiếu hiện hoạt)
  • linkIconSvg (biểu tượng)
  • isLinkActive (đánh dấu trang hiện tại)
  • companyItem (đánh dấu mục chuyên biệt cho công ty)

Lọc quyền truy cập

Hàm cốt lõi checkUserAndCompanyPolicy($policyName) xử lý toàn bộ luồng phân quyền:

  • Nếu là admin, luôn cho phép
  • Nếu mục menu công khai (public), cho phép với mọi người dùng đăng nhập
  • Với các quyền khác, kiểm tra người dùng có quyền trong công ty đang kích hoạt hay không
  • Chủ sở hữu công ty được xem tất cả các module bất kể quyền riêng lẻ

Điều này giúp loại bỏ các đoạn kiểm tra quyền rải rác và quản lý tập trung hơn.

Phân loại người dùng và menu

Loại người dùngMenu hiển thịCác module hiện trên menu
AdminBảng điều khiển adminNgười dùng, Công ty, Thanh toán...
Owner (Chủ công ty)Các module nghiệp vụTất cả 8 module + sidebar quản lý công ty
Nhân viênModule được phépChỉ module có quyền explicit

Chủ sở hữu luôn thấy đầy đủ, kể cả khi gói dịch vụ hết hạn, để họ nhận biết giới hạn ở tầng trang thay vì menu.

Điều hướng Sub-menu

Mỗi module không trỏ về route chính như /orders mà dẫn đến trang con mặc định người dùng đã chọn hoặc quy định trong hệ thống.

Các sub-route cũng phân quyền riêng biệt: ví dụ nhân viên có thể xem các tab kho hàng nhưng không được quyền quản trị cài đặt danh mục.

Mẫu First Allowed Route (FAR)

Mẫu này giúp tránh lỗi 403 bằng cách xác định trang hợp lệ đầu tiên cho người dùng dựa trên quyền hiện tại, dùng cho:

  • Chuyển hướng sau khi đăng nhập
  • Chuyển hướng khi truy cập trang bị cấm (403)
  • Cập nhật khi người dùng đổi công ty
  • Xác định đường dẫn trang chủ luôn hợp lệ

Trình xử lý ngoại lệ 403 sẽ tự động chuyển hướng đến route này với thông báo phù hợp.

Tùy chỉnh trang mặc định

Người dùng có thể lưu trang mặc định cho từng công ty trong profile, lưu trên bảng trung gian company_user.

Khi quyền thay đổi dẫn đến trang này không hợp lệ, hệ thống tự động xóa và cảnh báo người dùng.

Frontend: điều hướng trên desktop và mobile

  • Trên desktop, menu được render dưới dạng 2 tầng với tab trên cùng là module, dưới là các trang con. Trạng thái active được tính ở backend.

  • Trên mobile, menu chuyển sang cây phân cấp dạng list/accordion, với hiệu ứng trượt mượt nhờ CSS tăng tốc GPU.

  • Không dùng Vuex hay Pinia cho quản lý trạng thái, vue refs gọn nhẹ dùng để điều khiển các overlay menu.

  • Cạnh phải có pullout riêng dành cho profile, thông tin công ty, chọn ngôn ngữ, thao tác nhanh.

Testing toàn diện

Bộ test được viết với Pest bao phủ nhiều trường hợp:

  • Menu của admin biệt lập
  • Chủ công ty nhìn thấy đầy đủ module
  • Nhân viên chỉ thấy module được phép
  • Lọc phân quyền chính xác đến subpage
  • Xử lý mặc định và chuyển hướng khi quyền thay đổi
  • Thống nhất menu mobile/desktop
  • Các mục ẩn như thông báo, hỗ trợ vẫn truy cập được qua URL

Tóm tắt

Việc đẩy toàn bộ độ phức tạp về backend giúp frontend chỉ thuần render dữ liệu đã qua xử lý, tối giản logic client.

Một service duy nhất làm trung tâm dữ liệu điều hướng, phân quyền và routing, giúp dễ dàng bảo trì và mở rộng:

  • Thay đổi quyền cập nhật menu ngay lập tức
  • Thêm module đơn giản bằng cách thêm phần tử trong mảng cấu hình
  • Loại bỏ file cấu hình rời rạc hay bảng dữ liệu phức tạp

LaraFoundry hiện là dự án mã nguồn mở được phát triển công khai dựa trên kinh nghiệm xây dựng hệ thống CRM/ERP thực tế, mở ra giải pháp framework Laravel SaaS chuyên nghiệp.


Bài viết cung cấp cả góc nhìn kỹ thuật chi tiết và hướng đi thực tiễn cho các developers Laravel muốn xây dựng hệ thống SaaS đa tenant với yêu cầu quản lý điều hướng đa dạng và phân quyền phức tạp.

Bài viết được tổng hợp và biên soạn bằng AI từ các nguồn tin tức công nghệ. Nội dung mang tính tham khảo. Xem bài gốc ↗