Joomla Bài học 37: Xây dựng hệ thống Web Services (RESTful APIs) cho Custom Component
Trong các hệ thống phần mềm hiện đại, một Custom Component không chỉ phục vụ giao diện HTML hiển thị trên trình duyệt, mà còn phải cung cấp các giao diện lập trình ứng dụng (APIs) để tích hợp với Mobile App (Flutter/React Native), Single Page App (React/Vue.js) hoặc giao tiếp với các microservices khác qua HTTP.
Bắt đầu từ phiên bản Joomla 4 và được tối ưu hóa mạnh mẽ trên Joomla 5, hệ thống tích hợp sẵn một kiến trúc Web Services (RESTful APIs) chuẩn mực dựa trên nền tảng JSON:API. Trong Joomla Bài học 37, chúng ta sẽ tự tay xây dựng các endpoint API (GET, POST, PUT, DELETE) cho Custom Component com_custom mà không cần cài đặt thêm bất kỳ extension bên thứ ba nào như Joomla REST API hay Fabrik.
1. Kiến trúc Web Services trong Joomla 5
Hệ thống Web Services của Joomla hoạt động dựa trên các quy tắc chuẩn JSON:API Specification:
-
Điểm vào duy nhất (API Router): Mọi request gọi API đều đi qua tiền tố định tuyến
/api/index.php/v1/.... -
Component API Integration: Mỗi component muốn cung cấp API chỉ cần khai báo file định tuyến
api.xmlvà các lớpApiControllertương ứng trong không gian tênApi\Controller. -
Cơ chế xác thực (Authentication): Joomla hỗ trợ xác thực qua HTTP Basic Authentication (Dùng tài khoản User quản trị) hoặc Token-based Authentication (Token sinh ra từ User API Token trong Joomla 5).
2. Cấu trúc thư mục bổ sung cho Web Services (api/)
Để component com_custom hỗ trợ API, chúng ta bổ sung thư mục api/ vào cấu trúc gốc của component:
Plaintext
com_custom/
├── api/ <-- Khu vực xử lý Web Services (API)
│ ├── api.xml <-- Khai báo định tuyến API cho component
│ └── src/
│ └── Controller/
│ ├── ItemsController.php <-- Xử lý lấy danh sách (GET) và Thêm mới (POST)
│ └── ItemController.php <-- Xử lý lấy chi tiết (GET), Sửa (PUT) và Xóa (DELETE)
3. Khai báo định tuyến API (api/api.xml)
Tệp tin api.xml đóng vai trò map các đường dẫn URL của API với các Controller tương ứng.
Tạo file tại: src/administrator/components/com_custom/api.xml (hoặc đặt trong gói cài đặt component)
XML
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" method="upgrade">
<name>com_custom</name>
<!-- KHAI BÁO CÁC TUYẾN ĐƯỜNG API (ROUTES) -->
<routes>
<!-- Route cho danh sách bản ghi: GET /api/index.php/v1/custom/items -->
<route url="custom/items" component="com_custom" controller="Items" method="GET" />
<!-- Route cho thêm mới bản ghi: POST /api/index.php/v1/custom/items -->
<route url="custom/items" component="com_custom" controller="Items" method="POST" />
<!-- Route cho chi tiết / cập nhật / xóa một bản ghi: /api/index.php/v1/custom/items/{id} -->
<route url="custom/items/[id:\d+]" component="com_custom" controller="Item" method="GET" />
<route url="custom/items/[id:\d+]" component="com_custom" controller="Item" method="PUT" />
<route url="custom/items/[id:\d+]" component="com_custom" controller="Item" method="DELETE" />
</routes>
</extension>
4. Lập trình API Controller cho danh sách (api/src/Controller/ItemsController.php)
Controller này xử lý hai phương thức HTTP phổ biến: GET (Lấy danh sách bản ghi dưới dạng JSON) và POST (Tạo mới một bản ghi từ dữ liệu JSON gửi lên).
Tạo file tại: src/administrator/components/com_custom/api/src/Controller/ItemsController.php
PHP
<?php
namespace MyCompany\Component\Custom\Api\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\Component\MVC\Controller\ApiController;
use Joomla\CMS\Factory;
class ItemsController extends ApiController
{
protected $contentType = 'items';
protected $default_view = 'items';
/**
* Xử lý phương thức GET: Trả về danh sách dữ liệu định dạng JSON:API
*/
public function getList()
{
$app = Factory::getApplication();
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'alias', 'state', 'created']))
->from($db->quoteName('#__custom_items'))
->order($db->quoteName('id') . ' DESC');
$items = $db->setQuery($query)->loadAssocList();
// Trả về dữ liệu chuẩn JSON qua phương thức xuất dữ liệu của ApiController
return $this->sendResponse($items);
}
/**
* Xử lý phương thức POST: Tạo mới một bản ghi từ body JSON
*/
public function create()
{
$input = Factory::getApplication()->input;
$data = $input->json->getArray(); // Đọc dữ liệu JSON gửi lên từ client
if (empty($data['title'])) {
return $this->sendResponse(['error' => 'Tiêu đề không được để trống'], 400);
}
$db = Factory::getContainer()->get('DatabaseDriver');
$item = new \stdClass();
$item->title = $data['title'];
$item->alias = \Joomla\CMS\Filter\OutputFilter::stringURLSafe($data['title']);
$item->description = $data['description'] ?? '';
$item->state = 1;
$item->created = date('Y-m-d H:i:s');
try {
$db->insertObject('#__custom_items', $item, 'id');
$item->id = $db->insertid();
return $this->sendResponse($item, 201); // Trả về mã 201 Created
} catch (\Exception $e) {
return $this->sendResponse(['error' => $e->getMessage()], 500);
}
}
}
5. Lập trình API Controller cho một bản ghi đơn lẻ (api/src/Controller/ItemController.php)
Controller này xử lý các thao tác chi tiết trên một ID cụ thể: GET (Xem chi tiết), PUT (Cập nhật) và DELETE (Xóa bản ghi).
Tạo file tại: src/administrator/components/com_custom/api/src/Controller/ItemController.php
PHP
<?php
namespace MyCompany\Component\Custom\Api\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\Component\MVC\Controller\ApiController;
use Joomla\CMS\Factory;
class ItemController extends ApiController
{
protected $contentType = 'item';
/**
* Xử lý phương thức GET: Lấy chi tiết 1 bản ghi theo ID
*/
public function get($key = null)
{
$id = (int) $this->input->getInt('id');
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'title', 'alias', 'description', 'state', 'created']))
->from($db->quoteName('#__custom_items'))
->where($db->quoteName('id') . ' = ' . $id);
$item = $db->setQuery($query)->loadAssoc();
if (!$item) {
return $this->sendResponse(['error' => 'Không tìm thấy bản ghi'], 404);
}
return $this->sendResponse($item);
}
/**
* Xử lý phương thức DELETE: Xóa bản ghi theo ID
*/
public function delete($key = null)
{
$id = (int) $this->input->getInt('id');
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true)
->delete($db->quoteName('#__custom_items'))
->where($db->quoteName('id') . ' = ' . $id);
try {
$db->setQuery($query)->execute();
return $this->sendResponse(['success' => true, 'message' => 'Đã xóa bản ghi thành công #' . $id]);
} catch (\Exception $e) {
return $this->sendResponse(['error' => $e->getMessage()], 500);
}
}
}
6. Kiểm thử API bằng công cụ Postman / cURL
Sau khi đã hoàn thiện các file trên và cài đặt component, bạn có thể kiểm tra API trực tiếp thông qua các lệnh curl hoặc phần mềm Postman:
-
Lấy danh sách dữ liệu (GET):
Bash
curl -X GET "http://localhost:8080/api/index.php/v1/custom/items" \ -H "Accept: application/vnd.api+json" -
Thêm mới dữ liệu (POST - Yêu cầu xác thực tài khoản Admin qua Basic Auth):
Bash
curl -X POST "http://localhost:8080/api/index.php/v1/custom/items" \ -H "Content-Type: application/json" \ -H "Accept: application/vnd.api+json" \ -u "admin:your_password" \ -d '{"title": "Học lập trình API Joomla 5", "description": "Nội dung bài học RESTful API."}'
7. Tổng kết bài học
-
Hiểu rõ kiến trúc Web Services chuẩn JSON:API tích hợp sẵn trong Joomla 5.
-
Thành thạo việc cấu hình file định tuyến
api.xmlđể map các đường dẫn RESTful với các HTTP Methods (GET,POST,PUT,DELETE). -
Lập trình thành công các lớp
ApiControllerđể trả về phản hồi JSON an toàn và xử lý xác thực người dùng.
Trong Bài học 38, chúng ta sẽ mở rộng khả năng tùy biến của component với hệ thống sự kiện cốt lõi: Xây dựng hệ thống Plugin tích hợp (Plugins & Event Dispatcher) cho Custom Component.
All rights reserved