Essentials

Controllers

Organize request handling logic with BaseController

In the API skeleton, application controllers live in app/Controllers/ and usually extend Glueful\Controllers\BaseController.

Basic Controller

<?php

declare(strict_types=1);

namespace App\Controllers;

use Glueful\Controllers\BaseController;
use Glueful\Http\Response;
use Symfony\Component\HttpFoundation\Request;

class UserController extends BaseController
{
    public function index(Request $request): Response
    {
        $users = db($this->getContext())->table('users')->get();

        return $this->success($users);
    }
}

Registering Controllers

The router resolves [Controller::class, 'method'] handlers from the container, and there is no autowire fallback for unregistered classes. Register every controller in a provider's services() — in the skeleton, app/Providers/AppServiceProvider.php:

public static function services(): array
{
    return [
        \App\Controllers\WelcomeController::class => ['autowire' => true, 'shared' => true],
        \App\Controllers\UserController::class => ['autowire' => true, 'shared' => true],
    ];
}

An unregistered controller fails with "Service not found" when its route is first hit. php glueful scaffold:controller writes the class to app/Controllers (namespace App\Controllers) and prints the registration step, but it doesn't register the controller or add a route for you.

What BaseController Gives You

BaseController provides:

  • $this->getContext() / $this->context — the ApplicationContext, for helpers like db($this->getContext()), app(...), config(...)
  • $this->currentUser — the authenticated ?Glueful\Auth\UserIdentity (see Authentication)
  • $this->request — the current Symfony request
  • $this->authManager and $this->repositoryFactory
  • response helpers: success(), created(), notFound(), unauthorized(), forbidden(), validationError(), serverError(), paginated(), plus HTTP-caching helpers like cached() and checkNotModified()
  • request helpers: getRequestData() and getPutData() (JSON or form bodies)
  • lightweight validation via validateRequest() (required, email, max:N)

There is no $this->db property — use db($this->getContext()) or inject Glueful\Database\Connection.

Create / Update Example

class TaskController extends BaseController
{
    public function store(Request $request): Response
    {
        $data = $this->getRequestData();

        if ($error = $this->validateRequest($data, [
            'title' => 'required|max:255',
            'description' => 'max:1000',
        ])) {
            return $error;
        }

        // insert() returns the affected-row count; insertGetId() returns the new id
        $taskId = db($this->getContext())->table('tasks')->insertGetId([
            'title' => $data['title'],
            'description' => $data['description'] ?? null,
        ]);

        return $this->created([
            'id' => $taskId,
            'title' => $data['title'],
        ]);
    }
}

For anything beyond simple checks, accept a typed request DTO instead of calling validateRequest().

Constructor Injection

Registered with 'autowire' => true, a controller gets its constructor dependencies from the container:

use Glueful\Bootstrap\ApplicationContext;
use Glueful\Database\Connection;
use Psr\Log\LoggerInterface;

class UserController extends BaseController
{
    public function __construct(
        ApplicationContext $context,
        private readonly Connection $connection,
        private readonly LoggerInterface $logger,
    ) {
        parent::__construct($context);
    }

    public function index(Request $request): Response
    {
        $this->logger->info('Listing users');

        return $this->success($this->connection->table('users')->get());
    }
}

If you override the constructor, call parent::__construct($context) so the base dependencies are initialized.

ResourceController

Glueful also ships a built-in Glueful\Controllers\ResourceController behind generic /data/{table} CRUD routes (under the API prefix). Use it when you want convention-driven resource endpoints quickly, but prefer explicit controllers first while you are learning the framework.

For most new applications, start with explicit controllers in app/Controllers and move to generic resource wiring only when the tradeoff is clear.