Skip to content

Controller

Prefer Single Action Controllers

One of the main goals of classes in OOPs is to encapsulate data and behavior. For controllers, there is usually nothing to encapsulate and reuses between actions (public methods); for this reason, we prefer to have one action per controller.

php
final class ShowMemberProfileController
{
    public function __invoke(Request $request)
    {
        // ...
    }
}

No Parent class

There are usually no reasons of doing this when you follow other rules from these conventions.

Invokable controller naming conventions

There are no hard rules in naming controllers, so always think about what is the responsibility of the controller and try to make the name descriptive. A good rule to follow is to start the name with a verb. Below you can find some examples that can be treated as a baseline when naming invokable controllers.

Common operations:

  • Index: ListResourcesController
  • Create: CreateResourceController
  • Store: StoreResourceController
  • Show: ShowResourceController
  • Edit: EditResourceController
  • Update: UpdateResourceController
  • Destroy: DeleteResourceController

Other examples:

  • Merge: MergeResourcesController
  • Process: ProcessResourceController

Controllers Should Not Extend/Inherit

Controllers SHOULD NOT extend any base class: there are usually no reasons to do it. Prefer composition (using DI) instead of inheritance. Also, often common logic can be extracted into Middleware.

Singular resource name

Controllers that control a resource must use the singular resource name.

diff
-final class CoursesController
+final class CourseController

Stick to default CRUD action names

Stick to default CRUD action names when possible: index, create, store, show, edit, update, and destroy.

This is a loose guideline that doesn’t need to be enforced.

Inject route params, then Request, then other dependencies

Order of controller action parameters:

  1. Route params or bound Models
  2. Request instance
  3. Other dependencies
php
public function __invoke(User $user, Request $request, DetachUserFromTeamAction $detachAction)
{
    ...
}

The same for scalar GET params (good example: public function __invoke(int $teamId, Request $request).

Why?

Use Action classes

Actions are classes that take care of one specific task. Controller classes have access to the application context (Request, Cache, Session, etc.), extract the data from the context, validate it and pass to the Action class.

php
final class DetachTeamMemberController
{
    public function __invoke(Team $team, Member $member, DetachTeamMemberAction $detachAction): RedirectResponse
    {
        $this->authorize('update', $team);

        $detachAction->execute($team, $member);

        return redirect()->route('teams.show', [$team]);
    }
}