Should you use a construct with invoke - or just use invoke?

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Construct vs. Invoke: Where Should You Place Your Middleware and Authorization Logic in Laravel Controllers?

As we build more complex applications in Laravel, developers often find themselves wrestling with subtle architectural choices regarding how to structure their classes. A common point of confusion arises when deciding whether to initialize object state and dependencies within the __construct() method or embed the core logic directly into the single-method __invoke() method.

This post dives deep into your specific scenario—using resource controllers and class invocation—to determine the best architectural pattern for managing middleware and authorization policies.


The Core Distinction: Initialization vs. Execution

To answer this question effectively, we must first establish the fundamental roles of these two methods in PHP and Laravel context:

  1. __construct() (Initialization): This method is designed for setting up the initial state, injecting dependencies, and configuring the object before it is used. In an OOP sense, it’s where you define the object's prerequisites.
  2. __invoke() (Execution/Behavior): When a class implements __invoke(), it allows the class instance itself to be called as a method. It defines the primary action or behavior of the object—what the class does.

Analyzing Your Scenarios

Your example highlights two distinct ways of using controllers:

Scenario 1: The Resource Controller Pattern (Using __construct())

In your resource controller setup, you are defining a collection of standard CRUD operations (index, store, etc.). Applying middleware and authorization policies here is an excellent practice because it sets the context for all methods within that class.

public function __construct()
{
    // This sets up the rules for ALL methods in this controller
    $this->middleware('auth:sanctum')->only(['index', 'update', 'destroy']);
    $this->authorizeResource(User::class, 'user');
}

Why this is powerful: By placing authorization and middleware setup in the constructor, you ensure that every time a method on this controller is called (e.g., index() or store()), those essential security checks are automatically applied. This promotes DRY (Don't Repeat Yourself) principles and keeps your route definitions cleaner, which aligns perfectly with Laravel’s philosophy of convention over configuration.

Scenario 2: The Single-Method Controller Pattern (Using __invoke())

In contrast, when you use a single method controller where the class itself represents the action (often called a "Service Object" or "Action Class"), using __invoke() is appropriate for defining that singular behavior:

public function __invoke()
{
    // This defines the single action performed by the object
    return new UserResource(auth()->user());
}

When this works: Here, you are not initializing a set of routes; you are executing a specific task. Placing the instantiation logic within __invoke() makes sense because it encapsulates the result of that execution.

The Verdict: Where to Put Your Logic?

For managing infrastructure concerns like middleware and authorization policies that apply across multiple methods (like in a standard resource controller), the __construct() method is the superior choice.

Placing these configurations in the constructor adheres to better Object-Oriented Programming principles. It separates what the object is (its configuration) from what the object does (its execution logic). This separation makes your code more readable, maintainable, and easier for other developers (and future you!) to understand when debugging complex flows.

If you are working with sophisticated data relationships, remember that robust authorization checks should always leverage Laravel's built-in features, such as Eloquent Policies or Gates—as detailed on the Laravel documentation regarding authorization.

Conclusion

In summary, use __construct() when setting up the state and constraints of an object (like middleware, scopes, or dependency injection). Use __invoke() when defining the primary behavior that the object executes upon invocation. By adopting this distinction, you move from writing procedural code to building well-structured, highly maintainable Laravel applications.