Introspecting the name of placeholder variables from a Mojolicious controller - perl

I have a Mojolicious controller that fires in response to different URL routes. For example, given the URL path:
/v1/users/:someid
and a controller that fires:
sub handle_request ($self) {
my $place_holder_name = $self->route->?????? # how can I get 'someid'?
is($place_holder_name, 'someid', 'can access the placeholder name');
}
How can I find out the name of the placeholder?

Param
These are not currently documented under Mojolicious::Routes, so I can see why that's confusing. They're documented under Mojolicious::Controller#param,
What you have there is a Route param, so you can retreive that value with,
$c->param('someid');
Getting All Params Provided to a Controller
Though undocumneted, you can find the names of the captures in the internal hashref like this,
$self->stash->{'mojo.captures'};
Like this;
my $params = $self->stash->{'mojo.captures'};
warn for keys %$params;

Related

How to find route name for given path in Mojolicious?

When I parse html response body I want to find route names for all links found in the body. I use next code snippet:
my $url = Mojo::URL->new( $got );
my $method = uc( $url->query->clone->param( '_method' ) || 'GET' );
my $c = $t->app->build_controller;
my $m = Mojolicious::Routes::Match->new( root => $t->app->routes );
$m->find( $c => { method => $method, path => $url->path } );
Then $m->endpoint->name gives me the name of route.
But is there more simple way to find route name by given path?
I am looking for something like: $app->routes->find( '/api/v/users/146/link/7QRgs' ) which should return user_hash_check because I have next route:
$guest->get( '/users/:id/link/:hash', 'user_hash_check' )->to( 'user#hash_check' );
I have found only one place where we can find route by path. That is Mojolicious::Routes::Match and there is no other way to do this
The one ugly thing here to my mind is requirement to supply Mojolicious::Controller object. But controller is only required to make decision: dispatch or not dispatch Because it has extra info to make this decision: this is data to check conditions
The problem as I think is because of here are mixed two things:
Request
Path
And find should just return all routes matched against given arguments: path and method. Like selectors does The array result maybe cached (now routes with conditions are not cached)
Then dispatcher should check conditions against each route, not matcher. Here each condition may be called in context of right controller and not default one. And this will fix this issue. The routes have in most cases their own controller class have not?
Until this behavior will be fixed the example in the question is the best way to find routes

Bridge handler can't access stash data

I have the following code in our webapp written using Mojolicious, and it doesn't work as expected: the bridge handler doesn't get the correct stash data derived from routes (gets undef), so the rest of code fails, however, debug output of $self->stash('city') in any of route handlers is as expected.
...
# Router.
my $r = $self->routes->bridge->to('Main#common');
$r->route('/')->to('Main#index')->name('start');
$r->route('/:region/:city/category/:id')->to('Main#list_category')->name('list_category');
$r->route('/:region/:city/part/:id/:name')->to('Main#show_part')->name('show_part');
...
# Controller.
sub common
{
my $self=shift;
my $db=$self->db;
my $city=$self->stash('city');
my $region=$self->db->selectrow_hashref('select * from region where LOWER(translit)=? ORDER BY region_id LIMIT 1',undef,$city);
say "City=$city.";
if(!$region)
{
$region={};
}
$self->stash(region=>$region);
return 1;
}
...
I think it's correct behavior.
Look at this code.
Placeholder is added when the appropriate route is taken to the processing, i.e., step by step.
Really, look at you routes.
my $r = $self->routes->bridge->to('Main#common');
$r->route('/')->to('Main#index')->name('start');
$r->route('/:region/:city/category/:id')->to('Main#list_category')->name('list_category');
$r->route('/:region/:city/part/:id/:name')->to('Main#show_part')->name('show_part');
I can't understand what behavior you expect when go to route /.
Sub common will be invoked in this case. There are no value for placeholder city!
So, correct solution for your routes must look like this:
my $r = $self->routes;
$r->route('/')->to('Main#index')->name('start');
my $r_city = $r->bridge('/:region/:city/')->to('Main#common');
$r_city->route('/category/:id')->to('Main#list_category')->name('list_category');
$r_city->route('/part/:id/:name')->to('Main#show_part')->name('show_part');
By the way,
starting from Mojolicious version 6.0 bridge was deprecated to favor under. So, you need to replace bridge on under.
But, if you very-very want to have value of placeholder city in common function, you may look at this two line.
You need to write this BAD code in common sub:
sub common {
my $self = shift;
my $stack = $self->match->stack;
warn $self->dumper($stack);
...
}
Print $stack and you understand how to get value of placeholder city.

