In a new Laravel 13 app, what does the base App\Http\Controllers\Controller contain, and why do $this->authorize() and $this->validate() fail there?
answer
- abstract class with an empty body
- no parent class, no traits
- AuthorizesRequests and ValidatesRequests still ship
- Gate::authorize, $request->validate()
- extending it is optional
basics
~20 sThe base Controller in a Laravel 13 skeleton is an empty abstract class: no parent, no AuthorizesRequests or ValidatesRequests traits. $this->authorize() and $this->validate() are undefined unless you add those traits back; the usual replacements are Gate::authorize() and $request->validate().
solid answer
~40 sIn a Laravel 13 skeleton `app/Http/Controllers/Controller.php` is `abstract class Controller { // }`: it no longer extends `Illuminate\Routing\Controller` and no longer uses the `AuthorizesRequests` and `ValidatesRequests` traits, a change that came with Laravel 11's slim skeleton. So `$this->authorize()`, `$this->validate()`, `$this->middleware()` and `authorizeResource()` are undefined methods in a fresh app. The replacements are `Gate::authorize()` or policy attributes and middleware for authorization, `$request->validate()` or form requests for validation, and `HasMiddleware` or middleware attributes for controller middleware. If a team wants the old helpers, it can add `use AuthorizesRequests, ValidatesRequests;` to the base class; both traits still ship. Extending the base class is optional; it is only a home for shared code.
code
php · 21 lines<?php
namespace App\Http\Controllers;
use App\Models\Rental;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class RentalController extends Controller
{
public function update(Request $request, Rental $rental)
{
Gate::authorize('update', $rental);
$data = $request->validate(['returns_at' => ['required', 'date']]);
$rental->update($data);
return back();
}
}go deeper
Know the base controller is empty in new apps and use $request->validate() and Gate::authorize() instead of $this helpers.
Name what the old parent and traits provided, the modern replacement for each, and how to re-add the traits.
Handle mixed codebases after an upgrade, including the authorizeResource() and HasMiddleware conflict, and keep the base class from becoming a dumping ground.
Decide whether the team restores the trait helpers or adopts the facade and attribute idioms, and document it so new controllers follow one style.
## What the skeleton ships The Laravel 13 application skeleton contains exactly this base controller: ```php <?php namespace App\Http\Controllers; abstract class Controller { // } ``` It is **abstract** (you never route to it directly) and **empty**. Generated controllers extend it by default, but it contributes no behaviour. ## What it used to contain Before Laravel 11's slim skeleton, the base controller extended the framework's `Illuminate\Routing\Controller` and used the `AuthorizesRequests` and `ValidatesRequests` traits; older releases also used `DispatchesJobs`. Each piece supplied `$this->` helpers: | Source | Methods it gave controllers | Laravel 13 replacement | |---|---|---| | `Illuminate\Routing\Controller` | `$this->middleware()` | `HasMiddleware` or `#[Middleware]` attributes | | `AuthorizesRequests` trait | `$this->authorize()`, `authorizeForUser()`, `authorizeResource()` | `Gate::authorize()`, policy middleware or attributes | | `ValidatesRequests` trait | `$this->validate()`, `validateWith()`, `validateWithBag()` | `$request->validate()` or a form request | | `DispatchesJobs` trait (older releases) | `$this->dispatch()` | `SomeJob::dispatch()` or the `dispatch()` helper | In a new app none of these exist on `$this`, so calling one fails with PHP's *Call to undefined method* error. ## Why the change was made - **Less hidden inheritance**: a controller's capabilities are visible from its own code and imports. - **Better replacements exist**: facades, request methods and form requests do the same jobs without a parent class. - **Static middleware**: `HasMiddleware` lets the router read middleware without constructing the controller, which the old instance method could not. - **Smaller skeleton**: the Laravel 11 skeleton removed boilerplate everywhere (kernels, most providers, the base controller's contents). ## Getting the helpers back, if you want them Both traits still ship in the framework: - `Illuminate\Foundation\Auth\Access\AuthorizesRequests` - `Illuminate\Foundation\Validation\ValidatesRequests` Adding `use AuthorizesRequests, ValidatesRequests;` to the base controller restores `$this->authorize()` and `$this->validate()` for every subclass. Two caveats: 1. `authorizeResource()` registers `can:` middleware by calling `$this->middleware()`, so it also needs the base controller to extend `Illuminate\Routing\Controller`. 2. Extending `Illuminate\Routing\Controller` makes `HasMiddleware` unusable on those controllers, because its instance `middleware()` method clashes with the interface's static one. Apps **upgraded** from Laravel 10 keep their old base controller file; nothing forces them to empty it, so both shapes appear in real codebases. ## Is the base class needed at all? No. The router builds any class through the container and calls the action; it never checks for a parent. `make:controller` even drops `extends Controller` when the base file is missing. Keep the base class if you want one place for a shared helper, and keep it small: every method added there becomes an implicit dependency of every controller. ## Auditing an upgraded app When an app has been upgraded from Laravel 10 or earlier, check the base controller before assuming either shape: 1. Open `app/Http/Controllers/Controller.php` and see whether it still extends `Illuminate\Routing\Controller` and uses the traits. 2. Search controllers for `$this->middleware(`, `$this->authorize(`, `$this->validate(` and `authorizeResource(`; each one depends on the old base class. 3. Decide per team whether to keep the old base class for now or migrate call sites to `HasMiddleware`, `Gate::authorize()` and `$request->validate()`. 4. Migrate one controller at a time; a controller can stop extending the old base only once none of those calls remain in it. ## Answering the interview question A strong answer states what the file contains, names the removed parent and traits, gives the modern replacement for each helper, and knows the traits can be re-added, with the `authorizeResource()` / `HasMiddleware` trade-off.
- Why does authorizeResource() still fail after adding AuthorizesRequests to the base controller?`authorizeResource()` builds `can:` middleware for each resource method and registers it with `$this->middleware()`, which lives on `Illuminate\Routing\Controller`. The empty base class does not extend it, so the call is undefined; use the policy attribute or middleware per action instead, or extend that class and give up `HasMiddleware`.
- Does make:controller require the base controller to exist?No. When `app/Http/Controllers/Controller.php` is missing, the generator removes the `extends Controller` clause and its import from the stub, producing a standalone class that routes exactly the same way.
saying these in an interview costs you the question
- The Laravel 13 base Controller extends Illuminate\Routing\Controller.
- $this->validate() works in any new Laravel 13 controller.
- The AuthorizesRequests trait was deleted from the framework in Laravel 11.
- A controller must extend the base Controller or the router rejects it.
- Extending Illuminate\Routing\Controller restores $this->authorize().