Контроллер для модели User в Laravel: полное руководство
Контроллер пользователя в Laravel: создание, методы и варианты
Наиболее эффективное решение для работы с пользователями в Laravel - использование Resource Controller с полным набором CRUD-методов. Этот подход соответствует соглашениям фреймворка и легко масштабируется.
Создание контроллера выполняется командой:
php artisan make:controller UserController --resource --model=UserController user php (контроллер пользователя в php)
Данная команда генерирует класс UserController с методами index(), create(), store(), show(), edit(), update(), destroy() и автоматически подключает модель User.
Регистрация маршрутов в файле web.php:
Route::resource('users', UserController::class);
Пример реализации метода store():
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8',
]);
$user = User::create($validated);
$user->password = bcrypt($request->password);
$user->save();
return redirect()->route('users.index')->with('success', 'Пользователь создан');
}
Описание шагов:
- Валидация входных данных - обязательное поле, уникальный email, минимальная длина пароля.
- Создание записи в базе через
User::create(). - Хеширование пароля с помощью
bcrypt(). - Редирект на список пользователей с flash-сообщением.
Типичная ошибка: забыть хешировать пароль или использовать массовое присвоение без заполнения свойства $fillable в модели. Решение - добавить в модель User массив protected $fillable = ['name','email','password'];
Как организовать валидацию данных при создании пользователя с помощью FormRequest?
Вынос валидации в отдельный класс (Form Request) делает код чище и позволяет переиспользовать правила.
Создание Form Request:
php artisan make:request StoreUserRequest
Класс app/Http/Requests/StoreUserRequest.php:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8|confirmed',
];
}
}
Использование в контроллере:
public function store(StoreUserRequest $request)
{
$validated = $request->validated();
$validated['password'] = bcrypt($validated['password']);
$user = User::create($validated);
return redirect()->route('users.index');
}
Цель: разделение ответственности, автоматическая обработка ошибок валидации (редирект с ошибками).
Возможная проблема: FormRequest не срабатывает, если не указан путь в RouteServiceProvider или присутствуют конфликты с middleware. Решение - проверить регистрацию маршрута и убедиться, что authorize() возвращает true.
Как вынести бизнес-логику создания пользователя в отдельный сервис?
Сервисный слой позволяет избежать «распухания» контроллера и упрощает тестирование.
Создаём класс App\Services\UserService:
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
class UserService
{
public function create(array $data): User
{
$data['password'] = Hash::make($data['password']);
return User::create($data);
}
public function update(User $user, array $data): bool
{
if (isset($data['password'])) {
$data['password'] = Hash::make($data['password']);
}
return $user->update($data);
}
public function delete(User $user): bool
{
return $user->delete();
}
}
Контроллер с внедрением зависимости:
use App\Services\UserService;
class UserController extends Controller
{
protected $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function store(StoreUserRequest $request)
{
$user = $this->userService->create($request->validated());
return redirect()->route('users.index', $user);
}
}
Цель: изоляция логики, возможность подмены сервиса при тестировании.
Типичная ошибка: забыть зарегистрировать сервис в контейнере. В Laravel автоматическое разрешение работает для классов без интерфейсов, поэтому обычно регистрация не требуется. Если используется интерфейс, нужно связать в AppServiceProvider.
Как сделать одноразовый контроллер для регистрации пользователя (Invokable)?
Когда контроллер выполняет только одно действие (например, регистрация), удобно использовать invokable контроллер с единственным методом __invoke().
Создание контроллера:
php artisan make:controller RegisterController --invokable
Код контроллера:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreUserRequest;
use App\Services\UserService;
class RegisterController extends Controller
{
public function __invoke(StoreUserRequest $request, UserService $userService)
{
$user = $userService->create($request->validated());
auth()->login($user);
return redirect()->route('home');
}
}
Маршрут:
Route::post('/register', RegisterController::class);
Цель: отсутствие лишних методов, ясность кода.
Проблема: если требуется несколько действий (например, просмотр формы и обработка), invokable не подходит. В этом случае лучше использовать обычный контроллер.
Как вернуть данные пользователя в JSON для API-контроллера?
При разработке API используется контроллер с возвратом JSON. Для форматирования ответа создаются Resource-классы.
Создание ресурса:
php artisan make:resource UserResource
Класс app/Http/Resources/UserResource.php:
<?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,
];
}
}
Пример API-контроллера:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use App\Models\User;
class UserController extends Controller
{
public function index()
{
return UserResource::collection(User::paginate(10));
}
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}
}
Цель: единый формат ответа, исключение лишних полей.
Типичная ошибка: возврат коллекции без пагинации приводит к отсутствию мета-данных. Используйте paginate() или collection() с явным указанием ресурса.
Расширенные примеры кода с пояснениями
Пример 1. UserController с FormRequest и сервисным слоем
Полный код трёх компонентов, реализующих создание пользователя с расширенной валидацией и логикой.
Файл app/Http/Requests/StoreUserRequest.php:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
public function authorize(): bool
{
// Можно добавить проверку прав: только admin может создавать
return $this->user() && $this->user()->isAdmin();
}
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'confirmed', Password::min(8)->letters()->numbers()],
'role' => 'sometimes|in:user,admin',
'profile_picture' => 'nullable|image|mimes:jpg,png|max:2048',
];
}
public function messages(): array
{
return [
'password.letters' => 'Пароль должен содержать хотя бы одну букву',
'password.numbers' => 'Пароль должен содержать хотя бы одну цифру',
];
}
}
Файл app/Services/UserService.php:
<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
class UserService
{
public function createUser(array $data): User
{
// Хешируем пароль
$data['password'] = Hash::make($data['password']);
// Сохраняем аватар, если есть
if (isset($data['profile_picture'])) {
$path = $data['profile_picture']->store('avatars', 'public');
$data['profile_picture'] = $path;
}
// Создаём пользователя
$user = User::create($data);
// Назначаем роль (по умолчанию 'user')
$user->assignRole($data['role'] ?? 'user');
return $user;
}
public function updateUser(User $user, array $data): bool
{
if (isset($data['password'])) {
$data['password'] = Hash::make($data['password']);
}
if (isset($data['profile_picture'])) {
// Удаляем старый аватар
if ($user->profile_picture) {
Storage::disk('public')->delete($user->profile_picture);
}
$data['profile_picture'] = $data['profile_picture']->store('avatars', 'public');
}
return $user->update($data);
}
public function deleteUser(User $user): bool
{
// Удаляем аватар
if ($user->profile_picture) {
Storage::disk('public')->delete($user->profile_picture);
}
return $user->delete();
}
}
Метод контроллера store():
public function store(StoreUserRequest $request)
{
$user = $this->userService->createUser($request->validated());
return redirect()->route('users.show', $user->id)
->with('success', 'Пользователь создан');
}
Результат: При успешном выполнении пользователь перенаправляется на страницу просмотра созданного пользователя с сообщением. Если валидация не пройдена, Laravel автоматически возвращает предыдущую страницу с ошибками в сессии.
Пример 2. API-контроллер с пагинацией и фильтрацией
Контроллер для REST API, возвращающий список пользователей с возможностью сортировки и фильтрации.
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
class UserApiController extends Controller
{
public function index(Request $request)
{
$query = User::query();
// Фильтрация по имени
if ($request->has('name')) {
$query->where('name', 'like', '%' . $request->name . '%');
}
// Фильтрация по email
if ($request->has('email')) {
$query->where('email', $request->email);
}
// Сортировка (по умолчанию по id убывание)
$sortField = $request->get('sort', 'id');
$sortOrder = $request->get('order', 'desc');
$query->orderBy($sortField, $sortOrder);
// Пагинация
$users = $query->paginate($request->get('per_page', 15));
return UserResource::collection($users);
}
public function store(StoreUserApiRequest $request)
{
$user = User::create($request->validated());
$user->password = bcrypt($request->password);
$user->save();
return (new UserResource($user))
->response()
->setStatusCode(201);
}
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}
public function update(UpdateUserApiRequest $request, $id)
{
$user = User::findOrFail($id);
$user->update($request->validated());
return new UserResource($user);
}
public function destroy($id)
{
$user = User::findOrFail($id);
$user->delete();
return response()->json(['message' => 'Пользователь удалён'], 200);
}
}
Пример JSON-ответа для index():
{
"data": [
{
"id": 1,
"name": "Иван Иванов",
"email": "ivan@example.com"
},
{
"id": 2,
"name": "Пётр Петров",
"email": "petr@example.com"
}
],
"meta": {
"current_page": 1,
"last_page": 3,
"per_page": 10,
"total": 25
}
}
Пояснение шагов:
- Метод
index()принимает параметрыname,email,sort,order,per_page. - Построитель запросов
User::query()применяет фильтры и сортировку. - Пагинация через
paginate()автоматически добавляет метаданные. - Ответ обёрнут в
UserResource::collection(), что гарантирует единую структуру.
Пример 3. Использование middleware в контроллере
Добавление проверки роли через middleware прямо в конструкторе контроллера.
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Http\Requests\StoreUserRequest;
use App\Http\Requests\UpdateUserRequest;
use App\Services\UserService;
class UserController extends Controller
{
public function __construct()
{
// Все действия требуют аутентификации
$this->middleware('auth');
// Только для действий destroy (админ)
$this->middleware('can:delete,user')->only('destroy');
// Только для методов index и show (любой авторизованный)
$this->middleware('can:view,user')->only(['index', 'show']);
}
// ... остальные методы
}
Результат: Доступ к методам контроллера ограничивается в соответствии с политиками (Policies) Laravel. Ошибки авторизации приводят к HTTP-статусу 403.
Важно: Middleware auth проверяет, что пользователь вошёл в систему. can вызывает определённые политики. Для работы необходимо создать политику UserPolicy и зарегистрировать её.