What is the best way to get the action name with Zend Framework

I'm pretty new to Zend digging the docs but i can't find a good way to get the parameter i passed to my action...
Here is my uri :
/entreprise/name/foo
I would like to extract the name, foo. I'm actually able to get the action name with
$this->getRequest()->getActionName();
or the URI with
$this->getRequest()->getRequestUri();
With these two data i can parse the second string and get the name of my entreprise, but i quite surprised there is no best way to do this...
Is there a best to way to it ?
Get the action name with:
$this->getRequest()->getActionName();
And additional data passed to this action by GET with:
$name = $this->_getParam('name', NULL);
This will get the GET-Value name if passed by URI, if not $name is set to null.
Edit:
As mentioned in the comments the way to pass vars is a bit different. Change it to
/entreprise/name/var/foo
And you'll be able to access foo this way:
$var = $this->_getParam('var', NULL);
In the following URI : /entreprise/name/foo if entreprise is your controller and name is your action, then the additional parameter will be used to call your action so you can get it by doing the following:
public function nameAction( $name = null )
{
// $name == "foo"
}

Zend url : get parameter always stay in the url

I have some trouble using the Zend url helper with get parameter.
In a view, I have pagination which send extra parameters in get (so in the url), so that's ok. But that is not ok is that the parameters always stay in the url even if I change page.
In fact, the zend url helper - I use to generate the url of link or form's action - add automaticaly the parameter at the end of the url so whatever the link I click, I have this parameters...
//In my controller
$this->_view->url(array("action"=>"action-name");
// generate for example : "mywebsite/controller-name/action-name/pays/4" but I don't want the "/pays/4"
Thank you for your help
The url method accepts additional parameters. One of them resets the get-string parameters.
url (
array $ urlOptions = array(),
$ name = null,
$ reset = false,
$ encode = true
)
Generates an url given the name of a route.
Parameters:
array $urlOptions - Options passed to the assemble method of the Route object.
mixed $name - The name of a Route to use. If null it will use the current Route
bool $reset - Whether or not to reset the route defaults with those provided
Returns:
string Url for the link href attribute.
It's all in the doc. The above is for ZF version 1.10
The definition or url() is
public function url(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
So try setting the third parameter ($reset) to true

Zend_Controller_Router_Route_Regex allow '?' in pattern

Imagine situation, when the url should looks like
/catalog/sectionIdent?page=1
where page param is optional.
Of course, custom route should be defined. Consider the following code:
$route = new Zend_Controller_Router_Route_Regex(
'catalog/([-a-z]+)(?:\?page=([0-9]*))?',
array('controller'=>'catalog','action'=>'list','page'=>''),
array(1=>'section',2=>'page'),
'catalog/%s?page=%d'
);
$router->addRoute('catalog-section-page',$route);
But this route won't be triggered with '?' symbol in url.
Without '?' (for example, by adding escaped '!' symbol to pattern) everything works as it should.
Is there any way to achieve '?' presence in custom defined regex route? Maybe I'm doing something wrong in pattern?
P.S.: Don't offer to use '/' instead of '?', question is exactly about pattern restrictions in Zend_Controller_Router_Route_Regex implementation.
The ZF routing classes operate on the REQUEST_URI with the query string stripped off, so you may have a hard time get this working in the way you are expecting. However, I believe GET parameters are put into the request object by default, so you shouldn't need to cater for them in your routes. I'd suggest changing your route to remove the query string parts:
$route = new Zend_Controller_Router_Route_Regex(
'catalog/([-a-z]+)',
array('controller'=>'catalog','action'=>'list'),
array(1=>'section'),
'catalog/%s'
);
$router->addRoute('catalog-section-page',$route);
You should still be able to access the params in your controller as if they had been populated by the routes:
public function listAction()
{
echo $this->_getParam('page');
}
and you can use the same method to set a default:
public function listAction()
{
$page = $this->_getParam('page', 1); // defaults to 1 if no page in URL
}
You just may need to sanitise them there (make sure they are numeric).
Edit:
Example of URL helper with this route:
echo $this->url(array('section' => 'foo', 'page' => 2), 'catalog-section-page')