Laravel 5 Middleware - laravel-5

Hi I am using Middleware of Laravel 5 and what I am doing is I have middleware which is being used in few controllers, and each of my controllers has it's own configs, which I need to pass my middleware, how can I do that?

If you are using this Middleware in "Stack" (Defined at App/Http/Kernel.php in $middleware array), you don't have the $request->route() available.
BUT, if you are using it on routes.php file as middleware of a single route or group of routes, you can do this way:
Create a config file as you want, with config items named with same name of each route, then in your middleware you get route name and load proper configs.
Sorry if it's confuse, imagine this code:
Config/permissions.php
<?php
return [
// Route name here
'user.index' =>
// Your route options here
[
'option_a' => true,
'option_b' => false,
],
'user.edit' =>
[
'option_a' => true,
'option_b' => true,
],
/* ... */
];
?>
So in your middleware handle function you do something like this:
public function handle($request, Closure $next)
{
$route = $request->route()->getName();
/* Check if there is a permission to this route in config file */
if (\Config::get("permissions.{$route}")) {
$options = \Config::get("permissions.{$route}");
/* Here will be your options by route */
/* Now it's up to you do what you want */
} else {
/* If not, use default options */
}
return $next($request);
}
BTW, I recommend you using Laracast Discuss foruns:
www.laracast.com/discuss

Related

How to get subdomain inside RouteServiceProvider map() method while phpunit testing?

Is there a way to get the subdomain while running phpunit test in Laravel?
And more specific in this method:
app/Providers/RouteServiceProvider.php
/**
* Define the routes for the application.
*
* #return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
// inside here get subdomain while running a phpunit test??
So if I make some dummy test like this:
/** #test */
public function user_can_see_welcome_page()
{
$response = $this->call('GET', 'https://subdomain.domain.com');
$response->assertStatus(200);
}
I want to get the subdomain inside that map() method of RouteServiceProvider
In order to change the domain you can add it to the phpunit.xml to set it globally:
<php>
<env name="APP_URL" value="https://subdomain.domain.com"/>
</php>
But discussing your problem in the comments, this is the actual solution to your problem:
Multiple route files:
You could split each subdomain in its own map method and route file and register them in your RouteServiceProvider.
for each subdomain:
protected function mapSubDomainRoutes()
{
Route::group([
'middleware' => 'web',
'domain' => 'subdomain.domain.com',
'namespace' => $this->namespace,
], function () {
require base_path('routes/subdomain.domain.php');
});
}
Single route file:
Or if you have everything inside one route file you can wrap the routes inside a group:
Route::group(['domain' => ['subdomain.domain.com']], function () {
// domain specific routes
});

Call to a member function named() on null in laravel 5.7

I have this route
Route::get('/books/science', 'BookController#science')->name('scientific');
When I wanted to check this condition (according to document: Inspecting The Current Route)
public function handle($request, Closure $next)
{
if ($request->route()->named('scientific')) {
//
}
return $next($request);
}
I got this error. Also when I add "Route"
use Illuminate\Support\Facades\Route;
and dump these codes
dd(Route::currentRouteName());
dd(\Request::route());
I get null
$request->route() will return null in Global Middlewares.
Add your middleware in a different group as
protected $middlewareGroups = [
'web' => [
...
YOUR_MIDDLEWARE::class,
]
]
or add it in $routeMiddleware.
protected $routeMiddleware = [
...
'your_middleware' => YOUR_MIDDLEWARE::class,
];
and apply it to your route,
Route::middleware('your_middleware')->group(function () {
Route::get('/books/science', 'BookController#science')->name('scientific');
});

Returning a controller in Laravel 5.6 route file

I am trying to route a request to a controller method. When I do this it works:
Route::get('/path', 'controller#method');
I would like to set the locale before calling the controller. I tried different options and nothing works:
Route::get('/path', function(){
desired_function();
return action('controller#method');
});
and
Route::get('/path', function(){
desired_function();
return [
'uses' => 'controller#method'
];
});
What am I missing?
1) Create a app/Http/Middleware/SetLocale.php with content:
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class SetLocale
{
public function handle(Request $request, Closure $next)
{
\App::setLocale('en'); // or get it from request
// or:
// $request->attributes->set('locale', 'en');
// in action: $request->get('locale');
return $next($request);
}
}
2) Attach it to route:
Route::get('/path', 'controller#method')
->middleware('App\Http\Middleware\SetLocale');
or to route group:
Route::group([
'middleware' => [
'App\Http\Middleware\SetLocale'
]
],
function() {
Route::get('/path', 'controller#method');
});
if You want it to be used globally everywhere:
in app/Http/Kernel.php :
/**
* The application's global HTTP middleware stack.
*
* #var array
*/
protected $middleware = [
...
'App\Http\Middleware\SetLocale' // add it to end of array
];

Laravel Broadcast - Combining multiple middleware (web, auth:api)

