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


87

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

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

这是设置示例;

--routes.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()中间件和控制器中都重复了。我需要将数据从一个传递到另一个,以免重复。


我要问的差不多,我花了很长时间才找到这个答案。这是我的问题。出于SEO /可扩展性原因,我将在此处添加它,希望可以:Laravel 5.0-中间件和控制器中的加载模型。如何加载Users模型的实例,以便在中间件和Controller中都可以使用同一实例(仅一个数据库查询)?因为在中间件中,我想检查用户是否被授权,而在控制器中,我可能想提供有关用户的信息或以某种方式操纵用户。
Alieninlondon 2015年

Answers:


136

我相信(在Laravel 5.x中)执行此操作的正确方法是将自定义字段添加到attribute属性。

从源代码注释中,我们可以看到属性用于自定义参数:

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

因此,您可以按以下方式实现此目标:

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

然后,您可以通过调用以下方法来检索属性:

\Request::get('myAttribute');

或者来自laravel 5.5+中的请求对象

 $request->get('myAttribute');

1
然后如何在接收控制器中访问属性?
user985366 '16

1
将请求类添加到控制器方法参数(IoC容器)或调用静态类\ Request
Gaz_Edge

8
$ myAttribute = \ Request :: get('myAttribute');
Shawn C.

4
哇,这个解决方案看起来很干净!
schellingerht

3
您还可以使用$request->request->add(['myAttribute' => 'myValue']);能够快速使用魔术$request->myAttribute
起子的方法

29

可以使用控制反转模式并使用依赖项注入来代替自定义请求参数。

在中间件中,注册您的Page实例:

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

然后声明您的控制器需要一个Page实例:

class PagesController 
{
    protected $page;

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

Laravel将自动解决依赖关系,并使用Page您在中间件中绑定的实例实例化您的控制器。


1
这是一个非常好的主意,我继续创建服务提供商,然后注册了服务容器。这样,当需要一些属性时,我只需注入依赖项。更清洁,更透明。谢谢!
阿里安·阿科斯塔

1
@ArianAcosta拜托,你能用你的方式来详细说明答案吗?我的意思是,如何使用依赖项注入以及它如何与中间件关联。
JCarlosR

4
@JCarlos当然!想法是要有一个自定义的Service Container类,该类将需要在中间件和控制器之间传递的数据作为内部属性。如果使用$ this-> app-> singleton(...)将服务容器注册为单例,则每次注入时,它始终是同一实例。因此,从本质上讲,您首先要将其注入中间件(只需将其作为参数),然后将数据放入其中,最后将其要求在可以访问数据的控制器中即可。参见laravel.com/docs/5.4/container祝你好运
Arian Acosta

2
这是一个很好的答案。:)
Pietro

5
注意:在__constructor中它不起作用,因为在控制器的构造函数之后加载了中间件。但是您可以在控制器的任何动作中使用DI。
Serhii Topolnytskyi

18

在laravel> = 5中,您可以$request->merge在中间件中使用:

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

    $request->merge(array("myVar" => "1234"));

    return $next($request);
}

并在控制器中:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}

11
为什么要Request::instance()静态访问而不是使用$request
jjok

17

Laravel 5.7

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

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

7

如上面对laravel 5.3.x的评论之一所述

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

不起作用 但是在中间件中像这样设置变量是可行的

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

我可以在控制器中使用它来获取数据

$request->get('key');

6

我确信如果有可能将数据从中间件传递到控制器,那么它将在Laravel文档中。

看看这个这个,它可能会有所帮助。

简而言之,您可以将数据承载在传递给中间件的请求对象上。Laravel认证外观也可以做到这一点。

因此,在您的中间件中,您可以拥有:

$request->myAttribute = "myValue";

谢谢@诺曼-那是一个很好的解决方案,我不知道你能做到...!我当时在问我是否应该使用中间件,但似乎应该。该文档未提及任何内容。再次感谢
亚历克斯

1
@Alex是的,我想如果它是在每个控制器动作中执行的通用代码,将其实现为中间件并不是一个坏主意。
Noman Ur Rehman 2015年


2

如果您的网站有正在从数据库中获取的cms页面,并且想要在laravel应用程序所有页面的页眉和页脚块中显示其标题,请使用中间件。在中间件中编写以下代码:

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);

}

然后转到您的header.blade.php和footer.blade.php并编写以下代码以添加cms页面的链接:

<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>

非常感谢大家,并喜欢代码:)


1

我不会说英语,所以...对不起,可能有错误。

您可以为此使用IoC绑定。在您的中间件中,您可以这样做来绑定$ page实例:

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

之后,在您的控制器中调用该实例:

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

App :: instance不会重新实例化该类,而是返回事先绑定的实例。


1

我能够通过以下方式将值添加到请求对象:

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

并通过以下方式将其取回:

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

这是可能的,因为laravels Request扩展了symfonys Request,该请求具有ParameterBag类型的“ $ attributes ”属性,该属性旨在保存自定义参数

我认为这是将数据传递到后续中间件,控制器或可能访问请求对象的任何其他地方的最佳实践。

使用Laravel 5.6进行了测试,但也可能与其他版本一起使用。


1

$request是数组,因此我们可以向数组中添加值和键,然后$request在控制器中使用此键获取。

$request['id'] = $id;

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.