Oprogramowanie pośredniczące Laravel zwraca zmienną do kontrolera

90

Sprawdzam uprawnienia użytkownika, aby określić, czy może przeglądać stronę, czy nie. Obejmuje to najpierw przekazanie żądania przez niektóre oprogramowanie pośredniczące.

Problem polega na tym, że powielam to samo zapytanie do bazy danych w oprogramowaniu pośrednim i w kontrolerze przed zwróceniem danych do samego widoku.

Oto przykład konfiguracji;

- tours.php

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

- PageUserMiddleware.php (klasa 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]);
}

Jak widać, Page::with('users')->where('id', $id)->first()operacja powtarza się zarówno w oprogramowaniu pośrednim, jak i kontrolerze. Muszę przekazywać dane z jednego do drugiego, aby ich nie powielać.

Alex
źródło
Miałem zamiar zapytać o to samo, długo znajdowałem tę odpowiedź. Oto moje pytanie. Dodam to tutaj ze względów SEO / znalezienia, mam nadzieję, że wszystko w porządku: Laravel 5.0 - Załaduj model w oprogramowaniu pośrednim ORAZ kontrolerze. Jak załadować wystąpienie modelu Users, aby to samo wystąpienie (tylko jedno zapytanie bazy danych) było dostępne zarówno w oprogramowaniu pośredniczącym, jak i kontrolerze? Ponieważ w oprogramowaniu pośrednim chcę sprawdzić, czy użytkownik jest Autoryzowany, aw Kontrolerze mogę chcieć przedstawić informacje o użytkowniku lub jakoś nim manipulować.
alieninlondon

Odpowiedzi:

140

Uważam, że właściwym sposobem na to (w Laravel 5.x) jest dodanie własnych pól niestandardowych do właściwości atrybutów.

Z komentarzy do kodu źródłowego widać, że atrybuty są używane dla parametrów niestandardowych:

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

Więc zaimplementowałbyś to w następujący sposób;

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

Następnie możesz pobrać atrybut, wywołując:

\Request::get('myAttribute');

Lub z obiektu żądania w programie Laravel 5.5+

 $request->get('myAttribute');
Gaz_Edge
źródło
1
W jaki sposób uzyskujesz dostęp do atrybutów w kontrolerze odbierającym?
user985366
1
dodaj klasę żądania do argumentów metody kontrolera (kontener IoC) lub wywołaj klasę statyczną \ Request
Gaz_Edge
8
$ myAttribute = \ Request :: get ('myAttribute');
Shawn C.
4
Wow, to rozwiązanie wygląda bardzo czysto!
schellingerht
3
Możesz także użyć, $request->request->add(['myAttribute' => 'myValue']);aby móc użyć skrótu magicznego gettera$request->myAttribute
jonan.pineda
33

Zamiast niestandardowych parametrów żądania można zastosować wzorzec inwersji kontroli i użyć iniekcji zależności.

W oprogramowaniu pośrednim zarejestruj swoją Pageinstancję:

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

Następnie zadeklaruj, że Twój kontroler potrzebuje Pageinstancji:

class PagesController 
{
    protected $page;

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

Laravel automatycznie rozwiąże zależność i utworzy instancję kontrolera z Pageinstancją związaną z oprogramowaniem pośredniczącym.

crishoj
źródło
1
To naprawdę dobry pomysł, poszedłem do przodu i stworzyłem dostawcę usług, a następnie zarejestrowałem kontener usług. W ten sposób, gdy były potrzebne jakieś atrybuty, po prostu wstrzyknąłem zależności. Dużo czystszy i przejrzysty. Dzięki!
Arian Acosta
1
@ArianAcosta Proszę, czy możesz uściślić odpowiedź na swój sposób? Mam na myśli, jak używać iniekcji zależności i jak jest to związane z oprogramowaniem pośredniczącym.
JCarlosR
4
@JCarlos Sure! Pomysł polegałby na utworzeniu niestandardowej klasy Service Container, która przechowuje jako właściwości wewnętrzne dane, które należy przekazać między oprogramowaniem pośredniczącym a kontrolerem. Jeśli zarejestrujesz ten Service Container jako singleton za pomocą $ this-> app-> singleton (...), to zawsze będzie to ta sama instancja za każdym razem, gdy ją wprowadzisz. Zasadniczo więc najpierw wprowadziłbyś go do oprogramowania pośredniego (po prostu wymagając go jako argumentu), następnie umieścił dane w nim, a na koniec wymagałby ich w kontrolerze, gdzie masz dostęp do danych. Zobacz laravel.com/docs/5.4/container good luck
Arian Acosta
2
To świetna odpowiedź ... schludnie! :)
Pietro
5
Uwaga: w __constructor to nie działa, ponieważ oprogramowanie pośrednie ładowane po konstruktorze kontrolera. Ale możesz użyć DI w dowolnej akcji kontrolera.
Serhii Topolnytskyi
19

