How can you create wildcard routes on Lumen? - php

Let's say I have a controller called TeamsController. Controller has following method, that returns all teams user has access to.
public function findAll(Request $request): JsonResponse
{
//...
}
Then I have bunch of other controllers with the same method. I would like to create a single route, that would work for all controllers, so I would not need to add a line for each controller every time I create a new controller.
I am unable to catch the controller name from URI. This is what I have tried.
$router->group(['middleware' => 'jwt.auth'], function () use ($router) {
// This works
//$router->get('teams', 'TeamsController#findAll');
// This just returns TeamsController#findAll string as a response
$router->get('{resource}', function ($resource) {
return ucfirst($resource) . 'Controller#findAll';
});
});

You return a string instead of calling a controller action:
I believe Laravel loads the controllers this way (not tested)
$router->group(['middleware' => 'jwt.auth'], function () use ($router) {
$router->get('{resource}', function ($resource) {
$app = app();
$controller = $app->make('\App\Http\Controllers\'. ucfirst($resource) . 'Controller');
return $controller->callAction('findAll', $parameters = array());
});
});
But again, I don't really think it's a good idea.

Related

Laravel Routing Group - use value in Controller

All my requests are starting with a prefix, so I created a Route Group with multiple endpoints:
routes/web.php
Route::group(array('prefix' => $prefix), function() {
Route::get("/test/test2/{lang}", ['uses' => 'TestController#test2']);
...
});
Controller:
class TestController {
public function test2(Request $request, $lang) {}
}
With the following test URL:
domain.com/customprefix/test/test2/en
I reach my controller and can access $lang (=en). But how can I pass $prefix to my controller methods? (It should evaluate to "customprefix" in this example)
Unfortunately I didn't find information about that in the documentation or in the API specification.
In your Controller you can get prefixes as one of these solutions:
1.With $reques:
public function TestController(\Illuminate\Http\Request $request)
{
$request->route()->getPrefix();
}
2.Without $request:
$this->getRouter()->getCurrentRoute()->getPrefix()
Did you try to use route prefix already ? . If not, so it should look like this
public function test2(Request $request, $lang){
dd($request->route()->getPrefix());
}

Having trouble rebinding $request->setUserResolver

I'm attempting to rebind what $request->user() returns, and having poked through the built in authentication code, I found a service using app->rebinding to request->setUserResolver is how it's done? I tried it myself, with no luck. I created a service (well, coopted AuthServiceProvider, and changed the register to:
public function register()
{
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
$token = $this->request->bearerToken();
dd($token);
// error_log($token);
return array('user' => 1);
});
});
}
Ignoring the dd, which is there to test, how can I find where I'm going wrong? I even found a SO answer that seems to indicate this is the way to go but nothing gets dumped, nothing gets logged (when error log isn't commented out) and dumping $request->user() in my controller just returns null.
I know I can use the built in auth/guard setup, but I figured since I'm not using most of what the auth/guard setup has, why not try to learn and set it up myself? Of course, so far I've gotten nowhere. I'm going to fall back to using the built-in stuff, but I'd like to learn and improve.
As I realized it may make a difference, I'm running Lumen 5.4.
In Lumen, your App\Providers\AuthServiceProvider class comes by default with
public function boot()
{
// Here you may define how you wish users to be authenticated for your Lumen
// application. The callback which receives the incoming request instance
// should return either a User instance or null. You're free to obtain
// the User instance via an API token or any other method necessary.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
}
This is the place to define the user resolution logic. The rebinding you were registering in the register method was being supeseded by this one.
Just uncomment the $app->register(App\Providers\AuthServiceProvider::class); line in bootstrap/app.php to register your provider; don't modify the code in the vendor folder (if I understood correctly you were doing that).
Update
I now see what you mean, although I'm not sure it is really too much "load" for the auth/guard method.
However, in the interest of creating a minimal implementation, I think the solution would be overriding the prepareRequest method of the Application class.
In bootstrap/app.php replace
$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
with
$app = new class (realpath(__DIR__.'/../')) extends Laravel\Lumen\Application {
protected function prepareRequest(\Symfony\Component\HttpFoundation\Request $request)
{
if (! $request instanceof Illuminate\Http\Request) {
$request = Illuminate\Http\Request::createFromBase($request);
}
$request->setUserResolver(function () use ($request) {
return $request->bearerToken();
})->setRouteResolver(function () {
return $this->currentRoute;
});
return $request;
}
};
This way you can have the simple resolution logic for getting the bearer token (don't include the AuthServiceProvider then).
(This requires PHP 7 anonymous classes; alternatively just extend to a regular class).
You do not need to change the register() function.
Just uncomment the following lines in bootstrap/app.php file:
$app->withEloquent();
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);
And in app/Providers/AuthServiceProvider.php->boot(), it has default method to retrieve the authenticated user.
$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
You may use an API token in the request headers or query string, a bearer token on the request, or using any other approach your application requires.
After that, you may retrieve the authenticated user like this:
use Illuminate\Http\Request;
$app->get('/post/{id}', ['middleware' => 'auth', function (Request $request, $id) {
$user = Auth::user();
$user = $request->user();
//
}]);
The rebinding method will add an additional reboundCallbacks which this callback will be triggered right after the abstract is rebound. As long as your abstract is not rebound, the reboundCallbacks are not called. So, you can simply rebound your abstract, like so:
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
$token = $this->request->bearerToken();
dd($token);
// do the rest
});
});
// REBOUND HERE
$this->app->instance('request', $this->app->make('request'));
// TEST
// $this->app->make('request')->user(); // output is $token
Try uncomment the rebound line above, your dd will not called at all.
Extra
You can use refresh method (to register reboundCallbacks) combined with extend method (to rebound) for cleaner code:
public function register()
{
parent::register();
$this->app->refresh('request', $this, 'overrideUserResolver');
// REBOUND HERE, JUST ANOTHER WAY TO REBOUND
$this->app->extend('request', function ($request) { return $request; });
// TEST
$this->app->make('request')->user();
}
public function overrideUserResolver($request)
{
$request->setUserResolver(function ($guard = null) use ($request) {
$token = $request->bearerToken();
dd($token);
// do the rest
});
}

