0
0
Lập trình
NM

Xây Dựng API Động Với Symfony, Doctrine và MySQL

Đăng vào 3 tuần trước

• 11 phút đọc

Xây Dựng API Động Với Symfony, Doctrine và MySQL

Mục Lục

  1. Giới thiệu
  2. Thống kê
  3. Điểm Nổi Bật
  4. Sự Thật Thú Vị
  5. Câu Hỏi Thường Gặp
  6. Kết Luận
  7. Về Tác Giả

Giới thiệu

API trở nên giá trị hơn khi nó phục vụ dữ liệu động thực sự thay vì các phản hồi cố định. Trong hướng dẫn này, chúng ta sẽ mở rộng API Symfony của mình để kết nối với cơ sở dữ liệu MySQL, cho phép nó lưu trữ, truy xuất và trả về dữ liệu theo thời gian thực. Sử dụng Doctrine ORM, chúng ta sẽ ánh xạ các bảng cơ sở dữ liệu với các thực thể PHP, chạy các bản di chuyển để thiết lập lược đồ và tích hợp dữ liệu vào các điểm cuối API của chúng ta. Cuối cùng, bạn sẽ có một API hoàn toàn hoạt động, được cung cấp bởi cơ sở dữ liệu, sẵn sàng phục vụ bất kỳ ứng dụng frontend nào.

“Người chuyển núi bắt đầu bằng cách mang đi những viên đá nhỏ.” - Khổng Tử

Bước 1: Thiết Lập Cấu Hình Cơ Sở Dữ Liệu Trong Symfony

Trước khi chúng ta có thể lưu trữ và truy xuất dữ liệu, Symfony cần biết cách kết nối với cơ sở dữ liệu của bạn. Điều này được cấu hình trong tệp .env nằm ở thư mục gốc của dự án.

Mở tệp .env trong trình soạn thảo của bạn và tìm dòng DATABASE_URL.

Copy
DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"

Thay thế nó bằng thông tin kết nối MySQL của bạn. Ví dụ:

Copy
DATABASE_URL="mysql://root:root@127.0.0.1:3306/symfony-app?serverVersion=8.0.32&charset=utf8mb4"

Giải thích từng phần:

  • root:root: Tên người dùng và mật khẩu cơ sở dữ liệu.
  • 127.0.0.1:3306: Máy chủ và cổng nơi MySQL đang chạy.
  • symfony: Tên cơ sở dữ liệu của bạn.
  • serverVersion=8.0.32: Phiên bản MySQL chính xác mà bạn đang sử dụng.
  • charset=utf8mb4: Mã hóa ký tự để hỗ trợ toàn bộ Unicode (bao gồm cả emoji).

Mẹo: Lưu trữ thông tin nhạy cảm trong .env.local thay vì .env để nó không bị đẩy lên hệ thống kiểm soát phiên bản.

Bước 2: Cài Đặt Doctrine ORM

Doctrine là ORM (Object Relational Mapper) mặc định của Symfony. Nó hoạt động như một cầu nối giữa các đối tượng PHP (thực thể) và các bảng cơ sở dữ liệu. Chúng ta sẽ sử dụng nó để tạo bảng, lưu trữ dữ liệu và truy xuất nó trong API của chúng ta.

Để cài đặt Doctrine ORM và các công cụ liên quan, chạy lệnh sau:

Copy
composer require symfony/orm-pack
composer require --dev doctrine/doctrine-fixtures-bundle

Các gói này làm gì:

  • symfony/orm-pack: Cài đặt Doctrine ORM, bản di chuyển cơ sở dữ liệu và các tệp cấu hình.
  • doctrine/doctrine-fixtures-bundle: Cho phép bạn tải dữ liệu mẫu để thử nghiệm. (tùy chọn nhưng hữu ích trong quá trình phát triển)

