Routing and controllers
https://secure.gravatar.com/avatar/b28e536690cbbcc42568497fe280b4f4.png?d=wavatar&s=28
Wed Jul 07 09:58 Authored by zegenie

Pachno routes are defined in controller classes inside modules. Controller classes extends \pachno\core\framework\Action and are placed in the controllers namespace inside modules.

You can have as many controller classes as you like, and Pachno will read routes defined in any controller classes present in your modules - whether it's in \pachno\core\modules\module_name\controllers or \pachno\modules\module_name\controllers .

Creating a new route

Pachno makes extensive use of annotations throughout the application. For an overview of available annotations and how to use them, see Annotations.

You define new routes by annotating your controller method(s) with @Route annotations. All routing method names have to start with run, for example runIndex(). If you're not returning output (for example file downloads or json responses) directly from the controller method, you should create a matching html template file in the templates/ directory. The template file should have the same name as the route method without the run prefix, and must be all lowercased.

You can read more about templates and how they work in Templates

<?php

  // Example: controller class with a @Route annotated method

  namespace \pachno\modules\hello_world\controllers;

  class Main extends \pachno\core\framework\Action
  {

    /**
     * @Route(name="index", url="/index")
     */
    public function runIndex(\pachno\core\framework\Request $request)
    {
    }

  }

// The method above should have the following template file
// modules/
//   hello_world/
//     templates/
//       index.html.php
 

The @Route annotation has the following properties:

Property Example Description
url url="/index" The url pattern for the route.
name (optional) name="index" If provided, a name for the route. If omitted, the method name without run
methods (optional) methods="POST|DELETE" If omitted, all request methods will match this route. If provided, a pipe | separated list of allowed methods. If the request method doesn't match a method in this list, the route doesn't match.

Route url definitions

Pachno urls are relative to the web root and are automatically generated and matched by Pachno. Urls follow a simple pattern and allows for capturing named url parameters via :named url parts.

<?php

// Example @Route url definitions

// Basic url
// @Route(url="/boards")

// Capture board_id from the url
// @Route(url="/boards/:board_id")
// In the controller:
$board_id = $request->getParameter('board_id');
// or
$board_id = $request['board_id'];

// Capture board_id and milestone_id from the url
// @Route(url="/boards/:board_id/milestone/:milestone_id")
// In the controller:
$board_id = $request->getParameter('board_id');
$milestone_id = $request->getParameter('milestone_id');
// or
$board_id = $request['board_id'];
$milestone_id = $request['milestone_id'];


Route url or name prefixes

You can prefix all routes within a module or controller by annotating the class with a @Routes annotation. This lets you specify a url prefix and/or name prefix that applies to all annotated routes in that controller.

<?php

    namespace pachno\core\hello_world\publish\controllers;

    /**
     * @Routes(name_prefix="hello_", url_prefix="/hello")
     */
    class Main extends framework\Action
    {
      /**
       * @Route(name="index", url="/index")
       */
      public function runIndex(\pachno\core\framework\Request $request)
      {
      }
    }

In the example above, all routes in this controller will have their url prefixed by /hello, so in this case the full url to the index page will be /hello/index - very useful in cases where all routes share a common url namespace.

In addition, the name_prefix applies to all route names, so the refer to the index route in this controller, you would refer to hello_index - useful when you want to prefix all url route names to keep it consistent or related, or when the module name prefix is undesirable.

Using routes

When you have created a route, you don't use the url directly (as this may change), but refer to the route via the name defined in the @Route annotation (or via @Route + @Routes)

<?php

// Example @Route url definitions and usage

// Basic url
// @Route(url="/boards", name="boards")

// In the controller:
$url = $this->getRouting()->generate('boards');
// or in templates via the template make_url() helper
$url = make_url('boards');
$url = $pachno_routing->generate('boards');

// Urls with named parameters
// @Route(url="/boards/:board_id", name="board")
// In the controller:
$url = $this->getRouting()->generate('board', ['board_id' => $board->getId()]);
// or in templates: via the template make_url() helper or the $pachno_routing template variable
$url = make_url('board', ['board_id' => $board->getId()]);
$url = $pachno_routing->generate('board', ['board_id' => $board->getId()]);

CSRF Protection

Routes can be CSRF protected by adding a @CsrfProtected annotation to a @Route annotated method. If the route is @CsrfProtected Pachno will handle it automatically as long as a :csrf_token url parameter is present in the url pattern. If not present in the url (for example when posting forms), the csrf token can be retrieved via \pachno\core\framework\Context::getCsrfToken() and must be passed via a POST parameter named csrf_token

// Example @Route method with CSRF protection, allowing POST, PUT and DELETE

/**
 * @Route(name="post_user", url="/users/:user_id/:csrf_token", methods="POST|PUT|DELETE")
 * @CsrfProtected
 */
public function runUpdateUser(\pachno\core\framework\Request $request)
{
}

Specifying additional request parameters

Sometimes you may have a need to specify additional request parameters (such as when using a preExecute() method for all routes) per route. You can do this by using the @Parameters annotation on the route.

<?php

// Example route with two specified parameters

/**
 * @Route(url="/configure/project/:project_id/mailing", name="configure_settings")
 * @Parameters(config_module="core", section=15)
 */
public function runConfigureProjectSettings(\pachno\core\framework\Request $request)
{
  echo $request->getParameter('config_module'); // outputs 'core'
}

The parameters listed in the @Parameters annotation are available on the $request object in both the following controller methods and the preExecute() method.

Common handlers and pre execute methods

For route methods that handle more than one entry point - or for routes that share a common request parameter that you may want to retrieve from a database or verify - you can add a preExecute() method that will run before your route method is executed. Any properties set on the controller will be available to subsequent route methods.

<?php

// Example preExecute() method

public function preExecute(\pachno\core\framework\Request $request)
{
  // simplified project lookup
  $this->project = Projects::getTable()->selectById($request['project_id']);

  if (!$this->project instanceof Project) {
    return $this->return404('This project does not exist');
  }
}

/**
 * @Route(url="/projects/:project_id/mailing", name="configure_mail_settings")
 */
public function runConfigureProjectSettings(\pachno\core\framework\Request $request)
{
  echo $this->project->getId(); // echoes the id from the project object set in the preExecute() method
}

Handing off from one route to another

If you want to hand off a request from one route to another, use the internal redirect() method to move execution to that route instead. This does not send any response to the user, but simply redirects internally and continues execution in the redirected route as if it entered that route originally.

Note: when using this method, the route set in the routing object will still be the original matching route.

<?php

// Example redirecting to another method

/**
 * @Route(url="/projects/:project_id/mailing", name="configure_mail_settings")
 */
public function runConfigureProjectMailSettings(\pachno\core\framework\Request $request)
{
  return $this->redirect('configureprojectsettings'); // must match the method name redirected to, without the 'run' prefix
}

/**
 * @Route(url="/projects/:project_id/settings", name="configure_settings")
 */
public function runConfigureProjectSettings(\pachno\core\framework\Request $request)
{
  // this will be executed in both routes
}

Attachments 0

Comments 0

/unthemed/mono/no-comments.png
Expand, collaborate and share
Post a comment and get things done