Приложение RESTful API CRUD на Laravel 11 с применением лучших практик создается поэтапно: настройка, определение маршрутов, проверка, модель, ресурсы, создание контроллеров, реализация шаблона проектирования Repository и работа с моделями. Вот пошаговое руководство.
Этап 1. Настройка Laravel
composer create-project --prefer-dist laravel/laravel rest-api-crud
Этап 2. Конфигурация базы данных Mysql
В Laravel 11 по умолчанию задано DB_CONNECTION=sqlite
. Меняем в файле .env на DB_CONNECTION=mysql
:
Этап 3. Создание модели Product с миграцией
php artisan make:model Product -a
Этап 4. Миграция
В database/migrations/YYYY_MM_DD_HHMMSS_create_products_table.php обновляем функцию up
:
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('details');
$table->timestamps();
});
}
Этап 5. Создание интерфейса Product
Создаем интерфейс репозитория для модели Product, при таком разделении код проще в сопровождении и чище:
php artisan make:interface /Interfaces/ProductRepositoryInterface
В Interfaces создаем файл ProductRepositoryInterface.php и добавляем в него такой код:
<?php
namespace App\Interfaces;
interface ProductRepositoryInterface
{
public function index();
public function getById($id);
public function store(array $data);
public function update(array $data,$id);
public function delete($id);
}
Этап 6. Создание класса репозитория Product
Создаем класс репозитория для модели Product:
php artisan make:class /Repositories/ProductRepository
В классах создаем файл ProductRepository.php и добавляем в него такой код:
<?php
namespace App\Repository;
use App\Models\Product;
use App\Interfaces\ProductRepositoryInterface;
class ProductReposiotry implements ProductRepositoryInterface
{
public function index(){
return Product::all();
}
public function getById($id){
return Product::findOrFail($id);
}
public function store(array $data){
return Product::create($data);
}
public function update(array $data,$id){
return Product::whereId($id)->update($data);
}
public function delete($id){
Product::destroy($id);
}
}
Этап 7. Привязка интерфейса и реализации
Чтобы привязать ProductRepository
к ProductRepositoryInterface
, создаем поставщика сервисов:
php artisan make:provider RepositoryServiceProvider
В app/Providers/RepositoryServiceProvider.php обновляем функцию register
:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Interfaces\ProductRepositoryInterface;
use App\Repository\ProductReposiotry;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Регистрируем сервисы.
*/
public function register(): void
{
$this->app->bind(ProductRepositoryInterface::class,ProductReposiotry::class);
}
/**
* Выполняем начальную загрузку сервисов.
*/
public function boot(): void
{
//
}
}
Этап 8. Проверка запросов
Имеется два запроса, StoreProductRequest и UpdateProductRequest, добавляем такой код:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
class StoreProductRequest extends FormRequest
{
/**
* Определяем, авторизован ли пользователь для выполнения этого запроса.
*/
public function authorize(): bool
{
return true;
}
/**
* Получаем правила проверки, применяемые к запросу.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'details' => 'required'
];
}
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'success' => false,
'message' => 'Validation errors',
'data' => $validator->errors()
]));
}
}
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
class UpdateProductRequest extends FormRequest
{
/**
* Определяем, авторизован ли пользователь для выполнения этого запроса.
*/
public function authorize(): bool
{
return true;
}
/**
* Получаем правила проверки, применяемые к запросу.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'details' => 'required'
];
}
public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'success' => false,
'message' => 'Validation errors',
'data' => $validator->errors()
]));
}
}
Этап 9. Создание класса ApiResponseClass
Этот класс ответов — наилучшая практика, потому что ответ отправляется функцией use
, создаем его:
php artisan make:class /Classes/ApiResponseClass
Добавляем такой код:
<?php
namespace App\Classes;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Support\Facades\Log;
class ApiResponseClass
{
public static function rollback($e, $message ="Something went wrong! Process not completed"){
DB::rollBack();
self::throw($e, $message);
}
public static function throw($e, $message ="Something went wrong! Process not completed"){
Log::info($e);
throw new HttpResponseException(response()->json(["message"=> $message], 500));
}
public static function sendResponse($result , $message ,$code=200){
$response=[
'success' => true,
'data' => $result
];
if(!empty($message)){
$response['message'] =$message;
}
return response()->json($response, $code);
}
}
Этап 10. Создание ресурса Product
Создаем ресурс Product:
php artisan make:resource ProductResource
Добавляем такой код:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
/**
* Преобразуем ресурс в массив.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' =>$this->id,
'name' => $this->name,
'details' => $this->details
];
}
}
Этап 11. Класс productcontroller
Репозиторий готов, добавим код в контроллер. Открываем app/Http/Controllers/ProductController.php и обновляем код:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use App\Interfaces\ProductRepositoryInterface;
use App\Classes\ResponseClass;
use App\Http\Resources\ProductResource;
use Illuminate\Support\Facades\DB;
class ProductController extends Controller
{
private ProductRepositoryInterface $productRepositoryInterface;
public function __construct(ProductRepositoryInterface $productRepositoryInterface)
{
$this->productRepositoryInterface = $productRepositoryInterface;
}
/**
* Отображаем листинг ресурса.
*/
public function index()
{
$data = $this->productRepositoryInterface->index();
return ResponseClass::sendResponse(ProductResource::collection($data),'',200);
}
/**
* Показываем форму для создания нового ресурса.
*/
public function create()
{
//
}
/**
* Сохраняем вновь созданный ресурс в хранилище.
*/
public function store(StoreProductRequest $request)
{
$details =[
'name' => $request->name,
'details' => $request->details
];
DB::beginTransaction();
try{
$product = $this->productRepositoryInterface->store($details);
DB::commit();
return ResponseClass::sendResponse(new ProductResource($product),'Product Create Successful',201);
}catch(\Exception $ex){
return ResponseClass::rollback($ex);
}
}
/**
* Отображаем указанный ресурс.
*/
public function show($id)
{
$product = $this->productRepositoryInterface->getById($id);
return ResponseClass::sendResponse(new ProductResource($product),'',200);
}
/**
* Показываем форму для редактирования указанного ресурса.
*/
public function edit(Product $product)
{
//
}
/**
* Обновляем указанный ресурс в хранилище.
*/
public function update(UpdateProductRequest $request, $id)
{
$updateDetails =[
'name' => $request->name,
'details' => $request->details
];
DB::beginTransaction();
try{
$product = $this->productRepositoryInterface->update($updateDetails,$id);
DB::commit();
return ResponseClass::sendResponse('Product Update Successful','',201);
}catch(\Exception $ex){
return ResponseClass::rollback($ex);
}
}
/**
* Удаляем указанный ресурс из хранилища.
*/
public function destroy($id)
{
$this->productRepositoryInterface->delete($id);
return ResponseClass::sendResponse('Product Delete Successful','',204);
}
}
Этап 12. Маршрут Api
Публикуем файл маршрута API:
php artisan install:api
Чтобы сопоставить с конкретными маршрутами каждый определенный в контроллере метод, добавляем в routes/api.php такой код:
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
Route::apiResource('/products',ProductController::class);
Теперь проект можно запускать.
Читайте также:
- Laravel: неизвестный, но эффективный способ реализации фильтров в Eloquent
- 3 альтернативы репозитория в Laravel
- 5 ведущих веб-фреймворков 2023 года
Читайте нас в Telegram, VK и Дзен
Перевод статьи Sandalanka: Laravel 11 REST API CRUD with best practices