Laravel Middleware mengembalikan variabel ke controller

90

Saya melakukan pemeriksaan izin pada pengguna untuk menentukan apakah mereka dapat melihat halaman atau tidak. Ini melibatkan penyampaian permintaan melalui beberapa middleware terlebih dahulu.

Masalah yang saya alami adalah saya menduplikasi kueri database yang sama di middleware dan di pengontrol sebelum mengembalikan data ke tampilan itu sendiri.

Berikut adalah contoh penyiapan;

- routes.php

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

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

Seperti yang Anda lihat, Page::with('users')->where('id', $id)->first()ini diulangi di middleware dan controller. Saya harus meneruskan data dari satu ke yang lain agar tidak menduplikasi.

Alex
sumber
Saya akan menanyakan hal yang sama, butuh waktu lama untuk menemukan jawaban ini. Inilah pertanyaan saya. Saya akan menambahkannya di sini untuk alasan SEO / kemudahan ditemukan, semoga tidak masalah: Laravel 5.0 - Muat model di middleware AND controller. Bagaimana cara memuat contoh model Pengguna sehingga contoh yang sama (hanya satu kueri database) tersedia di Middleware dan Controller? Karena di middleware saya ingin memeriksa apakah pengguna Berizin dan di Kontroler saya mungkin ingin menyajikan informasi tentang Pengguna atau memanipulasi Pengguna entah bagaimana caranya.
alieninlondon

Jawaban:

140

Saya percaya cara yang benar untuk melakukan ini (di Laravel 5.x) adalah dengan menambahkan bidang khusus Anda ke properti atribut.

Dari komentar kode sumber, kita dapat melihat atribut yang digunakan untuk parameter khusus:

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

Jadi Anda akan menerapkan ini sebagai berikut;

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

Anda kemudian dapat mengambil atribut tersebut dengan memanggil:

\Request::get('myAttribute');

Atau dari objek permintaan di laravel 5.5+

 $request->get('myAttribute');
Gaz_Edge
sumber
1
Bagaimana Anda kemudian mengakses atribut dalam pengontrol penerima?
pengguna985366
1
tambahkan kelas permintaan ke argumen metode pengontrol (wadah
IoC
8
$ myAttribute = \ Request :: get ('myAttribute');
Shawn C.
4
Wow, solusi ini terlihat sangat bersih!
schellingerht
3
Anda juga dapat menggunakan $request->request->add(['myAttribute' => 'myValue']);untuk dapat menggunakan steno pengambil sihir$request->myAttribute
jonan.pineda
33

Sebagai ganti parameter permintaan khusus, Anda dapat mengikuti pola inversi kontrol dan menggunakan injeksi ketergantungan.

Di middleware Anda, daftarkan Pageinstance Anda :

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

Kemudian nyatakan bahwa pengontrol Anda membutuhkan sebuah Pageinstance:

class PagesController 
{
    protected $page;

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

Laravel akan secara otomatis menyelesaikan ketergantungan dan membuat contoh pengontrol Anda dengan Pageinstance yang Anda ikat di middleware Anda.

crishoj
sumber
1
Ini adalah ide yang sangat bagus, saya melanjutkan dan membuat Penyedia Layanan kemudian mendaftarkan Kontainer Layanan. Dengan cara itu ketika membutuhkan beberapa atribut, saya hanya akan memasukkan dependensi. Jauh lebih bersih dan transparan. Terima kasih!
Arian Acosta
1
@ArianAcosta Tolong, bisakah Anda menjelaskan jawaban dengan cara Anda? Maksud saya, bagaimana menggunakan injeksi ketergantungan dan bagaimana itu dikaitkan dengan middleware.
JCarlosR
4
@JCarlos Tentu! Idenya adalah memiliki kelas Service Container kustom yang menampung sebagai properti internal data yang Anda butuhkan untuk diteruskan antara middleware dan pengontrol. Jika Anda mendaftarkan Service Container tersebut sebagai singleton dengan $ this-> app-> singleton (...) maka, itu akan selalu menjadi instance yang sama setiap kali Anda memasukkannya. Jadi, pada dasarnya Anda pertama-tama akan memasukkannya ke dalam middleware (dengan hanya membutuhkannya sebagai argumen), lalu meletakkan data di dalamnya, dan terakhir memerlukannya di pengontrol tempat Anda dapat mengakses data. Lihat laravel.com/docs/5.4/container semoga sukses
Arian Acosta
2
Ini adalah jawaban yang BESAR ... rapi! :)
Pietro
5
Catatan: di __constructor tidak berfungsi, karena middleware dimuat setelah konstruktor pengontrol. Tapi Anda bisa menggunakan DI dalam aksi pengontrol apa pun.
Serhii Topolnytskyi
19

Di laravel> = 5 Anda dapat menggunakan $request->mergedi middleware:

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

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

    return $next($request);
}

