Single-Action Controllers
A member hands a book back. Lendly must mark the loan returned, record the date and make the book available again. Is that store? update? destroy? It is none of them cleanly. It is its own action with its own meaning. Laravel's answer is a controller that does exactly one thing.
The rule
One job, one class, one __invoke method.
Generate it with --invokable:
php artisan make:controller ReturnLoanController --invokableThe class has a single method, __invoke, which PHP calls when an object is used like a function:
namespace App\Http\Controllers;
use App\Models\Loan;
class ReturnLoanController extends Controller
{
public function __invoke(Loan $loan)
{
$loan->update(['returned_at' => now()]);
return to_route('loans.index');
}
}The route points at the class alone, with no method name:
Route::post('/loans/{loan}/return', ReturnLoanController::class)
->name('loans.return');The button is an ordinary form:
<form method="POST" action="{{ route('loans.return', $loan) }}">
@csrf
<button>Return book</button>
</form>Everything from this section still applies: route model binding hands __invoke the Loan, the name lets templates link to it, and the route can sit inside a group for its middleware.
When to reach for one:
- an action that is not plain create, read, update or delete, such as returning, renewing, approving or exporting
- a page with a lot of logic that would make a resource controller bloated
The shortcut
Before adding a method to a resource controller, ask "is this one of the seven?" If it is, it goes in the resource controller. If you have to explain it with a verb of its own, such as "return", "renew" or "publish", give it its own invokable class and name the class after that verb.
List a member's loans
One of the seven:
LoanController@index.
The trap is writing [ReturnLoanController::class, '__invoke'] or 'return' in the route. Pass the class name alone, and Laravel calls __invoke for you.
Drills
Answer fast — trust the rule.
Which flag makes make:controller generate a single-action controller?
How does a route point at ReturnLoanController?
Which Lendly action best fits a single-action controller?
In __invoke(Loan $loan) for /loans/{loan}/return, what fills $loan?
Why is the return button a POST form rather than a plain link?
Remember
- One job, one class, one
__invokemethod. - "Is this one of the seven?" If not, name an invokable class after the verb.
- Trap: route to the class alone, not to
__invokeby name.
