The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

Name

Forward::Guides::Routes::ResourceCustomization - Customizing resourceful routes

namespace

The -namespace option works like the namespace command. It might be needed to determine the full controller class name ($match->class). It also effects the names automatically generated for your routes, so make sure to use app_namespace instead of namespace for your application base namespace to keep route names as short as possible (use namespace for sub namespaces only).

my $r = Forward::Routes->new;
$r->add_resources('users' => -namespace => 'Admin');

# which is equivalent to
my $r = Forward::Routes->new->namespace('Admin');
$r->add_resources('users');


$m = $r->match(get => 'users');
# controller param is still 'Users'...
# $m->[0]->params is {controller => 'Users', action => 'index'};
# ... but:
# $m->[0]->class is "Admin::Users"
# $m->[0]->action is "index"
# $m->[0]->name is "admin_users_index"

my $path = $r->build_path('admin_users_index');
# $path->{path} is 'users'
# $path->{method} is 'get'

The -namespace option has no effect on the URL pattern,

# in order to match /admin/users instead of /users,
$r->add_route('admin')->add_resources('users' => -namespace => 'Admin');

This offers a lot more flexibility:

$r->bridge('admin')->to('Authorization#admin')
  ->add_resources('users' => -namespace => 'Admin');
# allows to check whether the current user is really the admin (using
# bridges) and break the dispatching cycle if authorization fails

Changes of a resources namespace also effects nested resources (inheritance)!

as The -as option changes automatic route and controller naming.

my $r = Forward::Routes->new;
$r->add_resources(
    'photos',
    'customers' => -as => 'users',
    'prices'
);

$m = $r->match(get => 'customers');
# $m->[0]->params is {controller => 'Users', action => 'index'};

$m = $r->match(get => 'users');
# $m is undef

my $path = $r->build_path('users_index');
# $path->{path} is 'customers'
# $path->{method} is 'get'

constraints

The resource id placeholder can be assigned a custom constraint using the -constraint option:

my $r = Forward::Routes->new;
$r->add_resources(
    'users',
    'photos' => -constraints => {id => qr/\d{6}/},
    'tags'
);

my $m = $r->match(get => 'photos/123456');
# $m->[0]->params is {controller => 'Photos', action => 'show', id => 123456};

$m = $r->match(get => 'photos/abc');
# $m is undef;

format

The format constraint can be changed with the help of the -format option:

my $r = Forward::Routes->new;
$r->add_resources(
    'users',
    'photos' => -format => 'html',
    'tags'
);

my $m = $r->match(get => 'photos/123456.html');
# $m->[0]->params is {format => 'html', controller => 'Photos', action => 'show',
#   id => 123456};

$m = $r->match(get => 'photos/123456');
# $m is undef;

Resources inherit the file extension of their parent. As a result, the format constraint can also be changed through format inheritance:

# in order to match get => /cities/paris/edit.html

# Routes root object
my $root = Forward::Routes->new;

# Create a parent route with format "html"
$route_with_html_extension = $root->add_route->format('html');

# add a resource on top of parent
$route_with_html_extension->add_resources('cities');

$m = $root->match(get => '/cities/paris/edit.html');
# $m->[0]->params is
#   {controller => 'Cities', action => 'cities_update_form',
#    format => 'html'};

The inherited format behaviour can be overwritten with the -format option for individual resources.

only

Instead of creating all default resourceful routes for a resource (i.e. index, create, show, update, delete, create_form, update_form, delete_form), only selected routes can be generated using the -only option.

# only generate the "create" and "show" routes for the "users" resource
my $root = Forward::Routes->new;
$root->add_resources(
    'photos',
    'users' => -only => ['create', 'show'],
    'prices'
);

my $m = $root->match(get => 'users');
# $m is undef, as "index" not included via "only"

$m = $root->match(post => 'users');
# create:
# $m->[0]->params is {controller => 'Users', action => 'create'};

$m = $root->match(get => 'users/1');
# show
# $m->[0]->params is {controller => 'Users', action => 'show', id => 1};