I am having issue getting the group route parameter in middleware here's what i am doing
I am using [PHP - SLIM 3 Framework]
route.php
$app->group('/{lang}', function() use ($container){
//routes ... (ignore the other routes)
})->add(new Middleware($container));
Middleware.php
//middleware.php
class Middleware {
public function __invoke($req, $res, $next)
{
//here is the part which is confusing about how can i get
// {lang} parameter
$req->getAttribute('route')
}
}
You can do this with the getArguments()-method
public function __invoke($req, $res, $next)
{
$route = $req->getAttribute('route');
$args = $route->getArguments();
$lang = $args['lang'];
return $res;
}
Note: you also need to set the slim setting determineRouteBeforeAppMiddleware to true. Otherwise the argument is not set in the middleware.
$container = [
'settings' => [
'determineRouteBeforeAppMiddleware' => true
]
]
$app = new \Slim\App($container);
Related
I have this route
Route::get('/getLocation/{postalCode}', function () {
$response = Http::get('https://theapi');
return $response->json();
});
Sadly this doesn't work
I can reach the api with
https://theapi?PostalCode=xxxx
How can i replicate this in my route to work?
You got double }} there, try delete it so it will be like this:
Route::get('/getLocation/{postalCode}', function () {
$response = Http::get('https://theapi');
return $response->json();
});
Edit:
Add the route parameter to API function parameters
Route::get('/getLocation/{postalCode}', function ($postalCode) {
// do whatever you want to postalCode variable
$response = Http::get('https://theapi', [
'PostalCode' => $postalCode
]);
return $response->json();
});
I managed to get some routes working with and without a prefix. Having routes that do not have the prefix work properly is important as it's too much work to go back and change all get/post links to the correct localized ones.
For example with the code below the URL localhost/blog redirects to localhost/en/blog (or any other language stored in session).
However, I noticed that URLs with parameters don't work, so /blog/read/article-name will result in a 404 instead of redirecting to /en/blog/read/article-name.
Routes:
Route::group([
'prefix' => '{locale}',
'middleware' => 'locale'],
function() {
Route::get('blog', 'BlogController#index');
Route::get('blog/read/{article_permalink}', 'BlogController#view');
}
);
Middleware is responsible for the redirects which don't seem to fire at all for some routes as if the route group isn't matching the URL.
public function handle($request, Closure $next)
{
if ($request->method() === 'GET') {
$segment = $request->segment(1);
if (!in_array($segment, config('app.locales'))) {
$segments = $request->segments();
$fallback = session('locale') ?: config('app.fallback_locale');
$segments = array_prepend($segments, $fallback);
return redirect()->to(implode('/', $segments));
}
session(['locale' => $segment]);
app()->setLocale($segment);
}
return $next($request);
}
I have two user types: Operations and Maintenance.
All routes that the Operations user type can be accessed by Maintenance user type but not all routes that Maintenance have can't be accessed by Operations.
Here's my existing code.
Route::group(['middleware'=>'maintenance'], function(){
//routes here
Route::group(['middleware'=>'operations'], function(){
//routes here
});
});
kernel.php
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'revalidate' => \App\Http\Middleware\RevalidateBackHistory::class,
'dev' => \App\Http\Middleware\isDev::class,
'operations' => \App\Http\Middleware\operations::class,
'maintenance' => \App\Http\Middleware\maintenance::class,
];
middleware/operations.php
public function handle($request, Closure $next)
{
$a = UserAccess::where(['employee_id'=>\Auth::user()->id, 'user_type_id'=>1])->first();
if($a){
return $next($request);
}
return redirect('/');
}
But it doesn't work. Maintenance can access all it's routes but doesn't have access to operations and Operations doesn't have access to any route at all.
NOTE: Both groups are inside the auth middleware group
Found a work around for this. I created another middleware that caters both Operations and Maintenance by passing two parameters to that middleware specifically 1 = Operations and 2 = Maintenance and had the middleware check for access. Here's the code:
Route::group(['middleware'=>'access:2,2'], function(){
//Routes that can be accessed by Maintenance user type
});
Route::group(['middleware'=>['access:1,2']], function(){
//Routes that can be accesed by Operations and Maintenance user type.
});
Here's the middleware:
public function handle($request, Closure $next, $ops, $main)
{
$a = UserAccess::where('employee_id',\Auth::user()->id)->whereIn('user_type_id', [$ops, $main])->first();
if($a){
return $next($request);
}
return redirect('/');
}
EDIT:
Optimized code to remove parameter redundancy.
Web.php:
Route::group(['middleware'=>'access:2'], function(){
//Routes that can be accessed by Maintenance user type
});
Route::group(['middleware'=>['access:1,2']], function(){
//Routes that can be accesed by Operations and Maintenance user type.
});
access.php
public function handle($request, Closure $next, $ops, $main = 0)
{
$a = UserAccess::where('employee_id',\Auth::user()->id)->whereIn('user_type_id', [$ops, $main])->first();
if($a){
return $next($request);
}
return redirect('/');
}
I think you've missed an additional array inside of your where query, try changing the code inside of your operations middleware like so:
$a = UserAccess::where([
['employee_id', \Auth::user()->id],
['user_type_id', 1]
])->first();
EDIT:
Then try moving both of your middlewares into $middlewareGroups below web middleware, like this:
'web' => [
...
],
'operations' => \App\Http\Middleware\operations::class,
'maintenance' => \App\Http\Middleware\maintenance::class,
EDIT 2:
Change the code for $a variable to this
$a = UserAccess::where([
['employee_id', Auth::user()->id], //Hence there is no \ before Auth
['user_type_id', 1]
])->first();
Then at the top of that same file include the following use:
use Illuminate\Support\Facades\Auth;
EDIT 3:
You can add additional parameters to the request which are then sent forward, like this:
Middlewares/operations.php
function handle($request, Closure $next) {
...
$request->attributes->add(['passedOperations' => true]);
return next($next);
}
Middlewares/maintenance.php
function handle($request, Closure $next) {
...
if($request->get('passedOperations')) {
return next($next);
} else {
//didn't pass operations
}
}
I'm trying to pass a variable through my route to my controller, but I have multiple routes (categories) leading to the same controller i.e.
Route::get('/category1/{region}/{suburb?}', 'SearchController#search');
Route::get('/category2/{region}/{suburb?}', 'SearchController#search');
Making /category1, 2, etc. to be a parameter /{category} is not an option and I don't want to make separate controller function for each category.
How do I send the first segment of the url to my search controller? i.e. category1 or category2?
At present controller is as follows:
public function search($region, $suburb = null) { }
Thanks!
You can specify a mask for your {category} parameter so that it must fit the format "category[0-9]+" in order to match the route.
Route::get('/{category}/{region}/{suburb?}', 'SearchController#search')
->where('category', 'category[0-9]+');
Now, your example url (from the comments) www.a.com/var1/var2/var3 will only match the route if var1 matches the given category regex.
More information can be found in the documentation for route parameters here.
Edit
Yes, this can work with an array of string values. It is a regex, so you just need to put your array of string values into that context:
Route::get('/{category}/{region}/{suburb?}', 'SearchController#search')
->where('category', 'hairdresser|cooper|fletcher');
Or, if you have the array built somewhere else:
$arr = ['hairdresser', 'cooper', 'fletcher'];
// run each array entry through preg_quote and then glue
// the resulting array together with pipes
Route::get('/{category}/{region}/{suburb?}', 'SearchController#search')
->where('category', implode('|', array_map('preg_quote', $arr)));
Edit 2 (solutions for original request)
Your original question was how to pass the hardcoded category segment into the controller. If, for some reason, you didn't wish to use the solution above, you have two other options.
Option 1: don't pass the value in, just access the segments of the request in the controller.
public function search($region, $suburb = null) {
$category = \Request::segment(1);
dd($category);
}
Option 2: modify the route parameters using a before filter (L4) or before middleware (L5).
Before filters (and middleware) have access to the route object, and can use the methods on the route object to modify the route parameters. These route parameters are eventually passed into the controller action. The route parameters are stored as an associative array, so that needs to be kept in mind when trying to get the order correct.
If using Laravel 4, you'd need a before filter. Define the routes to use the before filter and pass in the hardcoded value to be added onto the parameters.
Route::get('/hairdresser/{region}/{suburb?}', ['before' => 'shiftParameter:hairdresser', 'uses' => 'SearchController#search']);
Route::get('/cooper/{region}/{suburb?}', ['before' => 'shiftParameter:cooper', 'uses' => 'SearchController#search']);
Route::get('/fletcher/{region}/{suburb?}', ['before' => 'shiftParameter:fletcher', 'uses' => 'SearchController#search']);
Route::filter('shiftParameter', function ($route, $request, $value) {
// save off the current route parameters
$parameters = $route->parameters();
// unset the current route parameters
foreach($parameters as $name => $parameter) {
$route->forgetParameter($name);
}
// union the new parameters and the old parameters
$parameters = ['customParameter0' => $value] + $parameters;
// loop through the new set of parameters to add them to the route
foreach($parameters as $name => $parameter) {
$route->setParameter($name, $parameter);
}
});
If using Laravel 5, you'd need to define a new before middleware. Add the new class to the app/Http/Middleware directory and register it in the $routeMiddleware variable in app/Http/Kernel.php. The logic is basically the same, with an extra hoop to go through in order to pass parameters to the middleware.
// the 'parameters' key is a custom key we're using to pass the data to the middleware
Route::get('/hairdresser/{region}/{suburb?}', ['middleware' => 'shiftParameter', 'parameters' => ['hairdresser'], 'uses' => 'SearchController#search']);
Route::get('/cooper/{region}/{suburb?}', ['middleware' => 'shiftParameter', 'parameters' => ['cooper'], 'uses' => 'SearchController#search']);
Route::get('/fletcher/{region}/{suburb?}', ['middleware' => 'shiftParameter', 'parameters' => ['fletcher'], 'uses' => 'SearchController#search']);
// middleware class to go in app/Http/Middleware
// generate with "php artisan make:middleware" statement and copy logic below
class ShiftParameterMiddleware {
public function handle($request, Closure $next) {
// get the route from the request
$route = $request->route();
// save off the current route parameters
$parameters = $route->parameters();
// unset the current route parameters
foreach ($parameters as $name => $parameter) {
$route->forgetParameter($name);
}
// build the new parameters to shift onto the array
// from the data passed to the middleware
$newParameters = [];
foreach ($this->getParameters($request) as $key => $value) {
$newParameters['customParameter' . $key] = $value;
}
// union the new parameters and the old parameters
$parameters = $newParameters + $parameters;
// loop through the new set of parameters to add them to the route
foreach ($parameters as $name => $parameter) {
$route->setParameter($name, $parameter);
}
return $next($request);
}
/**
* Method to get the data from the custom 'parameters' key added
* on the route definition.
*/
protected function getParameters($request) {
$actions = $request->route()->getAction();
return $actions['parameters'];
}
}
Now, with the filter (or middleware) setup and in use, the category will be passed into the controller method as the first parameter.
public function search($category, $region, $suburb = null) {
dd($category);
}
I've got a route:
Route::get('/', array('before' => 'auth.basic', function() {
return Response::json(
array(
'name' => 'John Smith',
'age' => 42
)
);
}));
What I want is to add where active= 1 to the authentication query.
I know that I can add extra conditions to Auth::attempt() but I don't know how to do this using Auth::basic().
There's two ways to do this.
1. Modify the filter and use Auth::attempt()
That's the simple way. Head over to app/filter.php and change the auth.basic entry to this
Route::filter('auth.basic', function()
{
if(Auth::check()) return; // already logged in
if(Auth::attempt(array(
'email' => Request::getUser(),
'password' => Request::getPassword(),
'active' => 1))){
return;
}
$headers = array('WWW-Authenticate' => 'Basic');
return Response::make('Invalid credentials.', 401, $headers);
});
That's basically what the Laravel Guard class does when Auth::basic() gets called (except for the 'active' part obviously)
2. Extend Auth
Now that's the "more elegant" way to do it. Although I don't know if its really worth it in your case. I'm also not going to describe it in every detail.
To extend Laravel Auth you can use Auth::extend(). Here's an example:
Auth::extend('my_driver', function() {
$model = Config::get('auth.model');
$provider = new Illuminate\Auth\EloquentUserProvider(App::make('hash'), $model);
return new MyGuard($provider, App::make('session.store'));
});
The MyGuard class would extend Illuminate\Auth\Guard and override the getBasicCredentials() method. Finally in config/auth.php set 'driver' => 'my_driver'
If you choose the second method and need more help, write a comment...
How I did it (Laravel 5.8):
Methods basic and basicOnce from SessionGuard accepts two arguments: $field where you can set username column and $extraConditions where you can set, well, extra conditions.
You can extend AuthenticateWithBasicAuth and override handle method to get something like
class CustomBasicAuth extends AuthenticateWithBasicAuth
public function handle($request, Closure $next, $guard = null, $field = null)
{
$this->auth->guard($guard)->basic('email', ['active' => 1]);
return $next($request);
}
and don't forget to register and use new middleware.