skip to content

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.