Một khi đã cài đặt, Symfony sẽ tạo một tệp cấu hình mặc định tại config/packages/doctrine.yaml nơi lưu trữ các thiết lập của Doctrine. Nó cũng sẽ tự động sử dụng DATABASE_URL mà bạn đã thiết lập ở Bước 1.

Để xác minh rằng Doctrine đã được cài đặt, chạy:

Copy
php bin/console doctrine:database:create

Nếu mọi thứ được cấu hình chính xác, bạn sẽ thấy một thông báo xác nhận rằng cơ sở dữ liệu đã được tạo.

Copy
Created database `symfony-app` for connection named default

Bước 3: Tạo Thực Thể

Trong blog trước, điểm cuối /api/products của chúng ta đã trả về dữ liệu JSON cứng. Giờ đây, chúng ta sẽ làm cho nó trở nên động bằng cách kết nối nó với một bảng cơ sở dữ liệu lưu trữ các mục sản phẩm thực sự. Để làm điều này, chúng ta cần một thực thể Product mà Doctrine ORM sẽ ánh xạ tới một bảng cơ sở dữ liệu.

Một thực thể trong Symfony đại diện cho một bảng cơ sở dữ liệu, với mỗi thuộc tính ánh xạ tới một cột. Chúng ta sẽ tạo một thực thể Product chứa các trường title, descriptioncreatedAt.

Chạy lệnh sau trong terminal của bạn:

Copy
php bin/console make:entity

Khi được yêu cầu, nhập:

Định nghĩa thực thể Product với các trường title, description và createdAt.

Symfony sẽ tạo tệp src/Entity/Product.php. Nó sẽ giống như sau:

Copy
<?php

namespace App\Entity;

use App\Repository\ProductRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    private ?string $title = null;

    #[ORM\Column(type: Types::TEXT)]
    private ?string $description = null;

    #[ORM\Column]
    private ?\DateTimeImmutable $createdAt = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): static
    {
        $this->title = $title;

        return $this;
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setDescription(string $description): static
    {
        $this->description = $description;

        return $this;
    }

    public function getCreatedAt(): ?\DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeImmutable $createdAt): static
    {
        $this->createdAt = $createdAt;

        return $this;
    }
}

Bước 4: Chạy Các Bản Di Chuyển

Giờ đây, chúng ta đã tạo thực thể Product, chúng ta sẽ biến nó thành một bảng cơ sở dữ liệu thực sự để API của chúng ta có thể phục vụ các bản ghi thực tế. Symfony sử dụng các bản di chuyển Doctrine để chuyển đổi các định nghĩa thực thể thành các thay đổi lược đồ cơ sở dữ liệu. Chạy một bản di chuyển sẽ tạo bảng sản phẩm trong MySQL với các trường mà chúng ta đã định nghĩa ở bước 3.

Tạo tệp di chuyển

Copy
php bin/console make:migration

Tệp di chuyển được tạo thành công với make:migration.

Điều này tạo một lớp di chuyển mới trong thư mục migrations/. Nếu bạn mở nó, bạn sẽ thấy các câu lệnh SQL mà Doctrine sẽ thực hiện, ví dụ, tạo bảng sản phẩm với các cột id, title, description và created_at.

Áp dụng bản di chuyển vào cơ sở dữ liệu

Copy
php bin/console doctrine:migrations:migrate

Bạn sẽ được yêu cầu xác nhận. Gõ yes và nhấn Enter.

Bản di chuyển đã được áp dụng thành công với doctrine:migrations:migrate. Xác minh rằng bảng đã được tạo

Đăng nhập vào MySQL:

Copy
mysql -u root -p

Sau đó:

Copy
USE symfony-app;
SHOW TABLES;
DESCRIBE product;

Bạn nên thấy bảng sản phẩm của mình với các cột mà chúng ta đã định nghĩa trong thực thể Product.

