REST API CRUD в Laravel 11 с лучшими практиками

Андрей Шагин
NOP::Nuances of Programming
5 min readAug 20, 2024

Приложение 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);

Теперь проект можно запускать.

Читайте также:

Читайте нас в Telegram, VK и Дзен

--

--