Laravel Routing Group - use value in Controller - php

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());
}

Related

Laravel model user custom binding "/users/me/xxx"

I have routes with user binding like
Route::get('users/{user}/posts', [PostController::class, 'index']);
Route::get('users/{user}/comments', [CommentController::class, 'index']);
So I can use /users/1/posts, /users/5/posts etc, and in controller it's automatically available thanks to model binding
public function index(User $user)
{
dd($user);
}
But for current logged user I want to make possible to also use /me/ instead ID, like /users/me/posts
Is there a way to make it without defining separate controller methods where I would have to find user manually, and without duplicating all routes? So is it possible to "extend" default Laravel model binding globally?
I believe using a fixed route parameter like this is the most sustainable solution, especially if this is a shared code base. It will involve some repeated code, but makes it immediately clear what routes are available. And you can predefine the grouping callback to avoid repeating the route definitions.
$routeGroup = function ($r) {
$r->get('posts', [PostController::class, 'index']);
$r->get('comments', [CommentController::class, 'index']);
};
Route::prefix('users/{user}')->group($routeGroup);
Route::prefix('users/me')->group($routeGroup);
And then make the parameter optional in your controller method.
public function index(User $user = null)
{
$user = $user ?? Auth::user();
dd($user);
}
Another possibility is overriding the resolveRouteBinding method on your model. If you go with this method (or Patricus' solution) I'd suggest leaving comments in the route file explaining what you've done.
class User extends Model {
public function resolveRouteBinding($value, $field = null): ?self
{
return $value === 'me'
? Auth::user()
: parent::resolveRouteBinding($value, $field);
}
}
Sure, you just need to use explicit route model binding instead of the default implicit binding. No need to change your routes or your controllers.
In your RouteServiceProvider::boot() method, you can add the following binding for the user parameter:
// use App\Models\User;
// use Illuminate\Support\Facades\Auth;
// use Illuminate\Support\Facades\Route;
public function boot()
{
Route::bind('user', function ($value) {
if ($value == 'me') {
return Auth::user();
}
return User::findOrFail($value);
});
}
Now all your routes with the {user} parameter defined will use that function to bind the User model in the route.
You may want to update the function to be able to handle case sensitivity, or handle when the route is accessed as a guest, but that's up to your implementation details.

How can you create wildcard routes on Lumen?

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.

Laravel: How does Controller access parameters from Route?

I am very obviously a noob to Laravel and hope that someone can help me out.
The about screen is accessed through the route
Route::get('/about', array('as' => 'about', function()
{
return View::make('about')->with('title','About Screen')->with('class','about');
}));
The variables $title and $class are accessible in about.blade.php by {{ $title }} and {{ $class }}. If instead, I have a Controller in between,
Route::get('hello/create', array('as' => 'create', 'uses' =>
'HelloController#create', function()
{
return View::make('hello/create')->with('title','Create')->with('class','hello.create');
}));
How do I access $title and $class in the HelloController.php code (so that I can propagate the values to the coming View)?
P.S. I do know about the /hello/create/{name of variable} which is the answer on nearly all questions similar to this, but don't know how to use it to transmit variables NOT keyed onto the Http Request.
$title and $class are the values you are manually giving to the blade. These aren't the values that you are receiving in GET parameters in your route. So, you would do it the same way as you did in the closure.
Your route:
Route::get('hello/create', array('as' => 'create', 'uses' => 'HelloController#create'));
Controller method:
class HelloController{
public function create(){
return View::make('hello/create')->with('title','Create')->with('class','hello.create');
}
}
UPDATE:
From what I understood, you can also call controller's method inside the route's closure and pass parameters to the controller and call the view with these values inside the controller's method.
Your route file:
use App\Http\Controllers\HelloController;
Route::get('hello/create',function(){
$hello_obj = new HelloController();
return $hello_obj->create('create','hello.create');
});
Controller method:
class HelloController{
public function create($title,$class){
return View::make('hello/create')->with('title',$title)->with('class',$class);
}
}
First you need to clear your flow. You are -at the moment- manually setting the variables to be returnet to the view, so your route should look like this:
Route::get('hello/create', 'HelloController#create');
Then, your controller handles the logic:
public function create(Request $request)
{
return view('hello.create')->with('title','Create')->with('class','hello.create');
}
Now, if you need to send parameters from your frontend to your controller, you have two options:
Define route parameters.
Use query params.
Option 1
For the first option, you'll need to define your required/optional parameters in the route itselft:
Route::get('hello/create/{a_variable}', 'HelloController#create');
Then you access this parameter in any of this ways:
public function create(Request $request)
{
return view('hello.create')->with('a_variable', $request->a_variable);
}
or injecting the variable in the method:
public function create(Request $request, $a_variable)
{
return view('hello.create')->with('a_variable', $a_variable);
}
Option 2
For the use of query params, you should include this options when making the request. If your route looks like this:
Route::get('hello/create', 'HelloController#create');
You could specify query params like this:
GET www.my-domain.com/hello/create?first_parameter=value_1&second_parameter=value_2
So in your controller you access this values like this:
public function create(Request $request)
{
$value_1 = $request->get('first_parameter');
$value_2 = $request->get('second_parameter');
return view('hello.create')
->with('value_1', $value_1)
->with('value_2', $value_2);
}
You are alreading sending data to view using with().
Echo it in your view file using $variablename set in with() Example: <?php echo $title; ?>

Laravel 5.3 Route model binding in Route group with domain

I need to setup Route model binding in the group which defined subdomain.
When I run this code:
Route::bind('app', function ($value) {
return App\Models\App::where([
'slug' => $value,
])->firstOrFail();
});
Route::group(['domain' => '{appSlug}.upman.dev'], function(App\Models\App $app) {});
I just get the error message:
Argument 1 passed to App\Providers\RouteServiceProvider::{closure}()
must be an instance of App\Models\App, instance of
Illuminate\Routing\Router given.
I don't known, how to get it works.
Thank so much guys for any response!
You should define your explicit model bindings in the boot method of the RouteServiceProvider class:
public function boot(){
parent::boot();
Route::bind('app', function ($value) {
return App\Models\App::where([
'slug' => $value,
])->firstOrFail();
});
}
It should looks like this:
class IndexPageController extends Controller
{
public function index($domain, App\IndexPage $page = null) {
//$domain will send first parameter
}
}
to disable this parameter you can use in your middleware
$request->route()->forgetParameter('domain');

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.

Categories