Tại thời điểm này, ứng dụng Symfony của chúng ta đã sẵn sàng để lưu trữ và truy xuất dữ liệu sản phẩm từ cơ sở dữ liệu. Trong bước tiếp theo, chúng ta sẽ thêm một số bản ghi để API của chúng ta có thể trả về chúng thay vì JSON mẫu.

“Thực hiện tốt còn hơn nói tốt.” - Benjamin Franklin

Bước 5: Thêm và Truy Xuất Sản Phẩm từ Cơ Sở Dữ Liệu

Chúng ta sẽ sử dụng Doctrine Fixtures để chèn các bản ghi mẫu. Fixtures là một cách tiện lợi để tải dữ liệu vào cơ sở dữ liệu của bạn để phát triển và thử nghiệm.

Cài đặt gói orm-fixtures bằng composer

Copy
composer require orm-fixtures --dev

Tạo lớp Fixtures
Chạy:

Copy
php bin/console make:fixtures

Khi được yêu cầu:

Copy
Tên lớp của fixtures để tạo (ví dụ: AppFixtures):
 > ProductFixtures

Symfony sẽ tạo src/DataFixtures/ProductFixtures.php. Cập nhật nó như sau:

Copy
<?php

namespace App\DataFixtures;

use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;

class ProductFixtures extends Fixture
{
   public function load(ObjectManager $manager): void
   {
       $products = [
           ['title' => 'Sản phẩm mẫu', 'description' => 'Đây là mô tả sản phẩm mẫu.'],
           ['title' => 'Sản phẩm khác', 'description' => 'Mô tả ví dụ khác.'],
           ['title' => 'Sản phẩm thứ ba', 'description' => 'Một sản phẩm khác để thử nghiệm.'],
       ];

       foreach ($products as $data) {
           $product = new Product();
           $product->setTitle($data['title']);
           $product->setDescription($data['description']);
           // createdAt sẽ được thiết lập tự động bởi constructor
           $manager->persist($product);
       }

       $manager->flush();
   }
}

Chúng ta muốn mỗi bản ghi đều có dấu thời gian tạo mà không phải thiết lập trong fixtures hoặc biểu mẫu. Vì vậy, cập nhật src/Entity/Product.php constructor.

Copy
public function __construct()
{
    $this->createdAt = new \DateTimeImmutable();
}

Tải Fixtures vào Cơ Sở Dữ Liệu

Copy
php bin/console doctrine:fixtures:load

Bạn sẽ được yêu cầu xác nhận. Gõ yes. Doctrine sẽ chèn các bản ghi mẫu vào bảng sản phẩm.

Cập nhật API Controller để Truy Xuất từ Cơ Sở Dữ Liệu
Thay thế JSON cứng trong src/Controller/Api/ProductApiController.php bằng một truy vấn cơ sở dữ liệu:

Copy
<?php

namespace App\Controller\Api;

use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class ProductApiController extends AbstractController
{
    #[Route('/api/products', name: 'api_products', methods: ['GET'])]
    public function index(EntityManagerInterface $em): JsonResponse
    {
        $products = $em->getRepository(Product::class)->findAll();

        $data = [];
        foreach ($products as $product) {
            $data[] = [
                'title' => $product->getTitle(),
                'description' => $product->getDescription(),
            ];
        }

        return $this->json($data);
    }
}

Bây giờ, khi bạn truy cập http://localhost:8000/api/products, bạn sẽ thấy dữ liệu đến từ cơ sở dữ liệu thay vì các mảng cứng.

Bước 6: Xem Dữ Liệu Trong React

Giờ đây, thay vì trả về một mảng cố định, Symfony truy xuất các bản ghi trực tiếp từ cơ sở dữ liệu bằng Doctrine.

Mở ứng dụng React của bạn tại: http://localhost:5173

Frontend React bây giờ hiển thị các sản phẩm được truy xuất từ API Symfony với dữ liệu lưu trữ trong cơ sở dữ liệu.

Bạn sẽ thấy giao diện danh sách sản phẩm giống nhau, nhưng lần này nội dung đến từ cơ sở dữ liệu thông qua API của Symfony.