W laravel> = 5 możesz użyć $request->mergew oprogramowaniu pośrednim:

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

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

    return $next($request);
}

A w kontrolerze:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}
Winicjusz
źródło
11
Dlaczego miałbyś uzyskać dostęp Request::instance()statyczny, zamiast używać $request?
jjok,
18

Laravel 5.7

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

i

// to get in controller just use the resolve helper
$myObj = resolve('myObj');
Илья Зеленько
źródło
8

Jak wspomniano w jednym z powyższych komentarzy dla laravel 5.3.x

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

Nie działa. Ale ustawienie takiej zmiennej w oprogramowaniu pośrednim działa

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

Mogłem pobrać dane za pomocą tego w moim kontrolerze

$request->get('key');
Tariq Khan
źródło
6

Jestem pewien, że gdyby było możliwe przekazanie danych z oprogramowania pośredniego do kontrolera, to byłoby to w dokumentacji Laravela.

Spójrz na to i to , może pomóc.

Krótko mówiąc, możesz przenieść swoje dane na obiekt żądania, który jest przekazywany do oprogramowania pośredniczącego. Fasada uwierzytelniania Laravel również to robi.

Tak więc w swoim oprogramowaniu pośrednim możesz mieć:

$request->myAttribute = "myValue";
Noman Ur Rehman
źródło
Dzięki @norman - to dobre rozwiązanie i nie wiedziałem, że możesz to zrobić ...! Pytałem, czy w ogóle powinienem używać oprogramowania pośredniczącego w tym momencie, ale wydaje mi się, że powinienem. Dokumentacja nic takiego nie wspomina. Jeszcze raz dziękuję
Alex
1
@Alex Tak, myślę, że jeśli jest to wspólny fragment kodu, który jest wykonywany w każdej akcji kontrolera, wdrożenie go jako oprogramowania pośredniego nie jest złym pomysłem.
Noman Ur Rehman
5

To bardzo proste:

Oto kod oprogramowania pośredniego:

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

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

    return $next($request);
}

a oto kod kontrolera:

$request->customVar;
Ashfaq Muhammad
źródło
2

Jeśli Twoja witryna zawiera strony cms, które są pobierane z bazy danych i chcesz wyświetlać ich tytuły w bloku nagłówka i stopki na wszystkich stronach aplikacji laravel, użyj oprogramowania pośredniego. Napisz poniższy kod w swoim oprogramowaniu pośrednim:

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

}

Następnie przejdź do plików header.blade.php i footer.blade.php i napisz poniższy kod, aby dodać linki do stron 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>

Bardzo dziękuję wszystkim i życzymy udanego kodu :)

Kamlesh
źródło
1

nie mówię po angielsku, więc ... przepraszam za możliwe błędy.

Możesz użyć do tego powiązania IoC. W swoim oprogramowaniu pośrednim możesz to zrobić, aby powiązać instancję $ page:

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

Następnie w kontrolerze wywołujesz tę instancję:

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

App :: instance nie powoduje ponownego wystąpienia klasy, zamiast tego zwraca poprzednie powiązanie wystąpienia.

Carlos Porter
źródło
1

Udało mi się dodać wartości do obiektu Request za pomocą:

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

i odzyskaj je później za pomocą:

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

Jest to możliwe, ponieważ laravels Request rozszerza żądanie symfonys, które ma atrybut „ $ attributes ” typu ParameterBag, który jest przeznaczony do przechowywania parametrów niestandardowych .

Myślę, że powinno to być najlepszą praktyką, aby przekazać dane do kolejnego oprogramowania pośredniczącego, kontrolerów lub dowolnego innego miejsca, w którym można uzyskać dostęp do obiektu żądania.

Testowany z Laravelem 5.6 , ale prawdopodobnie działa również z innymi wersjami.

suud
źródło
1

$requestjest tablicą, abyśmy mogli po prostu dodać wartość i klucz do tablicy i uzyskać $requestz tym kluczem w kontrolerze.

$request['id'] = $id;

Durgesh Pandey
źródło