821 lines
20 KiB
Markdown
821 lines
20 KiB
Markdown
# Руководство по установке и настройке Laravel Sanctum с API авторизацией
|
||
|
||
## Описание
|
||
|
||
Данное руководство описывает процесс установки и настройки **Laravel Sanctum** для API аутентификации с использованием **wadakatu/laravel-spectrum** для автоматической генерации Swagger документации.
|
||
|
||
---
|
||
|
||
## Содержание
|
||
|
||
1. [Установка пакетов](#1-установка-пакетов)
|
||
2. [Настройка Sanctum](#2-настройка-sanctum)
|
||
3. [Структура API](#3-структура-api)
|
||
4. [API Endpoints](#4-api-endpoints)
|
||
5. [Использование API](#5-использование-api)
|
||
6. [Swagger документация](#6-swagger-документация)
|
||
7. [Команды Artisan](#7-команды-artisan)
|
||
|
||
---
|
||
|
||
## 1. Установка пакетов
|
||
|
||
### 1.1 Установка Laravel Sanctum
|
||
|
||
```bash
|
||
composer require laravel/sanctum
|
||
```
|
||
|
||
### 1.2 Установка wadakatu/laravel-spectrum
|
||
|
||
```bash
|
||
composer require wadakatu/laravel-spectrum
|
||
```
|
||
|
||
### 1.3 Публикация конфигураций
|
||
|
||
```bash
|
||
# Публикация Sanctum
|
||
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
|
||
|
||
# Публикация Spectrum (если требуется)
|
||
php artisan vendor:publish --provider="Wadakatu\LaravelSpectrum\LaravelSpectrumServiceProvider"
|
||
```
|
||
|
||
### 1.4 Запуск миграций
|
||
|
||
```bash
|
||
php artisan migrate
|
||
```
|
||
|
||
> **Примечание:** Если таблица `personal_access_tokens` уже существует, пропустите этот шаг.
|
||
|
||
---
|
||
|
||
## 2. Настройка Sanctum
|
||
|
||
### 2.1 Обновление модели User
|
||
|
||
Добавьте трейт `HasApiTokens` в модель `User`:
|
||
|
||
```php
|
||
// app/Models/User.php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||
use Illuminate\Notifications\Notifiable;
|
||
use Laravel\Sanctum\HasApiTokens;
|
||
|
||
class User extends Authenticatable
|
||
{
|
||
use HasApiTokens, HasFactory, Notifiable;
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### 2.2 Настройка middleware
|
||
|
||
Обновите файл `bootstrap/app.php`:
|
||
|
||
```php
|
||
// bootstrap/app.php
|
||
|
||
return Application::configure(basePath: dirname(__DIR__))
|
||
->withRouting(
|
||
web: __DIR__.'/../routes/web.php',
|
||
api: __DIR__.'/../routes/api.php',
|
||
commands: __DIR__.'/../routes/console.php',
|
||
health: '/up',
|
||
)
|
||
->withMiddleware(function (Middleware $middleware): void {
|
||
//
|
||
})
|
||
->withExceptions(function (Exceptions $exceptions): void {
|
||
//
|
||
})->create();
|
||
```
|
||
|
||
> **Важно:** Не используйте `statefulApi()` для чистого token-based API, это вызовет ошибку 419 CSRF.
|
||
|
||
### 2.3 Конфигурация Sanctum
|
||
|
||
```php
|
||
// config/sanctum.php
|
||
|
||
return [
|
||
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||
'%s%s',
|
||
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||
env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
|
||
))),
|
||
|
||
'guard' => ['web'],
|
||
|
||
'expiration' => null,
|
||
|
||
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
|
||
|
||
'middleware' => [
|
||
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
|
||
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
|
||
],
|
||
];
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Структура API
|
||
|
||
### 3.1 Директории
|
||
|
||
```
|
||
app/
|
||
├── Http/
|
||
│ ├── Controllers/
|
||
│ │ └── Api/
|
||
│ │ ├── AuthController.php
|
||
│ │ └── PostController.php
|
||
│ ├── Requests/
|
||
│ │ ├── LoginRequest.php
|
||
│ │ ├── RegisterRequest.php
|
||
│ │ ├── StorePostRequest.php
|
||
│ │ └── UpdatePostRequest.php
|
||
│ └── Resources/
|
||
│ ├── PostCollection.php
|
||
│ ├── PostResource.php
|
||
│ └── UserResource.php
|
||
└── Policies/
|
||
└── PostPolicy.php
|
||
```
|
||
|
||
### 3.2 API Resources
|
||
|
||
**Laravel Spectrum** автоматически определяет структуру ответа из API Resources.
|
||
|
||
```php
|
||
// app/Http/Resources/UserResource.php
|
||
|
||
namespace App\Http\Resources;
|
||
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Http\Resources\Json\JsonResource;
|
||
|
||
class UserResource extends JsonResource
|
||
{
|
||
public function toArray(Request $request): array
|
||
{
|
||
return [
|
||
'id' => $this->id,
|
||
'name' => $this->name,
|
||
'email' => $this->email,
|
||
'role' => $this->role,
|
||
'created_at' => $this->created_at,
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
```php
|
||
// app/Http/Resources/PostResource.php
|
||
|
||
namespace App\Http\Resources;
|
||
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Http\Resources\Json\JsonResource;
|
||
|
||
class PostResource extends JsonResource
|
||
{
|
||
public function toArray(Request $request): array
|
||
{
|
||
return [
|
||
'id' => $this->id,
|
||
'title' => $this->title,
|
||
'content' => $this->content,
|
||
'user_id' => $this->user_id,
|
||
'user' => new UserResource($this->whenLoaded('user')),
|
||
'created_at' => $this->created_at,
|
||
'updated_at' => $this->updated_at,
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.3 API Controllers
|
||
|
||
**Laravel Spectrum** автоматически анализирует контроллеры для генерации документации.
|
||
|
||
#### AuthController
|
||
|
||
```php
|
||
// app/Http/Controllers/Api/AuthController.php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Requests\LoginRequest;
|
||
use App\Http\Requests\RegisterRequest;
|
||
use App\Http\Resources\UserResource;
|
||
use App\Models\User;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Hash;
|
||
|
||
class AuthController extends Controller
|
||
{
|
||
/**
|
||
* Register a new user.
|
||
*/
|
||
public function register(RegisterRequest $request): JsonResponse
|
||
{
|
||
$user = User::create([
|
||
'name' => $request->name,
|
||
'email' => $request->email,
|
||
'password' => Hash::make($request->password),
|
||
]);
|
||
|
||
$token = $user->createToken('auth-token')->plainTextToken;
|
||
|
||
return response()->json([
|
||
'message' => 'User registered successfully',
|
||
'user' => new UserResource($user),
|
||
'token' => $token,
|
||
], 201);
|
||
}
|
||
|
||
/**
|
||
* Login user.
|
||
*/
|
||
public function login(LoginRequest $request): JsonResponse
|
||
{
|
||
$user = User::where('email', $request->email)->first();
|
||
|
||
if (!$user || !Hash::check($request->password, $user->password)) {
|
||
return response()->json([
|
||
'message' => 'Invalid credentials',
|
||
], 401);
|
||
}
|
||
|
||
$token = $user->createToken('auth-token')->plainTextToken;
|
||
|
||
return response()->json([
|
||
'message' => 'Login successful',
|
||
'user' => new UserResource($user),
|
||
'token' => $token,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Logout user.
|
||
*/
|
||
public function logout(Request $request): JsonResponse
|
||
{
|
||
$request->user()->currentAccessToken()->delete();
|
||
|
||
return response()->json([
|
||
'message' => 'Logged out successfully',
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Get authenticated user.
|
||
*/
|
||
public function user(Request $request): JsonResponse
|
||
{
|
||
return response()->json([
|
||
'user' => new UserResource($request->user()),
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### PostController
|
||
|
||
```php
|
||
// app/Http/Controllers/Api/PostController.php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Requests\StorePostRequest;
|
||
use App\Http\Requests\UpdatePostRequest;
|
||
use App\Http\Resources\PostCollection;
|
||
use App\Http\Resources\PostResource;
|
||
use App\Models\Post;
|
||
use Illuminate\Http\JsonResponse;
|
||
|
||
class PostController extends Controller
|
||
{
|
||
/**
|
||
* Display a listing of posts.
|
||
*/
|
||
public function index(): PostCollection
|
||
{
|
||
$posts = Post::with('user')->latest()->paginate(15);
|
||
return new PostCollection($posts);
|
||
}
|
||
|
||
/**
|
||
* Store a newly created post.
|
||
*/
|
||
public function store(StorePostRequest $request): JsonResponse
|
||
{
|
||
$post = Post::create([
|
||
'title' => $request->title,
|
||
'content' => $request->content,
|
||
'user_id' => $request->user()->id,
|
||
]);
|
||
|
||
$post->load('user');
|
||
|
||
return response()->json([
|
||
'message' => 'Post created successfully',
|
||
'post' => new PostResource($post),
|
||
], 201);
|
||
}
|
||
|
||
/**
|
||
* Display the specified post.
|
||
*/
|
||
public function show(Post $post): JsonResponse
|
||
{
|
||
$post->load('user');
|
||
return response()->json([
|
||
'post' => new PostResource($post),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Update the specified post.
|
||
*/
|
||
public function update(UpdatePostRequest $request, Post $post): JsonResponse
|
||
{
|
||
$this->authorize('update', $post);
|
||
|
||
$post->update($request->validated());
|
||
|
||
return response()->json([
|
||
'message' => 'Post updated successfully',
|
||
'post' => new PostResource($post),
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Remove the specified post.
|
||
*/
|
||
public function destroy(Post $post): JsonResponse
|
||
{
|
||
$this->authorize('delete', $post);
|
||
|
||
$post->delete();
|
||
|
||
return response()->json([
|
||
'message' => 'Post deleted successfully',
|
||
]);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.4 Form Requests
|
||
|
||
**Laravel Spectrum** автоматически определяет правила валидации из Form Requests.
|
||
|
||
```php
|
||
// app/Http/Requests/RegisterRequest.php
|
||
|
||
namespace App\Http\Requests;
|
||
|
||
use Illuminate\Foundation\Http\FormRequest;
|
||
|
||
class RegisterRequest extends FormRequest
|
||
{
|
||
public function authorize(): bool
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public function rules(): array
|
||
{
|
||
return [
|
||
'name' => ['required', 'string', 'max:255'],
|
||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
```php
|
||
// app/Http/Requests/LoginRequest.php
|
||
|
||
namespace App\Http\Requests;
|
||
|
||
use Illuminate\Foundation\Http\FormRequest;
|
||
|
||
class LoginRequest extends FormRequest
|
||
{
|
||
public function authorize(): bool
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public function rules(): array
|
||
{
|
||
return [
|
||
'email' => ['required', 'string', 'email'],
|
||
'password' => ['required', 'string'],
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
```php
|
||
// app/Http/Requests/StorePostRequest.php
|
||
|
||
namespace App\Http\Requests;
|
||
|
||
use Illuminate\Foundation\Http\FormRequest;
|
||
|
||
class StorePostRequest extends FormRequest
|
||
{
|
||
public function authorize(): bool
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public function rules(): array
|
||
{
|
||
return [
|
||
'title' => ['required', 'string', 'max:255'],
|
||
'content' => ['required', 'string'],
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. API Endpoints
|
||
|
||
### 4.1 Маршруты
|
||
|
||
```php
|
||
// routes/api.php
|
||
|
||
use App\Http\Controllers\Api\AuthController;
|
||
use App\Http\Controllers\Api\PostController;
|
||
use Illuminate\Support\Facades\Route;
|
||
|
||
/*
|
||
|--------------------------------------------------------------------------
|
||
| API Routes
|
||
|--------------------------------------------------------------------------
|
||
*/
|
||
|
||
// Swagger Documentation - редирект на Swagger UI
|
||
Route::get('/docs', function () {
|
||
return redirect()->to('/spectrum/openapi.html');
|
||
});
|
||
|
||
// Public routes (без авторизации)
|
||
Route::post('/register', [AuthController::class, 'register'])->name('api.register');
|
||
Route::post('/login', [AuthController::class, 'login'])->name('api.login');
|
||
|
||
// Protected routes (требуют авторизации)
|
||
Route::middleware('auth:sanctum')->group(function () {
|
||
// Auth
|
||
Route::post('/logout', [AuthController::class, 'logout'])->name('api.logout');
|
||
Route::get('/user', [AuthController::class, 'user'])->name('api.user');
|
||
|
||
// Posts
|
||
Route::get('/posts', [PostController::class, 'index'])->name('api.posts.index');
|
||
Route::post('/posts', [PostController::class, 'store'])->name('api.posts.store');
|
||
Route::get('/posts/{post}', [PostController::class, 'show'])->name('api.posts.show');
|
||
Route::put('/posts/{post}', [PostController::class, 'update'])->name('api.posts.update');
|
||
Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('api.posts.destroy');
|
||
});
|
||
```
|
||
|
||
### 4.2 Список Endpoints
|
||
|
||
| Метод | Endpoint | Описание | Авторизация |
|
||
|-------|----------|----------|-------------|
|
||
| POST | `/api/register` | Регистрация | Нет |
|
||
| POST | `/api/login` | Вход | Нет |
|
||
| POST | `/api/logout` | Выход | Да |
|
||
| GET | `/api/user` | Текущий пользователь | Да |
|
||
| GET | `/api/posts` | Список постов | Да |
|
||
| POST | `/api/posts` | Создание поста | Да |
|
||
| GET | `/api/posts/{id}` | Просмотр поста | Да |
|
||
| PUT | `/api/posts/{id}` | Обновление поста | Да |
|
||
| DELETE | `/api/posts/{id}` | Удаление поста | Да |
|
||
| GET | `/api/docs` | OpenAPI спецификация | Нет |
|
||
|
||
---
|
||
|
||
## 5. Использование API
|
||
|
||
### 5.1 Регистрация
|
||
|
||
```bash
|
||
curl -X POST http://la.test/api/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"name": "John Doe",
|
||
"email": "john@example.com",
|
||
"password": "password123",
|
||
"password_confirmation": "password123"
|
||
}'
|
||
```
|
||
|
||
### 5.2 Вход
|
||
|
||
```bash
|
||
curl -X POST http://la.test/api/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"email": "john@example.com",
|
||
"password": "password123"
|
||
}'
|
||
```
|
||
|
||
### 5.3 Создание поста
|
||
|
||
```bash
|
||
curl -X POST http://la.test/api/posts \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer {token}" \
|
||
-d '{
|
||
"title": "My First Post",
|
||
"content": "This is the content of my post."
|
||
}'
|
||
```
|
||
|
||
### 5.4 Выход
|
||
|
||
```bash
|
||
curl -X POST http://la.test/api/logout \
|
||
-H "Authorization: Bearer {token}"
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Swagger документация
|
||
|
||
### 6.1 Доступ к документации
|
||
|
||
Документация Swagger UI доступна по адресу:
|
||
|
||
```
|
||
{APP_URL}/api/docs
|
||
```
|
||
|
||
Например: `http://la.test/api/docs`
|
||
|
||
### 6.2 Генерация документации
|
||
|
||
Laravel Spectrum генерирует готовый HTML с Swagger UI:
|
||
|
||
```bash
|
||
php artisan spectrum:generate --format=html
|
||
```
|
||
|
||
Это создаёт файл `public/spectrum/openapi.html`.
|
||
|
||
### 6.3 Настройка доступа
|
||
|
||
При генерации с флагом `--format=html` symlink создаётся автоматически.
|
||
|
||
Если symlink отсутствует, создайте его вручную:
|
||
|
||
```bash
|
||
ln -sf /home/user/www/lara/storage/app/spectrum /home/user/www/lara/public/spectrum
|
||
```
|
||
|
||
### 6.4 Файлы документации
|
||
|
||
```
|
||
public/
|
||
└── spectrum/
|
||
├── openapi.html # Swagger UI (генерируется с --format=html)
|
||
└── openapi.json # OpenAPI спецификация
|
||
```
|
||
|
||
### 6.5 Маршрут для документации
|
||
|
||
```php
|
||
// routes/api.php
|
||
|
||
// Swagger Documentation
|
||
Route::get('/docs', function () {
|
||
return redirect()->to('/spectrum/openapi.html');
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Команды Laravel Spectrum
|
||
|
||
### 7.1 Основные команды
|
||
|
||
Laravel Spectrum поставляется с готовыми командами для работы с документацией.
|
||
|
||
| Команда | Описание |
|
||
|---------|----------|
|
||
| `php artisan spectrum:generate` | Генерация OpenAPI документации |
|
||
| `php artisan spectrum:watch` | Режим реального времени (port 8080) |
|
||
| `php artisan spectrum:mock` | Запуск mock сервера (port 8081) |
|
||
| `php artisan spectrum:cache clear` | Очистка кэша анализа |
|
||
|
||
### 7.2 Генерация документации
|
||
|
||
```bash
|
||
# Генерация документации в формате HTML
|
||
php artisan spectrum:generate --format=html
|
||
```
|
||
|
||
**Вывод:**
|
||
```
|
||
🚀 Generating API documentation...
|
||
🔍 Analyzing routes...
|
||
Found 9 API routes
|
||
📝 Generating OpenAPI specification...
|
||
✅ Documentation generated: /home/user/www/lara/public/spectrum/openapi.html
|
||
⏱️ Generation completed in 0.17 seconds
|
||
💾 Cache: 5 files, 7.04 KB
|
||
✅ Documentation generated successfully!
|
||
```
|
||
|
||
### 7.3 Режим реального времени
|
||
|
||
```bash
|
||
# Запуск режима watch
|
||
php artisan spectrum:watch
|
||
```
|
||
|
||
- Запускает локальный сервер на `http://localhost:8080`
|
||
- Автоматически обнаруживает изменения в файлах
|
||
- Обновляет документацию в реальном времени
|
||
|
||
### 7.4 Mock сервер
|
||
|
||
```bash
|
||
# Запуск mock API сервера
|
||
php artisan spectrum:mock
|
||
```
|
||
|
||
- Запускает mock сервер на `http://localhost:8081`
|
||
- Генерирует моковые данные на основе спецификации
|
||
- Позволяет тестировать API без реального бэкенда
|
||
|
||
### 7.5 Обновление документации
|
||
|
||
После добавления новых endpointов:
|
||
|
||
```bash
|
||
# Очистить кэш и перегенерировать
|
||
php artisan spectrum:cache clear
|
||
php artisan spectrum:generate
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Политики доступа
|
||
|
||
### 8.1 PostPolicy
|
||
|
||
```php
|
||
// app/Policies/PostPolicy.php
|
||
|
||
namespace App\Policies;
|
||
|
||
use App\Models\Post;
|
||
use App\Models\User;
|
||
|
||
class PostPolicy
|
||
{
|
||
public function update(User $user, Post $post): bool
|
||
{
|
||
return $user->id === $post->user_id;
|
||
}
|
||
|
||
public function delete(User $user, Post $post): bool
|
||
{
|
||
return $user->id === $post->user_id;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 8.2 Регистрация политики
|
||
|
||
Политики автоматически обнаруживаются Laravel. Для ручной регистрации:
|
||
|
||
```php
|
||
// app/Providers/AuthServiceProvider.php
|
||
|
||
namespace App\Providers;
|
||
|
||
use App\Models\Post;
|
||
use App\Policies\PostPolicy;
|
||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||
|
||
class AuthServiceProvider extends ServiceProvider
|
||
{
|
||
protected $policies = [
|
||
Post::class => PostPolicy::class,
|
||
];
|
||
|
||
public function boot(): void
|
||
{
|
||
$this->registerPolicies();
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. Конфигурация .env
|
||
|
||
```env
|
||
APP_NAME=Laravel
|
||
APP_ENV=local
|
||
APP_KEY=base64:...
|
||
APP_URL=http://la.test
|
||
|
||
DB_CONNECTION=sqlite
|
||
|
||
SESSION_DRIVER=database
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Тестирование
|
||
|
||
```bash
|
||
# Проверка списка маршрутов
|
||
php artisan route:list --path=api
|
||
|
||
# Генерация документации
|
||
php artisan spectrum:generate
|
||
|
||
# Запуск сервера
|
||
php artisan serve
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### Проблема: 401 Unauthorized
|
||
|
||
**Решение:**
|
||
1. Проверьте токен в заголовке `Authorization: Bearer {token}`
|
||
2. Убедитесь, что используете префикс `Bearer`
|
||
|
||
### Проблема: 419 CSRF Error
|
||
|
||
**Решение:**
|
||
Уберите `statefulApi()` из middleware в `bootstrap/app.php`:
|
||
|
||
```php
|
||
->withMiddleware(function (Middleware $middleware): void {
|
||
// Не используйте statefulApi() для token-based API
|
||
})
|
||
```
|
||
|
||
### Проблема: Rate limiter not defined
|
||
|
||
**Решение:**
|
||
Не используйте `throttleApi()`:
|
||
|
||
```php
|
||
// Неправильно
|
||
$middleware->throttleApi();
|
||
|
||
// Правильно (оставить пустым или удалить)
|
||
$middleware->api();
|
||
```
|
||
|
||
---
|
||
|
||
## Заключение
|
||
|
||
Теперь у вас есть полностью настроенный API с:
|
||
|
||
- ✅ **Laravel Sanctum** - аутентификация через токены
|
||
- ✅ **wadakatu/laravel-spectrum** - автоматическая генерация Swagger документации
|
||
- ✅ **API Resources** - форматирование ответов
|
||
- ✅ **Form Requests** - валидация
|
||
- ✅ **Swagger документация** - `/api/docs`
|
||
|
||
**Команды для работы:**
|
||
```bash
|
||
php artisan spectrum:generate # Генерация документации
|
||
php artisan spectrum:watch # Режим разработки
|
||
php artisan spectrum:mock # Mock сервер
|
||
```
|
||
|
||
**Документация:** `http://la.test/api/docs`
|