Переменная, возвращаемая контроллеру Laravel Middleware

Я провожу проверку разрешений для пользователя, чтобы определить, могут ли они просматривать страницу или нет. Это включает в себя передачу запроса через некоторое промежуточное программное обеспечение.

У меня проблема в том, что я дублирую один и тот же запрос к базе данных в промежуточном программном обеспечении и в контроллере перед возвратом данных в само представление.

Вот пример настройки;

- 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() повторяется как в промежуточном программном обеспечении, так и в контроллере. Мне нужно передать данные от одного к другому, чтобы не дублировать.


person Alex    schedule 13.05.2015    source источник
comment
Я собирался спросить примерно то же самое, мне потребовалось много времени, чтобы найти этот ответ. Вот мой вопрос. Я добавлю его сюда из соображений SEO / поиска, надеюсь, что все в порядке: Laravel 5.0 - Загрузить модель в промежуточное ПО и контроллер. Как загрузить экземпляр модели Users, чтобы один и тот же экземпляр (только один запрос к базе данных) был доступен как в промежуточном программном обеспечении, так и в контроллере? Потому что в промежуточном программном обеспечении я хочу проверить, авторизован ли пользователь, а в контроллере я могу захотеть представить информацию о пользователе или каким-то образом манипулировать им.   -  person    schedule 27.10.2015


Ответы (11)


Я считаю, что правильный способ сделать это (в Laravel 5.x) - добавить свои настраиваемые поля в свойство attributes.

Из комментариев к исходному коду мы видим, что атрибуты используются для настраиваемых параметров:

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

Итак, вы бы реализовали это следующим образом:

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

Затем вы можете получить атрибут, вызвав:

\Request::get('myAttribute');

Или из объекта запроса в laravel 5.5+

 $request->get('myAttribute');
