Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Ahondev/portfolio-v2/llms.txt

Use this file to discover all available pages before exploring further.

Controllers are the glue between routes and data. WP SSR Framework provides three base classes — WebController, ApiController, and the CPTControllerInterface — each tailored to a different concern. Web controllers render the React SPA shell (or return pure JSON for client-side navigation), API controllers return structured JSON responses for REST endpoints, and the CPT interface enforces a consistent archive/single contract for Custom Post Type routes. All three classes ultimately extend BaseController → Base, which provides the remember() transient cache helper and the service() dependency-injection accessor.

WebController

WebController extends BaseController and is the class every front-end controller should extend. Its single most important method is view().

view()

public function view(
    string $view,
    array $data = [],
    ?EloquentCPT $post = null
): string
view() assembles the $__wp_data__ payload that is embedded in the HTML shell for React to consume, or — when the request contains ?json=1 — returns the same payload as raw JSON for SPA navigation. What it does, step by step:
1

Collect Composer data

Calls ComposerManager::getView($view) to fetch all data registered by Composers that target this view or the wildcard '*'. The composer data is merged into $data.
2

Resolve SEO metadata

For page routes (no $post), SEO fields (seo_title, seo_description, seo_keywords, seo_author) are read from WordPress post meta on the matching Page object. For CPT singles, the same fields are read as ACF properties directly from the EloquentCPT instance.
3

Build the payload

Assembles a $__wp_data__ array containing view, seo, data, and assets (Vite manifest entries).
4

HTML or JSON mode

If $_GET['json'] === '1', responds with Content-Type: application/json and returns the payload without assets. Otherwise includes src/SSR/client.php, which embeds $__wp_data__ as an inline <script> and outputs the Vite entry-point tags.
JSON response shape (SPA navigation):
{
  "view": "home",
  "seo": {
    "site_title": "Ahon",
    "page_title": "Accueil – Ahon",
    "description": "...",
    "keywords": "...",
    "author": "...",
    "url": "/"
  },
  "data": {
    "services": [...],
    "site_email": "hello@ahon.fr",
    "site_services": [...]
  }
}
The React side reads window.__wp_data__ on first load. For subsequent client-side navigations the SPA fetches ?json=1 from the target URL and updates the store — no full-page reload required.

postView()

A thin shorthand for CPT single pages that swaps the argument order to make call sites read more naturally:
public function postView(
    string $view,
    ?EloquentCPT $post,
    array $data = []
): string
Internally it just calls $this->view($view, $data, $post).

remember()

Inherited from Base. Wraps WordPress transients for easy controller-level caching:
protected function remember(string $key, int $ttl, callable $callback): mixed
// Cache the service list for 5 minutes
$services = $this->remember('composer_services_v1', 300, function () {
    return Service::query()->all();
});

Real controller examples

namespace Ahon\WPCMS\App\Controllers\Web;

use Ahon\WPCMS\App\PostTypes\Service;
use Ahon\WPCMS\Controllers\WebController;

class HomeController extends WebController
{
    public function home(): string
    {
        $services = $this->remember('composer_services_v1', 300, function () {
            return Service::query()->all();
        });

        return $this->view('home', [
            'services' => $services,
        ]);
    }

    public function devis(): string
    {
        return $this->view('devis');
    }

    public function agence(): string
    {
        return $this->view('agence');
    }

    public function contact(): string
    {
        return $this->view('contact');
    }
}

CPTControllerInterface

Any controller registered via Route::CPT() must implement CPTControllerInterface. The interface enforces two methods:
interface CPTControllerInterface
{
    public function archive(): string|bool;
    public function single(?EloquentCPT $post): string|bool;
}
MethodWhen called$post value
archive()Exact match on the CPT base path, e.g. /blog— (no argument)
single()Any sub-path, e.g. /blog/my-articleEloquentCPT instance, or null if the slug doesn’t match any published post
When $post is null in single(), it means the slug didn’t match any post — treat this as a 404. Return false or render a not-found view explicitly; don’t assume the post is always present.

ApiController

ApiController extends BaseController and is the base class for every REST endpoint handler. Its methods return plain PHP arrays; the WP REST API serialises them to JSON automatically.

success()

protected function success(array $data, int $code = 200): array
Returns a normalised success envelope:
{
  "success": true,
  "code": 200,
  "data": { ... }
}

error()

protected function error(
    string $message = 'Une erreur est survenue',
    string $context = 'global',
    int $code = 500,
    array $errors = []
): array
Returns a normalised error envelope:
{
  "success": false,
  "code": 422,
  "error": {
    "message": "Validation error",
    "context": "contact",
    "errors": { "email": ["The email field is required."] }
  }
}

validate()

Delegates to a request class for input validation and throws ValidationException on failure:
protected function validate(WP_REST_Request $request, string $requestClass): array

Real controller example

// app/Controllers/Api/ContactController.php
namespace Ahon\WPCMS\App\Controllers\Api;

use Ahon\WPCMS\App\Requests\Contact\ContactRequest;
use Ahon\WPCMS\Controllers\ApiController;
use Ahon\WPCMS\Exceptions\ValidationException;

class ContactController extends ApiController
{
    public function contact(\WP_REST_Request $request): array
    {
        try {
            $validated = $this->validate($request, ContactRequest::class);
        } catch (ValidationException $e) {
            return $this->error('Validation error', 'contact', 422, $e->getErrors());
        }

        // ... process and send notification

        return $this->success([]);
    }
}
API controller methods always receive a \WP_REST_Request $request instance as their first argument. Use $request->get_param('key') for body and query parameters alike, or $request->get_body_params() for raw POST body fields.

Build docs developers (and LLMs) love