Controllers

Laravel Fundamentals
Course 1 ยท Chapter 3 ยท Controllers

๐ŸŽฎ Controllers

The closures in routes/web.php from the last chapter work fine for a one-line example โ€” real request-handling logic belongs in a controller, Laravel's direct equivalent of Django's views.py. This chapter covers generating controllers, resource controllers matching last chapter's Route::resource(), and route model binding โ€” a genuinely distinctive Laravel convenience.

Generating a Controller

php artisan make:controller BookController
# Controller created successfully.
# app/Http/Controllers/BookController.php
// app/Http/Controllers/BookController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BookController extends Controller
{
    public function index()
    {
        return 'All books';
    }
}
// routes/web.php โ€” wire the controller into a route
Route::get('/books', [BookController::class, 'index']);

Controller Methods vs Django Views

Django Function-Based View vs Laravel Controller Method

Django
def book_detail(request, book_id):
    query = request.GET.get("q", "")
    return HttpResponse(...)

request always arrives as the first positional argument.

Laravel
public function show(Request $request, string $id)
{
    $query = $request->query('q', '');
    return ...;
}

Request $request is type-hinted โ€” Laravel's service container injects it automatically based on the type, not its position.

This type-hint-based injection is a real PHP/Laravel-specific idea worth noticing early: any parameter Laravel recognizes by its type (a Request, a model class, a custom service) gets resolved and passed in automatically โ€” order relative to route parameters doesn't matter the way Python's positional arguments require.

๐Ÿ“ค Returning Responses

public function index()
{
    return view('books.index', ['books' => Book::all()]);  // Blade โ€” Chapter 4
}

public function apiIndex()
{
    return response()->json(['books' => Book::all()]);
}

public function store(Request $request)
{
    // ... save ...
    return redirect()->route('books.index');  // by named route, per Chapter 2
}

view()

Combines a Blade template + data array + response into one call โ€” the direct parallel to Django's render().

response()->json()

The equivalent of Django's JsonResponse โ€” wraps a PHP array as a proper JSON response with the correct content type.

Resource Controllers

--resource generates method stubs for exactly the seven actions Route::resource() expects โ€” the controller-side half of last chapter's route generation:

php artisan make:controller BookController --resource
class BookController extends Controller
{
    public function index() { /* GET /books */ }
    public function create() { /* GET /books/create */ }
    public function store(Request $request) { /* POST /books */ }
    public function show(Book $book) { /* GET /books/{book} */ }
    public function edit(Book $book) { /* GET /books/{book}/edit */ }
    public function update(Request $request, Book $book) { /* PUT /books/{book} */ }
    public function destroy(Book $book) { /* DELETE /books/{book} */ }
}

Wiring it up needs only one route line, exactly matching Chapter 2:

Route::resource('books', BookController::class);

๐Ÿ”— Route Model Binding

Notice show(Book $book) above, not show(string $id) โ€” this is Laravel's standout convenience. Type-hint a model class as a parameter whose name matches the route segment, and Laravel fetches the row for you automatically:

Manual Lookup vs Route Model Binding

Django: Manual Lookup
def book_detail(request, book_id):
    book = get_object_or_404(Book, pk=book_id)
    return render(request, "book_detail.html", {"book": book})
Laravel: Automatic Binding
public function show(Book $book)
{
    return view('books.show', ['book' => $book]);
}
// $book is ALREADY the fetched model โ€” no findOrFail() call written by hand

Laravel matches the route parameter {book} to the type-hinted Book $book by name, runs Book::findOrFail($id) internally, and injects the actual model instance โ€” a missing ID produces an automatic 404, with zero lookup code in the controller at all.

๐Ÿ’ป Coding Challenges

Challenge 1: Generate and Wire a Controller

Run php artisan make:controller AuthorController, add an index() method returning a plain string, and write the routes/web.php line needed to actually reach it at /authors.

Goal: Practice the generate-then-wire-up sequence, including the step that's easy to forget.

โ†’ Solution

Challenge 2: Write a Resource Controller

Generate a resource controller for a Product model with --resource, fill in index() and show(Product $product) to return simple placeholder responses, and wire it up with Route::resource().

Goal: Practice pairing a resource controller with its matching resource route from Chapter 2.

โ†’ Solution

Challenge 3: Convert Manual Lookup to Route Model Binding

Given a controller method show(string $id) that manually calls Book::findOrFail($id), rewrite it to use route model binding instead โ€” show(Book $book) โ€” and explain what happens differently if the ID doesn't exist.

Goal: Practice recognizing when manual lookup code can be replaced by Laravel's automatic binding.

โ†’ Solution

โš ๏ธ Gotcha: Generating a Controller Doesn't Wire It Up

php artisan make:controller only creates the file โ€” nothing routes to it until you add a matching entry in routes/web.php. This is the same two-step trap already seen twice in the Django courses (INSTALLED_APPS, include()): the code exists on disk, but the framework doesn't know about it yet. Separately, route model binding's automatic 404 is usually exactly what you want โ€” but it means a Book $book parameter can never actually be null inside the method; if you need to handle "not found" yourself instead of letting Laravel's default 404 page render, look up the model manually with Book::find($id) instead of relying on binding.

๐ŸŽฏ What's Next

Controllers can now return a view โ€” the next chapter covers what's actually inside one: Blade Templates, Laravel's templating syntax, control structures, and template inheritance compared to the Django Template Language.