Slim 3 Framework Rest Nested Routes - slim

Is there a way to do this:
1. $app->get('/phonenumbers', \PhonenumbersController::class . ':index');
2. $app->get('/users/1/phonenumbers', \PhonenumbersController::class . ':index');
Would like the second route to resolve to:
$app->get('/phonenumbers?user_id=1', \PhonenumbersController::class . ':index');
I basically need the user_id available as a request param.
Is this possible in Slim3? Without introducing .htaccess rewrites.
Thanks.

Yes, you can add parameter in path, that will be available in request
$app->get('/users/{user_id}/phonenumbers', \PhonenumbersController::class . ':index');
...
class PhonenumbersController
{
public function index($request, $response)
{
$request->getAttribute('user_id');
}
}

Related

perform a redirect from module class onbootstrap method

Is there a proper method to perform a redirect inside the Application Module class using zend framework?
I'd like to reconstruct this code to use a zend based redirection.
class Moudle
{
public function onBootstrap(MvcEvent $e)
{
$arg = 1;
if($arg==1){
header("Location: index.php?redirect=1");
exit();
}
}
I don't know how others do but here is an example of what I already did in a project :
class Module
{
public function onBootstrap(MvcEvent $e)
{
$arg = 1;
if($arg==1){
$response = $e->getResponse();
$response->getHeaders()->clearHeaders()->addHeaderLine('Location', '/index.php?redirect=1');
$response->setStatusCode(302)->sendHeaders();
exit();
}
};
If you don't want to hard code the url (which is not recommended according to me), then you can use the view helper url or the controller plugin url that you get from the service manager in order to use the name of your route instead of "/index.php?redirect=1". I always prefer to avoid hard coding url in my ZF projects as we can use the name of the routes. It's up to you.

Mass CRUD REST edit/update controller

I am trying to create a RESTful CRUD controller with a little but significant difference that might be in conflict with REST idea but anyway:
I am trying to mass edit items like so /photos/{photo}/edit where item id parameters are like /photos/0&2&7/edit
What is the proper way to establish that in Laravel 5.3?
Is there a way to use some method injections or at least to receive a collection of parameters in the controller method ?
public function edit($id) {
//.......
}
Appreciate your kind help, BR
Using Eloquent you can do whereIn, so you just need to explode the photo parameter so that all the ids are in an array:
public function edit($ids) {
$photo_ids = explode('&', $ids);
$images = Image::whereIn('id', $photo_ids)->get();
}
You can switch out statically accessing the Image model like I did in this example, you can just method inject or dependency inject the image model, let me know if you'd like assistance with dependency/method injection.
Hey i guess you are trying Model binding so you have to use like this
public function edit(Photo $photo) {
//.......
}
Your route should like this
Route::model('photos','App\Photo');
Route::resource('photos','PhotoController');
or you can try this way
your route and function like this
Route::resource('photos','PhotoController');
public function edit($id) {
$photo = Photo::findorFail($id);
}

Multiple Slim routes with the same signature

We are looking at using Slim 3 as the framework for our API. I have searched SO and the Slim docs, but cannot find an answer to the issue. If we have different route files (e.g. v1, v2, etc.) and if two routes have the same signature, an error is thrown. Is there any way to cascade the routes so that the last loaded route for a particular signature is used?
For example, say v1.php has a route for GET ("/test") and v2.php also contains this route, can we use the latest version? Even simpler would be if a file of routes contains two routes with the same signature, is there a way of the latter method being used (and no error being thrown)?
A similar question is asked here but this uses hooks (which have been removed from Slim 3 as per here)
I looked at the Slim code and I didn't find a simple way of allowing duplicated routes (preventing the exception).
The new Slim uses FastRoute as dependency. It calls FastRoute\simpleDispatcher and doesn't offer any configuration possiblity. Even if it did allow some configuration, FastRoute doesn't have any built-in option to allow duplicated routes. A custom implementation of a DataGenerator would be needed.
But following the instructions above, we can get a custom DataGenerator by passing to Slim App a custom Router which instantiates some FastRoute::Dispatcher implementation which then uses the custom DataGenerator.
First the CustomDataGenerator (let's go the easy way and do some copy and pasting from \FastRoute\RegexBasedAbstract and \FastRoute\GroupCountBased)
<?php
class CustomDataGenerator implements \FastRoute\DataGenerator {
/*
* 1. Copy over everything from the RegexBasedAbstract
* 2. Replace abstract methods with implementations from GroupCountBased
* 3. change the addStaticRoute and addVariableRoute
* to the following implementations
*/
private function addStaticRoute($httpMethod, $routeData, $handler) {
$routeStr = $routeData[0];
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
if ($route->matches($routeStr)) {
throw new BadRouteException(sprintf(
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
$routeStr, $route->regex, $httpMethod
));
}
}
}
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
unset($this->staticRoutes[$httpMethod][$routeStr]);
}
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
}
private function addVariableRoute($httpMethod, $routeData, $handler) {
list($regex, $variables) = $this->buildRegexForRoute($routeData);
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
unset($this->methodToRegexToRoutesMap[$httpMethod][$regex]);
}
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new \FastRoute\Route(
$httpMethod, $handler, $regex, $variables
);
}
}
Then the custom Router
<?php
class CustomRouter extends \Slim\Router {
protected function createDispatcher() {
return $this->dispatcher ?: \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
foreach ($this->getRoutes() as $route) {
$r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier());
}
}, [
'routeParser' => $this->routeParser,
'dataGenerator' => new CustomDataGenerator()
]);
}
}
and finally instantiate the Slim app with the custom router
<?php
$app = new \Slim\App(array(
'router' => new CustomRouter()
));
The code above, if a duplicated route is detected, removes the previous route and stores the new one.
I hope I didn't miss any simpler way of achieving this result.

Collection (as opposed to member) endpoint in Grails REST?

In rails there's an easy way to add a collection end point to routes. e.g.
resources :books do
member do
get 'publisher' # /books/id/publisher
end
collection do
get 'total_count' # /books/total_count
end
end
Is there a similar way to map the total_count endpoint in Grails? The example here ( http://grails.org/doc/2.3.1/guide/single.html#urlMappings ) only shows a member route.
"/books"(resources: "book") {
"/publisher"(controller:"publisher")
"/total_count"(controller: "publisher") // ??? can this work?
}
I am currently using Grails 2.3.4.
It was simpler than I thought though if there's a more canonical way of solving this I'd appreciate the feedback.
Basically I defined the collection endpoint before the resource endpoint.
class UrlMappings {
static mappings = {
"/books/total_count" (controller: "Book", action: "totalCount", method: "GET")
"/books" (resources: "Book")
}
}
So far it appears to be working.

CakePHP: RESTful prefixed action

I have troubles to do REST application in CakePHP, requesting GET /admin/quote_authors/1.json sends me to the 'view' action, not 'admin_view'.
route.php:
Router::parseExtensions('json');
Router::mapResources(array(':controller'), array('prefix' => '/admin/'));
QuoteAuthorsController.php:
public $components = array('RequestHandler');
public function admin_view($id) {
var_dump('admin view');
}
public function view($id) {
var_dump('view');
}
Thanks.
Answering because I can't comment.
You seem to be missing the action part of the request /admin/quote_authors/view/1.json
So for other request it would be like /admin/:controller/:action/:params in general.
And, of course, like thaJeztah said, remove the slashes of the prefix (that's why it's giving you that error, it's considering the parameter "1" as the action it has to execute)