Dan di pengontrol:

public function index(Request $request)
{

    $myVar = $request->instance()->query('myVar');
    ...
}
Vinicius
sumber
11
Mengapa Anda mengakses Request::instance()secara statis, daripada menggunakan $request?
jjok
18

Laravel 5.7

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

dan

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

Seperti yang disebutkan dalam salah satu komentar di atas untuk laravel 5.3.x

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

Tidak berhasil. Tetapi pengaturan variabel seperti ini di middleware berfungsi

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

Saya dapat mengambil data menggunakan ini di pengontrol saya

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

Saya yakin jika mungkin untuk melewatkan data dari middleware ke controller maka itu akan ada di dokumentasi Laravel.

Coba lihat ini dan ini , mungkin bisa membantu.

Singkatnya, Anda dapat mendukung data Anda pada objek permintaan yang diteruskan ke middleware. Fasad otentikasi Laravel juga melakukannya.

Jadi, di middleware Anda, Anda dapat memiliki:

$request->myAttribute = "myValue";
Noman Ur Rehman
sumber
Terima kasih @norman - itu solusi yang bagus dan saya tidak tahu Anda bisa melakukannya ...! Saya bertanya-tanya apakah saya harus menggunakan middleware pada saat ini sama sekali, tetapi tampaknya saya harus melakukannya. Dokumentasi tidak menyebutkan hal semacam itu. Terima kasih lagi
Alex
1
@ Alex Ya, menurut saya jika ini adalah bagian kode umum yang dijalankan di setiap tindakan pengontrol, bukan ide yang buruk untuk mengimplementasikannya sebagai middleware.
Noman Ur Rehman
5

Ini sangat sederhana:

Berikut adalah kode middleware:

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

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

    return $next($request);
}

dan berikut adalah kode pengontrol:

$request->customVar;
Ashfaq Muhammad
sumber
2

Jika website Anda memiliki halaman cms yang diambil dari database dan ingin menampilkan judulnya di blok header dan footer pada semua halaman aplikasi laravel maka gunakan middleware. Tulis kode di bawah ini di middleware Anda:

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

}

Kemudian buka header.blade.php dan footer.blade.php Anda dan tulis kode di bawah ini untuk menambahkan tautan halaman 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>

Terima kasih banyak untuk semua dan nikmati kodenya :)

Kamlesh
sumber
1

saya tidak bisa berbahasa Inggris, jadi ... maaf atas kemungkinan kesalahan.

Anda dapat menggunakan pengikatan IoC untuk ini. Di middleware Anda, Anda dapat melakukan ini untuk mengikat $ page instance:

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

Setelah itu, di pengontrol Anda, Anda memanggil instance itu:

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

App :: instance tidak mengembalikan instance kelas, sebagai gantinya mengembalikan instance yang mengikat sebelumnya.

Carlos Porter
sumber
1

Saya bisa menambahkan nilai ke objek Request dengan:

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

dan mendapatkannya kembali nanti dengan:

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

Hal ini dimungkinkan karena Permintaan laravels memperluas Permintaan symfonys yang memiliki atribut " $ atribut " jenis ParameterBag yang dimaksudkan untuk menampung parameter khusus .

Saya pikir ini harus menjadi Praktik Terbaik untuk meneruskan data ke Middleware berikutnya, Pengendali atau tempat lain di mana dimungkinkan untuk mengakses objek Permintaan.

Diuji dengan Laravel 5.6 , tetapi mungkin juga bekerja dengan versi lain.

suud
sumber
1

$requestadalah array sehingga kita bisa menambahkan nilai dan kunci ke array dan mendapatkan $requestdengan kunci ini di pengontrol.

$request['id'] = $id;

Durgesh Pandey
sumber