Laravel Middleware gibt die Variable an den Controller zurück

85

Ich führe eine Berechtigungsprüfung für einen Benutzer durch, um festzustellen, ob er eine Seite anzeigen kann oder nicht. Dazu muss die Anforderung zuerst über eine Middleware weitergeleitet werden.

Das Problem, das ich habe, ist, dass ich dieselbe Datenbankabfrage in der Middleware und im Controller dupliziere, bevor ich die Daten an die Ansicht selbst zurückgebe.

Hier ist ein Beispiel für das Setup.

- route.php

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

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

Wie Sie sehen können, Page::with('users')->where('id', $id)->first()wird dies sowohl in der Middleware als auch im Controller wiederholt. Ich muss die Daten von einem zum anderen weitergeben, um sie nicht zu duplizieren.

Alex
quelle
Ich wollte so etwas fragen, brauchte lange, um diese Antwort zu finden. Hier ist meine Frage. Ich werde es hier aus SEO / Findability-Gründen hinzufügen, hoffe, das ist in Ordnung: Laravel 5.0 - Modell in Middleware UND Controller laden. Wie lade ich eine Instanz des Benutzermodells, damit dieselbe Instanz (nur eine Datenbankabfrage) sowohl in der Middleware als auch im Controller verfügbar ist? Weil ich in der Middleware überprüfen möchte, ob der Benutzer autorisiert ist, und im Controller möchte ich möglicherweise Informationen über den Benutzer präsentieren oder den Benutzer irgendwie manipulieren.
Alieninlondon

Antworten:

133

Ich glaube, der richtige Weg, dies zu tun (in Laravel 5.x), besteht darin, Ihre benutzerdefinierten Felder zur Attributeigenschaft hinzuzufügen.

Aus den Quellcode-Kommentaren können wir ersehen, dass Attribute für benutzerdefinierte Parameter verwendet werden:

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

Sie würden dies also wie folgt implementieren:

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

Sie können das Attribut dann abrufen, indem Sie Folgendes aufrufen:

\Request::get('myAttribute');

Oder vom Anforderungsobjekt in Laravel 5.5+

 $request->get('myAttribute');
Gaz_Edge
quelle
1
Wie greifen Sie dann auf die Attribute im empfangenden Controller zu?
user985366
1
Fügen Sie die Anforderungsklasse zu den Argumenten der Controller-Methode (IoC-Container) hinzu oder rufen Sie die statische Klasse \ Request
Gaz_Edge
8
$ myAttribute = \ Request :: get ('myAttribute');
Shawn C.
4
Wow, diese Lösung sieht sehr sauber aus!
schellingerht
3
Sie können auch verwenden $request->request->add(['myAttribute' => 'myValue']);, um die Kurzschrift Magic Getter verwenden zu können$request->myAttribute
jonan.pineda
29

Anstelle von benutzerdefinierten Anforderungsparametern können Sie dem Kontrollinversionsmuster folgen und die Abhängigkeitsinjektion verwenden.

Registrieren Sie in Ihrer Middleware Ihre PageInstanz:

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

Erklären Sie dann, dass Ihr Controller eine PageInstanz benötigt:

class PagesController 
{
    protected $page;

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

Laravel löst die Abhängigkeit automatisch auf und instanziiert Ihren Controller mit der PageInstanz, die Sie in Ihrer Middleware gebunden haben.

Crishoj
quelle
1
Dies ist eine wirklich gute Idee. Ich habe einen Dienstanbieter erstellt und dann einen Dienstcontainer registriert. Auf diese Weise würde ich bei Bedarf nur einige Abhängigkeiten einfügen. Viel sauberer und transparenter. Vielen Dank!
Arian Acosta
1
@ArianAcosta Bitte, können Sie eine Antwort mit Ihrem Weg ausarbeiten? Ich meine, wie man die Abhängigkeitsinjektion verwendet und wie sie mit der Middleware verbunden ist.
JCarlosR
4
@ JCarlos Sicher! Die Idee wäre, eine benutzerdefinierte Service Container-Klasse zu haben, die als interne Eigenschaften die Daten enthält, die Sie zwischen der Middleware und dem Controller weitergeben müssen. Wenn Sie diesen Service Container als Singleton mit $ this-> app-> singleton (...) registrieren, ist er bei jeder Injektion immer dieselbe Instanz. Im Wesentlichen würden Sie es also zuerst in die Middleware einfügen (indem Sie es einfach als Argument benötigen), dann die Daten darin ablegen und es schließlich in der Steuerung benötigen, wo Sie auf die Daten zugreifen können. Siehe laravel.com/docs/5.4/container viel Glück
Arian Acosta
2
Dies ist eine großartige Antwort ... ordentlich! :)
Pietro
5
Anmerkung: In __constructor funktioniert es nicht, da die Middleware nach dem Konstruktor des Controllers geladen wurde. Sie können DI jedoch in jeder Aktion des Controllers verwenden.
Serhii Topolnytskyi
18

