Repository Pattern là gì ? Sử dụng Repository Pattern trong Laravel

0
42
Rate this post

Trong Laravel, Repository Pattern là một cách tổ chức mã nguồn mà chúng ta cần tìm hiểu. Dù bạn mới bắt đầu làm quen với Laravel hay làm thực tập tại công ty, từ khóa này cũng sẽ xuất hiện. Vậy Repository Pattern là gì? Có thực sự cần thiết không? Chúng ta cùng tìm hiểu nhé.

1. Repository Pattern – Khái niệm

Repository Pattern là một cách tổ chức mã nguồn trong Laravel. Bằng cách nhìn vào ảnh dưới đây, bạn có thể hiểu Repository Pattern là lớp trung gian nằm giữa tầng truy cập dữ liệu (Data Access) và tầng xử lý logic (Business Logic). Repository Pattern giúp việc truy cập dữ liệu trở nên chặt chẽ và bảo mật hơn.

Thường thì khi chúng ta muốn lấy dữ liệu từ cơ sở dữ liệu để hiển thị trên giao diện, chúng ta có thể đơn giản viết một Controller truy vấn cơ sở dữ liệu và lấy dữ liệu ra. Tuy nhiên, với Repository Pattern, chúng ta có thể tạo một lớp trung gian – Repository, nằm giữa Controller và Model. Khi có request gọi tới Controller, Controller sẽ gọi tới Repository để lấy và xử lý dữ liệu. Điều này giúp việc truy vấn dữ liệu trở nên dễ dàng và gọn lẹ hơn.

2. Sử dụng Repository Pattern trong Laravel

Giả sử bạn có một lớp “Post” và bạn muốn lấy danh sách bài viết theo thứ tự giảm dần của ID. Thì việc này đơn giản chỉ việc vào “PostController” và viết một hàm như sau:

public function getPost() {
    $posts = Post::orderBy('id', 'desc')->get();
    return view('post.index', compact('posts'));
}

Nhưng nếu viết theo Repository Pattern, chúng ta sẽ tạo một lớp mới là “PostRepository” trong thư mục “Repositories”.

<?php

    namespace AppRepositories;

    use AppModelsPost;

    class PostRepository {

        public function getPostById() {
            return Post::orderBy('id', 'desc')->get();
        }
    }

Và trong “PostController”, chúng ta sẽ thay đổi như sau:

class PostController extends Controller {

    protected $postRepository;

    public function __construct(PostRepository $postRepository) {
        $this->postRepository = $postRepository;
    }

    public function getPost() {
        $posts = $this->postRepository->getPostById();
        return view('post.index', compact('posts'));
    }
}

Đến đây, bạn có thể tự hỏi tại sao lại cần phải làm như vậy? Tại sao lại phải tạo thêm một lớp “PostRepository” khi ban đầu có thể lấy dữ liệu ngay từ Model mà không cần dùng đến lớp này?

Lợi ích của việc sử dụng Repository Pattern nằm ở chỗ: khi Controller gắn chặt và làm việc trực tiếp với Model, nếu Model thay đổi hoặc cấu trúc bảng thay đổi (ví dụ như tên cột “title” thay đổi thành “title_post”), chúng ta sẽ gặp rắc rối khi phải tìm code trong Controller để sửa. Hoặc khi khách hàng yêu cầu sắp xếp theo một tiêu chí khác (ví dụ như lượt xem giảm dần thay vì ID giảm dần), chúng ta lại phải sửa code trong Controller.

Để giải quyết vấn đề này, chúng ta tạo một Interface chung cho các loại repositories. Đầu tiên, tạo một thư mục “Contracts” và bên trong tạo thêm một thư mục “Repositories” để viết Interface chung. Tiếp theo, tạo một interface tên là “PostRepositoryInterface”. Ví dụ, một blog có các Interface như “TagRepositoryInterface”, “QuestionRepositoryInterface” và chúng đều nằm trong thư mục “AppContractsRepositories”.

namespace AppContractsRepositories;

interface PostRepositoryInterface {
    public function getPostById();
    ...
}

Để tránh việc phải viết lại các hàm chung trong từng Interface, chúng ta có thể tạo một Interface chung để khai báo các hàm cần thiết. Và các Interface sẽ extend từ Interface chung này.

namespace AppContractsRepositories;

interface AbstractRepositoryInterface {
    public function model();
    public function all();
    public function store(array $data);
    public function show($id);
    public function edit($id);
    ...
}

Nếu cần thêm một hàm riêng không có trong Interface chung, chúng ta có thể khai báo thêm trong từng Interface extend từ Interface chung.

namespace AppContractsRepositories;

interface PostRepositoryInterface extends AbstractRepositoryInterface {
    public function pending($id);
    public function getTags($id);
   ...
}

Sau đó, lớp “PostRepository” mà chúng ta đã viết ở trên sẽ implements từ “PostRepositoryInterface” như sau:

namespace AppRepositories;

use AppModelsPost;

class PostRepository implements PostRepositoryInterface {

    public function getPostById() {
        return Post::orderBy('id', 'desc')->get();
    }
}

Redis hay Mongo cũng sẽ implements từ “PostRepositoryInterface”. Và trong “PostController” chúng ta sẽ thay đổi như sau:

class PostController extends Controller {

    protected $postRepository;

    public function __construct(PostRepositoryInterface $postRepository) {
        $this->postRepository = $postRepository;
    }

    public function getPost() {
        $posts = $this->postRepository->getPostById();
        return view('post.index', compact('posts'));
    }
}

Với Laravel, khi chúng ta thay đổi “PostController” như vậy, sẽ bị lỗi vì “PostRepositoryInterface” là một Interface và không thể tạo instance được. Tuy nhiên, với Service Container của Laravel, chúng ta có thể bind Interface vào implement của nó. Ví dụ, chúng ta có thể tạo một “AppServiceProvider” riêng để bind Interface.

Trong phương thức “register()” của “AppServiceProvider”, chúng ta thêm đoạn mã sau:

public function register() {
    $this->app->bind(
        'AppContractsRepositoriesPostRepositoryInterface',
        'AppRepositoriesPostRepository'
    );
}

Nếu dự án cần bind nhiều Interface, tốt nhất là tạo một file riêng trong “app/Providers” thay vì dùng “AppServiceProvider”. Đồng thời, chúng ta phải khai báo file đó trong “config/app.php” và thêm vào “providers”.

'providers' => [
    ...
    AppProvidersRepositoryServiceProvider::class,
],

Quay trở lại với “PostController”, bây giờ chúng ta có thể inject “PostRepositoryInterface”. Nhớ thêm namespaces “PostRepositoryInterface” nữa nhé.

class PostController extends Controller {

    protected $postRepository;

    public function __construct(PostRepositoryInterface $postRepository) {
        $this->postRepository = $postRepository;
    }

    public function getPost() {
        $posts = $this->postRepository->getPostById();
        return view('post.index', compact('posts'));
    }
}

Như vậy, đã giới thiệu xong về Repository Pattern trong Laravel. Hi vọng bài viết này có thể giúp bạn hiểu và áp dụng Repository Pattern vào các dự án của mình. Để biết thêm thông tin, bạn có thể tham khảo các liên kết sau:


Bài viết đã được chỉnh sửa bởi Dnulib. Mời bạn đọc tham khảo thêm tại dnulib.edu.vn.