person Gaz_Edge    schedule 16.07.2015
comment
Я изменил это на правильный ответ. Хотя Норман был прав, похоже, это соответствует лучшим практикам Laravel. Спасибо - person Alex; 26.08.2015
comment
Как тогда получить доступ к атрибутам в принимающем контроллере? - person user985366; 26.02.2016
comment
добавить класс запроса в аргументы метода контроллера (контейнер IoC) или вызвать статический класс \ Request - person Gaz_Edge; 26.02.2016
comment
$ myAttribute = \ Request :: get ('myAttribute'); - person Shawn C.; 28.03.2016
comment
Вау, это решение выглядит очень чистым! - person schellingerht; 12.05.2016
comment
по какой-то причине $request->attributes->add(['key => 'value'] ); у меня не работал, но $request->attributes->set('key', 'value') работал, я мог получить данные с помощью $request->get('key'), - person developernaren; 12.08.2016
comment
Какую версию laravel вы используете? - person Gaz_Edge; 12.08.2016
comment
Что, если кто-то захочет добавить объект, а не просто строковое значение? - person Hafiz; 26.10.2016
comment
Это просто массив, поэтому объект можно добавить так же, как и для любого массива. - person Gaz_Edge; 26.10.2016
comment
Не могу заставить это работать - значение все еще пустое. Laravel 5.3.4. Попытка использовать код в ответе и пример @developernaren :( - person Gediminas; 16.02.2017
comment
@Gediminas, можешь ли ты разместить где-нибудь свой фрагмент кода? У меня он отлично работает. Убедитесь, что промежуточное ПО вызывается при вызове контроллера. - person developernaren; 16.02.2017
comment
@Gaz_Edge Большое спасибо! Использование Laravel 5.4.23 и размещение массивов / объектов внутри атрибутов работает как шарм! - person Vinay Vissh; 30.06.2017
comment
Вы также можете использовать $request->request->add(['myAttribute' => 'myValue']);, чтобы иметь возможность использовать сокращение magic getter $request->myAttribute - person jonan.pineda; 17.11.2017
comment
Это действительно хорошая практика? Разве объект запроса не должен представлять только то, что мы получили от браузера? - person Olle Härstedt; 08.09.2020

Вместо настраиваемых параметров запроса вы можете следовать шаблону инверсии управления и использовать внедрение зависимостей.

В промежуточном программном обеспечении зарегистрируйте свой Page экземпляр:

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

Затем объявите, что вашему контроллеру нужен экземпляр Page:

class PagesController 
{
    protected $page;

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

Laravel автоматически разрешит зависимость и создаст экземпляр вашего контроллера с экземпляром Page, который вы связали в своем промежуточном программном обеспечении.

person crishoj    schedule 23.10.2015
comment
Это действительно хорошая идея, я пошел дальше и создал поставщика услуг, а затем зарегистрировал контейнер службы. Таким образом, когда требовались некоторые атрибуты, я просто вставлял зависимости. Намного чище и прозрачнее. Спасибо! - person Arian Acosta; 16.10.2016
comment
@ArianAcosta Пожалуйста, не могли бы вы дать ответ по-своему? Я имею в виду, как использовать внедрение зависимостей и как это связано с промежуточным программным обеспечением. - person JCarlosR; 27.04.2017
comment
@JCarlos Конечно! Идея состоит в том, чтобы иметь собственный класс контейнера службы, который в качестве внутренних свойств хранит данные, которые необходимо передавать между промежуточным программным обеспечением и контроллером. Если вы зарегистрируете этот сервисный контейнер как синглтон с помощью $ this- ›app-› singleton (...), тогда он всегда будет одним и тем же экземпляром каждый раз, когда вы его вводите. Итак, по сути, вы должны сначала внедрить его в промежуточное программное обеспечение (просто потребовав его в качестве аргумента), затем поместите в него данные и, наконец, потребуйте их в контроллере, где вы можете получить доступ к данным. См. laravel.com/docs/5.4/container удачи - person Arian Acosta; 29.04.2017
comment
Это ОТЛИЧНЫЙ ответ ... аккуратно! :) - person Pietro; 19.09.2017
comment
Замечание: в __constructor это не работает, потому что промежуточное ПО загружается после конструктора контроллера. Но вы можете использовать DI в любом действии контроллера. - person Serhii Topolnytskyi; 28.03.2019
comment
@SerhiiTopolnytskyi В моем случае это работает в конструкторе. - person hizmarck; 19.03.2020

В 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');
    ...
}
person Vinicius    schedule 07.11.2015
comment
Зачем вам обращаться к Request::instance() статически, а не использовать $request? - person jjok; 07.09.2017

Laravel 5.7

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

а также

// to get in controller just use the resolve helper
$myObj = resolve('myObj');
person Илья Зеленько    schedule 12.10.2018

Как упоминалось в одном из комментариев выше для laravel 5.3.x

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

Не работает. Но установка такой переменной в промежуточном программном обеспечении работает

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

Я мог бы получить данные, используя это в своем контроллере

$request->get('key');
person Tariq Khan    schedule 27.09.2017

Я уверен, что если бы можно было передавать данные из промежуточного программного обеспечения в контроллер, это было бы в документации Laravel.

Взгляните на это и this, это может помочь.

Короче говоря, вы можете скопировать свои данные в объект запроса, который передается промежуточному программному обеспечению. Фасад аутентификации Laravel тоже делает это.

Итак, в вашем промежуточном программном обеспечении вы можете иметь:

$request->myAttribute = "myValue";
person Noman Ur Rehman    schedule 13.05.2015
comment
Спасибо @norman - это хорошее решение, и я не знал, что у вас это получится ...! Я задавался вопросом, следует ли мне вообще использовать промежуточное ПО на данном этапе, но, похоже, я должен. В документации ничего подобного не упоминается. Спасибо еще раз - person Alex; 13.05.2015
comment
@Alex Да, я думаю, что если это обычный фрагмент кода, который выполняется в каждом действии контроллера, неплохо было бы реализовать его как промежуточное ПО. - person Noman Ur Rehman; 13.05.2015

Это очень просто:

Вот код промежуточного программного обеспечения:

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

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

    return $next($request);
}

и вот код контроллера:

$request->customVar;
person Ashfaq Muhammad    schedule 07.08.2018

Если на вашем веб-сайте есть 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>

Большое спасибо всем и наслаждайтесь кодом :)

person Kamlesh    schedule 19.01.2019

$request - это массив, поэтому мы можем просто добавить значение и ключ к массиву и получить $request с этим ключом в контроллере.

$request['id'] = $id;

person Durgesh Pandey    schedule 15.11.2017

Я не говорю по-английски, так что ... извините за возможные ошибки.

Для этого вы можете использовать привязку IoC. В вашем промежуточном программном обеспечении вы можете сделать это для привязки экземпляра $ page:

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

После этого в вашем контроллере вы вызываете этот экземпляр:

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

App :: instance не повторно создает экземпляр класса, а возвращает предыдущую привязку экземпляра.

person Carlos Porter    schedule 18.08.2015

Мне удалось добавить значения к объекту запроса с помощью:

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

и вернуть их позже с помощью:

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

Это возможно, потому что запрос laravels расширяется < href = "https://github.com/symfony/http-foundation/blob/master/Request.php" rel = "nofollow noreferrer"> запрос symfonys с атрибутом "$ attributes" типа ParameterBag, предназначенный для хранения специальные параметры.

Я думаю, что это должна быть Лучшая практика для передачи данных последующему промежуточному программному обеспечению, контроллерам или любому другому месту, где можно получить доступ к объекту запроса.

Протестировано с Laravel 5.6, но, вероятно, также работает с другими версиями.

person suud    schedule 06.07.2018