Controller
A controller defines the entry point of your application. To do this, it interprets the URL of the current HTTP request.
Defining a Controller
A controller is defined as a class that usually extends n2n\web\http\controller\ControllerAdapter. In the following example we create a controller in the package atusch\controller.
<?php
namespace atusch\controller;
use n2n\web\http\controller\ControllerAdapter;
class ExampleController extends ControllerAdapter {
public function index() {
echo 'hallo atusch';
}
}
To be able to call the controller directly, we must define a context path under the [routing] group in app.ini.
[routing]
controllers[] = "/example > atusch\controller\ExampleController"
If you now call http://[url-to-n2n-public]/example with your browser, the index method is executed and "hallo atusch" is output.
Alternatively, you can also define the context path as follows:
[routing]
controllers[/example] = "atusch\controller\ExampleController"
Action Methods
Typical action methods in controllers are Do methods. These are marked with the prefix "do" and executed when the path of the current request matches the method signature. If, for example, we define a method doDetail() in the ExampleController from the example above, it is available at http://[url-to-n2n-public]/example/detail.
<?php
namespace atusch\controller;
use n2n\web\http\controller\ControllerAdapter;
class ExampleController extends ControllerAdapter {
public function index() {
echo 'hallo atusch';
}
public function doDetail() {
echo 'hallo atusch detail';
}
}
The name of the method (minus the "do" prefix) must match the first part of the path after the context path. This part must be lowercase, even if the method name contains uppercase letters. The method doAdvancedDetail() in the example above could therefore only be called via http://[url-to-n2n-public]/example/advanceddetail.
Path Parameters
You can have path parts passed to the method as parameters:
public function doDetail($param1, $param2, $param3 = null) {
echo 'param1: ' . $param1 . ' param2: ' . $param2 . 'param3: ' . $param3;
}
The method can now be called, for example, via http://[url-to-n2n-public]/example/detail/foo/bar/baz or, since $param3 is optional, also via http://[url-to-n2n-public]/example/detail/foo/bar.
You can also have the path parameters passed as n2n\web\http\controller\ParamPath objects:
public function doDetail(ParamPath $param1, ParamPath $param2, ParamPath $param3 = null) {
echo 'param1: ' . $param1 . ' param2: ' . $param2 . 'param3: ' . $param3;
}
Query Parameters
In the same way you can have query parameters passed as n2n\web\http\controller\ParamQuery objects:
public function doDetail(ParamQuery $param1, ParamQuery $param2 = null) {
echo 'param1: ' . $param1 . ' param2: ' . $param2;
}
This method is callable via http://[url-to-n2n-public]/example/detail?param1=foo¶m2=bar or http://[url-to-n2n-public]/example/detail?param1=foo.
Path Extension
You can also enforce a path extension by extending the method with a parameter $extension:
public function doDetail($param1, $extension) {
echo 'param1: ' . $param1 . ' extension: ' . $extension;
}
This method is, for example, callable via http://[url-to-n2n-public]/example/detail/foo.txt.
Summarizing Path Parts
If you define a path parameter as an array, you can summarize path parts:
public function doDetail($param1, array $params) {
echo 'param1: ' . $param1 . ' params: ' . implode(', ', $params);
}
In this example $params allows one or more path parts. This method is, for example, callable via http://[url-to-n2n-public]/example/detail/foo/bar/baz/... Of course you can also make an array parameter optional:
public function doDetail($param1, array $params = array()) {
echo 'param1: ' . $param1 . ' params: ' . implode(', ', $params);
}
doDetail() would now also be callable via http://[url-to-n2n-public]/example/detail/foo.
All the examples shown above also work with the index() method, except that there the path part "detail" must of course be omitted when calling it.
Annotations
Annotations allow you to control controller calls even more precisely. For example, you can annotate a path pattern for controller methods via n2n\web\http\annotation\AnnoPath:
<?php
namespace atusch\controller;
use n2n\web\http\controller\ControllerAdapter;
use n2n\reflection\annotation\AnnoInit;
use n2n\web\http\annotation\AnnoPath;
class ExampleController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->m('detail', new AnnoPath('param1:*/const/param2:*'));
}
public function detail($param1, $param2) {
echo 'param1: ' . $param1 . ' params2: ' . $param2;
}
}
In this example detail() is callable via http://[url-to-n2n-public]/example/foo/const/bar. If the pattern contains a ":", the name of the associated method parameter (optional) is expected before the ":" and a regular expression (with delimiter #) or "" for any path part after the ":". With : the ":" can also be omitted.
Methods for which a pattern was annotated can also be combined with query parameters.
Constant path parts in the pattern must be encoded according to RFC 1738. E.g. foo/b%26r/baz
Modifiers
With modifiers you can determine in the pattern how many times a path part may occur in succession. The following modifiers are available:
? optional; 0 or 1
- 0 or more
- 1 or more
The modifier must be placed in the pattern at the end of a path part, but before any ":".
Examples:
| Pattern | Examples of matching paths |
|---|---|
| test/foo? | test, test/foo |
| test/foo* | test, test/foo, test/foo/foo, test/foo/foo/foo, ... |
| test/foo+ | test/foo, test/foo/foo, test/foo/foo/foo, ... |
| test/*? | test, test/foo |
| test/** | test, test/foo, test/foo/bar, test/foo/bar/baz, ... |
| test/*+ | test/foo, test/foo/bar, test/foo/bar/baz, ... |
| test/param?:#^[a-z]+$# | test, test/foo |
| test/param*:#^[a-z]+$# | test, test/foo, test/foo/bar, test/foo/bar/baz, ... |
| test/param+:#^[a-z]+$# | test/foo, test/foo/bar, test/foo/bar/baz, ... |
The following example demonstrates the use of modifiers:
class ExampleController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->m('detail', new AnnoPath('const?/params+:#^[0-9]+$#'));
}
public function detail(array $params) {
echo 'params: ' . implode(', ', $params);
}
}
detail() is, for example, callable via http://[url-to-n2n-public]/example/const/1 or http://[url-to-n2n-public]/example/1/2/33.
Path Extension
You can restrict the path extensions via the annotation n2n\web\http\annotation\AnnoExt.
class ExampleController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->m('doDetail', new AnnoExt('txt'));
$ai->m('number', new AnnoPath('param:#^[0-9]$#'), new AnnoExt('txt', 'html'));
}
public function doDetail($param, $extension) {
echo 'param: ' . $param . ' extension: ' . $extension;
}
public function number($param, $extension) {
echo 'param: ' . $param . ' extension: ' . $extension;
}
}
doDetail() is, for example, callable via http://[url-to-n2n-public]/example/detail/foo.txt and number() via http://[url-to-n2n-public]/example/1.txt or http://[url-to-n2n-public]/example/1.html.
Path extensions can also be defined for the whole controller.
class ExampleController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->c(new AnnoExt('txt', 'html'));
}
// methods
}
Path extensions that were defined for the whole controller apply to all methods that are not themselves annotated with AnnoExt.
Predefined Methods
prepare() Is called first as soon as the controller is executed. This method is also executed when no controller method matches the request path. notFound() Is executed when no controller method matches the request path or when an HttpStatusException with status 404 was thrown in such a method.
Dependency Injections
All action methods are "magical" and allow dependency injections. In addition, the _init() method can be implemented in the controller. The _init() method is called as soon as the controller is instantiated. Since this does not necessarily mean that the controller is also executed, it is useful to use the prepare() method as an alternative.
Context Path
The same rules apply to the pattern of the context paths registered in app.ini as for the patterns annotated via n2n\web\http\controller\AnnosController::PATH. In the pattern of the context path, however, the placeholder {locale} can also be used.
[routing]
controllers[] = \"/{locale}/example > atusch\controller\ExampleController\"
locales[] = \"de_CH\"
locales[] = \"fr\"
locales[] = \"en_US\"
When called, all locales specified under the "locales" property are accepted for this placeholder. If, for example, the locale "de_CH" is specified, "de-ch" is accepted for this path part. If you do not like this representation, you can define an alias in app.ini in the [web] group under the "locale_aliases" property:
[web]
locale_aliases[de_CH] = \"de\"
In this example the ExampleController is callable via http://[url-to-n2n-public]/de/example, http://[url-to-n2n-public]/fr/example or http://[url-to-n2n-public]/en-us/example. The corresponding locale is assigned directly to the request and can be read in the controller via $this->getRequest()->getLocale() if needed.
Context Parameters
Parameters that are defined in the pattern of a context path can be passed to controller methods.
controllers[] = \"/cParam1:#^(foo|bar)$#/cParam2:* > atusch\controller\ExampleController\"
The following example shows how you can access "cParam1" and "cParam2" in the controller:
class ExampleController extends ControllerAdapter {
private static function _annos(AnnoInit $ai) {
$ai->m('number', new AnnoPath('param:#^[0-9]+$#'));
}
private $cParam1;
public function prepare($cParam1) {
$this->cParam1 = $cParam1;
}
public function index($cParam2, $param) {
echo 'index (cParam1: ' . $this->cParam1 . '; cParam2: ' . $cParam2 . '; param: ' . $param . ')';
}
public function doDetail($cParam2, $param) {
echo 'doDetail (cParam1: ' . $this->cParam1 . '; cParam2: ' . $cParam2 . '; param: ' . $param . ')';
}
public function number($cParam2, $param) {
echo 'number (cParam1: ' . $this->cParam1 . '; cParam2: ' . $cParam2 . '; param: ' . $param . ')';
}
}
Call Examples:
http://localhost/php-hnm-zend/default/n2n-7/public/foo/baz/qux
Output: index (cParam1: foo; cParam2: baz; param: qux)
http://localhost/php-hnm-zend/default/n2n-7/public/foo/baz/detail/qux
Output: doDetail (cParam1: foo; cParam2: baz; param: qux)
http://localhost/php-hnm-zend/default/n2n-7/public/foo/baz/24
Output: number (cParam1: foo; cParam2: baz; param: 24)