Application Controllers
Application Controllers
Controllers are a fundamental part of the Model-View-Controller (MVC) architecture. They act as the glue between the user, the data (Model), and the presentation (View). When a request is received by the application, it is routed to a specific controller action. The controller's job is to interpret the request, interact with the necessary models to retrieve or modify data, and then return a response to the user. This response could be a rendered HTML view, a JSON object, an XML document, or any other type of output.
In Hazaar, controllers are classes that extend the Hazaar\Controller base class, either directly or via one of the specialized controller classes such as Hazaar\Controller\Basic or Hazaar\Controller\Action. Each public method in a controller class represents an "action" that can be triggered by a URL.
By default, controllers are stored in the app/controllers directory. All controller classes must be defined within the App\Controller namespace to be correctly located by the application router.
Controller Initialization
Hazaar provides an init() method that is called immediately after the controller is instantiated and fully set up. This is the recommended place to put any initialization code for your controller.
public function init(): void
{
// Controller setup code here
}While not required, the init() method can optionally return a Response object. If a response is returned, the main action method will not be executed. This can be useful for preventing action execution during initialization, such as enforcing access control or redirecting unauthorized users.
public function init(): ?Response
{
// Check if user is authenticated
if (!$this->session->isAuthenticated()) {
// Return a redirect response - action will not be executed
return $this->response->redirect('/login');
}
// Continue with normal initialization
$this->view->set('user', $this->request->getAttribute('auth'));
return null; // or return nothing to allow action execution
}Tips
This is legacy functionality that should ideally be replaced by middleware, but remains available for backward compatibility.
Warning
When a controller is instantiated, the __construct() method is used to set up the object. However, in Hazaar, the __construct() method is used by the framework to set up the controller environment. Because of this, if you override the __construct() method you must ensure that you call the parent constructor or the controller will not work correctly.
Accessing the Request Specification
The controller object has a request property that contains the Hazaar\Application\Request object for the current request. This object contains all the information about the request including GET/POST parameters, headers, cookies, and more.
public function index(): void
{
// Get a query parameter
$id = $this->request->get('id');
// Check if the request is a POST request
if ($this->request->isPost()) {
// Do something
}
}Creating a Response
While you can return a response object directly from an action, the controller also provides a response helper to make creating common responses easier. This helper is available as the response property on the controller.
public function index(): Response
{
// Create a redirect response
return $this->response->redirect('/login');
}For more information on how to use built-in responses, see Responses.
Action Arguments
If your route contains parameters, these can be passed directly to your action method as arguments. This keeps your code clean and avoids having to manually retrieve parameters from the request object.
For example, if you have a route like /user/{id}, you can define your action like this:
public function user(int $id): Response
{
// $id will contain the value from the URL
return $this->response->json(['id' => $id]);
}Basic Controllers
The Hazaar\Controller\Basic controller extends the Hazaar\Controller class and is the simplest form of controller in the framework. It is intended for use when you need absolute control over the response sent back to the client. This makes it the perfect choice for implementing RESTful APIs, AJAX handlers, or simply returning raw data streams where the overhead of a view rendering system is unnecessary.
Unlike Action controllers, Basic controllers do not automatically look for or render view files. Instead, your action methods typically return a Hazaar\Controller\Response object directly or throw an exception that is handled by the framework.
Here is an example of a Basic controller used for a simple system status API. This controller has two actions, index and ping, both returning JSON responses.
<?php
declare(strict_types=1);
namespace App\Controller;
use Hazaar\Controller\Basic;
use Hazaar\Controller\Response\JSON;
class Status extends Basic
{
public function index(): JSON
{
return new JSON([
'status' => 'OK',
'timestamp' => time(),
'service' => 'My API Service'
]);
}
public function ping(): JSON
{
return new JSON(['pong' => true]);
}
}Action Controllers
The Hazaar\Controller\Action controller extends the Hazaar\Controller base class and adds a layer of functionality specifically for rendering views. This is the controller type you will use for most user-facing web pages where you need to return HTML.
When an Action controller is initialized, it sets up a ViewRenderer. By default, when an action method is called, the controller expects to find a view script matching the name of the action. It also typically supports wrapping this view in a layout template, allowing you to maintain a consistent look and feel across your site.
Here is an example of an Action controller that handles standard page views and form submissions:
<?php
declare(strict_types=1);
namespace App\Controller;
use Hazaar\Controller\Action;
use Hazaar\Controller\Response;
use Hazaar\Validation\Assert;
class Main extends Action
{
// The init method is called before any action is executed
public function init(): void
{
$this->view->set('user', $this->request->getAttribute('auth'));
}
public function index(): void
{
// Explicitly load the 'index' view
$this->view('index');
}
public function login(): ?Response
{
// Check if the request is a POST (form submission)
if ($this->request->isPost()) {
$username = $this->request->get('username');
$password = $this->request->get('password');
Assert::that($username)->string()->notEmpty('Username is required');
Assert::that($password)->string()->notEmpty('Password is required');
$result = $this->session->authenticate($username, $password);
if ($result->success) {
// Redirect the user on success
return $this->response->redirect('/dashboard');
}
// Pass error message to the view
$this->view->set('error', 'Invalid username or password');
}
// Render the 'login' view if not a POST or if login failed
$this->view('login');
// Modify the response headers before returning (optional)
return $this->response->ok()->header('X-Custom-Header', 'LoginPage');
}
public function dashboard(): void
{
$this->view('dashboard');
}
}RPC Controllers
Hazaar supports building full RPC services from controllers, including JSON-RPC and XML-RPC protocols. You can expose public controller methods as remote procedures, validate inputs with type hints, and return structured data while the framework handles serialization and error responses.
For a complete guide on configuring RPC servers and using JSON-RPC/XML-RPC clients, see RPC.
Error Controllers
It is possible to create custom error controllers that you have full control over the look and feel over. While the built-in error controller is great for rapidly prototyping your projects, it is not customisable in any way. This is by design so that a consistent error handling path can be defined for even the most fatal of errors.
At some point you are going to want to display your errors with a custom error controller so that you can use your own views and layouts for a seamless user experience. Fortunately this is VERY easy with Hazaar and can be done in only two steps.
Step 1 - Define an error controller
You define an error controller just like any other controller in your application by creating a file in the application/controllers directory that contains a controller class. The difference is the method names that you need to use. Because we are handling an error and not executing an action, only 5 method names are supported and are based on the type of response that needs to be given. The possible methods are:
- html() - This is called when the response should be a Hazaar\Controller\Response\Html object. This is the method that will pretty much be called all the time as it is how you define the unique look and feel of your application to the user.
- json() - This is called when the response should be a Hazaar\Controller\Response\Json object. JSON responses will not normally need to be handled as the default error controller should provide adequate handling. You can however use this method if you want to change how JSON request errors are handled.
- xmlrpc() - This is called when the response should be a Hazaar\Controller\Response\Xml object. Similar to theJSON response except this is called when the original controller executed was an XML-RPC controller. See: XMLRPC for more information on XML-RPC controllers.
- text() - This is called when the response should be a Hazaar\Controller\Response\Text object. Use this if you just want to return plain text as the error response.
- run() - This is a generic catch all method that will be executed if an appropriate response method does not exist. If you decide to use this, be careful as you will have to handle all the response types manually.
Here, we will create a new error controller aptly named ErrorController.
class ErrorController extends \Hazaar\Controller\Error {
protected function html()
{
$out = new \Hazaar\Controller\Response\Layout('app');
$out->addHelper('bootstrap');
$out->add('error');
$out->code = $this->code;
$out->message = $this->errstr;
return $out;
}
}In the above controller we are using a Layout controller response. This is appropriate because the Layout controller response is extended from the Html controller response (as is the View controller response). This means we can use our standard application.phtml file as the response and keep our application look and feel. Then we inject a view into the layout, in this case the 'error' view, and our error will be displayed as though it is part of our application.
Because we are using a layout view we also need to create a custom error view. We will just call this 'error', so we create the file application/views/error.phtml and put the following content in there.
<h1>A <?=$this->code;?> error occurred</h1>
<h3><?=$this->message;?></h3>Step 2 - Configure the application
The next step is simple. All you need to do now that you have built your custom error controller is tell Hazaar that you want to use it instead of the default controller. We do this in the application.ini file by adding the following configuration directive:
{
"development": {
"app": {
"errorController": "Error""
}
}
}Where 'Error' is the name of our controller.
Remember
The actual name of the controller is not the same as the name of the class. The name is the part of the class name before Controller.
Using the above code in our example application that is provided with Hazaar, if you were to navigate to a page that doesn't exist you should now get something like this:
That's it!
With your error controller now built you can pretty much do whatever you want with your errors. Just keep in mind that if you throw an error inside your error controller, an error loop may occur. Hazaar will protect you from these as best it can however.