Laravel Middleware restituisce la variabile al controller


90

Sto eseguendo un controllo delle autorizzazioni su un utente per determinare se può visualizzare una pagina o meno. Ciò comporta il passaggio della richiesta prima attraverso un middleware.

Il problema che ho è che sto duplicando la stessa query di database nel middleware e nel controller prima di restituire i dati alla vista stessa.

Ecco un esempio della configurazione;

- route.php

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

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

Come puoi vedere, Page::with('users')->where('id', $id)->first()viene ripetuto sia nel middleware che nel controller. Devo passare i dati dall'uno all'altro in modo da non duplicarli.


Stavo per chiedere più o meno la stessa cosa, mi ci è voluto molto tempo per trovare questa risposta. Ecco la mia domanda. Lo aggiungerò qui per motivi SEO / reperibilità, spero che sia ok: Laravel 5.0 - Carica il modello nel middleware E nel controller. Come si carica un'istanza del modello Users in modo che la stessa istanza (una sola query di database) sia disponibile sia nel middleware che nel controller? Perché nel middleware voglio verificare se l'utente è autorizzato e nel controller potrei voler presentare informazioni sull'utente o manipolare l'utente in qualche modo.
alieninlondon

Risposte:


140

Credo che il modo corretto per farlo (in Laravel 5.x) sia aggiungere i campi personalizzati alla proprietà degli attributi.

Dai commenti del codice sorgente, possiamo vedere che gli attributi vengono utilizzati per i parametri personalizzati:

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

Quindi dovresti implementarlo come segue;

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

È quindi possibile recuperare l'attributo chiamando:

\Request::get('myAttribute');

O dall'oggetto richiesta in laravel 5.5+

 $request->get('myAttribute');

1
Come si accede quindi agli attributi all'interno del controller ricevente?
user985366

1
aggiungi la classe di richiesta agli argomenti del metodo del controller (contenitore IoC) o chiama la classe statica \ Request
Gaz_Edge

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

4
Wow, questa soluzione sembra molto pulita!
schellingerht

3
Puoi anche usare $request->request->add(['myAttribute' => 'myValue']);per essere in grado di usare la stenografia magic getter$request->myAttribute
jonan.pineda

33

Invece di parametri di richiesta personalizzati, puoi seguire il modello di inversione del controllo e utilizzare l'inserimento delle dipendenze.

Nel tuo middleware, registra la tua Pageistanza:

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

Quindi dichiara che il tuo controller ha bisogno di Pageun'istanza:

class PagesController 
{
    protected $page;

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

Laravel risolverà automaticamente la dipendenza e istanzerà il tuo controller con l' Pageistanza che hai associato al tuo middleware.


1
Questa è davvero una buona idea, sono andato avanti e ho creato un fornitore di servizi, quindi ho registrato un contenitore di servizi. In questo modo, quando servono alcuni attributi, inserisco solo le dipendenze. Molto più pulito e trasparente. Grazie!
Arian Acosta

1
@ArianAcosta Per favore, puoi elaborare una risposta a modo tuo? Voglio dire, come utilizzare l'inserimento delle dipendenze e come è associato al middleware.
JCarlosR

4
@JCarlos Sure! L'idea sarebbe quella di avere una classe del contenitore di servizi personalizzata che contenga come proprietà interne i dati che è necessario passare tra il middleware e il controller. Se registri quel contenitore di servizi come singleton con $ this-> app-> singleton (...), sarà sempre la stessa istanza ogni volta che lo inietti. Quindi, essenzialmente dovresti prima iniettarlo nel middleware (semplicemente richiedendolo come argomento), quindi inserire i dati al suo interno e infine richiederlo nel controller da cui puoi accedere ai dati. Vedi laravel.com/docs/5.4/container good luck
Arian Acosta

2
Questa è un'ottima risposta ... pulita! :)
Pietro

5
Nota: in __constructor non funziona, perché il middleware viene caricato dopo il costruttore del controller. Ma potresti usare DI in qualsiasi azione del controller.
Serhii Topolnytskyi

19

In laravel> = 5 puoi usare $request->mergenel middleware:

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

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

    return $next($request);
}

E nel controller:

public function index(Request $request)
{

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

11
Perché dovresti accedere in Request::instance()modo statico, invece di utilizzare $request?
jjok

18

Laravel 5.7

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

e

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

8

Come accennato in uno dei commenti sopra per laravel 5.3.x

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

Non funziona Ma l'impostazione della variabile in questo modo nel middleware funziona

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

Potrei recuperare i dati usando questo nel mio controller

$request->get('key');

6

Sono sicuro che se fosse possibile trasferire i dati da un middleware a un controller, sarebbe nella documentazione di Laravel.

Dai un'occhiata a questo e questo , potrebbe aiutare.

In breve, puoi trasportare i tuoi dati sull'oggetto richiesta che viene passato al middleware. Anche la facciata di autenticazione Laravel lo fa.

Quindi, nel tuo middleware, puoi avere:

$request->myAttribute = "myValue";

Grazie @norman - questa è una buona soluzione e non sapevo che potessi farlo ...! Mi chiedevo se dovessi usare il middleware a questo punto, ma sembra che dovrei. La documentazione non menziona nulla del genere. Grazie ancora
Alex

1
@Alex Sì, penso che se è un pezzo di codice comune che viene eseguito in ogni azione del controller, non è una cattiva idea implementarlo come middleware.
Noman Ur Rehman

5

È molto semplice:

Ecco il codice middleware:

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

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

    return $next($request);
}

ed ecco il codice del controller:

$request->customVar;

2

Se il tuo sito web ha pagine cms che vengono recuperate dal database e vuoi mostrare i loro titoli nell'intestazione e nel blocco piè di pagina su tutte le pagine dell'applicazione laravel, usa il middleware. Scrivi sotto il codice nel tuo middleware:

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

}

Quindi vai al tuo header.blade.php e footer.blade.php e scrivi sotto il codice per aggiungere link alle pagine 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>

Grazie mille a tutti e godetevi il codice :)


1

non parlo inglese, quindi ... scusa per possibili errori.

È possibile utilizzare l'associazione IoC per questo. Nel tuo middleware puoi eseguire questa operazione per associare l'istanza di $ page:

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

Dopo, nel tuo controller chiami quell'istanza:

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

L'App :: instance non reistanza la classe, ma restituisce l'istanza precedente al binding.


1

Sono stato in grado di aggiungere valori all'oggetto richiesta con:

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

e recuperarli in un secondo momento con:

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

Ciòèpossibile perché laravels Request estende symfonys Request che ha l'attributo " $ attributes " di tipo ParameterBag che è destinato a contenere parametri personalizzati .

Penso che questa dovrebbe essere la migliore pratica per passare i dati al middleware successivo, ai controller o qualsiasi altro luogo in cui è possibile accedere all'oggetto richiesta.

Testato con Laravel 5.6 , ma probabilmente funzionante anche con altre versioni.


1

$requestè l'array in modo che possiamo semplicemente aggiungere valore e chiave all'array e ottenere $requestcon questa chiave nel controller.

$request['id'] = $id;

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.