Controllers

Controllers group your application’s business logic into organised classes. In LacePHP your controllers live under weave/Controllers. We’ve structured them so you can:

  • Use a shared base class to bundle common helpers (request, response, auth, etc.)

  • Keep each action method focused on handling a single route and returning data or HTML

  • Follow good coding standards with type hints, clear naming and single-responsibility methods

Helper Functions

Grab the current request or responder inside any controller:

$request   = sole_request();
$responder = kickback();

This keeps your code concise and easy to test.

BaseController for Shared Logic

Rather than repeating the same calls in every controller, create your own BaseController:

<?php
namespace Weave\Controllers;

use Lacebox\Sole\Http\ShoeRequest;

abstract class BaseController
{
    protected ShoeRequest $request;

    public function __construct()
    {
        $this->request = ShoeRequest::grab();
    }

    protected function json(array $data, int $status = 200): string
    {
        return kickback()->json($data, $status);
    }

    protected function html(string $content, int $status = 200): string
    {
        return kickback()->html($content, $status);
    }

    protected function abort(int $code = 404, string $message = ''): string
    {
        switch ($code) {
            case 401:
                return kickback()->unauthorized($message);
            case 500:
                return kickback()->serverError($message);
            default:
                return kickback()->notFound($message);
        }
    }
}

Example Controller

<?php
namespace Weave\Controllers;

class LaceUpController extends BaseController
{
    /**
     * Responds with JSON on GET /hello
     */
    public function hello(): string
    {
        $name = $this->request->input('name', 'Guest');
        return $this->json([
            'message' => "Hello, {$name}! You laced up LacePHP.",
            'version' => '1.0.0'
        ]);
    }

    /**
     * Responds with HTML on GET /html/{id}
     */
    public function html(string $id = ''): string
    {
        $safeId = htmlspecialchars($id, ENT_QUOTES, 'UTF-8');
        return $this->html("<h1>Welcome to LacePHP {$safeId}</h1>");
    }

    /**
     * Simulate an error
     */
    public function error(): string
    {
        return $this->abort(500, 'Something went wrong.');
    }
}

Why This Design?

  • Constructor Injection Pulling ShoeRequest into $this->request ensures every method uses the same sanitised input object.

  • Shared Helpers Moving json(), html() and abort() into a base class avoids duplication and keeps controllers focused.

  • Type Safety & Clarity Return types (: string) and parameter hints make your code self-documenting and IDE-friendly.

  • Separation of Concerns Controllers handle input and output; models handle data; views handle presentation.

Advanced Tips

  • Dependency Injection If you require other services (database, mailer), inject them via your constructor:

    public function __construct(UserRepository $users)
    {
        parent::__construct();
        $this->users = $users;
    }
    
  • Validation Check and validate inputs before passing them to models:

    $email = $this->request->input('email');
    if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return $this->abort(422, 'Invalid email address');
    }
    
  • Consistency Keep one controller per feature area and name methods after HTTP actions (for example index(), show(), store(), update()).

Warning

  • Do not mix raw header() or echo calls inside controllers—always return through your responder methods.

  • Escape all output when rendering HTML to prevent cross-site scripting.

  • Keep logic thin—move complex operations into services or models for better maintainability and testing.

With this pattern your LacePHP controllers remain clean, maintainable and ready to evolve as your application grows.