Dynamic routes in laravel instead of manual routes

I want to prevent write all route in Laravel route.php,actually i follow MVC routing like this www.example.com/controller/action/p1/p2/p3
if you have any good idea give it to me,
i wrote this
$controller = ucfirst(Request::segment(1));
$controller = $controller . 'Controller';
$result=App::make('indexController')->ChechIfExistController($controller);
if($result){
if(Request::segment(2))
$action=Request::segment(2);
else
$action='index';
if(Request::segment(5))
Route::any('/{controller?}/{action?}/{p1?}/{p2?}/{p3?}',array('uses'=>$controller.'#'.$action));
else if(Request::segment(4))
Route::any('/{controller?}/{action?}/{p1?}/{p2?}',array('uses'=>$controller.'#'.$action));
else if(Request::segment(3))
Route::any('/{controller?}/{action?}/{p1?}',array('uses'=>$controller.'#'.$action));
else
Route::any('/{controller?}/{action?}',array('uses'=>$controller.'#'.$action));
} else{
echo '404';
EXIT;
}
but i don't know how to control and check controller and action in laravel to understand if it exist or not.
i need your help.
thanks a lot.
ifound it,this code fix the problem and check if action exist or not,but i would like to do that with laravel but it seems laravel does not have any thing for checking controller and actions
$controller=='Controller'?$controller='IndexController':$controller;
$controllers=new $controller ();
if(method_exists($controllers,$action)){...}
and in composer define my route,
that's all
routes.php
Route::controllers([
'auth' => 'Auth\AuthController',
]);
in AuthController you can do that:
// will be available as METHODNAME /auth/url/{one?}/{two?}/{three?}/{four?}/{five?}
public [methodName]Url($one, $two, $three, $four, $five)
{
//...
}
// for example POST /auth/register
public function postRegister(Request $request)
{
// ...
}
// GET /auth/login
public function getLogin()
{
//...
}
it's not documented, but you can see that in sources:
https://github.com/laravel/framework/blob/5.0/src%2FIlluminate%2FRouting%2FControllerInspector.php
https://github.com/laravel/framework/blob/5.0/src%2FIlluminate%2FRouting%2FRouter.php#L238
That can be done such way:
First we have to write static routes and after that, dynamic route which uses database.
routes.php
Route::get('/', function () {
return 'welcome';
});
Route::get('/faq', function () {
return 'faq';
});
Route::get('/about', function () {
return 'about';
});
Route::get('/{slug}', function ($slug) {
return Article::where('slug', $slug)->first();
});

