Laravel Middleware renvoie la variable au contrôleur

87

J'effectue une vérification des autorisations sur un utilisateur pour déterminer s'il peut afficher une page ou non. Cela implique d'abord de passer la requête via un middleware.

Le problème que j'ai est que je duplique la même requête de base de données dans le middleware et dans le contrôleur avant de renvoyer les données à la vue elle-même.

Voici un exemple de la configuration;

- routes.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]);
}

Comme vous pouvez le voir, le Page::with('users')->where('id', $id)->first()est répété à la fois dans le middleware et le contrôleur. J'ai besoin de transmettre les données de l'une à l'autre pour ne pas les dupliquer.

Alex
la source
J'allais demander un peu la même chose, il m'a fallu beaucoup de temps pour trouver cette réponse. Voici ma question. Je vais l'ajouter ici pour des raisons de référencement / trouvabilité, j'espère que ça va: Laravel 5.0 - Charger le modèle dans le middleware ET le contrôleur Comment charger une instance du modèle Users afin que la même instance (une seule requête de base de données) soit disponible à la fois dans le Middleware et le Controller? Parce que dans le middleware, je veux vérifier si l'utilisateur est autorisé et dans le contrôleur, je pourrais vouloir présenter des informations sur l'utilisateur ou manipuler l'utilisateur d'une manière ou d'une autre.
alieninlondon

Réponses:

136

Je pense que la bonne façon de faire cela (dans Laravel 5.x) est d'ajouter vos champs personnalisés à la propriété attributes.

À partir des commentaires du code source, nous pouvons voir que les attributs sont utilisés pour les paramètres personnalisés:

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

Vous implémenteriez donc ceci comme suit;

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

Vous pouvez ensuite récupérer l'attribut en appelant:

\Request::get('myAttribute');

Ou à partir de l'objet de requête dans laravel 5.5+

 $request->get('myAttribute');
Gaz_Edge
la source
1
Comment accédez-vous ensuite aux attributs dans le contrôleur récepteur?
user985366
1
ajouter une classe de requête aux arguments de méthode de contrôleur (conteneur IoC) ou appeler la classe statique \ Request
Gaz_Edge
8
$ myAttribute = \ Request :: get ('monAttribute');
Shawn C.
4
Wow, cette solution a l'air très propre!
schellingerht
3
Vous pouvez également utiliser $request->request->add(['myAttribute' => 'myValue']);pour pouvoir utiliser la sténographie du getter magique$request->myAttribute
jonan.pineda
29

Au lieu de paramètres de demande personnalisés, vous pouvez suivre le modèle d'inversion de contrôle et utiliser l'injection de dépendances.

Dans votre middleware, enregistrez votre Pageinstance:

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

Déclarez ensuite que votre contrôleur a besoin d'une Pageinstance:

class PagesController 
{
    protected $page;

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

Laravel résoudra automatiquement la dépendance et instanciera votre contrôleur avec l' Pageinstance que vous avez liée dans votre middleware.

Crishoj
la source
1
C'est une très bonne idée, je suis allé de l'avant et j'ai créé un fournisseur de services puis enregistré un conteneur de services. De cette façon, lorsque j'avais besoin de certains attributs, je injecterais simplement les dépendances. Beaucoup plus propre et transparent. Merci!
Arian Acosta
1
@ArianAcosta S'il vous plaît, pouvez-vous élaborer une réponse à votre façon? Je veux dire, comment utiliser l'injection de dépendances et comment elle est associée au middleware.
JCarlosR
4
@JCarlos Bien sûr! L'idée serait d'avoir une classe de conteneur de service personnalisée contenant comme propriétés internes les données que vous devez transmettre entre le middleware et le contrôleur. Si vous enregistrez ce conteneur de services en tant que singleton avec $ this-> app-> singleton (...) alors, ce sera toujours la même instance à chaque fois que vous l'injecterez. Donc, essentiellement, vous l'injecteriez d'abord dans le middleware (en l'exigeant simplement comme argument), puis vous y mettriez les données et enfin vous en demanderiez dans le contrôleur où vous pouvez accéder aux données. Voir laravel.com/docs/5.4/container bonne chance
Arian Acosta
2
C'est une excellente réponse ... soignée! :)
Pietro
5
Remarque: dans __constructor cela ne fonctionne pas, car le middleware est chargé après le constructeur du contrôleur. Mais vous pouvez utiliser DI dans n'importe quelle action du contrôleur.
Serhii Topolnytskyi
18