I am using Laravel Event Broadcast and Pusher to utilize websockets both on my API and Web. If I try them individually, both work fine. What I mean is:
Broadcast::routes(['middleware' => 'web']); // this works for my Laravel website
Broadcast::routes(['middleware' => 'auth:api']); // this works for my api
However, if I want to use both at the same time like this:
Broadcast::routes(['middleware' => ['auth:api', 'web']]); // doesn't work
... it crashes for both, which I suspect that it is assuming I am trying to enable for both auth:api && web middlewares.
Is there a way to use an OR kind of statement for this (auth::api || 'web')? What if I want to use both at the same time and if it passes one middleware, it bypasses the middleware.
Please note that I am using Laravel Passport for my api.
Or is there a way to combine and creating a mixed middleware for both (which will essentially check for either api or web)? So I can use something like this maybe:
Broadcast::routes(['middleware' => 'broadcast']); // or auth:broadcast
Update:
As far as I understand, if I create a new Middleware called broadcast, I can do:
class BroadcastMiddleware() {
public function handle() {
$web = Auth::guard('web')->user();
if ($web) {
return response()->json($web);
}
$api = Auth::guard('api')->user();
if ($api) {
return response()->json($api);
}
return response()->json('Unauthorized.', 500);
}
}
But then how do I change /broadcasting/auth route? If I try this:
Route::post('/realtime/auth', function(){
return true;
})->middleware('broadcast');
This returns the user object info, however instead, it should return something like: auth:"374f7ff42b7da877aa35:c29addedec281b418331a61dc3cfc74da8b108222565fa4924a8..."
Why not just use something like this in the BroadcastServiceProvider? This creates two separate endpoints with separate middleware assigned.
Broadcast::routes(['middleware' => 'web']);
Broadcast::routes(['prefix' => 'api', 'middleware' => 'api']);
I finally figured out how to do it.
I am not sure if it is the best way of achieving this, and I'd highly appreciate any improvements.
How I achieved is created a new middleware for 'web' and left the other one as it it. Here are the steps.
1) In 'BroadcastServiceProvider', left only auth:api guard for Broadcast::routes(['middleware' => 'auth:api']);.
This way, Laravel's auth:api method for authenticating broadcasting works as expected.
2) Created a middleware called "Broadcast" and mapped it in Kernel.php like so:
'broadcast' => \App\Http\Middleware\Broadcast::class
and the Broadcast.php middleware looks like this:
public function handle($request, Closure $next)
{
$web = Auth::guard('web')->user();
if ($web) {
return response()->json(\Illuminate\Support\Facades\Broadcast::auth($request));
}
return response()->json('Unauthorized.', 500);
}
3) Created a unique route other than Laravel's /broadcasting/auth in my routes>web.php
Route::post('/guard/broadcast/auth', function(\Illuminate\Support\Facades\Request $req){
return true;
})->middleware('broadcast');
4) And then only on my blade, I use it like so:
<script>
let pusher = new Pusher("{{ env('PUSHER_APP_KEY') }}", {
cluster: 'us2',
encrypted: true,
auth: {
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
}
},
authEndpoint: '{{ env('APP_URL') }}' + '/guard/broadcast/auth',
});
let channel = pusher.subscribe('private-channel.{{ Auth::user()->id }}');
channel.bind('my-event', addMessage);
function addMessage(data) {
console.log(data);
}
</script>
I'm preferable just using middleware that extends to both auth:api and web middlewares.
like what I posted in here: https://github.com/tlaverdure/laravel-echo-server/issues/266#issuecomment-365599129. So, I just maintenance 1 middleware if I wanted to change it in the future
BroadcastServiceProvider
if (request()->hasHeader('authorization')){
Broadcast::routes(['middleware' => 'auth:api']);
} else {
Broadcast::routes();
}
It is better to use prefix approach for achieve multiple authorization types.
If you will use a middleware - it is just redundant middleware.
If you will use if block (as on a code snippet below): you will face problem with routes caching, it will return 403 error becuase Laravel should cache a route with a set of middlewares.
if (request()->hasHeader('authorization')){
Broadcast::routes(['middleware' => 'auth:api']);
} else {
Broadcast::routes();
}
You even may register separate service providers for web and api to split responsibilities and it will work.
For Web
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Broadcast::routes(['middleware' => ['web']);
require base_path('routes/channels.php');
}
}
And for Api
class ApiBroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);
require base_path('routes/dam-channels.php');
}
}

How to grab URL path after locale "us_en" in Laravel 5.4?

Im am new in Laravel and Im tryig to grab in the route, everything that comes after locale ("us_en/").
This my controler
<?php
namespace App\Http\Controllers\SitePublic;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\App;
class HomeController extends Controller
{
public function index(Request $request,$account,$location) {
dd($request->path());
$loc=explode("_", $location);
$locale = $loc[0];
$lang= $loc[1];
App::setLocale($lang);
return view('homeProfPublic')->with(['account' => $account,'active' => 'home','lang'=>$lang,"locale"=>$locale]);
}
}
For now I am using $request->path() to grab everything in the route.
My Route map is like this
mySite.com/userAccount =>User's home page with English as default language
mySite.com/userAccount/us_sp =>User's home page in Spanish
mySite.com/userAccount/us_sp/contact =>User's contac page in Spanish
...
So the $request->path() could give me the full URL and I could use php explode to grab all after locale (if it is the case).
So my question is if is there some function or method already done in Laravel to solve this situation? If yes what is it?
You don't need to use $request
If you set the route properly: you can receive the parameters in your controller function
You can set either 3 routes to separate the logic in different functions or one with optional parameters
Route::get('/userAccount/{language?}/{method?}', 'YourController#index');
And then, in your controller specify default values for the parameters:
public function index($language=null, $method=null) {
list($locale, $lang) = explode('_', $language);
var_dump($locale);
var_dump($lang);
var_dump($method);
}
And your will get for the url /userAccount/us_sp/contact
string 'us' (length=2)
string 'sp' (length=2)
string 'contact' (length=7)
UPDATE You can create a middleware and process all the urls from there
php artisan make:middleware ProcessLanguageRoutes
You need to register the middleware in the app/Http/Kernel.php file
protected $middlewareGroups = [
'web' => [
...
\App\Http\Middleware\ProcessLanguageMenu::class,
],
And then, you can process the data in the handle function checking the $request variable
public function handle($request, Closure $next)
{
if ($request) { // check something
// do something
}
return $next($request);
}

Resources