How to do restful ajax routes to methods in Laravel 5?

So I have a route that looks like this:
Route::any('some/page', ['as' => 'some-page', 'uses' => 'SomePageController#index']);
However, I also have ajax calls at the same URL (using a request parameter called ajax like: some/page/?ajax=my_action) that I want to hit methods on my controller:
index already routes: 'SomePageController#index'
ajax = my_action needs to route: 'SomePageController#ajaxMyAction'
ajax = my_other_action needs to route: 'SomePageController#ajaxMyOtherAction'
ajax = blah_blah needs to route: 'SomePageController#ajaxBlahBlah
...
What's the elegant solution to setting this up in my routes.php file?
After inspection of Laravel's Http Request and Route classes, I found the route() and setAction() methods could be useful.
So I created a middleware to handle this:
<?php namespace App\Http\Middleware;
class Ajax {
public function handle($request, Closure $next)
{
// Looks for the value of request parameter called "ajax"
// to determine controller's method call
if ($request->ajax()) {
$routeAction = $request->route()->getAction();
$ajaxValue = studly_case($request->input("ajax"));
$routeAction['uses'] = str_replace("#index", "#ajax".$ajaxValue, $routeAction['uses']);
$routeAction['controller'] = str_replace("#index", "#ajax".$ajaxValue, $routeAction['controller']);
$request->route()->setAction($routeAction);
}
return $next($request);
}
}
Now my route looks like:
Route::any('some/page/', ['as' => 'some-page', 'middleware'=>'ajax', 'uses' => 'SomePageController#index']);
And correctly hits my controller methods (without disturbing Laravel's normal flow):
<?php namespace App\Http\Controllers;
class SomePageController extends Controller {
public function index()
{
return view('some.page.index');
}
public function ajaxMyAction(Requests\SomeFormRequest $request){
die('Do my action here!');
}
public function ajaxMyOtherAction(Requests\SomeFormRequest $request){
die('Do my other action here!');
}
...
I think this is a fairly clean solution.
You can't make this dispatch in the routing layer if you keep the same URL. You have two options :
Use different routes for your AJAX calls. For example, you can prefix all your ajax calls by /api. This is a common way :
Route::group(['prefix' => 'api'], function()
{
Route::get('items', function()
{
//
});
});
If the only different thing is your response format. You can use a condition in your controller. Laravel provides methods for that, for example :
public function index()
{
$items = ...;
if (Request::ajax()) {
return Response::json($items);
} else {
return View::make('items.index');
}
}
You can read this http://laravel.com/api/5.0/Illuminate/Http/Request.html#method_ajax and this http://laravel.com/docs/5.0/routing#route-groups if you want more details.

Laravel: How to redirect a request to controller function and use View::make at the same time?

**Route**
Route::get('admin', function()
{
return View::make('theme-admin.main');
});
**Controller**
class Admin_Controller extends Base_Controller {
public function action_index()
{
echo __FUNCTION__;
}
If I forward request to controller, then I have to define View::make in every function in controller. If I don't forward it, action function doesn't work.
Should I just forward requests to controller and use View::make inside action functions or there are better alternatives?
Actually isn't necessary to define View::make in every function of your controllers.
You can, for example, execute an action and then redirect to another action, that could View::make.
Let's say you want to create an user and then show its profile, in a RESTful way. You could do:
# POST /users
public function user_create()
{
$user = User::create(...);
// after you have created the user, redirect to its profile
return Redirect::to_action('users#show', array($user->id));
// you don't render a view here!
}
# GET /users/1
public function get_show($id)
{
return View::make('user.show');
}
you can call the controller function like this
$app = app();
$controller = $app->make('App\Http\Controllers\EntryController');
return $controller->callAction('getEntry', $parameters = array());
or you can simply dispatch the request to another controller url
$request = \Request::create(route("entryPiont"), 'POST', array()));
return \Route::dispatch($request);

Categories