1. INTRODUCTION
Controllers hold the PHP logic that your routes and forms need. A route chooses what to display, while a controller prepares the result.
Create your controllers in App/Backend/Controllers/. They are ordinary PHP classes in the Backend\Controllers namespace. There is no base Controller class to extend.
Let’s create a small controller that prepares a greeting, then use it from a route and a form.
2. CREATING A CONTROLLER
Create App/Backend/Controllers/GreetingController.php:
<?php declare(strict_types=1);
namespace Backend\Controllers;
defined("ACCESS") or exit("Access Denied");
class GreetingController
{
public function getMessage(string $name = "Visitor"): string
{
$name = trim($name);
if ($name === "")
{
$name = "Visitor";
}
return "Hello, " . $name . "!";
}
}
The method returns a message for the supplied name. An empty name uses Visitor instead. Returning the text lets you use it in a page, a form response or another PHP method.
Note!
Match the filename to the class name, including capitalization. Composer loads GreetingController from GreetingController.php.
3. CALLING A CONTROLLER FROM A ROUTE
Open App/Backend/Routes/Routes.php and add this import below the namespace, before the class:
use Backend\Controllers\GreetingController;
Register a greeting route in the constructor, before handleRoutes():
parent::addRoute("greeting", "greetingHandler");
This example uses ENABLE_LANGUAGES set to false. For language-prefixed URLs, use the registration loop shown in Routes.
Add the handler inside your Routes class:
public function greetingHandler(string $name = "Visitor"): void
{
$controller = new GreetingController();
$message = $controller->getMessage(rawurldecode($name));
$this->render("greeting", [
"message" => $message,
]);
}
The route creates the controller, calls its method and passes the result to the template. rawurldecode() turns URL characters such as %20 back into spaces.
The registered handler is a method on your Routes class. The route calls the controller from there.
4. DISPLAYING THE RESULT
Create App/templates/greeting.php:
<h1><?= esc_html($message); ?></h1>
Open /greeting/Alex/ to see “Hello, Alex!”. Opening /greeting/ uses the default name and displays “Hello, Visitor!”.
Important!
Escape controller results when displaying them. Use esc_html() for HTML text and esc_url() for URLs in HTML attributes.
The route handles rendering, so your controller can return values without printing page content. See Pages for layouts and template examples.
5. USING A CONTROLLER WITH FORMS
You can reuse the same controller from a form handler. Add this form to greeting.php, below the heading:
<form method="post">
<?php CSRF::create("greeting_form", "submit_greeting"); ?>
<label for="greeting-name">Your name</label>
<input id="greeting-name" name="name" type="text" required>
<button type="submit">Show greeting</button>
</form>
In App/Backend/Routes/FormRequests.php, add these imports before the class:
use Backend\Controllers\GreetingController;
use Backend\Core\Session;
Then add the form method inside that class:
public function submit_greeting(array $request): void
{
$name = $request["name"] ?? "";
if (!is_string($name) || trim($name) === "")
{
Session::add("greeting_feedback", "Please enter your name.");
return;
}
$controller = new GreetingController();
Session::add("greeting_feedback", $controller->getMessage($name));
}
Kristal Framework checks the form token before calling this method. The method checks the submitted name and uses the controller to prepare the message.
After the submission, the framework redirects back to the previous page on your site, or / if none is available. Add this to greeting.php to display the saved feedback:
<?php $feedback = Session::get("greeting_feedback"); ?>
<?php if (is_string($feedback)): ?>
<p><?= esc_html($feedback); ?></p>
<?php Session::remove("greeting_feedback"); ?>
<?php endif; ?>
The feedback appears once, then is removed. See Forms for more about submissions and form tokens.
6. WORKING WITH OTHER CLASSES
Controllers can also call the framework’s Database, Cache and Mailer classes. Import the class you need near the top of the controller file, then call it from a method.
Keep related work together in the controller and return the result to the caller. This lets several routes or forms reuse the same logic.
If PHP cannot find your controller, check the namespace, class name and filename. To rebuild Composer’s class list, run this from the project root:
composer dump-autoload --optimize
