Как использовать паттерн Repository в приложении Laravel
В этой статье мы рассмотрим, как реализовать шаблон «Репозиторий» в ваших приложениях Laravel. Для этого мы создадим API для управления заказами, полученными от клиентов для компании.
Репозиторий можно определить как уровень абстракции между уровнями домена и отображения данных, который обеспечивает возможность посредничества между ними через интерфейс, подобный коллекции, для доступа к объектам домена.
Ключевым преимуществом шаблона «Репозиторий» является то, что он позволяет нам использовать принцип инверсии зависимостей (или кодировать абстракции, а не конкретизацию). Это делает наш код более устойчивым к изменениям, например, если позже было принято решение переключиться на источник данных, который не поддерживается Eloquent.
Начиная
Создайте новый проект Laravel и перейдите в каталог, используя следующие команды:
laravel new note_block cd note_block
Настройте базу данных
В этом уроке мы будем использовать MySQL в качестве базы данных. Для этого в файле .env обновите параметры, связанные с базой данных, как показано ниже.
Наконец, используя предпочитаемое вами приложение для управления базой данных, создайте новую базу данных с именем note_block.
Генерация исходных данных для базы данных
Мы создаем приложение для управления заказами, поэтому давайте создадим для него модель, выполнив следующую команду.
php artisan make:model User -a
-a
аргумент сообщает Artisan, что мы хотим создать файл миграции, сеялку, фабрику и контроллер для модели User.
Приведенная выше команда создаст пять новых файлов:
- controller in app/Http/Controllers/UserController.php - database factory in database/factories/UserFactory.php - migration file in database/migrations/YYYY_MM_DD_HHMMSS_create_users_table.php - model located in app/Models/User.php - seeder file in database/seeders/UserSeeder.php
В database/migrations/YYYY_MM_DD_HHMMSS_create_users_table.php
обновите функцию up, чтобы она соответствовала следующему.
public function up() { Schema::create('users', function (Blueprint $table) { $table->id(); $table->text('username'); $table->string('email'); $table->string('password'); $table->boolean('status')->default(false); $table->timestamps(); }); }
Как указано в файле миграции, таблица заказов будет иметь следующие столбцы:
Далее давайте обновим UserFactory, чтобы он мог генерировать фиктивных пользователей для заполнения базы данных. В database/factories/UserFactory.php
обновите функцию определения, чтобы она соответствовала информации в файле миграции.
Затем откройте database/seeders/UserSeeder.php
и обновите run
функцию, чтобы она соответствовала следующему.
public function run() { User::factory()->times(50)->create(); }
В database/seeders/DatabaseSeeder.php
добавьте следующее в функцию запуска.
$this->call([UserSeeder::class]);
Наконец, запустите миграцию и заполните базу данных, используя следующую команду.
php artisan migrate --seed
Создайте репозиторий
Прежде чем мы создадим репозиторий для модели Order, давайте определим интерфейс для указания всех методов, которые должен объявить репозиторий. Вместо того, чтобы напрямую полагаться на класс репозитория, наш контроллер (и любой пользовательский компонент, который мы можем создать в будущем) будет зависеть от интерфейса.
Это делает наш код гибким, поскольку, если в будущем возникнет необходимость внесения изменений, контроллер останется незатронутым. Например, если мы решили передать управление пользователями стороннему приложению, мы можем создать новый модуль, соответствующий сигнатуре UserRepositoryInterface, и поменять местами объявления привязки, и наш контроллер будет работать точно так, как ожидалось, не затрагивая ни единой строки кода в контроллер.
В каталоге приложения создайте новую папку с именем Contract
. Затем в этой папке создайте новый файл с именем UserRepositoryInterface.php
и добавьте в него следующий код.
<?php namespace App\Contract; use App\Models\User; interface UserRepositoryInterface { public function getAllUsers(); public function getUserById(User $user); public function deleteUser(User $user); public function createUser(array $attributes); public function updateUser(User $user, array $attributes); }
После этого в папке приложения создайте новую папку с именем Repositories
. В этой папке создайте новый файл с именем UserRepository.php
и добавьте в него следующий код.
<?php namespace App\Repositories; use App\Contract\UserRepositoryInterface; use App\Models\User; class UserRepository implements UserRepositoryInterface { public function getAllUsers() { return User::all(); } public function getUserById(User $user) { return $user; } public function deleteUser(User $user) { $user->delete(); } public function createUser(array $attributes) { return User::create($attributes); } public function updateUser(User $user, array $attributes) { return $user->update($attributes); } }
Помимо гибкости, обеспечиваемой интерфейсом, инкапсуляция запросов таким способом имеет дополнительное преимущество: нам не нужно дублировать запросы по всему приложению.
Создание контроллеров
Имея наш репозиторий, давайте добавим немного кода в наш контроллер. Откройте app/Http/Controllers/UsersController.php
и обновите код, чтобы он соответствовал следующему.
<?php namespace App\Http\Controllers; use App\Models\User; use App\Contract\UserRepositoryInterface; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\Hash; class UsersController extends Controller { public function __construct( private UserRepositoryInterface $repository ) { } public function index(): JsonResponse { return response()->json([ 'data' => $this->repository->getAllUsers() ]); } public function store(Request $request): JsonResponse { $attributes = $request->only([ 'username', 'email' ]); return response()->json( [ 'data' => $this->repository->createUser(array_merge($attributes, [ 'password' => Hash:make($request->input('password') ])) ], Response::HTTP_CREATED ); } public function show(User $user): JsonResponse { return response()->json([ 'data' => $user ]); } public function update(User $user, Request $request): JsonResponse { $attributes = $request->only([ 'username', 'email' ]); return response()->json([ 'data' => $this->repository->updateUser($user, $attributes) ]); } public function destroy(User $user): JsonResponse { $this->repository->deleteUser($user); return response()->json(null, Response::HTTP_NO_CONTENT); } }
Свяжите интерфейс и реализацию
Последнее, что нам нужно сделать, это связать UserRepository с UserRepositoryInterface в сервисном контейнере Laravel; мы делаем это через поставщика услуг. Создайте его, используя следующую команду.
php artisan make:provider RepositoryServiceProvider
Откройте app/Providers/RepositoryServiceProvider.php
и обновите функцию регистрации, чтобы она соответствовала следующему.
public function register() { $this->app->bind(UserRepositoryInterface::class, UserRepository::class); }
Не забудьте включить оператор импорта для UserRepository и UserRepositoryInterface.
Наконец, добавьте нового поставщика услуг в массив поставщиков в файле config/app.php
.
'providers' => [ // ...other declared providers App\Providers\RepositoryServiceProvider::class, ];
Добавление маршрутов
Чтобы сопоставить каждый метод, определенный в контроллере, с конкретными маршрутами, добавьте следующий код в routes/api.php
.
Route::get('users', [UsersController::class, 'index']); Route::get('users/{user}', [UsersController::class, 'show']); Route::post('users', [UsersController::class, 'store']); Route::put('users/{user}', [UsersController::class, 'update']); Route::delete('users/{user}', [UsersController::class, 'delete']);
Протестируйте приложение
Запустите приложение, используя следующую команду.
php artisan serve
По умолчанию обслуживаемое приложение будет доступно по адресу localhost:8000 . Используя Postman или cURL, мы можем отправлять запросы к нашему недавно созданному API.
Запустите следующую команду, чтобы проверить /api/users
конечную точку с помощью cURL:
Вывод
В этой статье мы узнали о шаблоне «Репозиторий» и о том, как его использовать в приложении Laravel. Мы также увидели некоторые преимущества, которые он предлагает в крупномасштабном проекте, одним из которых является слабосвязанный код, в котором мы пишем абстракции, а не конкретные реализации.