Skip to main content

Hello Controller

Controllers are central to every n2n module. They receive requests, access the database via models and call views. Our first step is a controller for a simple Hello World!

Creating a Controller​

n2n is built according to the Model-View-Controller (MVC) principle. The controller is the part of the program that receives and processes the requests of the user.

In n2n a separate file is created for every class. The directory structure is freely selectable. We recommend storing all controller classes of a module in the subfolder {module-namespace}/controller.

Create /app/qs/controller/BlogController.php with this content:

<?php
namespace qs\controller;

use n2n\web\http\controller\ControllerAdapter;

class BlogController extends ControllerAdapter {
public function index() {
echo 'Hello World!';
}
}

Note that n2n uses namespaces. The namespace for our controller is: qs\controller

A normal controller inherits from the ControllerAdapter class. It provides all properties required for controllers.

If a controller is called without further parameters, the index() method is executed. To be able to call this controller, we have to register a routing for it.

Registering a Routing​

A routing can be registered for every controller. If the path registered in the routing is called by the visitor, the ControllingPlan of n2n invokes the registered controller.

Application and Module Configuration​

Every n2n application has an app.ini. It is located in the /var/etc directory:

/var/etc/app.ini

In this app.ini the most important configurations of the application are set. Every module can add to these configurations via its own app.ini. Every n2n application therefore has the general app.ini and the app.ini of its modules.

Attention

All configuration values of the application that are not changed at runtime are stored in the app.ini files.

So let's create our own app.ini for our module. Create the following file:

/var/etc/qs/app.ini

The new file should have the following content:

[routing]
controllers[/blog] = "qs\controller\BlogController"

With [routing] we indicate that controller routings are registered with the following configuration values. The paths are registered via controllers. The registered path (/blog) is in the square brackets. Spelled out, the configuration value entered above means the following: "Every request to /blog should be delegated to the BlogController". Note: in the app.ini the controller class must be entered with its full namespace!

If you take a look at the app.ini of the application, you will notice that there is a [routing] group there as well. The context path for our controller could just as well be set in the app.ini of the application. We recommend storing configuration values that relate only to one module in the app.ini of the module. It is also conceivable that the app.ini of the application and the app.ini of the module contain values that conflict with each other.

Attention

If the app.ini of the application and the app.ini of a module conflict, the value of the app.ini of the application takes precedence.

After saving our app.ini, the controller we programmed can now be called as follows:

http://localhost/[path-to-the-public-folder-of-your-project]/blog (e.g. http://localhost/quickstart/public/blog)

If everything works correctly, you will now see "Hello World!" in your browser under the URL shown above.

Action Methods​

Now we add the methods doDetail() and doThanks() to our controller:

<?php
namespace qs\controller;

use n2n\web\http\controller\ControllerAdapter;

class BlogController extends ControllerAdapter {
public function index() {
echo 'Hello World!';
}

public function doDetail(int $id) {
echo 'Detail: ' . $id;
}

public function doThanks() {
echo 'Thanks';
}
}

We call every public method (public) on a controller that starts with do an action method. Action methods can be called via their name (without "do"). To see the effect, call the following URL in your browser:

http://localhost/[path-to-the-public-folder-of-your-project]/blog/thanks

Try the same for the action method doDetail():

http://localhost/[path-to-the-public-folder-of-your-project]/blog/detail

If everything works correctly, you get a 404 error message on the path given above! The reason for this is the $id parameter of the action method! The action method doDetail() necessarily expects an ID to be given! Try it with:

http://localhost/[path-to-the-public-folder-of-your-project]/blog/detail/1

We notice that the 1 from the path is passed as $id to the method. If the action method also allowed NULL for the parameter $id (int $id = null), n2n would also call the action method doDetail() without the ID parameter being given. As you can see, n2n fills additional paths into the parameters of the action method.

Attention

Action methods must be public and start with do.

To verify this, you can add the following method and call it:

public function noDo() {
echo 'does not work without a do prefix';
}

This method can be called neither with:

