Repository design pattern hoàn thiện trong Laravel

Bài viết được sự cho phép của BBT Tạp chí Lập trình

Trong bài viết này tôi sẽ chỉ cho bạn cách thiết lập Repository design pattern trong Laravel từ đầu. Tôi sẽ sử dụng phiên bản Laravel 5.8.3, nhưng phiên bản Laravel cũng không thực sự quá quan trọng. Trước khi chúng tôi bắt đầu code, có một vài điều bạn cần biết về repository design pattern.

Repository design pattern hoàn thiện trong Laravel

Repository Pattern là lớp trung gian giữa tầng Data Access và Business Logic. Repository design pattern cho phép bạn sử dụng các đối tượng mà không cần phải biết các đối tượng này được duy trì như thế nào. Về cơ bản nó là một sự trừu tượng hóa của lớp dữ liệu.

Điều này có nghĩa là Business Logic của bạn không cần phải biết cách lấy lại dữ liệu hay nguồn dữ liệu là gì. Business Logic dựa trên repository để lấy dữ liệu chính xác.

Một quan niệm sai lầm mà tôi thấy rất nhiều là các repository đang được thực hiện theo cách tạo hay cập nhật các bản ghi. Đây không phải là những gì mà repository nên làm. Các kho lưu trữ không nên tạo hoặc cập nhật dữ liệu, nhưng chỉ nên được sử dụng để truy xuất dữ liệu.

Tuyển dụng Laravel lương cao cho bạn

  18 designer hàng đầu dự đoán về xu hướng UI/ UX trong năm 2022
  30 tiện ích Chrome cho designer và dev

Hãy bắt tay vào việc code bây giờ.

Bởi vì tôi hướng dẫn các bạn làm từ đâu, đầu tiên ta phải tạo một project laravel trước.

php artisan config:clear

Đối với phần hướng dẫn này, tôi sẽ tạo ra một blog nhỏ. Bây giờ chúng tôi đã tạo một project, chúng tôi cần tạo Controller và Model cho blog.

php artisan config:clear

Cái này sẽ tạo BlogController trong tệp app/Http/Controllers

php artisan config:clear

Ghi chú: Lựa chọn -m sẽ tạo ra một Data Migration (chuyển đổi dữ liệu). File này có thể được tìm thấy trong database/migrations

Điều này sẽ tạo Model cho Blog của bạn và lưu trữ nó trong thư mục App / Models. Đây chỉ là một trong những cách để lưu trữ các Model của bạn, và là phương pháp mà tôi thích.

Bây giờ chúng ta đã có Controller và Model, đã đến lúc xem tệp chuyển đổi mà chúng ta đã tạo. Bây giờ blog cần một tiêu đề, nội dung và trường user_id; bên cạnh các trường timestamp (dấu thời gian) mà là mặc định của Laravel.

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateBlogsTable extends Migration
{
   public function up()
   {
       Schema::create('blogs', function (Blueprint $table) {
           $table->bigIncrements('id');
           $table->string('title');
           $table->text('content');
           $table->integer('user_id');
           $table->timestamps();
          
           $table->foreign('user_id')
                 ->references('id')
                 ->on('users');
       });
   }

   public function down()
   {
       Schema::dropIfExists('blogs');
   }
}

Ghi chú: Nếu bạn đang dùng phiên bản cũ hơn Laravel 5.8, bạn nên thay thế dòng

php artisan config:clear

Thiết lập kho database:

Tôi dùng MySQL cho ví dụ này. Bước đầu tiên là tạo một cơ sở dữ liệu mới.

mysql -u root -p
create database laravel_repository;

Điều này sẽ tạo ra một database gọi là laravel_repository. Tiếp theo chúng ta phải thêm thông tin cơ sở dữ liệu vào tệp .env

DB_DATABASE=laravel_repository
DB_USERNAME=root
DB_PASSWORD=secret

Điều này sẽ tạo ra một database gọi là laravel_repository. Tiếp theo chúng ta phải thêm thông tin cơ sở dữ liệu vào tệp .env

Sau khi bạn đã thay đổi tệp .env, chúng tôi phải xóa bộ đệm cấu hình:

php artisan config:clear

Chạy chuyển đổi dữ liệu

Sau khi ta đã set-up xong phần database, ta có thể bắt đầu chạy phần chuyển đổi dữ liệu

php artisan config:clear

Điều này sẽ tạo blog với các trường tiêu đề, nội dung và user_id mà chúng tôi đã khai báo trong chuyển đổi dữ liệu.

Thực hiện repository design pattern

Với những gì ta đã làm, bây giờ chúng ta có thể bắt đầu thực hiện repository design pattern. Chúng tôi sẽ bắt đầu bằng cách tạo thư mục Repositories trong thư mục App. Tiếp theo chúng ta sẽ tạo thư mục Interfaces. Thư mục này sẽ được đặt trong thư mục Repositories mà chúng ta vừa tạo.

