In Laravel, what does php artisan make:middleware generate, and how does the generated handle() method let a request through or stop it?
answer
- lands in app/Http/Middleware
- handle(Request $request, Closure $next): Response
- return $next($request) to continue
- return a redirect or response to stop
- --test, --pest or --phpunit adds a test
basics
~10 smake:middleware creates a class in app/Http/Middleware with handle(Request $request, Closure $next): Response. Returning $next($request) passes the request inward; returning your own response, such as a redirect, stops it before the controller runs.
solid answer
~40 s`php artisan make:middleware EnsureRestaurantIsOpen` writes `app/Http/Middleware/EnsureRestaurantIsOpen.php` from the framework's middleware stub: a plain class with one method, `handle(Request $request, Closure $next): Response`, whose body is `return $next($request);`. `$next` is the rest of the pipeline; calling it with the request lets inner middleware and the controller run and gives you back their response, which you must return. To stop the request, you return a response of your own instead, for example `redirect()->route('menu')` or `response()->json([...], 503)`, and the controller never runs. The return type is Symfony's `Response`, which every Laravel response extends. The command also accepts `--test`, `--pest` or `--phpunit` to generate a matching test, and the class still has to be registered before any request reaches it.
code
bash · 1 linephp artisan make:middleware EnsureRestaurantIsOpen --pestgo deeper
Recall the generated signature, handle(Request $request, Closure $next): Response, and the two outcomes: return $next($request) or return your own response.
Explain what $next represents, why the return type matters, and how redirect, JSON and abort() differ as ways to stop a request.
Keep middleware small and single-purpose, test the reject and pass paths, and check that each early response suits both browser and API clients.
Decide which rules belong in middleware versus policies or form requests, so request filtering stays predictable across teams.
## What make:middleware produces A **middleware** in Laravel is a class that sits between the incoming HTTP request and the code that handles it. The generator creates one for you: ```bash php artisan make:middleware EnsureRestaurantIsOpen ``` The command, `Illuminate\Routing\Console\MiddlewareMakeCommand`, writes the file into the `App\Http\Middleware` namespace, which is `app/Http/Middleware/` in a default application. The body comes from the framework's `middleware.stub`: ```php <?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; class EnsureRestaurantIsOpen { public function handle(Request $request, Closure $next): Response { return $next($request); } } ``` A few facts about the generator itself: - It takes a single `name` argument; nested names such as `Orders/EnsureCartIsNotEmpty` create a sub-namespace. - It accepts `--test`, `--pest` and `--phpunit` to create a matching test class alongside. - After `php artisan stub:publish`, a customised `stubs/middleware.stub` in the project root is used instead of the framework's copy. - The class is **not registered** anywhere. Until it is attached to a route, a group or the global stack, it never runs. ## The two arguments of handle() - `$request` is the current `Illuminate\Http\Request`: headers, input, the authenticated user, the matched route (for route middleware). - `$next` is a `Closure` that represents **everything inside this middleware**: the remaining middleware and, finally, the controller. Calling `$next($request)` runs all of it and returns the resulting response. The return type `Symfony\Component\HttpFoundation\Response` covers every response Laravel builds: `Illuminate\Http\Response`, `JsonResponse`, `RedirectResponse`, streamed and file responses all extend it. ## Letting the request through The stub's single line is the pass-through case. When a middleware has nothing to object to, it returns whatever `$next($request)` returns. Forgetting the `return` is a classic bug: `handle()` then returns `null`, and with the `: Response` return type PHP throws a `TypeError` instead of sending the page. ## Stopping the request To stop a request, the middleware simply **does not call `$next`** and returns a response of its own: ```php public function handle(Request $request, Closure $next): Response { if (! $this->kitchen->isOpen()) { return redirect()->route('menu')->with('status', 'The kitchen is closed.'); } return $next($request); } ``` Common ways to build that response: | Response | Typical use in a food-ordering app | |---|---| | `redirect()->route('menu')` | Browser users sent back to the menu page | | `response()->json(['message' => 'Kitchen closed'], 503)` | Mobile or SPA clients calling the API | | `response()->view('closed', status: 503)` | A friendly HTML page | | `abort(403)` | Throws instead of returning; the exception handler renders the response | Whatever is returned travels back out through the middleware that wrapped this one, exactly like a controller's response would. ## What the generated class does not decide The class file only defines behaviour. Three decisions live elsewhere: 1. **Where it runs**: registration in `bootstrap/app.php` or on routes. 2. **Which parameters it receives**: extra arguments after `$next` come from the route string, for example `role:admin`. 3. **Which services it uses**: dependencies are type-hinted in the constructor, because the container builds the middleware. ## Reading the generated docblock The stub annotates `$next` as `Closure(Request): (Response)`. PHP itself cannot express a closure's signature in a type declaration, so this docblock is for static analysers and IDEs: it tells them that `$next` takes the request and returns a response, which lets them flag a `return $next;` (returning the closure itself) or a missing return. Keep it when editing the class. ## Common mistakes - Changing `$request` after calling `$next`, expecting the controller to see the change; by then the controller has already run. - Returning a string or an array from `handle()`; unlike a controller action, the declared `: Response` type rejects it. - Doing the rejection check **after** `$next`, when the controller has already performed the order it was supposed to block. ## Checklist for a new middleware - Name it after the rule it enforces (`EnsureRestaurantIsOpen`), not the mechanism. - Keep the `: Response` return type and always return something. - Return early for the rejection path; keep the happy path as `return $next($request);`. - Write the test with `--test` or `--pest` while the behaviour is fresh.
- What happens if a Laravel middleware calls $next($request) but forgets to return its result?`handle()` returns `null`. Because the generated method declares `: Response`, PHP throws a `TypeError` at that point, the exception handler renders a 500, and the controller's response is lost even though the controller ran. Always `return $next($request);`.
- Does a class created by make:middleware run as soon as it exists in app/Http/Middleware?No. The generator only writes the file. Laravel does not scan that directory; the class runs only after it is attached to a route or group, or added to the global stack in `bootstrap/app.php`.
saying these in an interview costs you the question
- Expects the new middleware to run automatically once the file exists
- Calls $next($request) and forgets to return the response
- Thinks returning false from handle() rejects the request
- Believes the controller still runs after an early redirect
- Says make:middleware also edits app/Http/Kernel.php