Skip to content

AbstractController

v1.1.0

The AbstractController is the foundation of every generated Controller.

Instead of manually implementing CRUD operations, validation, DTO conversion and standardized responses, generated Controllers inherit these behaviors automatically.

Every Controller generated by php artisan make:domain extends this class.


Overview

When extending this class, your Controller automatically gains:

  • Complete REST CRUD
  • Automatic FormRequest validation
  • Request → DTO conversion
  • Service delegation
  • Resource serialization
  • Standardized JSON responses
  • Pagination support
  • Exception normalization
  • Relationship eager loading

Minimal example:

php
class UserController extends AbstractController
{
    protected mixed $service;

    protected ?string $requestValidate = UserRequest::class;

    protected ?string $requestDto = UserDTO::class;

    protected ?string $resource = UserResource::class;

    public function __construct(UserService $service)
    {
        $this->service = $service;
    }
}

Internal flow

Every request follows the same execution pipeline.

RequestFormRequestDTOServiceRepository

This keeps HTTP concerns isolated from business logic.


Protected Properties

$service

php
protected mixed $service;

Holds the injected Service instance.

The Controller never talks directly to the Model.

Instead:

text
Controller

 Service

Repository

 Model

Example:

php
public function __construct(UserService $service)
{
    $this->service = $service;
}

$requestValidate

php
protected ?string $requestValidate;

Defines which FormRequest validates the store() action.

Example:

php
protected ?string $requestValidate = UserRequest::class;

During execution:

  1. Laravel validates the request.
  2. Validation errors become HTTP 422 automatically.
  3. The validated payload becomes a DTO.

$requestValidateUpdate

php
protected ?string $requestValidateUpdate;

Defines the FormRequest used during updates.

Example:

php
protected ?string $requestValidateUpdate = UserUpdateRequest::class;

This allows different validation rules for create and update.


$requestDto

php
protected ?string $requestDto;

Defines which DTO will be instantiated during creation.

Example:

php
protected ?string $requestDto = UserDTO::class;

Internally:

php
UserDTO::fromRequest($request);

$requestDtoUpdate

Used by update().

php
protected ?string $requestDtoUpdate = UserUpdateDTO::class;

This keeps update-specific fields isolated.


$resource

Defines which Resource serializes responses.

Example:

php
protected ?string $resource = UserResource::class;

Instead of returning Models directly:

php
return new UserResource($user);

Output:

json
{
  "public_id": "01JXYZABCDEF123",
  "nome": "John Doe"
}

$with

php
protected array $with = [];

Automatically eager-loads relationships.

Example:

php
protected array $with = [
    'municipio'
];

Equivalent to:

php
User::with('municipio');

CRUD Methods

index()

Returns a paginated Resource collection.

Flow:

text
Repository

paginate()

Resource::collection()

Example response:

json
{
  "data": [],
  "links": {},
  "meta": {}
}

show()

Finds one record.

Supports public identifiers automatically.

Example:

text
GET /api/users/01JXYZABCDEF

The Controller delegates lookup to the Repository.


store()

Creates a new record.

Execution:

  1. Validate Request.
  2. Create DTO.
  3. Execute Service.
  4. Serialize Resource.
  5. Return HTTP 201.

Example:

php
POST /api/users

Request:

json
{
  "nome": "John",
  "email": "john@example.com"
}

update()

Uses update-specific Request and DTO.

Flow:

text
Request

Update Request

Update DTO

Service

destroy()

Deletes the record.

When the Model uses SoftDeletes:

php
use SoftDeletes;

The generated Controller automatically performs a soft delete.


Automatic DTO Conversion

One of the biggest advantages of the generated Controllers is automatic DTO conversion.

Instead of:

php
$request->validated();

the Controller executes:

php
UserDTO::fromRequest($request);

Benefits:

  • typed objects
  • immutable payload
  • cleaner Services

Automatic Resources

Generated Controllers never expose Models directly.

Instead:

php
return new UserResource($user);

Advantages:

  • hide internal IDs
  • consistent API responses
  • easier frontend integration

Pagination

Pagination is automatic.

Example:

php
$this->service->paginate();

Response includes:

  • data
  • links
  • meta

No additional Controller code is required.


Exception Handling

Exceptions are normalized.

Instead of exposing internal stack traces, the Controller returns predictable JSON.

Example:

json
{
  "type": "error",
  "status": 404,
  "message": "Resource not found."
}

Success Response

Successful operations share the same structure.

json
{
  "type": "success",
  "status": 200,
  "data": {}
}

Error Response

Validation:

json
{
  "type": "error",
  "status": 422
}

Authentication:

json
{
  "type": "error",
  "status": 401
}

Not Found:

json
{
  "type": "error",
  "status": 404
}

Permissions

Authorization can be customized by overriding the generated methods.

Example:

php
public function update(...)
{
    $this->authorize('update', $user);

    return parent::update(...);
}

This keeps compatibility with Laravel Policies.


Best Practices

  • Keep Controllers thin.
  • Put business rules inside Services.
  • Receive DTOs instead of Requests.
  • Return Resources instead of Models.
  • Use public identifiers for external APIs.

Generated Controllers follow the same layered architecture used throughout Laravel Domain Generator, keeping HTTP concerns separate from business logic while reducing boilerplate code.

Released under the MIT License.