Laravel 5 : passing a Model parameter to the middleware - laravel

I would like to pass a model parameter to a middleware. According to this link (laravel 5 middleware parameters) , I can just include an extra parameter in the handle() function like so :
public function handle($request, Closure $next, $model)
{
//perform actions
}
How would you pass it in the constructor of the Controller? This isn't working :
public function __construct(){
$model = new Model();
$this->middleware('myCustomMW', $model);
}
**NOTE : ** it is important that I could pass different Models (ex. ModelX, ModelY, ModelZ)

First of all make sure that you're using Laravel 5.1. Middleware parameters weren't available in prior versions.
Now I don't believe you can pass an instantiated object as a parameter to your middleware, but (if you really need this) you can pass a model's class name and i.e. primary key if you need a specific instance.
In your middleware:
public function handle($request, Closure $next, $model, $id)
{
// Instantiate the model off of IoC and find a specific one by id
$model = app($model)->find($id);
// Do whatever you need with your model
return $next($request);
}
In your controller:
use App\User;
public function __construct()
{
$id = 1;
// Use middleware and pass a model's class name and an id
$this->middleware('myCustomMW:'.User::class.",$id");
}
With this approach you can pass whatever models you want to your middleware.

A more eloquent way of resolving this problem is to create a constructor method in the middleware, inject the model(s) as dependencies, pass them to class variables, and then utilize the class variables in the handle method.
For authority to validate my response, see app/Http/Middleware/Authenticate.php in a Laravel 5.1 installation.
For middleware MyMiddleware, model $myModel, of class MyModel, do as follows:
use App\MyModel;
class MyMiddleware
{
protected $myModel;
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function handle($request, Closure $next)
{
$this->myModel->insert_model_method_here()
// and write your code to manipulate the model methods
return $next($request);
}
}

You don't need to pass the model to middleware, Because you already have access to model instance inside the middleware!
Lets say we have a route like this:
example.test/api/post/{post}
now in our middleware if we want to have access to that post dynamically we go like this
$post = $request->route()->parameter('post');
now we can use this $post, for example $post->id will give us the id of the post, or $post->replies will give us the replies belong to the post.

Related

Get a route prefix variable in the abstract base controller

So we are building a CMS for a school project and we need to make it dynamic in the way that there are multiple organisations. In the prefix of a group I add a $gid variable that represents the ID of this organisation.
Route::middleware('auth')->prefix('{$gid}/admin')->group(function()
So I also created an abstract base controller that every controller in the admin section will inherit.
abstract class BaseAdminController extends Controller
What I want now is the $gid variable from the route and parse it to the base controller in the constructor.
protected $gid;
public function __construct() {
$this->gid = .....;
}
Now I can access this ID everywhere instead of having to parse it to each individual controller and function.
You don't need to "parse" it in every action but you should keep it because otherwise you wouldn't be able to access the routes' other parameters because the first one is missing and Laravel wouldn't be able to assign the next one's correctly. And you don't need the dollar sign at the start of a parameter.
// routes/web.php
// ↓ No dollar sign here
Route::middleware('auth')->prefix('{group}/admin')->group(function() {
// Routes
});
// In any of your Controllers
use Illuminate\Http\Request:
class SomeController extends BaseController
{
public function index(Request $request, Group $group) // you might add more parameters here
{
// Access your $group model
}
}
If you don't need the group model, don't define the type of the parameter and the related data will not be loaded:
public function index(Request $request, $group) // Now $group is just a number

Access session data from parent controller w/o passing it in

Can I access session data from Controller, without passing the request from MyController?
class Controller extends BaseController
{
public function __construct()
{
// ** next line throws error:
// "Session store not set on request."
$userdata = request()->session()->get('userdata');
// I want to inject `userdata` into every template without
// passing data from child controllers.
view()->share(['userdata' => $userdata);
}
}
class MyController extends Controller
{
public function __construct(Request $request)
{
// This works, so the data is in fact in the session.
// I don't want to pass it, or `$request` to the parent from here.
$userdata = $request->session()->get('userdata');
...
}
}
The reason it won't be working in your __construct() method is because the StartSession middleware won't have been run yet.
To get around this you can simply use the middleware() method on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$userdata = $request->session()->get('userdata');
view()->share(compact('userdata'));
return $next($request);
});
}
Laravel 5.3 Upgrade guide (Scroll down the Controllers section)
In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly in your controller's constructor.

Setting the route parameter in the middleware in Laravel

