Routing

Routes map a URL and a method to a controller method. Routes are defined inside appinfo/routes.php by passing a configuration array to the registerRoutes method. An example route would look like this:

<?php
namespace OCA\MyApp\AppInfo;

$application = new Application();
$application->registerRoutes($this, [
    'routes' => [
        ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
    ]
]);

The route array contains the following parts:

  • url: The URL that is matched after /index.php/apps/myapp ` name: The controller and the method to call; page#index is being mapped to PageController→index(), articles_api#drop_latest would be mapped to ArticlesApiController→dropLatest(). The controller that matches the page#index name would have to be registered in the following way inside lib/AppInfo/Application.php:

<?php
namespace OCA\MyApp\AppInfo;

use \OCP\AppFramework\App;
use \OCA\MyApp\Controller\PageController;

class Application extends App {

    public function __construct(array $urlParams=[]){
        parent::__construct('myapp', $urlParams);

        $container = $this->getContainer();

        /**
         * Controllers
         */
        $container->registerService('PageController', function($c) {
            return new PageController(
                $c->query('AppName'),
                $c->query('Request')
            );
        });
    }

}
  • method (Optional, defaults to GET): The HTTP method that should be matched, (e.g., GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH) ` requirements (Optional): lets you match and extract URLs that have slashes in them (see Matching suburls) ` postfix (Optional): lets you define a route id postfix. Since each route name will be transformed to a route id (page#methodmyapp.page.method) and the route id can only exist once you can use the postfix option to alter the route id creation by adding a string to the route id e.g.: 'name' ⇒ 'page#method', 'postfix' ⇒ 'test' will yield the route id myapp.page.methodtest. This makes it possible to add more than one route/url for a controller method ` defaults (Optional): If this setting is given, a default value will be assumed for each URL parameter which is not present. The default values are passed in as a key ⇒ value par array

Extracting Values From the URL

It is possible to extract values from the URL to allow for RESTful URL design. To extract value, you have to wrap it inside curly braces:

<?php

// Request: GET /index.php/apps/myapp/authors/3

// appinfo/routes.php
['name' => 'author#show', 'url' => '/authors/{id}', 'verb' => 'GET'],

// controller/authorcontroller.php
class AuthorController {

    public function show($id) {
        // $id is '3'
    }

}

The identifier used inside the route is being passed into the controller method by reflecting the method parameters. To summarize, if you want to get the value of {id} in your method, you need to add $id to your method parameters.

Matching Sub-URLs

Sometimes you need to match more than one URL fragment. An example of this would be to match a request for all URLs that start with OPTIONS /index.php/apps/myapp/api. To do this, use the requirements parameter in your route, which is an array containing pairs of 'key' ⇒ 'regex':

<?php

// Request: OPTIONS /index.php/apps/myapp/api/my/route

// appinfo/routes.php
[
  'name' => 'author_api#cors',
  'url' => '/api/{path}',
  'verb' => 'OPTIONS',
  'requirements' => ['path' => '.+']
],

// controller/authorapicontroller.php
class AuthorApiController {

    public function cors($path) {
        // $path will be 'my/route'
    }

}

Default Values for Sub-URL

Apart from matching requirements, a sub-URL may also have a default value. Say you want to support pagination (a page' parameter) for your `/posts sub-URL that displays posts entries list. You may set a default value for the page' parameter, that will be used if not already set in the URL. Use the `defaults parameter in your route which is an array containing pairs of 'urlparameter' ⇒ 'defaultvalue':

<?php

// Request: GET /index.php/app/myapp/post

// appinfo/routes.php
[
    'name'     => 'post#index',
    'url'      => '/post/{page}',
    'verb'     => 'GET',
    'defaults' => ['page' => 1] // this allows same url as /index.php/myapp/post/1
],

// controller/postcontroller.php
class PostController
{
    public function index($page = 1)
    {
        // $page will be 1
    }
}

Registering Resources

When dealing with resources, writing routes can become quite repetitive since most of the time routes for the following tasks are needed:

  • Get all entries

  • Get one entry by id

  • Create an entry

  • Update an entry

  • Delete an entry

To prevent repetition, it’s possible to define resources. The following routes:

<?php
namespace OCA\MyApp\AppInfo;

$application = new Application();
$application->registerRoutes($this, [
    'routes' => [
        ['name' => 'author#index', 'url' => '/authors', 'verb' => 'GET'],
        ['name' => 'author#show', 'url' => '/authors/{id}', 'verb' => 'GET'],
        ['name' => 'author#create', 'url' => '/authors', 'verb' => 'POST'],
        ['name' => 'author#update', 'url' => '/authors/{id}', 'verb' => 'PUT'],
        ['name' => 'author#destroy', 'url' => '/authors/{id}', 'verb' => 'DELETE'],
        // your other routes here
    ]
]);

can be abbreviated by using the resources key:

<?php
namespace OCA\MyApp\AppInfo;

$application = new Application();
$application->registerRoutes($this, [
    'resources' => [
        'author' => ['url' => '/authors']
    ],
    'routes' => [
        // your other routes here
    ]
]);

Using the URLGenerator

Sometimes its useful to turn a route into a URL 1) to make the code independent from the URL design or to 2) generate an URL for an image in img/. For those use cases, the ServerContainer provides a service that can be used in your container:

<?php
namespace OCA\MyApp\AppInfo;

use \OCP\AppFramework\App;
use \OCA\MyApp\Controller\PageController;

class Application extends App {

    public function __construct(array $urlParams=[]){
        parent::__construct('myapp', $urlParams);

        $container = $this->getContainer();

        /**
         * Controllers
         */
        $container->registerService('PageController', function($c) {
            return new PageController(
                $c->query('AppName'),
                $c->query('Request'),

                // inject the URLGenerator into the page controller
                $c->query('ServerContainer')->getURLGenerator()
            );
        });
    }

}

Inside the PageController the URL generator can now be used to generate an URL for a redirect:

<?php
namespace OCA\MyApp\Controller;

use \OCP\IRequest;
use \OCP\IURLGenerator;
use \OCP\AppFramework\Controller;
use \OCP\AppFramework\Http\RedirectResponse;

class PageController extends Controller {

    private $urlGenerator;

    public function __construct(
      $appName,
      IRequest $request,
      IURLGenerator $urlGenerator
    ) {
        parent::__construct($appName, $request);
        $this->urlGenerator = $urlGenerator;
    }

    /**
     * redirect to /apps/news/myapp/authors/3
     */
    public function redirect() {
        // route name: author_api#do_something
        // route url: /apps/news/myapp/authors/{id}

        // # needs to be replaced with a . due to limitations and prefixed
        // with your app id
        $route = 'myapp.author_api.do_something';
        $parameters = array('id' => 3);

        $url = $this->urlGenerator->linkToRoute($route, $parameters);

        return new RedirectResponse($url);
    }

}

URLGenerator is case-sensitive, so appName must match exactly the name you use in configuration <configuration>. If you use a camel-case name as myCamelCaseApp,

<?php
$route = 'myCamelCaseApp.author_api.do_something';