0

Basic controllers — extending Controller, action methods

Beginner5 min read·lv-08-001

Concept

Controllers in Laravel are classes that group related HTTP request handling logic. They receive a Request, interact with services/models, and return a Response. Controllers are thin coordinators — they should not contain business logic.

Extending App\Http\Controllers\Controller: The base Controller class provides authorize(), validate(), dispatch(), and other helpers. You don't have to extend it, but you lose these conveniences.

Action methods: Methods that correspond to HTTP actions. The naming convention for resource controllers: index, create, store, show, edit, update, destroy. For non-resource controllers, any descriptive verb: search, export, activate.

Dependency injection in controllers: Laravel automatically injects type-hinted dependencies in controller constructors AND in action method parameters. public function index(Request $request, UserRepository $repo) — both are injected. Route parameters and injectable services are distinguished by whether they appear in the route definition.

Controller response return types: Return anything that Blade/JSON serialization understands: View, JsonResponse, RedirectResponse, Response, arrays (auto-JSON), Eloquent models/collections (auto-JSON), strings.

Single-responsibility principle: One controller per resource group. UserController handles users, OrderController handles orders. If a controller grows beyond 7-10 methods, consider splitting.

Code Example

php
<?php
namespace App\Http\Controllers;

use App\Models\User;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\View\View;

class UserController extends Controller
{
    // Constructor injection — available to all methods
    public function __construct(
        private readonly UserService $userService,
    ) {}

    // GET /users — list all users
    public function index(Request $request): View|JsonResponse
    {
        $users = User::with('profile')
            ->when($request->search, fn($q, $s) => $q->where('name', 'like', "%$s%"))
            ->paginate(15);

        if ($request->expectsJson()) {
            return response()->json($users);
        }
        return view('users.index', compact('users'));
    }

    // GET /users/{user} — show one user
    public function show(User $user): View
    {
        // $user auto-resolved via route model binding
        return view('users.show', compact('user'));
    }

    // POST /users — create user
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name'     => 'required|string|max:255',
            'email'    => 'required|email|unique:users',
            'password' => 'required|string|min:8',
        ]);

        $user = $this->userService->create($validated);
        return response()->json($user, 201);
    }

    // PUT /users/{user} — update user
    public function update(Request $request, User $user): JsonResponse
    {
        $this->authorize('update', $user); // policy check

        $validated = $request->validate([
            'name'  => 'sometimes|string|max:255',
            'email' => 'sometimes|email|unique:users,email,' . $user->id,
        ]);

        $user->update($validated);
        return response()->json($user);
    }

    // DELETE /users/{user} — delete user
    public function destroy(User $user): JsonResponse
    {
        $this->authorize('delete', $user);
        $user->delete();
        return response()->json(null, 204);
    }
}