I am developing a Laravel application. I am doing route model binding in the middleware.
I have the routes like this
Route::group([ 'prefix' => 'department/{department}', 'middleware' => [ 'auth.department' ] ], function () {
Route::post('employee/create', 'EmployeeController#store')->name('employees.store');
});
This is my auth.department middleware (AuthDepartment)
class AuthDepartment
{
public function handle($request, Closure $next)
{
$department = Department::find($request->department);
//do something with the department
//I want to set the $department (Department model) in the place of {department} in the route.
return $next($request);
}
}
This is EmployeeController
class EmployeeController extends Controller {
public function store($department)
{
}
}
As you can see in the code, I am using $department parameter to get the department id from the route. But instead of getting the integer as the parameter, I want to bind the model like this.
class EmployeeController extends Controller {
public function store(Department $department)
{
}
}
With my current code, it is not working. I tried to set the route parameter in the middleware as follow to match (bind model) the value in the action.
$request->route()->setParameter('department', $department)
But it is just not working. How can I set/ replace the route parameter with a model in the middleware which can be binded to the parameter in the action of the controller? Is it possible? What could be the better approach?
If I used
$request->route()->setParameter('department', $department)
to set the parameter, I cannot set type in the action of the controller like this.
store(Department $department)
But this is fine
store(Department $department)
But I want this
store(Department $department)
Laravel already has this built in. It is called Route Model Binding.
https://laravel.com/docs/5.7/routing#route-model-binding
Remove the middleware and instead keep your controller as it is. Laravel will automatically use the ID in the request to find the model and give you an instance of it. If the model cannot be found, Laravel will throw a 404 response for you.

Laravel 5.4 Sessions and Auth::user() not available in controller's constructor

I would like to use a User class throught the application. So, I would like to create CustomUser and then inject it into controllers that need it (it would be most of them).
Now, I create an empty instance in serviceprovider. Next, I want to fill it with data that are already saved in Auth::user(). After long time I have not found where to do it.
Auth::user() is empty in middlewares, but is filled with the user data in controllers. I am missing the step where Laravel queries the database and fills Auth:user() with data. I want to avoid making the same query again.
Thanks for any help!
You can use base controller with __get() method. For example:
class Controller
{
public function __get(string $name)
{
if($name === 'user'){
return Auth::user();
}
return null;
}
}
And in the child controllers can call $this->user
Since Laravel 5.3, you do not have access to sessions in controller constructors. This is because the middleware has not been run yet. I know it's difficult to locate, but in the migration documentation from 5.2 > 5.3 (you're probably on 5.4), it shows that the proper way to resolve data from sessions (which auth() is just a wrapper around a session() call to get the user), is to use the following method:
class MyController extends Controller {
protected $user;
public function __construct() {
$this->middleware(function ($request, $next) {
$this->user= auth()->user();
return $next($request);
});
}
}
Then $this->user will reference the auth user to any methods inside of this controller.
Hopefully his helps.
In Laravel 5.6 i used this
$this->middleware(function ($request, $next) {
$id = Auth::user()->id;
$res = $this->validateAnyFunction($id);
if(!$res){
//to redirect to any other route
return $next(redirect()->route("any")->with("failed","Invalid")->send());
}
//this is used to proccess futher funcitons of controller
return $next($request);
});

Laravel 5.3 Middleware Request Merge Not Return Any Value

All my code is working on Laravel 5.2. Now I try to upgrade to Laravel 5.3 it breaks on middleware.
// Verify Middleware
public function handle($request, Closure $next)
{
// I already make sure the data is exists
$user = User::find('abc');
if (!$user) {
return responseHandler()->unauthorized('Unauthorized');
}
$request->merge(['user_id' => $user->id, 'device' => $device]);
return $next($request);
}
// User Controller
public function __construct(Request $request)
{
var_dump($request->all());
$this->user_id = $request->user_id;
$this->device = $request->device;
}
public function getProfile(Request $request)
{
$data = User::find($this->user_id);
$result = Fractal::item($data, new UserTransformer)->getArray();
return responseHandler()->success(0, $result, 'user');
}
The problem is $this->user_id is always null. But if I request from getProfile function it return correctly.
var_dump result only the login info. it not received any merge request from middleware
On Laravel 5.2 this code is working properly. Any solution?.
Reference-: https://www.laravel.com/docs/5.3/upgrade#upgrade-5.3.0
Session In The Constructor
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly in your controller's constructor. Before using this feature, make sure that your application is running Laravel 5.3.4 or above:
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class ProjectController extends Controller
{
/**
* All of the current user's projects.
*/
protected $projects;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->projects = Auth::user()->projects;
return $next($request);
});
}
}
Of course, you may also access the request session data or authenticated user by type-hinting the Illuminate\Http\Request class on your controller action:
It seems you can't access the session data in Controller constructor because the middleware not run yet.
Refer laravel change log here
you can't access the session or authenticated user in your
controller's constructor because the middleware has not run yet.
In your case you can call session with request object
public function getProfile(Request $request)
{
$data = User::find($request->session()->get('user_id'));
$result = Fractal::item($data, new UserTransformer)->getArray();
return responseHandler()->success(0, $result, 'user');
}

Resources