[Series] Xây dựng RESTful API từ con số 0 với PHP Thuần & MVC - Phần 17: Quản lý Danh mục (Category CRUD)
Chào các bạn, mình đã quay trở lại!
Sau khi đã làm chủ được module Sản phẩm ở Phần 16, việc xây dựng module Danh mục sẽ trở nên "dễ thở" hơn rất nhiều vì chúng ta đã nắm vững tư duy CRUD. Tuy nhiên, Danh mục lại có những đặc thù riêng như: ảnh đại diện của danh mục, mô tả ngắn và đặc biệt là slug để làm SEO URL.
Trong bài viết này, chúng ta sẽ thực hiện trọn bộ 5 API tiêu chuẩn để quản lý danh mục một cách chuyên nghiệp nhất.
1. Tầng Model: Quản lý dữ liệu Danh mục (Category.php)
Model Category sẽ đảm nhận việc giao tiếp với bảng Categories trong Database. Chúng ta vẫn trung thành với Prepared Statements để đảm bảo an toàn tuyệt đối.
File: app/Models/Category.php
<?php
namespace App\Models;
use App\Core\Database;
class Category {
protected $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* Lấy toàn bộ danh mục, mới nhất xếp trước
*/
public function getAll() {
return $this->db->query("SELECT * FROM Categories ORDER BY created_at DESC")->fetchAll();
}
/**
* Tìm danh mục theo ID
*/
public function findById($id) {
$stmt = $this->db->prepare("SELECT * FROM Categories WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
/**
* Tạo danh mục mới
*/
public function create($data) {
$stmt = $this->db->prepare("
INSERT INTO Categories (name, slug, description, image, created_at, updated_at)
VALUES (?, ?, ?, ?, NOW(), NOW())
");
$stmt->execute([
$data['name'],
$data['slug'],
$data['description'],
$data['image']
]);
return $this->db->lastInsertId();
}
/**
* Cập nhật thông tin danh mục
*/
public function update($id, $data) {
$stmt = $this->db->prepare("
UPDATE Categories SET name = ?, slug = ?, description = ?, image = ?, updated_at = NOW()
WHERE id = ?
");
return $stmt->execute([
$data['name'],
$data['slug'],
$data['description'],
$data['image'],
$id
]);
}
/**
* Xóa vĩnh viễn danh mục
*/
public function delete($id) {
$stmt = $this->db->prepare("DELETE FROM Categories WHERE id = ?");
return $stmt->execute([$id]);
}
}
2. Tầng Controller: Điều phối logic (CategoryController.php)
Controller sẽ xử lý các request từ Client, gọi Model và trả về JSON thông qua lớp Response thần thánh mà chúng ta đã xây dựng.
File: app/Controllers/CategoryController.php
<?php
namespace App\Controllers;
use App\Models\Category;
use App\Core\Response;
class CategoryController
{
public function index() {
$categories = (new Category())->getAll();
Response::json(['status' => 'success', 'categories' => $categories]);
}
public function show($id) {
$category = (new Category())->findById($id);
if (!$category) {
Response::json(['error' => 'Không tìm thấy danh mục này'], 404);
}
Response::json(['status' => 'success', 'category' => $category]);
}
public function store() {
$data = json_decode(file_get_contents("php://input"), true);
// Validation cơ bản
if (empty($data['name']) || empty($data['slug'])) {
Response::json(['error' => 'Tên và Slug danh mục là bắt buộc'], 422);
}
$id = (new Category())->create($data);
Response::json(['message' => 'Tạo danh mục thành công', 'id' => $id], 201);
}
public function update($id) {
$model = new Category();
if (!$model->findById($id)) {
Response::json(['error' => 'Danh mục không tồn tại'], 404);
}
$data = json_decode(file_get_contents("php://input"), true);
$model->update($id, $data);
Response::json(['message' => 'Cập nhật danh mục thành công']);
}
public function destroy($id) {
$model = new Category();
if (!$model->findById($id)) {
Response::json(['error' => 'Danh mục không tồn tại'], 404);
}
$model->delete($id);
Response::json(['message' => 'Đã xóa danh mục thành công']);
}
}
3. Cấu hình Route (index.php)
Đăng ký các "ngã rẽ" cho module Danh mục vào file index.php.
File: public/index.php
use App\Controllers\CategoryController;
$categoryController = new CategoryController();
// Nhóm API Danh mục
if ($uri === '/api/categories' && $method === 'GET') {
$categoryController->index();
} elseif (preg_match('#^/api/categories/(\d+)$#', $uri, $matches) && $method === 'GET') {
$categoryController->show($matches[1]);
} elseif ($uri === '/api/categories' && $method === 'POST') {
$categoryController->store();
} elseif (preg_match('#^/api/categories/(\d+)$#', $uri, $matches) && $method === 'PUT') {
$categoryController->update($matches[1]);
} elseif (preg_match('#^/api/categories/(\d+)$#', $uri, $matches) && $method === 'DELETE') {
$categoryController->destroy($matches[1]);
}
4. Kiểm thử API (Test with Curl)
Đến lúc xem thành quả rồi! Hãy bật terminal lên và thử các lệnh sau:
Tạo danh mục mới:
curl -X POST http://localhost:8000/api/categories \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop Gaming",
"slug": "laptop-gaming",
"description": "Dành cho các game thủ chuyên nghiệp",
"image": "https://example.com/gaming.jpg"
}'
Cập nhật danh mục ID 3:
curl -X PUT http://localhost:8000/api/categories/3 \
-H "Content-Type: application/json" \
-d '{"name": "Laptop Gaming (High-end)", "slug": "laptop-gaming-high-end"}'
Tạm kết
Vậy là chúng ta đã hoàn thiện module Danh mục. Giờ đây, bạn có thể gán các sản phẩm của mình vào những danh mục tương ứng để hệ thống trở nên ngăn nắp và chuyên nghiệp hơn. Hãy để lại bình luận phía dưới nhé! Đừng quên Upvote để ủng hộ mình ra bài đều đặn. Chúc bạn code vui vẻ, "bố đời"!
All rights reserved