0

Joomla Bài học 36: Xây dựng Custom Router (SEF URLs) giúp chuyển đổi URL thô thành đường dẫn thân thiện cho Custom Component

Ở các bài học trước, chúng ta đã xây dựng thành công giao diện hiển thị danh sách và chi tiết cho Custom Component com_custom. Tuy nhiên, khi bạn nhìn lên thanh địa chỉ trình duyệt, các đường dẫn vẫn đang hiển thị dưới dạng thô sơ truyền thống (Ví dụ: index.php?option=com_custom&view=item&id=5).

Khi bật chế độ SEF URLs (như đã học ở Bài 14), nếu không có một bộ định tuyến riêng, Joomla sẽ tạo ra các đường dẫn không tối ưu hoặc bị lỗi. Trong Joomla Bài học 36, chúng ta sẽ tiến hành xây dựng Custom Router chuẩn kiến trúc Joomla 5 (RouterView), giúp tự động biến đổi các URL thô thành các đường dẫn thân thiện với SEO (/custom-items/5-tieu-de-bai-viet).

1. Bản chất của Custom Router trong Joomla 5

Từ phiên bản Joomla 4 và hoàn thiện mạnh mẽ trên Joomla 5, hệ thống định tuyến sử dụng lớp trừu tượng RouterView. Thay vì phải viết các biểu thức chính quy (Regex) phức tạp để phân tích chuỗi URL như các nền tảng khác, Joomla định nghĩa Router dựa trên cấu trúc các Views của Component thông qua hai quá trình ngược nhau:

  1. Quá trình Build (Mã hóa từ URL thô →\rightarrow SEF URL): Khi code của bạn gọi hàm Route::_('index.php?option=com_custom&view=item&id=5'), Router sẽ bắt lấy các tham số view và id, tra cứu cơ sở dữ liệu để lấy alias tương ứng, sau đó ráp lại thành đường dẫn thân thiện (Ví dụ: /custom-items/5-tieu-de-bai-viet).

  2. Quá trình Parse (Giải mã từ SEF URL →\rightarrow URL thô): Khi khách truy cập gõ đường dẫn /custom-items/5-tieu-de-bai-viet vào trình duyệt, Router sẽ bóc tách các đoạn (Segments) trên URL, nhận diện đây là view item có ID là 5, rồi trả về các biến $_GET tiêu chuẩn để Component xử lý.

2. Cấu trúc thư mục và Vị trí file Router

Trong cấu trúc chuẩn của một Component hiện đại trên Joomla 5, tệp tin Router được đặt trong thư mục dịch vụ (Service):

Plaintext

com_custom/
└── admin/
    └── src/
        └── Service/
            └── Router.php              <-- Tệp tin định tuyến trung tâm của Component

3. Lập trình lớp Custom Router hoàn chỉnh (Service/Router.php)

Tạo file tại: src/administrator/components/com_custom/src/Service/Router.php

PHP

<?php
namespace MyCompany\Component\Custom\Service;

\defined('_JEXEC') or die;

use Joomla\CMS\Component\Router\RouterView;
use Joomla\CMS\Component\Router\RouterViewConfiguration;
use Joomla\CMS\Component\Router\Rules\StandardRules;
use Joomla\CMS\Component\Router\Rules\NomenuRules;
use Joomla\CMS\Component\Router\Rules\MenuRules;
use Joomla\Database\DatabaseInterface;
use Joomla\CMS\Application\SiteApplication;

class Router extends RouterView
{
    protected $db;

    public function __construct(SiteApplication $app, DatabaseInterface $db)
    {
        $this->db = $db;

        // 1. Định nghĩa cấu hình cho View 'items' (Danh sách)
        $items = new RouterViewConfiguration('items');
        $this->registerView($items);

        // 2. Định nghĩa cấu hình cho View 'item' (Chi tiết bản ghi theo ID)
        $item = new RouterViewConfiguration('item');
        $item->setKey('id'); // Khóa định danh bản ghi trên URL là 'id'
        $this->registerView($item);

        parent::__construct($app, $db);

        // 3. Gắn các quy tắc định tuyến tiêu chuẩn của Joomla
        $this->attachRule(new MenuRules($this));
        $this->attachRule(new StandardRules($this));
        $this->attachRule(new NomenuRules($this));
    }

    /**
     * Quá trình BUILD: Chuyển đổi ID thành Alias trên URL SEF
     */
    public function getItemSegment($id, $query)
    {
        db = $this->db;
        $query = $db->getQuery(true)
            ->select($db->quoteName('alias'))
            ->from($db->quoteName('#__custom_items'))
            ->where($db->quoteName('id') . ' = ' . (int) $id);

        $alias = $db->setQuery($query)->loadResult();

        if ($alias) {
            return [$id => $id . '-' . $alias];
        }

        return [$id => $id];
    }

    /**
     * Quá trình PARSE: Trích xuất ID từ Alias trên URL khi người dùng truy cập
     */
    public function getItemId($segment, $query)
    {
        // Tách lấy phần số ID ở đầu đoạn segment (Ví dụ: "5-tieu-de" -> lấy "5")
        $parts = explode('-', $segment, 2);
        $id = (int) $parts[0];

        if ($id > 0) {
            return $id;
        }

        // Fallback: Tìm kiếm trực tiếp bằng alias nếu cấu hình không dùng ID
        $db = $this->db;
        $query = $db->getQuery(true)
            ->select($db->quoteName('id'))
            ->from($db->quoteName('#__custom_items'))
            ->where($db->quoteName('alias') . ' = ' . $db->quote($segment));

        return (int) $db->setQuery($query)->loadResult();
    }
}

4. Đăng ký Router trong file Manifest (custom.xml)

Để Joomla nhận diện file Router.php vừa tạo, bạn cần khai báo thẻ <namespace> hoặc cấu hình dịch vụ trong file Manifest của Component (custom.xml):

XML

<!-- Thêm đoạn khai báo namespaces vào file custom.xml của component -->
<namespace path="src">MyCompany\Component\Custom</namespace>

Khi khai báo đúng Namespace này, Joomla 5 sẽ tự động tìm kiếm lớp MyCompany\Component\Custom\Service\Router mà không cần cấu hình file phụ trợ nào khác.

5. Tổng kết bài học

  • Hiểu rõ cơ chế hai chiều Build (Mã hóa URL thô sang SEF) và Parse (Giải mã SEF sang URL thô) của Custom Router trong Joomla 5.

  • Thành thạo việc kế thừa lớp RouterView và đăng ký các cấu hình view (items, item) bằng RouterViewConfiguration.

  • Viết thành công hai phương thức getItemSegment và getItemId để ánh xạ mượt mà giữa ID bản ghi và Alias trên thanh địa chỉ.

Trong Bài học 37, chúng ta sẽ tiến hành xây dựng các tính năng tương tác nâng cao tiếp theo cho Custom Component: Xây dựng hệ thống Web Services (RESTful APIs) cho Custom Component.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.