“API RESTful được xây dựng với Symfony cho bạn tự do để cung cấp bất kỳ thứ gì từ một ứng dụng React đơn giản đến một nền tảng doanh nghiệp, tất cả với cùng một backend sạch sẽ.” - Nhóm Kỹ Thuật SensioLabs

Thống kê

  • Báo cáo sử dụng chính thức của Symfony cho thấy hơn 600,000 nhà phát triển trên toàn cầu dựa vào Symfony và Doctrine ORM để xây dựng các ứng dụng mạnh mẽ.
  • Theo kho lưu trữ GitHub của Doctrine, ORM đã được tải xuống hơn 1 tỷ lần, khiến nó trở thành một trong những lớp trừu tượng cơ sở dữ liệu PHP được sử dụng nhiều nhất.

Điểm Nổi Bật

  • Symfony và Doctrine cung cấp một cách sạch để kết nối API với cơ sở dữ liệu.
  • Các thực thể ánh xạ các lớp PHP đến các bảng cơ sở dữ liệu, làm cho mã của bạn trở nên dễ hiểu và dễ bảo trì.
  • Doctrine Migrations xử lý việc cập nhật lược đồ một cách an toàn mà không cần SQL thủ công.
  • Fixtures giúp dễ dàng tải dữ liệu mẫu cho phát triển và thử nghiệm.
  • Bằng cách thay thế JSON cứng bằng các truy vấn cơ sở dữ liệu thực tế, API của bạn ngay lập tức trở nên động và sẵn sàng cho sản xuất.

Sự Thật Thú Vị

  • Doctrine ORM được sử dụng trong hàng ngàn dự án Symfony trên toàn thế giới, khiến nó trở thành một trong những ORM đã được thử thách trong thực tế nhất trong hệ sinh thái PHP.
  • Lớp cơ sở dữ liệu của Symfony có thể kết nối không chỉ với MySQL mà còn với PostgreSQL, SQLite và thậm chí Oracle.
  • Với trình tạo truy vấn của Doctrine, bạn có thể viết các truy vấn dễ hiểu trong PHP mà không cần chuyển đổi giữa SQL và mã PHP.

Câu Hỏi Thường Gặp

Q1: Tôi có thể sử dụng PostgreSQL thay vì MySQL với Symfony không?
A: Có. Doctrine hỗ trợ nhiều engine cơ sở dữ liệu. Bạn chỉ cần cập nhật DATABASE_URL trong .env

Q2: Tôi có cần fixtures trong môi trường sản xuất không?
A: Không. Fixtures chủ yếu dành cho phát triển và thử nghiệm. Trong môi trường sản xuất, bạn thường chèn dữ liệu thông qua ứng dụng của mình hoặc các bản di chuyển.

Q3: Điều gì xảy ra nếu tôi thay đổi thực thể của mình sau này?
A: Bạn có thể chạy php bin/console make:migration sau đó là php bin/console doctrine:migrations:migrate để cập nhật lược đồ cơ sở dữ liệu một cách an toàn.

Kết Luận

Việc chuyển từ dữ liệu cứng sang API dựa trên cơ sở dữ liệu là một cột mốc quan trọng trong phát triển backend. Với Symfony, Doctrine và MySQL, quá trình này rất đơn giản và thiết lập một nền tảng vững chắc cho các ứng dụng có thể mở rộng. Khi API của bạn được kết nối với dữ liệu thực, bạn mở khóa vô số khả năng, cho dù đó là cung cấp một frontend React, một ứng dụng di động, hay các hệ thống khách khác. Bước này biến API của bạn từ một bản demo thành một thứ sẵn sàng cho việc sử dụng trong thế giới thực.

Về Tác Giả

Balasaranya Varadhalingam, Kỹ Sư Phần Mềm tại AddWebSolution, chuyên về PHP, Symfony và phát triển API.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào