Set Global Variable accessible in all controller methods Laravel 5.3 - php

I have this route
Route::group(['middleware' => 'returnphase'], function () {
Route::get('/', 'FrontendController#home')->name('homepage');
});
My middleware check in what Phase (logic is non important now) is my application, i need that the controller setting up a global variable that i can use in all methods inside FrontendController because i need to read from database some data that depend from that check:
Middleware code, i need to set a phase_id varibale that i can use in may frontend controller.
namespace Cbcc\Http\Middleware;
use Closure;
class ReturnPhaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
/**
* TODO: Phase id check logic
*/
// SETTING GLOBAL PHASE ID VARIABLE (EXAMPLE PHASE_ID = 1)
return $next($request);
}
}
My frontend controller
//FrontEndController
namespace Cbcc\Http\Controllers;
use Cbcc\Page;
use Illuminate\Http\Request;
class FrontendController extends Controller
{
public function home()
{
$page = Page::where([
['phase_id',/**** I NEED GLOBAL PHASE ID HERE SETTING BY MIDDLEWARE***/],
['type','home']
])->get()[0];
return view('frontend.index',compact('page'));
}
}
Any ideas to do that?

You might want to user Session variables. What about using flash session variables since it seems you only use this variable once :
Middleware
class ReturnPhaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
/**
* TODO: Phase id check logic
*/
$request->session()->flash('PHASE_ID', '1');
return $next($request);
}
}
Front-end controller
class FrontendController extends Controller
{
public function home(Request $request)
{
$page = Page::where([
['phase_id', $request->session()->get('PHASE_Id')],
['type','home']
])->get()[0];
return view('frontend.index',compact('page'));
}
}
The special property of flash session variables is that they get destroyed at the next request.
Reference : https://laravel.com/docs/5.4/session#flash-data

Good strategy, and without using session?
What do you think about my solution?
// Frontend Controlller
namespace Cbcc\Http\Controllers;
use Cbcc\Lib\CheckPhaseInterface;
use Cbcc\Page;
class FrontendController extends Controller
{
protected
$phase_id;
public function __construct()
{
$this->middleware(function ($request, $next) {
// this 2 lines return phase id logic, for example $check->run() return 1 (int)
$check = resolve(CheckPhaseInterface::class);
$this->phase_id = $check->run();
return $next($request);
});
}
public function home()
{
$page = $this->getPageContent();
return view('frontend.index',compact('page'));
}
protected function getPageContent()
{
return Page::where([
['phase_id',$this->phase_id],
['type','home']
])->get()[0];
}
}

Related

What is difference between Auth::onceUsingID() and Auth::setUser() in Laravel-8

I want to implement Impersonate functionality into Laravel-8 without using any package.
Only super-admin can use this functionality.
I used laravel sanctum to authenticate.
to access impersonate functionality user should be super-admin. (is_admin(boolean) flag is set into users table).
Here is my middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ImpersonateUser
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
$impersonateId = $request->cookie('x-impersonate-id');
if($request->user()->is_admin && $impersonateId) {
$user = User::findOrFail($impersonateId);
if($user->is_admin) {
return response()->json(["message" => trans("You cannot impersonate an admin account.")], 400);
}
Auth::setUser($user);
}
return $next($request);
}
}
My route file:
// Impersonate routes.
Route::middleware(['auth:sanctum', 'impersonate'])->group(function () {
// checklist routes
Route::get('checklists', [ChecklistController::class, "index"]);
});
Whether use Auth::setUser($user) is safe or I have to use Auth::onceUsingId($userId); ?
Auth::onceUsingId($userId); not working with auth::sanctum middleware. So Auth::setUser($user) is safe or not?
I used laravel to develop backend API only.(SPA)
They should be the same in terms of safety. OnceUsingId() calls setUser() in the background.
From the Illuminate\Auth\SessionGuard class
/**
* Log the given user ID into the application without sessions or cookies.
*
* #param mixed $id
* #return \Illuminate\Contracts\Auth\Authenticatable|false
*/
public function onceUsingId($id)
{
if (! is_null($user = $this->provider->retrieveById($id))) {
$this->setUser($user);
return $user;
}
return false;
}
/**
* Set the current user.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #return $this
*/
public function setUser(AuthenticatableContract $user)
{
$this->user = $user;
$this->loggedOut = false;
$this->fireAuthenticatedEvent($user);
return $this;
}
Both of these methods come from the SessionGuard though. I don't know if Sanctum implements its own version.

Make a variable available in all controllers

I am not sure the title of the question is clear, so I will try to explain it in details.
I want to execute a piece of code in every controller automatically, assign the result to a variable that will be globally accessible everywhere.
So the code that need to be run will be like this:
function getLanguage() {
session('lang') ? session('lang') : 'en';
}
$LANG = getLanguage();
In any controller I need to access that variable like this:
myModel::all($LANG);
Also in the view, it would be helpful to access that variable as well (if possible)
<div> User language: {{$LANG}}</div>
Is there any place where I can execute that piece of code automatically?
Create a middleware
Add new middleware to App\Http\Kernels $middleware property, if you want it to run on every request. You may also put into $middlewareGroups property's web key.
Your middleware's handle method will be like this
public function handle(Request $request, Closure $next)
{
Config::set('some-name.some-sub-name', session('lang') ?: 'en');
return $next($request);
}
You will be updating a config in your middleware. This config has to be set only in this middleware to prevent possible problems of shared global state. (it is also important to be unique)
Then you can use it with config('some-name.some-sub-name')
In your use-case, you should implement a global middleware which sets the locale as you wish
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\SessionManager;
use Illuminate\Contracts\Foundation\Application;
class CheckLocale
{
/**
* The application instance.
*
* #var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The session manager instance.
*
* #var \Illuminate\Session\SessionManager
*/
protected $sessionManager;
public function __construct(Application $app, SessionManager $sessionManager)
{
$this->app = $app;
$this->sessionManager = $sessionManager;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
$this->app->setLocale($this->sessionManager->get('lang', 'en'));
return $next($request);
}
}
After setting it as a global middleware, you can access it wherever you need it from a controller or view
Controller
public function foo(Application $app)
{
$lang = $app->getLocale();
}
In a Blade view
#inject('app', Illuminate\Contracts\Foundation\Application::class)
{{ $app->getLocale() }}
For any other variable, you may directly use Laravel container
In a service provider register method:
$this->app->singleton('lang', function ($app) {
return $app['session']->get('lang', 'en');
});
And wherever else
app('lang');

Middleware for custom request

I created my request in which I validate the data. Some of this data I need to convert to JSON.
For this, I decided to create middleware. But when I try to get a request in the controller, it doesn't have anything I added in the middleware.
This seems to be because it is not my own request 'MyRequest $request' that gets into the middleware. How can this be resolved?
middlevare
class TransformData
{
/**
* #param $request
* #param Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$next($request);
$request->merge(['user_id' => \Auth::user()->id]);
$request->merge(['select_products' => json_encode($request->select_products)]);
return $request;
}
}
my request is called OfferRequest, there are just validation rules
controller
class BaseController extends Controller
{
public function __construct()
{
$this->middleware('transform.offer.data')->only('store');
}
}
public function store(OfferRequest $request)
{
$all = $request->all();
dd($all); // there is nothing here that I added in the middleware
}
I added the middleware to the kernel - protected $routeMiddleware
Update your Middleware like the following :
TransformData.php
class TransformData
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$request->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
return $next($request);
}
}
The above would work, but I would suggest you to use prepareForValidation() method link in form request class for update request data.
Using prepareForValidation() method you could add or update your request parameters.
OfferRequest.php
class AccountFilterRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
//your rules
}
/**
* Modify the input.
*/
public function prepareForValidation()
{
$this->merge([
'user_id' => \Auth::user()->id,
'select_products' => json_encode($request->select_products)
]);
}
}
prepareForValidation() method is executed before validation so you can add new data or update data and validate those.

