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


88

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

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

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

- 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, чтобы один и тот же экземпляр (только один запрос к базе данных) был доступен как в промежуточном программном обеспечении, так и в контроллере? Потому что в промежуточном программном обеспечении я хочу проверить, авторизован ли пользователь, а в контроллере я могу захотеть представить информацию о пользователе или как-то манипулировать им.
alieninlondon

Ответы:


138

Я считаю, что правильный способ сделать это (в 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');

1
Как тогда получить доступ к атрибутам в принимающем контроллере?
user985366

1
добавить класс запроса в аргументы метода контроллера (контейнер IoC) или вызвать статический класс \ Request
Gaz_Edge

8
$ myAttribute = \ Request :: get ('myAttribute');
Шон С.

4
Вау, это решение выглядит очень чистым!
schellingerht

3
Вы также можете использовать, $request->request->add(['myAttribute' => 'myValue']);чтобы иметь возможность использовать сокращение magic getter$request->myAttribute
jonan.pineda

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 Конечно! Идея состоит в том, чтобы иметь собственный класс контейнера службы, который в качестве внутренних свойств хранит данные, которые необходимо передавать между промежуточным программным обеспечением и контроллером. Если вы зарегистрируете этот сервисный контейнер как синглтон с помощью $ this-> app-> singleton (...), тогда он всегда будет одним и тем же экземпляром каждый раз, когда вы его вводите. Итак, по сути, вы сначала должны внедрить его в промежуточное ПО (просто потребовав его в качестве аргумента), затем поместите в него данные и, наконец, потребуйте их в контроллере, где вы можете получить доступ к данным. См. Laravel.com/docs/5.4/container удачи
Ариан Акоста

2
Это ОТЛИЧНЫЙ ответ ... аккуратно! :)
Пьетро

5
Замечание: в __constructor это не работает, потому что промежуточное ПО загружается после конструктора контроллера. Но вы можете использовать DI в любом действии контроллера.
Сергей Топольницкий

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 07

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

Спасибо @norman - это хорошее решение, и я не знал, что у тебя это получится ...! Я задавался вопросом, следует ли мне вообще использовать промежуточное программное обеспечение на данном этапе, но, похоже, я должен. В документации ничего подобного не упоминается. Еще раз спасибо
Alex

1
@Alex Да, я думаю, что если это обычный фрагмент кода, который выполняется в каждом действии контроллера, неплохо было бы реализовать его как промежуточное ПО.
Номан Ур Рехман,

5

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

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

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

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

    return $next($request);
}

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

$request->customVar;

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, который имеет атрибут « $ attributes » типа ParameterBag, который предназначен для хранения пользовательских параметров .

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

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


1

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

$request['id'] = $id;

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.