http://localhost/[path-to-the-public-folder-of-your-project]/blog/noDo

nor with

http://localhost/[path-to-the-public-folder-of-your-project]/blog/do

Since the method noDo() does not start with do, n2n assumes that this method must not be called from outside. n2n responds with a 404 error message because it does not find any matching action methods on the BlogController.

Now let's try the following private method:

private function doStuff() {
echo 'Private methods cannot be called';
}

If you try to call this method via the following path, something new happens:

http://localhost/[path-to-the-public-folder-of-your-project]/blog/stuff

n2n throws an Exception! High time to get to know exceptions!

Excursus: Exceptions​

Exceptions are very important in the concept of n2n! When developing n2n, it was a central concern of ours to return precise error messages. They should give the developer exact instructions about the source of the error.

Exceptions are only output if the system is registered as a test system. n2n recognizes a test system by the value SetEnv N2N_STAGE development in the file public/.htaccess. Exceptions must not be output on production systems, because they reveal many details about the system. On production systems an exception is logged and sent by e-mail to the responsible developer. All properties of the exception handling can be managed in the app.ini in the group [error].

In our case the exception tells us that doStuff() must be set public. Change this and check whether it works afterwards! If you did it right, the exception was already able to help you fix an error quickly and easily for the first time!

Custom Paths for Action Methods​

The variant of setting paths shown so far is simple, but not very flexible. That is why URL paths for action methods in n2n can also be defined via annotations. Annotations are a way to attach descriptive information to classes, properties and methods. Unfortunately, PHP - unlike Java - does not officially support annotations. Other PHP frameworks help themselves by capturing annotations via comments. We do not find this approach satisfactory, because for example auto-completion in comments is not guaranteed. In n2n annotations are therefore captured via a dedicated method. The following example shows how a custom controller path can be annotated for the action method detail().

<?php
namespace qs\controller;

use n2n\reflection\annotation\AnnoInit;
use n2n\web\http\controller\ControllerAdapter;
use n2n\web\http\annotation\AnnoPath;

class BlogController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->m('detail', new AnnoPath('/urlPart:*'));
}

public function index() {
echo 'Hello World!';
}

public function detail(string $urlPart) {
echo 'Detail: ' . $urlPart;
}

public function doThanks() {
echo 'Thank you for your comment.';
}

}

Attention

In n2n all annotations are always defined via a private, static method _annos()! So that this method is clearly visible, we place it first in every class, before all properties and other methods. An AnnoInit is always passed to this method. Via this object you can annotate the class itself ($ai->c()), its methods ($ai->m()) and properties ($ai->p()). All annotation classes available in n2n start with Anno.

The code example shown above annotates a path (AnnoPath()) for the detail() method (->m()), which is generated according to the pattern * and passed to the parameter urlPart. A method to which a path is assigned via an annotation no longer has to start with do! It even makes sense to do without the do, so that there are not two different paths via which the method can be called!

The * causes any characters to be accepted. They are passed to the detail() method as $urlPart. It is best to try it out right away:

http://localhost/[path-to-the-public-folder-of-your-project]/blog/anything-you-like

Change the last part of the URL part as you like. The only thing you cannot insert is another /.

Instead of * a regular expression could also be used, which defines more precisely how the path should be formed. Try the following examples:

AnnoPath()Example for the call
'/urlPart:*'http://localhost/[path-to-the-public-folder-of-your-project]/blog/anything
'read/urlPart:#[0-9]+#'http://localhost/[path-to-the-public-folder-of-your-project]/blog/read/999

Attention: regular expressions must be delimited with the hash symbol (#) at the front and back. Apart from that, the well-known notation of regular expressions can be used.

Perhaps you noticed that the * pattern creates a conflict between the detail() and doThanks() methods. The path /thanks fits the detail() method as well as the doThanks() method. This illustrates that the definition of controller paths must be done carefully, to prevent ambiguous paths from existing. In the present case n2n first checks whether the path can be resolved by non-annotated action methods. Annotated action methods have second priority. The path /thanks therefore leads to the doThanks() method.