Create group route with Closure in PHP

How can I create group routing in PHP with Closure? I'm creating my own REST API from scratch in PHP for practice and learning.
In bootstrap file i call App class:
$app = new App/App();
$app->import('routes.php');
I have routes.php file with:
$app->group('/api/v1', function() use ($app)
{
$app->group('/users', function() use ($app)
{
$app->get('/', 'User', 'index');
$app->post('/', 'User', 'post');
$app->get('/{id}', 'User', 'get');
$app->put('/{id}', 'User', 'put');
$app->delete('/{id}', 'User', 'delete');
});
});
It needs to create routes like this:
/api/v1/users/
/api/v1/users/
/api/v1/users/{id}
/api/v1/users/{id}
/api/v1/users/{id}
App class:
class App
{
public function group($link, Closure $closure)
{
$closure();
}
}
And it sets routes like this:
/
/
/{id}
/{id}
/{id}
What should I do to prefix urls ? How can I "foreach" these other $app->get(), $app->post() method callings ?
Figured it out! Added DI container to App class which handles Router, Route and RouteGroup classes. PHP SLIM framework was my inspiration - https://github.com/slimphp/Slim/tree/3.x/Slim
First I call group() method from App class with calls pushGroup() method from Router class. Then I invoke RouteGroup class with $group();. After that I cal popGroup() to return only last route group.
When adding groups url to Route, just run processGroups() method in Router class to add prefix links.
App class
/**
* Route groups
*
* #param string $link
* #param Closure $closure
* #return void
*/
public function group($link, Closure $closure)
{
$group = $this->container->get('Router')->pushGroup($link, $closure);
$group();
$this->container->get('Router')->popGroup();
}
Router class
/**
* Process route groups
*
* #return string
*/
private function processGroups()
{
$link = '';
foreach ($this->route_groups as $group) {
$link .= $group->getUrl();
}
return $link;
}
/**
* Add a route group to the array
* #param string $link
* #param Closure $closure
* #return RouteGroup
*/
public function pushGroup($link, Closure $closure)
{
$group = new RouteGroup($link, $closure);
array_push($this->route_groups, $group);
return $group;
}
/**
* Removes the last route group from the array
*
* #return RouteGroup|bool The RouteGroup if successful, else False
*/
public function popGroup()
{
$group = array_pop($this->route_groups);
return ($group instanceof RouteGroup ? $group : false);
}
Route class is basic class with routing parameters - method, url, controller, action and additional parameters so I won't copy it here.
RouteGroup class
/**
* Create a new RouteGroup
*
* #param string $url
* #param Closure $closure
*/
public function __construct($url, $closure)
{
$this->url = $url;
$this->closure = $closure;
}
/**
* Invoke the group to register any Routable objects within it.
*
* #param Slinky $app The App to bind the callable to.
*/
public function __invoke()
{
$closure = $this->closure;
$closure();
}

How can I use laravel 5.1 middleware parameter for multiple auth and protected routes?

I'm new to laravel 5.1.
How can I use middleware parameter to protect my admin routes from users ?
something like this:
Route::group(['middleware' => 'auth:admin'], function()
/* Admin only Routes*/
{
//////
});
I have a field "role" in my "users" table that get two values:
1 for admin
2 for users
In my application, users, have their protected route.
I don't want to use packages.
You can do something like this. Inject the Guard class, then use it to check the user. You dont need to pass the parameter really. Just name your middleware 'admin' or something. The following middleware will check if the current user's role is admin, and if not, redirect to another route. You can do whatever you prefer on failure.
<?php
namespace Portal\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Admin
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($this->auth->user()->role != 'admin') {
return redirect()->route('not-an-admin');
}
return $next($request);
}
}
In case you do want to pass the parameter, you can do this:
public function handle($request, Closure $next, $role)
{
if($this->auth->user()->role != $role) {
return redirect()->route('roles-dont-match');
}
return $next($request);
}

Categories