Cơ bản về Repository trong Laravel

Cơ bản về Repository trong Laravel

Design Patterns là gì ?

Design Pattern (mẫu thiết kế) là giải pháp chung để giải quyết các vấn đề phổ biến  khi thiết kế phần mềm trong lập trình hướng đối tượng OOP. Ví dụ:

Khi gặp vấn đề, mỗi developer sẽ có những phương án khác nhau để giải quyết. Tuy
nhiên các phương án đó chưa thực sự tối ưu. Do đó, Design Pattern sinh ra để giải 
quyết vấn đề này, khiến các vấn đề có tính chất tương tự được giải quyết trong một 
khuôn mẫu nhất định. Các khuôn mẫu này đã được chuẩn tối ưu nhất.

Việc sử dụng Design Pattern sẽ giúp chúng ta tiết kiệm thời gian và công sức để tìm cách giải quyết cho những vấn đề đã có phương án tối ưu. Tuy nhiên nó khá trừu tượng với những coder mới tìm hiểu.

Các framework hiện nay như Laravel, Codeigniter, Spring.. đều có sử dụng những Design Patterns có sẵn.

Repository Pattern Trong Laravel

Repository Pattern là một mẫu thiết kế trong Design Pattern. Repository là lớp trung gian nằm giữa BLL (Business Logic Layer) và DAL (Data Access Layer). Mục đích của Repository là để giúp cho việc truy cập dữ liệu chặt chẽ và bảo mật hơn.

Repository là phần trung gian giữa dữ liệu và logic

Hiểu một cách đơn giản, Repository sẽ nằm giữa lớp ModelController. Thay vì viết code xử lí trong Controller thì ta sẽ viết trong  Repository rồi gọi trong constructor của Controller.

Việc sử dụng Repository trong một dự án sẽ khiến Code dễ đọc hơn, hạn chế các lỗi trong việc truy vấn và lặp code.

Ví dụ: Trong phần lớn các ứng dụng Laravel, chúng ta sẽ gặp mã như sau trong Controller.

class UsersController extends Controller
{
   public function index()
   {
       $users = User::all();

       return view('users.index', [
           'users' => $users
       ]);
   }
}

Trông thì có vẻ không có vấn đề gì. Tuy nhiên nếu khách hàng đề xuất thay đổi các cấu trúc dữ liệu và thay vì trong MySQL hay PostgresSQL thì bây giờ chúng ta sẽ lưu dữ liệu ở một nơi khác, trong một công cụ mà không có Eloquent hỗ trợ thì việc viết như vậy sẽ dẫn đến tình trạng rất khó để thay đổi tất cả các Controller. Mọi việc triển khai phải dựa trên các interface . Trong trường hợp có sự thay đổi, bạn không cần thay đổi toàn bộ project mà chỉ cần tạo một interface. Ví dụ:

class UsersController extends Controller
{
   private $userRepository;

   public function __construct(UserRepositoryInterface $userRepository)
   {
       $this->userRepository = $userRepository;
   }

   public function index()
   {
       $users = $this->userRepository->all();

       return view('users.index', [
           'users' => $users
       ]);
   }
}

Với cách viết như thế này, khi phải thay đổi cấu trúc dữ liệu thì chúng ta sẽ tạo một lớp implement  UserRepositoryInterface và chứa các logic cho phép lấy data theo một cách mới.  

Cách xây dựng Repository trong Laravel

  1. Tạo base Repository

Tạo thư mục Repository trong app như sau:
-- app
---- Repository
------ Eloquent
-------- UserRepository.php
-------- BaseRepository.php
------ UserRepositoryInterface.php
------ EloquentRepositoryInterface.php

Hiểu nôm na, BaseRepositoryEloquentRepositoryInterface là các lớp cha chứa các methods được sử dụng phổ biến trong mọi repository. Trong đó, BaseRepository implements RepositoryInterface là nơi triển khai các phương thức như CRUD, còn RepositoryInterface là nơi khai báo các phương thức đó.

EloquentRepository

namespace App\Repository;


use Illuminate\Database\Eloquent\Model;

/
* Interface EloquentRepositoryInterface
* @package App\Repositories
*/
interface EloquentRepositoryInterface
{
   /
    * @param array $attributes
    * @return Model
    */
   public function create(array $attributes): Model;

   /**
    * @param $id
    * @return Model
    */
   public function find($id): ?Model;
}
...

BaseRepository.php

<?php

namespace App\Repository\Eloquent;

use App\Repository\EloquentRepositoryInterface; 
use Illuminate\Database\Eloquent\Model;

class BaseRepository implements EloquentRepositoryInterface 
{
    /
     * @var Model
     */
     protected $model;

    /
     * BaseRepository constructor.
     *
     * @param Model $model
     */
    public function __construct(Model $model)
    {
        $this->model = $model;
    }
 
    /
    * @param array $attributes
    *
    * @return Model
    */
    public function create(array $attributes): Model
    {
        return $this->model->create($attributes);
    }
 
    /
    * @param $id
    * @return Model
    */
    public function find($id): ?Model
    {
        return $this->model->find($id);
    }
    ...
}

2. Khai báo  Service Provider

php artisan make:provider RepositoryServiceProvider

Khai báo trong hàm register()

public function register() 
   { 
       $this->app->bind(EloquentRepositoryInterface::class, BaseRepository::class);
       $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
   }

Tiếp theo là khai báo Provider mà chúng ta vừa tạo ở config/app.php. Thêm vào 'providers' :

App\Providers\RepositoryServiceProvider::class

3. Viết Interface và Repository tương ứng với Model.

UserRepositoryInterface.php

<?php
namespace App\Repository;

use App\Model\User;
use Illuminate\Support\Collection;

interface UserRepositoryInterface
{
    // Lấy tất cả users
   public function all(): Collection;
}

UserRepository.php

<?php

namespace App\Repository\Eloquent;

use App\Model\User;
use App\Repository\UserRepositoryInterface;
use Illuminate\Support\Collection;

class UserRepository extends BaseRepository implements UserRepositoryInterface
{

   /
    * Hàm constructor.
    *
    * @param User $model
    */
   public function construct(User $model)
   {
       // Khai báo model User
       parent::construct($model);
   }

   /
    * @return Collection
    */
   public function all(): Collection
   {
       // Lấy hết các users
       return $this->model->all();
   }
}

4. Viết Controller

UserController.php

class UsersController extends Controller
{
   private $userRepository;
  
   public function __construct(UserRepositoryInterface $userRepository)
   {
       $this->userRepository = $userRepository;
   }

   public function index()
   {
       $users = $this->userRepository->all();

       return view('users.index', [
           'users' => $users
       ]);
   }
}

Tổng kết

Như vậy, chúng ta đã tìm hiểu cơ bản về Repository và thực hành. Trong trường hợp không muốn triển khai Eloquent, chúng ta thay đổi App\Repository\Eloquent\UserRepository.php ví dụ App\Repository\Mongo\UserRepository.php. Mọi thứ vẫn sẽ hoạt động bình thường ngay cả khi chúng ta thay đổi về phía dữ liệu.

Tài liệu tham khảo

How to use Repository Pattern in Laravel | LaraShout
The Repository Pattern in Laravel is a very useful pattern with a couple of great uses. One of the biggest advantage is the abstraction that it provides.
Laravel Repository Pattern – PHP Design Pattern | ASPER BROTHERS
Do you use the Laravel framework in your projects? See how to implement Pattern Repository. This will make your work easier and make the application architecture more readable.