skip to content

Endpoint Classes

Controllers group route actions into classes: plain multi-action ones, single-action __invoke classes and resource controllers wired to conventional routes. Interviewers probe how thin they should be.

on this pageshow

explore

questions

12

In Laravel 13, what does php artisan make:controller generate, and how does a route reach one of that controller's methods?

level: juniorimportance: must knowfreq 72%

answer

  1. one class, many public actions
  2. app/Http/Controllers, App\Http\Controllers
  3. plain stub extends the base Controller
  4. [CarController::class, 'show'] stored as Class@method
  5. container builds it when the route matches

basics

~20 s

make:controller writes a class into app/Http/Controllers whose public methods are actions. A route targets one with the array [CarController::class, 'show']; on a match Laravel builds the controller through the container and calls that method with the route parameters.

solid answer

~40 s

`php artisan make:controller CarController` creates `app/Http/Controllers/CarController.php` in the `App\Http\Controllers` namespace from the **plain stub**: an empty class that extends the app's base `Controller` and imports `Illuminate\Http\Request`. Every public method you add is an action. A route points at one with a callable-style array, `Route::get('/cars/{car}', [CarController::class, 'show'])`, which the router stores internally as `App\Http\Controllers\CarController@show`. When the route matches, the controller is resolved from the service container (so its constructor can type-hint dependencies) and `show` is called with the route parameters. Run with no name at all, the command first asks for one and then offers a type: Empty, Resource, Singleton, API or Invokable.

code

bash · 1 line
bash
php artisan make:controller CarController

go deeper

for a junior

Know the command, the folder and namespace it writes to, and the [Class::class, 'method'] route syntax; be able to write a two-action controller from memory.

for a middle

Explain that the container builds the controller per matched request, how route parameters and type-hints reach the action, and why the array form beats the string form.

for a senior

Talk about keeping actions thin, customising generated code with stub:publish for team conventions, and catching broken route actions with a route test rather than at runtime.

for a principal

Frame controllers as the HTTP adapter of the app: which conventions (naming, one class per resource or per use case) a team should standardise so routes stay navigable as the codebase grows.

## What a controller is in Laravel A **controller** is a plain PHP class that groups related request handlers. Instead of writing every handler as a closure in `routes/web.php`, you put them on a class, and each **public method** becomes an **action** a route can call. In a car-rental app, a `CarController` might hold `index` (the fleet list) and `show` (one car's page). ## What `make:controller` writes `php artisan make:controller CarController` is the generator for that class. With no flags it: - writes `app/Http/Controllers/CarController.php` (the default namespace is `App\Http\Controllers`; a name like `Admin/CarController` adds a sub-namespace and folder); - uses the **plain stub**, an empty class with `use Illuminate\Http\Request;` already imported; - adds `extends Controller` only when `app/Http/Controllers/Controller.php` exists, otherwise it drops the `extends` clause, because a controller does not need a parent class; - refuses to overwrite an existing file unless you pass `--force`; - can also write a matching test with `--test`, `--pest` or `--phpunit`. If you run it **without a name**, Laravel Prompts asks for the name and then asks *"Which type of controller would you like?"* with the choices Empty, Resource, Singleton, API and Invokable. Given a name and no flags, it silently writes the plain stub. The stub itself can be customised: `php artisan stub:publish` copies `controller.plain.stub` (and the others) into a `stubs/` folder at the project root, and the command prefers that copy when it exists. ## Pointing a route at an action Routes reference an action with a two-element array: the class constant and the method name. | Form | Example | Notes | |---|---|---| | Array (current style) | `[CarController::class, 'show']` | IDEs and static analysis can follow and rename the class | | `Class@method` string | `'App\Http\Controllers\CarController@show'` | Still parsed; needs the fully qualified name because the skeleton sets no controller namespace prefix | | Class only | `ReturnCarController::class` | Only for a class with `__invoke` | Internally the router's `RouteAction::parse` turns the array into the `Class@method` string, so both forms end up identical; the array is preferred because a typo in the class name becomes an immediate "class not found" in your editor rather than a string nobody checks. ## What happens when the request arrives 1. The router matches the URI and HTTP verb to the route. 2. It gathers the route's middleware, including any the controller declares, and sends the request through that pipeline. 3. At the end of the pipeline it asks the **service container** for the controller, so constructor dependencies are injected. 4. It resolves the action's parameters: route parameters by position, plus anything the method type-hints (a `Request`, a bound model, a service). 5. It calls the method and converts whatever it returns (a view, an array, a model, a redirect) into an HTTP response. ## Mistakes that show up in interviews - **A misspelled method name** in `[CarController::class, 'shwo']` is not caught when routes load; the request fails with a *call to undefined method* error when it arrives. - **Passing only the class** of a controller that has no `__invoke` fails as soon as the routes file loads, with an `UnexpectedValueException` reading *Invalid route action*. - **Forgetting the `use App\Http\Controllers\CarController;` import** in the routes file makes `CarController::class` resolve to the wrong namespace and the class is not found at dispatch time. - **Putting business logic in the action** works, but it makes the controller the only place that logic can run; controllers are best kept as thin HTTP adapters. A controller is therefore just a class the router knows how to build and call; the generator only saves typing and keeps the folder and namespace conventional.

  • What does make:controller do when you run it without a class name?
    It prompts for the name, then asks which type of controller you want: Empty, Resource, Singleton, API or Invokable. For API, Resource and Singleton it also offers to link a model. When you pass a name and no flags, it asks nothing and writes the plain stub.
  • Does the old 'CarController@show' string syntax still work in Laravel 13?
    Yes. The router converts the array form into exactly that `Class@method` string internally. But a Laravel 13 skeleton applies no controller namespace prefix, so the string must be fully qualified, and it gives up the IDE and static-analysis checks the `CarController::class` constant provides.
  • When is the controller instance created relative to the route's middleware?
    For a controller that declares middleware through `HasMiddleware` or attributes, or none at all, the router builds the instance at the end of the middleware pipeline, just before calling the action. Only the legacy style, which extends `Illuminate\Routing\Controller` and calls `$this->middleware()`, forces the instance to be built earlier so its middleware can be read.