Dans laravel> = 5, vous pouvez utiliser $request->mergedans le middleware:

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

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

    return $next($request);
}

Et dans le contrôleur:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}
Vinicius
la source
11
Pourquoi accéderiez-vous de Request::instance()manière statique plutôt que d'utiliser $request?
jjok
17

Laravel 5.7

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

et

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

Comme mentionné dans l'un des commentaires ci-dessus pour laravel 5.3.x

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

Ça ne marche pas. Mais définir la variable comme celle-ci dans le middleware fonctionne

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

Je pourrais récupérer les données en utilisant ceci dans mon contrôleur

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

Je suis sûr que s'il était possible de transmettre des données d'un middleware à un contrôleur, ce serait dans la documentation Laravel.

Jetez un œil à ceci et cela , cela pourrait aider.

En bref, vous pouvez récupérer vos données sur l'objet de requête qui est transmis au middleware. La façade d'authentification Laravel le fait aussi.

Ainsi, dans votre middleware, vous pouvez avoir:

$request->myAttribute = "myValue";
Noman Ur Rehman
la source
Merci @norman - c'est une bonne solution et je ne savais pas que vous pouviez le faire ...! Je me demandais si je devais utiliser un middleware à ce stade, mais il semble que je devrais le faire. La documentation ne mentionne rien de tel. Merci encore
Alex
1
@Alex Oui, je pense que si c'est un morceau de code commun qui s'exécute dans chaque action du contrôleur, ce n'est pas une mauvaise idée de l'implémenter en tant que middleware.
Noman Ur Rehman
5

C'est très simple:

Voici le code middleware:

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

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

    return $next($request);
}

et voici le code du contrôleur:

$request->customVar;
Ashfaq Muhammad
la source
2

Si votre site Web a des pages cms qui sont extraites de la base de données et que vous souhaitez afficher leurs titres dans le bloc d'en-tête et de pied de page sur toutes les pages de l'application laravel, utilisez un middleware. Écrivez ci-dessous le code dans votre 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);

}

Ensuite, allez à votre header.blade.php et footer.blade.php et écrivez le code ci-dessous pour ajouter des liens de pages 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>

Merci beaucoup à tous et profitez du code :)

Kamlesh
la source
1

je ne parle pas anglais, donc ... désolé pour d'éventuelles erreurs.

Vous pouvez utiliser la liaison IoC pour cela. Dans votre middleware, vous pouvez le faire pour lier l'instance $ page:

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

Ensuite, dans votre contrôleur, vous appelez cette instance:

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

L'instance App :: ne réinstalle pas la classe, mais retourne l'instance précédemment liée.

Carlos Porter
la source
1

J'ai pu ajouter des valeurs à l'objet Request avec:

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

et récupérez-les ultérieurement avec:

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

Ceci est possible parce que laravels Request étend symfonys Request qui a l'attribut " $ attributes " de type ParameterBag qui est destiné à contenir des paramètres personnalisés .

Je pense que cela devrait être la meilleure pratique pour transmettre des données à un middleware, à des contrôleurs ou à tout autre endroit où il est possible d'accéder à l'objet de requête.

Testé avec Laravel 5.6 , mais fonctionnant probablement aussi avec d'autres versions.

suud
la source
1

$requestest le tableau afin que nous puissions simplement ajouter une valeur et une clé au tableau et obtenir le $requestavec cette clé dans le contrôleur.

$request['id'] = $id;

Durgesh Pandey
la source