Variável de retorno do Middleware Laravel para o controlador

87

Estou realizando uma verificação de permissões em um usuário para determinar se ele pode visualizar uma página ou não. Isso envolve passar a solicitação por meio de algum middleware primeiro.

O problema que tenho é que estou duplicando a mesma consulta de banco de dados no middleware e no controlador antes de retornar os dados para a própria visualização.

Aqui está um exemplo da configuração;

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

Como você pode ver, o Page::with('users')->where('id', $id)->first()é repetido no middleware e no controlador. Preciso passar os dados de um para o outro para não duplicar.

Alex
fonte
Eu ia perguntar o mesmo, levei muito tempo para encontrar essa resposta. Aqui está minha pergunta. Vou adicioná-lo aqui por motivos de SEO / localização, espero que não haja problema: Laravel 5.0 - Carregar modelo no middleware E no controlador. Como carrego uma instância do modelo Usuários de forma que a mesma instância (apenas uma consulta de banco de dados) esteja disponível tanto no Middleware quanto no Controlador? Porque no middleware eu quero verificar se o usuário está autorizado e no controlador posso querer apresentar informações sobre o usuário ou manipular o usuário de alguma forma.
alieninlondon

Respostas:

136

Acredito que a maneira correta de fazer isso (no Laravel 5.x) é adicionar seus campos personalizados à propriedade de atributos.

A partir dos comentários do código-fonte, podemos ver os atributos usados ​​para parâmetros personalizados:

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

Portanto, você implementaria isso da seguinte maneira;

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

Você pode então recuperar o atributo chamando:

\Request::get('myAttribute');

Ou do objeto de solicitação em laravel 5.5+

 $request->get('myAttribute');
Gaz_Edge
fonte
1
Como você acessa os atributos no controlador de recebimento?
user985366
1
adicione a classe de solicitação aos argumentos do método do controlador (recipiente IoC) ou chame a classe estática \ Request
Gaz_Edge
8
$ myAttribute = \ Request :: get ('myAttribute');
Shawn C.
4
Nossa, essa solução parece muito limpa!
Schellingerht
3
Você também pode usar $request->request->add(['myAttribute' => 'myValue']);para poder usar a abreviação de getter mágico$request->myAttribute
jonan.pineda
29

Em vez de parâmetros de solicitação personalizados, você pode seguir o padrão de inversão de controle e usar injeção de dependência.

Em seu middleware, registre sua Pageinstância:

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

Em seguida, declare que seu controlador precisa de uma Pageinstância:

class PagesController 
{
    protected $page;

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

O Laravel irá resolver automaticamente a dependência e instanciar seu controlador com a Pageinstância que você vinculou em seu middleware.

Crishoj
fonte
1
Esta é uma ideia muito boa, eu fui em frente e criei um provedor de serviços e registrei um contêiner de serviços. Assim, quando precisasse de alguns atributos, eu apenas injetaria as dependências. Muito mais limpo e transparente. Obrigado!
Arian Acosta,
1
@ArianAcosta Por favor, você pode elaborar uma resposta do seu jeito? Quero dizer, como usar injeção de dependência e como ela está associada ao middleware.
JCarlosR de
4
@JCarlos Claro! A ideia seria ter uma classe customizada de Service Container que contivesse como propriedades internas os dados que você precisa passar entre o middleware e o controlador. Se você registrar esse Service Container como um singleton com $ this-> app-> singleton (...), então, ele sempre será a mesma instância toda vez que você o injetar. Portanto, essencialmente, você primeiro o injetaria no middleware (simplesmente exigindo-o como um argumento), depois colocaria os dados dentro dele e, finalmente, exigiria no controlador, onde você pode acessar os dados. Consulte laravel.com/docs/5.4/container good luck
Arian Acosta
2
Esta é uma ótima resposta ... legal! :)
Pietro
5
Observação: em __constructor não funciona, pois o middleware é carregado após o construtor do controlador. Mas você pode usar DI em qualquer ação do controlador.
Serhii Topolnytskyi
18

Em laravel> = 5 você pode usar $request->mergeno middleware:

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

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

    return $next($request);
}

E no controlador:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}
Vinicius
fonte
11
Por que você Request::instance()acessaria estaticamente, em vez de usar $request?
jjok
17

Laravel 5.7

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

e

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

Conforme mencionado em um dos comentários acima para laravel 5.3.x

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

Não funciona. Mas definir a variável assim no middleware funciona

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

Eu poderia buscar os dados usando isso no meu controlador

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

Tenho certeza que se fosse possível passar dados de um middleware para um controlador então estaria na documentação do Laravel.

Ter um olhar para isso e isso , pode ajudar.

Resumindo, você pode carregar seus dados no objeto de solicitação que está sendo passado para o middleware. A fachada de autenticação do Laravel também faz isso.

Portanto, em seu middleware, você pode ter:

$request->myAttribute = "myValue";
Noman Ur Rehman
fonte
Obrigado @norman - essa é uma boa solução e eu não sabia que você conseguiria ...! Eu estava questionando se eu deveria usar middleware neste momento, mas parece que deveria. A documentação não menciona nada do tipo. Obrigado novamente
Alex
1
@Alex Sim, acho que se é um código comum que é executado em todas as ações do controlador, não é uma má ideia implementá-lo como um middleware.
Noman Ur Rehman
5

É muito simples:

Aqui está o código de middleware:

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

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

    return $next($request);
}

e aqui está o código do controlador:

$request->customVar;
Ashfaq Muhammad
fonte
2

Se o seu site tem páginas cms que estão sendo buscadas no banco de dados e deseja mostrar seus títulos no bloco de cabeçalho e rodapé em todas as páginas do aplicativo laravel, use middleware. Escreva o código abaixo em seu 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);

}

Em seguida, vá para header.blade.php e footer.blade.php e escreva o código abaixo para adicionar links de páginas 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>

Muito obrigado a todos e aproveitem o código :)

Kamlesh
fonte
1

eu não falo inglês, então ... desculpe por possíveis erros.

Você pode usar a ligação IoC para isso. Em seu middleware, você pode fazer isso para vincular a instância $ page:

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

Depois, em seu controlador, você chama essa instância:

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

O App :: instance não reinstala a classe, em vez disso retorna a vinculação anterior da instância.

Carlos Porter
fonte
1

Consegui adicionar valores ao objeto Request com:

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

e recuperá-los posteriormente com:

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

Isso é possível porque laravels Request estende Symfonys Request que tem o atributo " $ attribute " do tipo ParameterBag que se destina a manter parâmetros personalizados .

Eu acho que essa deveria ser a melhor prática para passar dados para middleware, controladores ou qualquer outro lugar onde seja possível acessar o objeto-solicitação.

Testado com Laravel 5.6 , mas provavelmente também funcionando com outras versões.

Suud
fonte
1

$requesté o array para que possamos apenas adicionar valor e chave ao array e obter o $requestcom esta chave no controlador.

$request['id'] = $id;

Durgesh Pandey
fonte