Zum Inhalt springen

AI blog · DotApp PHP Framework 2.0

Controllers and Response in DotApp PHP Framework

A Shop controller is a class under app/modules/Shop/Controllers/ that extends \Dotsystems\App\Parts\Controller. Methods are public static — there is no $this. The first argument is $request. You return an HTML string, new Response($code, $body), Response::json, Response::redirect, or DotApp::DotApp()->ajaxReply. Route strings pick the method: Shop:Home@index! skips DI. Drop the ! only when you type-hint services. This article is a complete Home controller: page render, empty-view guard, and the return table.

Common mistakes

Wrong Right
Type-hint Renderer $renderer on a method reached with Shop:Home@index! Trailing ! skips DI. Call Renderer::new() inside, or drop the !
Use instance methods / $this public static function index($request)
Assume a missing view throws You get "". Check the string; log; return new Response(500, 'Template error')
Response::send() or static status() Those methods do not exist. Return the Response (or the HTML string)
Browser channel answered with Response::json ajaxReply + client parseReply. Ordinary HTTP JSON: Response::json
Read POST with $request->data() then hash a password $request->data(true)Request lifecycle

When this is the page handler

Every GET/POST you register in initialize() lands here. Closures on the router are fine for a one-liner; a named controller is the Shop default. Grammar of the callable string: Callable strings. Routing verbs: How routing works.

What you return

Return Effect
HTML string Becomes the response body
new Response($code, $body) / Response::make($code, $body) A distinct object. Short-circuits the pipeline
Response::json($array, $code = 200) JSON body + Content-Type: application/json; charset=utf-8
Response::redirect($url, 302) Redirect
DotApp::DotApp()->ajaxReply($body, $code) Base64 JSON. HTTP code is set when $code > 0. Client parseReply
null Keep the existing body

Static Response::json / redirect mutate a shared response object bound to the request and return it for chaining. There is no send(). Use new Response(...) or Response::make() when you need a distinct object to return. JSON APIs: How to build JSON endpoints with Router. Four failure styles: Error handling and return values.

Complete Shop Home controller

File: app/modules/Shop/Controllers/Home.php. Scaffold with php dotapper.php --module=Shop --create-controller=Home — do not hand-write the class file. Route: Router::get($p . '/', 'Shop:Home@index!', Router::STATIC_ROUTE).


<?php
namespace Dotsystems\App\Modules\Shop\Controllers;

use Dotsystems\App\Parts\Config;
use Dotsystems\App\Parts\DB;
use Dotsystems\App\Parts\Logger;
use Dotsystems\App\Parts\Renderer;
use Dotsystems\App\Parts\Response;

class Home extends \Dotsystems\App\Parts\Controller
{
    public static function index($request)
    {
        $rows = DB::module('RAW')->q(function ($qb) {
            $qb->select(['id', 'title'])->from('shop_items')->orderBy('id', 'DESC')->limit(50);
        })->all();

        $html = Renderer::new()
            ->module(static::moduleName())
            ->setView('home', 'fallback/empty')
            ->setViewVar('title', 'Shop')
            ->setViewVar('items', $rows)
            ->setViewVar('prefix', Config::module('Shop', 'prefix'))
            ->renderView();

        if ($html === '') {
            Logger::use()->error('Shop home view produced empty output');
            return new Response(500, 'Template error');
        }
        return $html;
    }

    public static function item($request)
    {
        $id = (int) ($request->matchData()['id'] ?? 0);
        if ($id < 1) {
            return Response::redirect(Config::module('Shop', 'prefix') . '/', 302);
        }
        return Renderer::new()
            ->module('Shop')
            ->setView('item')
            ->setViewVar('id', $id)
            ->renderView();
    }
}
    

all() on RAW is [] when empty — safe. Do not call first() unguarded. Route params live in matchData(), not $request->id. Views: How to render views and layouts. Database: How to use the database.

Same method with DI (no trailing !)


public static function index($request, \Dotsystems\App\Parts\Renderer $renderer)
{
    return $renderer->module(static::moduleName())
        ->setView('home')
        ->setViewVar('title', 'Shop')
        ->renderView();
}
    

Route: 'Shop:Home@index' — no !. Hot Shop pages keep the bang and call Renderer::new(). Bind your own services with $dotApp->bind / singletonDependency injection. Call another controller: DotApp::call('Shop:Home@helper!', $arg) or static::call('otherMethod', $request).

FAQ

Can I use $this in a controller?

No. Methods are static. Use static::moduleName(), facades, and Renderer::new().

How do I set a header?

Return a Response you configured, or use the shared instance methods on Response. There is no $request->headers() reader.

header('Location') still works?

It bypasses the Response object. Prefer return Response::redirect($url, 302).

When is ajaxReply required?

When the browser posted through /assets/dotapp/dotapp.js (<fo-rm>, load(), Bridge). Public JSON that a generic HTTP client calls uses Response::json.

May I paste a controller class by hand?

Scaffold with DotApper. Namespace and the parent class must match the module folder.

Where is POST?

$request->data(true) after crcCheck() on the channel. Request lifecycle.

See also