Slim: Proposal: route creation from arrays?

Created on 9 Jan 2019  Â·  7Comments  Â·  Source: slimphp/Slim

This is a proposal for a more convenient route configuration, because we always do the same things when setting up our Slim $app:

  • Create a route instance with $route = $app->get( … ) or $app->map(["GET", "POST"], …)
  • Name it: $route->setName('hello')
  • Add route-specific middelware, using $route->add( … )

EDIT – See imporoved prosal below. The example outlined in this post has wording and code style issues. Thanks and sorry.

Let's imagine a two-dimensional array like this:

$routes = array(
  "index" => array(
    "url" => "/",
    "via" => [ "GET" ],
    "controller" => "IndexPageService",
    "middleware" => []
  ), 

  "contact" => array(
    "url" => "/contact",
    "via" => [ "GET", "POST" ],
    "controller" => "ContactPageService",
    "middleware" => [   "RouteMiddlewareService", "OtherFancyStuff" ]
  ),

  // ... more ...
)

…we then simply pass each item to a new route method in $app, like so:

foreach ($routes as $name => $route_data):
  $app->route( $name, $route_data );
endforeach;

The new route method roughly outlined:

class App
{

  /**   
   * Most generic route: Just GET, no middleware stuff. 
   */
  public $default_route_data = array(
    "via" => [ "GET" ],
    "middleware" => [ ]
  );

  /**   
   * @return RouteInterface
   */
  public function route( string $name, array $route )
  {
    // Glue data
    $route_data = array_merge($this->default_route_data, $route);
    extract( $route_data ); # Readability hack

    // Create named route instance,
    // add route middleware
    $route = $this->app->map($via, $url, $controller);
    $route->setName($name);

    foreach( $middleware as $mw ):
        $route->add($mw);
    endforeach;

    return $route;
  }
}

Conclusion:

  • The route method shown here saves recurring work
  • Definitions and configuration are now separated
  • What kind of variable the controller or middleware items are still is up to the user

What do you think?

Best regards,
Carsten.

Most helpful comment

We should not add something like this. It is again some array magic which we have to communicate how this is working, we have to validate the array to give descriptive errors.
Also it is just fine to call $app->map(…)->setName($name)->add($mw)->add($mw2).

Something like managing routes per config should either be an additional module or directly application logic.

All 7 comments

I think in general, if you have to add a comment that contains the word 'hack', you are doing something wrong

IMHO Nobody will take the "hack" word too literally. The above was meant as a _roughly outline_ … did'nt want to trigger you or someone else. Sorry for that :beers:

Alright, no offense taken, but I would like to argue against some of the ideas/methods:

  • extract obfuscates the code. the function definition array $route doesn't tell anything specific,
    and in the next lines you are just using variables that came seemingly from nothing.
  • I know for a fact the PHPStrom (and probably other ide's) will complain about the undefined variables.

    • specifically: $route = $this->app->map($via, $url, $controller); it is not obvious where the $url is coming from; maybe add all options to an example or update the default_route_data

    • the controller doesn't specify a controller method, is it supposed to be a callable class ?

I would either go through the route definition array (key by key) and do what needs to be done with them, or define a simple RouteDefinition class that handles the array and use that in the following steps.

I like the idea of separating the configuration and the definition

@zelding Your points are valid; I realize the extract is no good means for examples.

May I give it a second try – with the vars named according to the Slim User guide ‘vocabulary’ and source code. Also I felt it is necessary to not force a user to use a route name. I personally like them a lot and have my routes in a YAML map, but others may have their routes in a DB.

Route data $route_data requires these keys:

  • 'pattern'
  • 'callable'

Optional keys in $route_data:

  • 'methods' (optional, defaults to GET)
  • 'name' (optional route name, overrides array's name entry)
  • 'middleware' Array with middleware
class App
{

  /**   
   * Most generic route: Just GET, no middleware stuff. 
   */
  public $default_route_data = array(
    "name" => null,
    "methods" => [ "GET" ],
    "middleware" => [ ]
  );


  /**
   * @param  array  $route_data 
   * @param  string $route_name Optional: custom route name
   * @return RouteInterface
   */
  public function route( array $route_data, string $route_name = null  )
  {
    // Enable user omitting defaults
    $route_data = array_merge($this->default_route_data, $route_data);

    // Create route instance
    $route = $this->map(
        $route_data['methods'],
        $route_data['pattern'],
        $route_data['callable']
    );

    // Name it (parameter name overrides route data)
    $route_name = $route_name ?: $route_data['name'];
    if ($route_name):
        $route->setName($route_name);
    endif;

    // Add route middleware
    foreach( $middleware as $mw_callable ):
        $route->add($mw_callable);
    endforeach;

    return $route;
  }
}

The _route_ method is just a per-route wrapper around ‘eat this, then call map method’. It creates one route as do _get_, _map_ and their friends. IMHO by itself it does not need further validation, as anything thrown in _map_ (and later) will bubble.

zelding's RouteDefinition idea: Why not, a _RouteDefinition_ class can help dealing with route defaults, shortening the method logic. On the other hand, it also would add complexity elsewhere since the instances have to be created somehow after YAML or DB retrieval. Anyhow, the _route_ method could then accept both array or _RouteDefinition_. – Standardization is good, _PSR-66 Routes_ perhaps ;-).

--

Edit: Removed surfluous "->app" in example.

Looking forward for PSR-66 :D

We should not add something like this. It is again some array magic which we have to communicate how this is working, we have to validate the array to give descriptive errors.
Also it is just fine to call $app->map(…)->setName($name)->add($mw)->add($mw2).

Something like managing routes per config should either be an additional module or directly application logic.

I agree with @danopz. This is an application-level thing where people who want it can extend App.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

odahcam picture odahcam  Â·  3Comments

aranel616 picture aranel616  Â·  3Comments

codeguy picture codeguy  Â·  4Comments

jaklimoff picture jaklimoff  Â·  4Comments

RobDWaller picture RobDWaller  Â·  4Comments