Developer manual

The router

When the eID router script of this extension is called, it will lookup the first segment of the route parameter:

http://localhost/index.php?eID=routing&route=extension-key/custom/segments

That is, extension-key, and check all routes from file typo3conf/ext/extension-key/Configuration/Routes.yaml (or typo3conf/ext/extension-key/Configuration/Routes.yml) until one can return the correct URI for the specified arguments.

Note

If no matching route can be found, a 404 status code is sent for the HTTP response.

Routes

A route describes the way from your browser to the controller.

With the uriPattern you can define how a route is represented in the browser’s address bar. By setting defaults you can specify package, controller and action that should apply when a request matches the route. Besides you can set arbitrary default values that will be available in your controller. They are called defaults because you can overwrite them by so called dynamic route parts.

URI patterns

The URI pattern defines the appearance of the URI. In a simple setup the pattern only consists of static route parts and is equal to the actual URI (without protocol, host and the routing/extension-key/ prefix).

In order to reduce the amount of routes that have to be created, you are allowed to insert markers, so called dynamic route parts, that will be replaced by the Routing Framework.

But first things first.

Static route parts

Let’s create a route that calls the listAction of the ProductController when browsing to http://localhost/routing/extension-key/my/demo:

Example: Simple route with static route parts Configuration/Routes.yaml

-
  name: 'Static demo route'
  uriPattern: 'my/demo'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Product'
    '@action':     'list'

Note

name is optional, but it’s recommended to set a name for all routes to make debugging easier.

Dynamic route parts

Dynamic route parts are enclosed in curly brackets and define parts of the URI that are not fixed.

Let’s add some dynamics to the previous example:

Example: Simple route with static and dynamic route parts - Configuration/Routes.yaml

-
  name: 'Dynamic demo route'
  uriPattern: 'my/demo/{@action}'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Product'

Now http://localhost/routing/extension-key/my/demo/list calls the listAction just like in the previous example.

With http://localhost/routing/extension-key/my/demo/new you’d invoke the newAction and so on.

Note

It’s not allowed to have successive dynamic route parts in the URI pattern because it wouldn’t be possible to determine the end of the first dynamic route part then.

The @ prefix should reveal that action has a special meaning here. Other predefined keys are @package, @plugin, @controller and @format. But you can use dynamic route parts to set any kind of arguments:

Example: dynamic parameters - Configuration/Routes.yaml

-
  name: 'Dynamic demo route with parameter'
  uriPattern: 'products/list/{sortOrder}.{@format}'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Product'
    '@action':     'list'

Browsing to http://localhost/routing/extension-key/products/list/descending.xml will then call the listAction in your Product controller and the request argument sortOrder has the value of descending.

By default, dynamic route parts match any simple type and convert it to a string that is available through the corresponding request argument.

Object route parts

If a route part refers to an object, that is known to the Persistence Manager, it will be instantied from its technical identifier (uid) automatically:

Example: object parameters - Configuration/Routes.yaml

-
  name: 'Single product route'
  uriPattern: 'products/{product}'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Product'
    '@action':     'show'

If you add this route above the previously generated dynamic routes, an URI pointing to the show action of the ProductController should look like http://localhost/routing/extension-key/products/123.

Request Methods

Usually the Routing Framework does not care whether it handles a GET or POST request and just looks at the request path. However in some cases it makes sense to restrict a route to certain HTTP methods. This is especially true for REST APIs where you often need the same URI to invoke different actions depending on the HTTP method.

This can be achieved with a setting httpMethods, which accepts an array of HTTP verbs:

-
  uriPattern: 'some/path'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Standard'
    '@action':     'action1'
  httpMethods: ['GET']
-
  uriPattern: 'some/path'
  defaults:
    '@package':    'MyVendor.Demo'
    '@plugin':     'MyPlugin'
    '@controller': 'Standard'
    '@action':     'action2'
  httpMethods: ['POST', 'PUT']

Given the above routes a GET request to http://localhost/routing/extension-key/some/path would invoke the action1Action() while POST and PUT requests to the same URI would call action2Action().

Global routes

The Routing Framework lets you register global routes as well, that is, without any extension-key segment. As the extension key is then missing, you should manually register corresponding YAML files to be globally available by adding a line to your ext_localconf.php:

$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['routing']['globalRoutes'][] = 'EXT:' . $_EXTKEY . '/Configuration/MyGlobalRoutes.yaml';

Localization

Native TYPO3 localization works out-of-the-box if the call to the eID router script of this extension is made in the context of a given page; that is, if a GET parameter id exists. E.g.,

http://localhost/routing/extension-key/my-demo/1234?id=12

Demo Routing

This shows how to update your extension to route request automatically and handle requests like:

http://localhost/routing/extension-key/my-demo/1234
http://localhost/routing/extension-key/my-demo/1234.json
http://localhost/routing/extension-key/my-demo/99

where 1234 and 99 will be mapped to some method parameter (and converted to domain object if needed) and json will set the response format to json.

ext_localconf.php

<?php
if (!defined('TYPO3_MODE')) {
    die ('Access denied.');
}

\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
    'MyVendor.' . $_EXTKEY,
    'API',
    ['Dummy' => 'demo'],
    ['Dummy' => 'demo']
);

Configuration/Routes.yaml

-
  name: 'Demo action with a parameter in a given format (JSON, ...)'
  uriPattern: 'my-demo/{value}.{@format}'
  defaults:
    '@package':    'MyVendor.ExtensionKey'
    '@plugin':     'API'
    '@controller': 'Dummy'
    '@action':     'demo'
-
  name: 'Demo action with a parameter'
  uriPattern: 'my-demo/{value}'
  defaults:
    '@package':    'MyVendor.ExtensionKey'
    '@plugin':     'API'
    '@controller': 'Dummy'
    '@action':     'demo'

Classes/Controller/DummyController.php

<?php
namespace MyVendor\ExtensionKey\Controller;

class DummyController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {

    /**
     * @param int $value
     * @return string
     */
    public function demoAction($value) {
        $response = ['value' => $value];

        if ($this->request->getFormat() === 'json') {
            // Hint: you should use \TYPO3\CMS\Extbase\Mvc\View\JsonView instead
            header('Content-Type: application/json');
            $response = json_encode($response);
        } else {
            $response = var_export($response, TRUE);
        }

        return $response;
    }

}

Using JsonView

Let’s assume you have the list of persons to be exported with the action demo above.

Create a file Classes/View/Dummy/DemoJson.php:

<?php
namespace MyVendor\ExtensionKey\View\Dummy;

class DemoJson extends \TYPO3\CMS\Extbase\Mvc\View\JsonView {

    protected $configuration = [
        'persons' => [
            '_descendAll' => [
                //'_only' => ['property1', 'property2'],
                '_exclude' => ['pid']
            ]
        ]
    ];

}

and modify your action demo:

/**
 * @param int $value
 * @return void
 */
public function demoAction($value) {
    $persons = $this->personRepository->findAll();
    $this->view->assign('persons', $persons);
    $this->view->setVariablesToRender(['persons']);
}

and you’re done! Extbase’s dispatcher will see your special view “Demo” to be used for format “Json” and instantiate it instead of the default view. Your domain objects will be serialized and the JSON header sent automatically.

Hint

The class name pattern is @vendor\@extension\View\@controller\@action@format, meaning you will have to specify a JSON output by ending your route with .json.