Trong thư mục Interfaces, chúng ta tạo lớp BlogReositoryInterface với hai phương thức:

  • Phương thức all sẽ trả về tất cả các blog
  • Phương thức getByUser sẽ trả về tất cả các blog mà được tạo bởi một user cụ thể.
<?php

namespace App\Repositories\Interfaces;

use App\User;

interface BlogRepositoryInterface
{
   public function all();

   public function getByUser(User $user);
}

Lớp cuối cùng mà chúng ta sẽ tạo là lớp BlogRepository,  lớp này sẽ triển khai lớp BlogRepositoryInterface. Chúng tôi sẽ thực hiện việc nay đơn giản nhất có thể.

<?php

namespace App\Repositories;

use App\Models\Blog;
use App\User;
use App\Repositories\Interfaces\BlogRepositoryInterface;

class BlogRepository implements BlogRepositoryInterface
{
   public function all()
   {
       return Blog::all();
   }

   public function getByUser(User $user)
   {
       return Blog::where('user_id'. $user->id)->get();
   }
}

Thư mục Repositories của bạn nên trông như thế này:

app/
└── Repositories/
   ├── BlogRepository.php
   └── Interfaces/
       └── BlogRepositoryInterface.php

Bây giờ bạn đã thành công tạo repository! Giờ ta sẽ bắt đầu sử dụng nó.

Sử dụng Repository

Để bắt đầu sử dụng BlogRepository, chúng ta nên đưa nó vào BlogController. Vì repository sẽ được chèn, nên sẽ dễ dàng trao đổi nó với một thực thi khác. Controller sẽ trông như sau:

<?php

namespace App\Http\Controllers;


use App\Repositories\Interfaces\BlogRepositoryInterface;
use App\User;

class BlogController extends Controller
{
   private $blogRepository;

   public function __construct(BlogRepositoryInterface $blogRepository)
   {
       $this->blogRepository = $blogRepository;
   }

   public function index()
   {
       $blogs = $this->blogRepository->all();

       return view('blog')->withBlogs($blogs);
   }

   public function detail($id)
   {
       $user = User::find($id);
       $blogs = $this->blogRepository->getByUser($user);

       return view('blog')->withBlogs($blogs);
   }
}

Như bạn có thể thấy mã trong controller ngắn và do đó có thể đọc được. Bạn không cần mười dòng mã để có được bộ dữ liệu bạn muốn, tất cả có thể được thực hiện trong một dòng mã nhờ vào repository. Điều này cũng rất tốt cho kiểm thử đơn vị, vì các phương thức của repository có thể dễ dàng kiểm tra qua.

Repository design pattern cũng giúp dễ dàng thay đổi giữa các nguồn dữ liệu. Trong ví dụ này, chúng tôi đang sử dụng cơ sở dữ liệu để truy xuất blog của mình. Chúng ta dựa vào Eloquent để làm điều đó cho. Nhưng giả sử ta tìm thấy một blog  API tuyệt vời ở đâu đó trên mạngvà chúng tôi muốn sử dụng API này. Tất cả những gì chúng ta phải làm là viết lại BlogRepository để sử dụng API đó thay vì Eloquent.

RepositoryServiceProvider

Thay vì chèn BlogRepository vào trong BlogController, ta có thể chèn BlogRepositoryInterface và sau đó để Service Container quyết định repository nào sẽ được sử dụng. Điều này có thể được thực hiện trong phương thức boot của AppServiceProvider, nhưng tôi thích tạo 1 provider mới cho việc này để giữ mọi thứ clean.

php artisan config:clear

Lý do ta tạo ra một provider mới cho việc này là vì mọi thứ trở nên thực sự lộn xộn khi dự án của bạn bắt đầu phát triển. Hãy tưởng tượng một dự án với hơn 10 model và mỗi model đều có repository riêng. AppServiceProvider của bạn sẽ trở nên không thể đọc được.

RepositoryServiceProvider của bạn sẽ như sau:

<?php

namespace App\Providers;

use App\Repositories\BlogRepository;
use App\Repositories\Interfaces\BlogRepositoryInterface;
use Illuminate\Support\ServiceProvider;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(
            BlogRepositoryInterface::class, 
            BlogRepository::class
        );
    }
}

Hãy nhớ rằng việc hoán đổi BlogRepository với một repository khác dễ như thế nào.

Đừng quên thêm RepositoryServiceProvider vào danh sách các provider trong tệp config/app.php. Sau đó, chúng ta phải xóa bộ đệm cấu hình một lần nữa.

php artisan config:clear

Và bạn đã thực hiện thành công repository design pattern. Cũng không khó quá đúng không?

Link bài viết: https://itnext.io/repository-design-pattern-done-right-in-laravel-d177b5fa75d4

Dịch: Dương Nhật Minh

Bài viết gốc được đăng tải tại Tạp chí Lập trình

Có thể bạn quan tâm:

Xem thêm các tuyển dụng it đà nẵng hấp dẫn tại TopDev