Повернення змінної Laravel Middleware до контролера


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

Відповіді:


136

Я вважаю, що правильний спосіб зробити це (у Laravel 5.x) - це додати власні поля до властивості атрибутів.

З коментарів вихідного коду ми бачимо, що атрибути використовуються для користувацьких параметрів:

 /**
 * 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) або викликати статичний клас \ Запит
Gaz_Edge

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

4
Ого, це рішення виглядає дуже чисто!
schellingerht

3
Ви також можете скористатися, $request->request->add(['myAttribute' => 'myValue']);щоб мати змогу скористатися скороченням магічного геттера$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 Звичайно! Ідея полягала б у створенні спеціального класу Service Container, який зберігає як внутрішні властивості дані, які потрібно передати між проміжним програмним забезпеченням та контролером. Якщо ви зареєструєте цей контейнер послуг як синглтон за допомогою $ 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

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 - це хороше рішення, і я не знав, що ти можеш це зробити ...! Я розпитував, чи варто взагалі використовувати проміжне програмне забезпечення, але, схоже, слід. У документації нічого подібного не згадується. Ще раз спасибі
Алекс

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 :: не повторно інсталює клас, замість цього повертає екземпляр, попередньо прив’язаний.


1

Я зміг додати значення до об'єкта Request за допомогою:

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

і поверніть їх пізніше за допомогою:

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

Це можливо, оскільки запит laravels розширює запит symfonys, який має атрибут " $ attributes " типу ParameterBag , призначений для зберігання нестандартних параметрів .

Я вважаю, що це повинна бути найкраща практика для передачі даних наступним проміжним програмним забезпеченням, контролерам або будь-якому іншому місці, де можна отримати доступ до об’єкта запиту.

Протестовано за допомогою Laravel 5.6 , але, ймовірно, також працює з іншими версіями.


1

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

$request['id'] = $id;

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.