Repository Pattern và Service Pattern trong Laravel thông qua ứng dụng Todo

Repository Pattern và Service Pattern trong Laravel thông qua ứng dụng Todo

Để áp dụng Repository Pattern một cách chuyên nghiệp và tuân thủ nguyên tắc Dependency Injection, chúng ta cần tạo interface cho Repository và đăng ký nó trong Service Provider của Laravel. Việc này giúp cho code dễ bảo trì hơn, cũng như tăng tính linh hoạt khi muốn thay đổi cách xử lý của Repository (ví dụ, chuyển sang dùng một repository khác mà không cần thay đổi service hoặc controller).

1. Tạo Interface cho TodoRepository

Tạo một interface để định nghĩa các phương thức mà TodoRepository cần triển khai. Interface này giúp tách biệt lớp Repository thực thi và dịch vụ sử dụng nó, tăng tính linh hoạt cho ứng dụng.

Tạo file app/Repositories/TodoRepositoryInterface.php:

<?php

namespace App\Repositories;

interface TodoRepositoryInterface
{
    public function getAll();

    public function create(array $data);

    public function find($id);

    public function update($id, array $data);

    public function delete($id);
}
PHP

2. Triển khai Interface trong TodoRepository

Viết code cho file TodoRepository để implement interface TodoRepositoryInterface. Điều này đảm bảo rằng TodoRepository phải tuân thủ các phương thức được định nghĩa trong interface.

Tạo file app/Repositories/TodoRepository.php:

<?php

namespace App\Repositories;

use App\Models\Todo;

class TodoRepository implements TodoRepositoryInterface
{
    public function getAll()
    {
        return Todo::all();
    }

    public function create(array $data)
    {
        return Todo::create($data);
    }

    public function find($id)
    {
        return Todo::findOrFail($id);
    }

    public function update($id, array $data)
    {
        $todo = Todo::findOrFail($id);
        $todo->update($data);
        return $todo;
    }

    public function delete($id)
    {
        return Todo::destroy($id);
    }
}
PHP

3. Đăng ký Binding trong Service Provider

Chúng ta cần đăng ký interface TodoRepositoryInterface với lớp triển khai TodoRepository để Laravel biết khi nào cần sử dụng TodoRepository thông qua interface.

Tạo Service Provider bằng lệnh sau nếu chưa có file RepositoryServiceProvider:

php artisan make:provider RepositoryServiceProvider
Bash

Mở file app/Providers/RepositoryServiceProvider.php và thêm binding cho interface và lớp triển khai:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Repositories\TodoRepositoryInterface;
use App\Repositories\TodoRepository;

class RepositoryServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(TodoRepositoryInterface::class, TodoRepository::class);
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}
PHP

4. Đăng ký RepositoryServiceProvider trong config/app.php

Để Laravel nhận diện Service Provider này, bạn cần đăng ký nó trong mảng providers ở file config/app.php:

'providers' => [
    // Các provider khác
    App\Providers\RepositoryServiceProvider::class,
],
PHP

5. Sử dụng Interface trong Service

Bây giờ, trong TodoService, bạn có thể sử dụng TodoRepositoryInterface để tăng tính linh hoạt và dễ bảo trì. Điều này cũng giúp bạn dễ dàng thay đổi sang các repository khác mà không phải sửa đổi nhiều mã nguồn.

Tạo file app/Services/TodoService.php như sau:

<?php

namespace App\Services;

use App\Repositories\TodoRepositoryInterface;
use Illuminate\Support\Facades\Storage;

class TodoService
{
    protected $todoRepository;

    public function __construct(TodoRepositoryInterface $todoRepository)
    {
        $this->todoRepository = $todoRepository;
    }

    public function getAllTodos()
    {
        return $this->todoRepository->getAll();
    }

    public function createTodo(array $data)
    {
        if (isset($data['image'])) {
            $data['image_path'] = $data['image']->store('images', 'public');
        }

        return $this->todoRepository->create($data);
    }

    public function updateTodo($id, array $data)
    {
        if (isset($data['image'])) {
            $todo = $this->todoRepository->find($id);
            if ($todo->image_path) {
                Storage::disk('public')->delete($todo->image_path);
            }
            $data['image_path'] = $data['image']->store('images', 'public');
        }

        return $this->todoRepository->update($id, $data);
    }

    public function deleteTodo($id)
    {
        $todo = $this->todoRepository->find($id);
        if ($todo->image_path) {
            Storage::disk('public')->delete($todo->image_path);
        }

        return $this->todoRepository->delete($id);
    }
}
PHP

6. Controller

Tạo app/Http/Controllers/TodoController.php để xử lý các request từ người dùng.

<?php

namespace App\Http\Controllers;

use App\Services\TodoService;
use Illuminate\Http\Request;

class TodoController extends Controller
{
    protected $todoService;

    public function __construct(TodoService $todoService)
    {
        $this->todoService = $todoService;
    }

    public function index()
    {
        $todos = $this->todoService->getAllTodos();
        return response()->json($todos);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);

        $todo = $this->todoService->createTodo($data);
        return response()->json($todo, 201);
    }

    public function update(Request $request, $id)
    {
        $data = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
            'status' => 'boolean',
        ]);

        $todo = $this->todoService->updateTodo($id, $data);
        return response()->json($todo);
    }

    public function destroy($id)
    {
        $this->todoService->deleteTodo($id);
        return response()->json(['message' => 'Todo deleted successfully']);
    }
}
PHP

7. Route

Thêm route vào file routes/api.php:

use App\Http\Controllers\TodoController;

Route::apiResource('todos', TodoController::class);
PHP

8. Feedback và Giải thích

  • Repository Pattern: Giúp dễ dàng thay đổi cách dữ liệu được lưu trữ hoặc truy xuất mà không cần thay đổi logic nghiệp vụ trong các service hoặc controller.
  • Service Pattern: Tách biệt logic nghiệp vụ khỏi controller, giúp dễ dàng quản lý và mở rộng các tính năng.
  • Xử lý hình ảnh: Lưu trữ hình ảnh trong thư mục public/images với cấu hình driver lưu trữ public.

Lời Kết

Việc sử dụng interface giúp chúng ta dễ dàng thay đổi implementation của TodoRepository trong tương lai mà không phải thay đổi toàn bộ các phần khác trong ứng dụng. Bằng cách này, bạn có thể tuân thủ nguyên tắc Dependency Inversion trong SOLID, giúp ứng dụng dễ bảo trì và mở rộng hơn.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *