Mojolicious: determine controller based on placeholder parameters - perl

I know that it's possible to write code like this.
$r->post('/v1/:controller/:action')
And mojo will call appropriate controller class.
Is it possible to do something like this in Mojo (maybe with stash):
$r->post('/v1/:model/:method')->to(
action => ':method',
controller => sub {
my ($captures) = #_;
my $controller = $captures->{'model'};
...
return $controller;
}
)

Related

Slim 4 get all routes into a controller without $app

I need to get all registed routes to work with into a controller.
In slim 3 it was possible to get the router with
$router = $container->get('router');
$routes = $router->getRoutes();
With $app it is easy $routes = $app->getRouteCollector()->getRoutes();
Any ideas?
If you use PHP-DI you could add a container definition and inject the object via constructor injection.
Example:
<?php
// config/container.php
use Slim\App;
use Slim\Factory\AppFactory;
use Slim\Interfaces\RouteCollectorInterface;
// ...
return [
App::class => function (ContainerInterface $container) {
AppFactory::setContainer($container);
return AppFactory::create();
},
RouteCollectorInterface::class => function (ContainerInterface $container) {
return $container->get(App::class)->getRouteCollector();
},
// ...
];
The action class:
<?php
namespace App\Action\Home;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Response;
use Slim\Http\ServerRequest;
use Slim\Interfaces\RouteCollectorInterface;
final class HomeAction
{
/**
* #var RouteCollectorInterface
*/
private $routeCollector;
public function __construct(RouteCollectorInterface $routeCollector)
{
$this->routeCollector = $routeCollector;
}
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
{
$routes = $this->routeCollector->getRoutes();
// ...
}
}
This will display basic information about all routes in your app in SlimPHP 4:
$app->get('/tests/get-routes/', function ($request, $response, $args) use ($app) {
$routes = $app->getRouteCollector()->getRoutes();
foreach ($routes as $route) {
echo $route->getIdentifier() . " → ";
echo ($route->getName() ?? "(unnamed)") . " → ";
echo $route->getPattern();
echo "<br><br>";
}
return $response;
});
From there, one can use something like this to get the URL for a given route:
$routeParser = \Slim\Routing\RouteContext::fromRequest($request)->getRouteParser();
$path = $routeParser->urlFor($nameofroute, $data, $queryParams);
With the following caveats:
this will only work for named routes;
this will only work if the required route parameters are provided -- and there's no method to check whether a route takes mandatory or optional route parameters.
there's no method to get the URL for an unnamed route.

to identify if there is a route zend framework 2

I have a url that redraw $ this->view->url($item['action']) this way but this variable can contain the route in the system or not, how can I do this check, check if this route exists in ZF2 ?
One option in my mind is to pass the navigation object to the view
In controller:
return [
'navigation' => $this->navigation,
];
In view:
if($this->navigation->findOneBy('route', $item['action'])) {
$url = $this->view->url($item['action']);
}
Another option would be to enclose the call in a try/catch. If the function throws an exception, the route does not exist
try {
$url = $this->view->url($item['action']);
} catch(Zend\Router\Exception\RuntimeException $e) {
$url = '';
}

mojolicious helper storing an elasticsearch connection

i'm experimenting with elasticsearch within mojolicious.
I'm reasonably new at both.
I wanted to create a helper to store the ES connection and I was hoping to pass the helper configuration relating to ES (for example the node info, trace_on file etc).
If I write the following very simple helper, it works;
has elasticsearch => sub {
return Search::Elasticsearch->new( nodes => '192.168.56.21:9200', trace_to => ['File','/tmp/elasticsearch.log'] );
};
and then in startup
$self->helper(es => sub { $self->app->elasticsearch() });
however if I try to extend that to take config - like the following -
it fails. I get an error "cannot find index on package" when the application calls $self->es->index
has elasticsearch => sub {
my $config = shift;
my $params->{nodes} = '192.168.56.21:' . $config->{port};
$params->{trace_to} = $config->{trace_to} if $config->{trace_to};
my $es = Search::Elasticsearch->new( $params );
return $es;
};
and in startup
$self->helper(es => sub { $self->app->elasticsearch($self->config->{es}) });
I assume I'm simply misunderstanding helpers or config or both - can someone enlighten me?
Just fyi, in a separate controller file I use the helper as follows;
$self->es->index(
index => $self->_create_index_name($index),
type => 'crawl_data',
id => $esid,
body => {
content => encode_json $data,
}
);
that works fine if I create the helper using the simple (1st) form above.
I hope this is sufficient info? please let me know if anything else is required?
First of all, has and helper are not the same. has is a lazily built instance attribute. The only argument to an attribute constructor is the instance. For an app, it would look like:
package MyApp;
has elasticsearch => sub {
my $app = shift;
Search::ElasticSearch->new($app->config->{es});
};
sub startup {
my $app = shift;
...
}
This instance is then persistent for the life of the application after first use. I'm not sure if S::ES has any reconnect-on-drop logic, so you might need to think about it a permanent object is really what you want.
In contrast a helper is just a method, available to the app, all controllers and all templates (in the latter case, as a function). The first argument to a helper is a controller instance, whether the current one or a new one, depending on context. Therefore you need to build your helper like:
has (elasticsearch => sub {
my ($c, $config) = #_;
$config ||= $c->app->config->{es};
Search::ElasticSearch->new($config);
});
This mechanism will build the instance on demand and can accept pass-in arguments, perhaps for optional configuration override as I have shown in that example.
I hope this answers your questions.

"Dynamic" routes in Mojolicious

I would like to implement something like "dynamic" routes in my Mojolicious app. I have some pre-defined "static" routes and a DB table with URL aliases: '/alias' -> '/URL'. Now I'm defining routes on-the-fly and it looks like this:
before_dispatch => sub {
my ($self, $controller) = #_;
my $path = $controller->tx->req->url->path->to_string;
if ( my $alias = $controller->app->model->alias->find({ alias => $path }) ) {
my $match = Mojolicious::Routes::Match->new( get => $alias->{uri} );
my $routes = $controller->app->routes;
$match->match( $routes );
$routes->route( $path )->to( $match->captures );
}
But is there any better way?
You are adding routes at runtime which seems a good approach (although you should probably check if a route exists before overriding it). You could also do it as a catchall with a wildcard placeholder then handoff the request a bit later on.
http://mojolicio.us/perldoc/Mojolicious/Guides/Routing#Wildcard_Placeholders
$r->get('/(*everything)' )->to('mycontroller#aliases');

Zend Framework: Need advice on how to implement a controller helper

i need advice on how i can implement this action helper. currently, i have something like
class Application_Controller_Action_Helper_AppendParamsToUrl extends Zend_Controller_Action_Helper_Abstract {
function appendParamsToUrl($params = array()) {
$router = Zend_Controller_Front::getInstance()->getRouter();
$url = $router->assemble($params);
if (!empty($_SERVER['QUERY_STRING'])) {
$url .= $_SERVER['QUERY_STRING'];
}
return $url;
}
}
but as you can see, i think the function should be a static function? but how will that find into this Zend_Controller_Action_Helper thingy?
Make the function public and in your BootStrap.php ensure the controller helper can be autoloaded
// add paths to controller helpers
Zend_Controller_Action_HelperBroker::addPath( APPLICATION_PATH .'/controllers/helpers');
You should then be able to call the helper from your controller via
$this->_helper->appendParamsToUrl->appendParamsToUrl();
You can also rename appendParamsToUrl() function to direct()
function direct( $params = array() ) {...}
In this case, you'll be able to access it from controller with
$this->_helper->appendParamsToUrl( $params );