Data & Validation

Validation & Sessions

The Validateable trait adds a fluent validation DSL to any action, while native PHP sessions are wrapped in small static facades for storage and flash messages.

Validation

Use vfield($key, $value) and chain rules; each rule registers a first-wins error message.

<?php

namespace App\Actions;

use System\Http\Action;
use System\Http\Traits\Validateable;

class RegisterAction extends Action {
    use Validateable;

    public function middlewares() { return []; }

    public function main() {
        $this->vfield("email", $this->input("email"))->required()->email();
        $this->vfield("password", $this->input("password"))->required()->min_length(8);

        if (!$this->validated()) {
            return response()->status(422)->json(["errors" => $this->errors()]);
        }

        return response()->json(["ok" => true]);
    }
}

Available rules

RuleExampleDescription
file()->file()Value must be a valid upload (isfile(): UPLOAD_ERR_OK + is_uploaded_file).
mimes([...])->mimes(["image/png", "image/jpeg"])File MIME must be in the list (sniffed with finfo).
max_size(MB)->max_size(2)Upload size limit in megabytes (chainable).
required()->required()Value must be set and non-empty (after trim).
email()->email()Valid email via FILTER_VALIDATE_EMAIL.
min_length(n)->min_length(8)String length must be ≥ n.
max_length(n)->max_length(100)String length must be ≤ n.
equals($x)->equals($confirm)Strict equality with another value.
custom($cb, $msg)->custom(fn($v) => in_array($v, ["a","b"]))User callback must return truthy.

Trait methods

MethodDescription
error($key, $message)Register an error (first-wins per key).
validated()Runs an optional validate() method if defined, then returns empty($errors).
errors()Returns the error map [key => message].
Current caveats
  • required(), email(), min_length() and max_length() assume scalar input — they crash with a TypeError on array input (e.g. email[]=). Validate files with file()/mimes() and treat arrays as invalid before these rules.
  • required() uses PHP’s empty() internally, so "0" / 0 / "" count as missing.
  • file() requires a real HTTP upload, so it fails under CLI/tests.

Sessions

Sessions use native PHP sessions, started in public/index.php with hardened cookie parameters (httponly on, samesite=Lax, and secure when the request is HTTPS) before session_start().

use System\Support\Session;

Session::put("user_id", 42);
Session::get("user_id");     // 42
Session::has("user_id");     // true
Session::delete("user_id");
Returns false, not null

Session::get() returns false (not null) when a key is missing or empty.

Flash messages

Flash messages are JSON-encoded into the session and consumed on read:

use System\Support\Flash;

Flash::success(["Account created!"]);
Flash::error(["Invalid password"]);
Flash::warning(["Disk almost full"]);
Flash::info(["New version available"]);

Render them in a view — @message is replaced once per item, then the flash is cleared:

<?php
Flash::render("success", '<div class="alert alert-success">@message</div>');
Flash::render("error", '<div class="alert alert-danger">@message</div>');
?>

render($type, $html, $escape = true) escapes each message with htmlspecialchars by default — pass false as the third argument only when the message is trustworthy raw HTML.

Raw flashes

Flash::set("just_saved", true);
Flash::get("just_saved"); // true (consumed on read)

Flash in a redirect cycle

public function main() {
    $this->vfield("name", $this->input("name"))->required()->min_length(2);

    if (!$this->validated()) {
        Flash::error($this->errors());
        return response()->redirect(route("products.index"));
    }

    Flash::success(["Product created!"]);
    return response()->redirect(route("products.index"));
}

Part of the Lunaris documentation · Source on Codeberg · MIT License