In laravel> = 5 können Sie $request->mergein der Middleware Folgendes verwenden :

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

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

    return $next($request);
}

Und in der Steuerung:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}
Vinicius
quelle
11
Warum sollten Sie Request::instance()statisch zugreifen , anstatt zu verwenden $request?
JJOK
16

Laravel 5.7

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

und

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

Wie in einem der obigen Kommentare für Laravel 5.3.x erwähnt

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

Funktioniert nicht Das Einstellen der Variablen in der Middleware funktioniert jedoch

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

Ich könnte die Daten damit in meinem Controller abrufen

$request->get('key');
Tariq Khan
quelle
6

Ich bin sicher, wenn es möglich wäre, Daten von einer Middleware an einen Controller zu übergeben, wäre dies in der Laravel-Dokumentation enthalten.

Schauen Sie sich dies und das an , es könnte helfen.

Kurz gesagt, Sie können Ihre Daten auf das Anforderungsobjekt zurücksetzen, das an die Middleware übergeben wird. Das macht auch die Laravel-Authentifizierungsfassade.

In Ihrer Middleware können Sie also Folgendes haben:

$request->myAttribute = "myValue";
Noman Ur Rehman
quelle
Danke @norman - das ist eine gute Lösung und ich wusste nicht, dass du es schaffen kannst ...! Ich habe mich gefragt, ob ich zu diesem Zeitpunkt überhaupt Middleware verwenden soll, aber anscheinend sollte ich das tun. In der Dokumentation wird nichts dergleichen erwähnt.
Alex
1
@Alex Ja, ich denke, wenn es ein allgemeiner Code ist, der in jeder Controller-Aktion ausgeführt wird, ist es keine schlechte Idee, ihn als Middleware zu implementieren.
Noman Ur Rehman
5

Es ist sehr einfach:

Hier ist Middleware-Code:

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

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

    return $next($request);
}

und hier ist Controller-Code:

$request->customVar;
Ashfaq Muhammad
quelle
2

Wenn Ihre Website CMS-Seiten enthält, die aus der Datenbank abgerufen werden und deren Titel im Kopf- und Fußzeilenblock auf allen Seiten der Laravel-Anwendung angezeigt werden sollen, verwenden Sie Middleware. Schreiben Sie den folgenden Code in Ihre 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);

}

Gehen Sie dann zu Ihrer header.blade.php und footer.blade.php und schreiben Sie den folgenden Code, um Links von cms-Seiten hinzuzufügen:

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

Vielen Dank an alle und viel Spaß mit dem Code :)

Kamlesh
quelle
1

Ich spreche kein Englisch, also ... Entschuldigung für mögliche Fehler.

Sie können hierfür die IoC-Bindung verwenden. In Ihrer Middleware können Sie dies tun, um die $ page-Instanz zu binden:

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

Danach rufen Sie in Ihrem Controller diese Instanz auf:

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

Die App :: -Instanz instanziiert die Klasse nicht erneut, sondern gibt die zuvor verbindliche Instanz zurück.

Carlos Porter
quelle
1

Ich konnte dem Request-Objekt Werte hinzufügen mit:

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

und holen Sie sie zu einem späteren Zeitpunkt zurück mit:

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

Dies ist möglich , weil laravels Antrag erstreckt symfonys anfordern , die das Attribut „ Attribute $ “ vom Typ ParameterBag , die zu halten , sollten benutzerdefinierte Parameter .

Ich denke, dies sollte eine bewährte Methode sein, um Daten an nachfolgende Middleware, Controller oder einen anderen Ort zu übergeben, an dem auf das Request-Objekt zugegriffen werden kann.

Getestet mit Laravel 5.6 , aber wahrscheinlich auch mit anderen Versionen.

suud
quelle
1

$requestist das Array, so dass wir dem Array einfach Wert und Schlüssel hinzufügen und den $requestmit diesem Schlüssel im Controller abrufen können.

$request['id'] = $id;

Durgesh Pandey
quelle