saying these in an interview costs you the question

  • Controllers must extend a Laravel base class or the router will not call them.
  • A misspelled action method is caught when the routes file loads.
  • make:controller with a name and no flags generates the seven resource methods.
  • The controller is instantiated once at boot and reused for every request.
  • The 'Controller@method' string resolves relative to App\Http\Controllers automatically in Laravel 13.
open as a page

In Laravel, which routes does Route::resource('books', BookController::class) register, with their HTTP verbs, URIs, actions and route names?

level: juniorimportance: must knowfreq 75%

basics

~10 s

Route::resource('books', ...) registers seven routes: GET /books (index), GET /books/create (create), POST /books (store), GET /books/{book} (show), GET /books/{book}/edit (edit), PUT/PATCH /books/{book} (update) and DELETE /books/{book} (destroy), named books.index and so on.

open as a page

In a Laravel 13 controller, how does implementing HasMiddleware assign middleware, and how do Middleware only and except narrow it?

level: middleimportance: must knowfreq 50%

basics

~10 s

A controller implementing HasMiddleware defines a static middleware() method returning middleware names, closures or Middleware objects; new Middleware('log', only: ['index']) limits one to listed methods and except excludes methods. It replaces constructor $this->middleware() calls.

open as a page

In Laravel, what does Route::apiResource change compared with Route::resource, and how do only() and except() narrow a resource?

level: middleimportance: must knowfreq 60%

basics

~20 s

Route::apiResource registers the resource routes minus the create and edit form pages, leaving index, store, show, update and destroy. only([...]) keeps just the listed actions and except([...]) removes listed ones; make:controller --api stubs the matching five methods.

open as a page

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?

level: middleimportance: should knowfreq 40%

basics

~20 s

The 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().

open as a page

In Laravel, when would you write a single-action controller with __invoke, and how do you generate and route it?

level: middleimportance: should knowfreq 55%

basics

~20 s

A single-action controller has one __invoke method, generated with make:controller Name --invokable. Routes pass just the class, Route::post('/rentals/{rental}/return', ReturnRental::class); use it for an action that is not CRUD or deserves its own class and dependencies.

open as a page

In Laravel, what URIs and route names does Route::resource('books.copies', BookCopyController::class) register, and what does ->shallow() change?

level: middleimportance: should knowfreq 48%

basics

~10 s

Dot notation nests the resource: /books/{book}/copies, /books/{book}/copies/{copy} and so on, named books.copies.index and similar. ->shallow() keeps the parent only on index, create and store; show, edit, update and destroy become /copies/{copy} named copies.show.

open as a page

A Laravel car-rental app's RentalController::store validates input, checks availability, prices the booking, charges the card and emails the customer; how would you slim it?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Keep only HTTP work in the controller: a form request validates, a policy authorizes, an injected CreateRental action class runs availability, pricing and payment in one transaction, a queued notification emails, and store returns a redirect.

open as a page

In a Laravel nested resource books.copies, why can /books/1/copies/99 load a copy of another book, and how does scoped() fix it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Without scoping, Laravel binds {book} and {copy} independently, so copy 99 loads even if it belongs to book 2. ->scoped() makes it resolve the copy through $book->copies(), and a copy outside that book yields a 404.

open as a page

In Laravel, when would you register Route::singleton instead of Route::resource, and what do creatable() and apiSingleton change?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Route::singleton is for a resource that exists at most once per parent or user, such as a book's cover: it registers show, edit and update with no id. creatable() adds create, store and destroy; apiSingleton drops the create and edit forms.

open as a page

In Laravel 13, how do the #[Middleware] and #[WithoutMiddleware] controller attributes work at class and method level?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

#[Middleware('auth')] on a controller class or method attaches route middleware, with optional only and except; method attributes merge with class ones and parents' class attributes are inherited. #[WithoutMiddleware] removes named route middleware, never global middleware.

open as a page

In Laravel, how do names(), parameters() and Route::resourceVerbs customise the route names, placeholders and URI verbs a resource registers?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

names() overrides route names, per action with an array or for every action with a base string; parameters() renames the {book} placeholder; Route::resourceVerbs(), called in a service provider's boot, replaces the create and edit URI segments for every resource registered afterwards.

open as a page