Laravel 中间件将变量返回给控制器

我正在对用户进行权限检查,以确定他们是否可以查看网页。这涉及到首先通过一些中间件传递请求。

我遇到的问题是,在将数据返回给视图本身之前,我在中间件和控制器中复制了相同的数据库查询。

下面是一个设置的例子;

路线 php

Route::get('pages/{id}', [
'as' => 'pages',
'middleware' => 'pageUser'
'uses' => 'PagesController@view'
]);

—— PageUserMiddleware.php (类 PageUserMiddleware)

public function handle($request, Closure $next)
{
//get the page
$pageId = $request->route('id');
//find the page with users
$page = Page::with('users')->where('id', $pageId)->first();
//check if the logged in user exists for the page
if(!$page->users()->wherePivot('user_id', Auth::user()->id)->exists()) {
//redirect them if they don't exist
return redirect()->route('redirectRoute');
}
return $next($request);
}

—— PagesController.php

public function view($id)
{
$page = Page::with('users')->where('id', $id)->first();
return view('pages.view', ['page' => $page]);
}

如您所见,Page::with('users')->where('id', $id)->first()在中间件和控制器中都是重复的。我需要将数据从一个传递到另一个,这样就不会重复。

76789 次浏览

I am sure if it was possible to pass data from a middleware to a controller then it would be in the Laravel documentation.

Have a look at this and this, it might help.

In short, you can piggy back your data on the request object which is being passed to the middleware. The Laravel authentication facade does that too.

So, in your middleware, you can have:

$request->myAttribute = "myValue";

I believe the correct way to do this (in Laravel 5.x) is to add your custom fields to the attributes property.

From the source code comments, we can see attributes is used for custom parameters:

 /**
* Custom parameters.
*
* @var \Symfony\Component\HttpFoundation\ParameterBag
*
* @api
*/
public $attributes;

So you would implement this as follows;

$request->attributes->add(['myAttribute' => 'myValue']);

You can then retrieved the attribute by calling:

\Request::get('myAttribute');

Or from request object in laravel 5.5+

 $request->get('myAttribute');

i don't speak english, so... sorry for possible errors.

You can use the IoC binding for this. In your middleware you can do this for binding $page instance:

\App::instance('mi_page_var', $page);

After, in your controller you call that instance:

$page = \App::make('mi_page_var');

The App::instance not re-instance the class, instead return the instance previusly binding.

Instead of custom request parameters, you can follow the inversion-of-control pattern and use dependency injection.

In your middleware, register your Page instance:

app()->instance(Page::class, $page);

Then declare that your controller needs a Page instance:

class PagesController
{
protected $page;


function __construct(Page $page)
{
$this->page = $page;
}
}

Laravel will automatically resolve the dependency and instantiate your controller with the Page instance that you bound in your middleware.

In Laravel >= 5 you can use $request->merge in the middleware.

public function handle($request, Closure $next)
{
$request->merge(["myVar" => "1234"]);
    

return $next($request);
}

And in the controller

public function index(Request $request)
{
$myVar = $request->myVar;
...
}

As mentioned in one of the comments above for laravel 5.3.x

$request->attributes->add(['key => 'value'] );

Doesn't work. But setting the variable like this in the middleware works

$request->attributes->set('key', 'value');

I could fetch the data using this in my controller

$request->get('key');

$request is the array so that we can just add value and key to the array and get the $request with this key in the controller.

$request['id'] = $id;

I was able to add values to the Request-object with:

$request->attributes->set('key', 'value');

and get them back at a later point with:

$request->attributes->get('key');

This is possible because laravels Request extends symfonys Request which has the attribute "$attributes" of type ParameterBag that is intended to hold custom parameters.

I think this should be Best Practice to pass data to subsequent Middleware, Controllers or any other place where it's possible to access the Request-object.

Tested with Laravel 5.6, but probably also working with other versions.

It is very simple:

Here is middleware code:

public function handle($request, Closure $next)
{


$request->merge(array("customVar" => "abcde"));


return $next($request);
}

and here is controller code:

$request->customVar;

Laravel 5.7

// in Middleware register instance
app()->instance('myObj', $myObj);

and

// to get in controller just use the resolve helper
$myObj = resolve('myObj');

If your website has cms pages which are being fetched from database and want to show their titles in the header and footer block on all pages of laravel application then use middleware. Write below code in your middleware:

namespace App\Http\Middleware;


use Closure;


use Illuminate\Support\Facades\DB;


public function handle($request, Closure $next)
{


$data = DB::table('pages')->select('pages.id','pages.title')->where('pages.status', '1')->get();


\Illuminate\Support\Facades\View::share('cms_pages', $data);


return $next($request);


}

Then goto your header.blade.php and footer.blade.php and write below code to add links of cms pages:

<a href="\{\{ url('/') }}">Home</a> |


@foreach ($cms_pages as $page)


<a href="\{\{ url('page/show/'.$page->id) }}">\{\{ $page->title }}</a> |


@endforeach


<a href="\{\{ url('contactus') }}">Contact Us</a>

Thanks a lot to all and enjoy the code :)

The accepted answer is the best practice and I will expand as to why.

There are multiple ways to add parameters to the request, as some suggested you can do:

$request->merge(["foo" => "bar"]);

The problem with this, is it will add your parameter to the query of the request. If later on in your code you try to get the query parameters of the url and do:

$request->query();

You will unexpectedly obtain the added parameter above that is actually not in the query of the current url, this could introduce bugs in your code, as an example if you are building some link with the full path of the current url and query string.

To avoid such a side effect its best to store custom parameters in the attributes property of the request.

$request->attributes->add(["foo" => "bar"])

Note that if you do above you CANNOT obtain the parameter by using magic methods such as:

$request->foo // Does not work, returns null!

You need to use the get() method on the request:

$request->get('foo'); // works!