Laravel Middleware ส่งคืนตัวแปรไปยังคอนโทรลเลอร์


90

ฉันกำลังดำเนินการตรวจสอบสิทธิ์ของผู้ใช้เพื่อตรวจสอบว่าพวกเขาสามารถดูเพจได้หรือไม่ สิ่งนี้เกี่ยวข้องกับการส่งคำขอผ่านมิดเดิลแวร์ก่อน

ปัญหาที่ฉันมีคือฉันกำลังทำสำเนาแบบสอบถามฐานข้อมูลเดียวกันในมิดเดิลแวร์และในคอนโทรลเลอร์ก่อนที่จะส่งคืนข้อมูลไปยังมุมมองนั้นเอง

นี่คือตัวอย่างของการตั้งค่า

- route.php

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

- PageUserMiddleware.php (คลาส 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]);
}

อย่างที่คุณเห็นการPage::with('users')->where('id', $id)->first()ทำซ้ำทั้งในมิดเดิลแวร์และคอนโทรลเลอร์ ฉันต้องการส่งผ่านข้อมูลจากที่หนึ่งไปยังอีกที่หนึ่งเพื่อไม่ให้ซ้ำกัน


ฉันจะถามแบบเดียวกันใช้เวลานานในการค้นหาคำตอบนี้ นี่คือคำถามของฉัน ฉันจะเพิ่มที่นี่ด้วยเหตุผลด้าน SEO / ความสามารถในการค้นหาหวังว่าจะโอเค: Laravel 5.0 - โหลดโมเดลในมิดเดิลแวร์และคอนโทรลเลอร์ ฉันจะโหลดอินสแตนซ์ของโมเดลผู้ใช้อย่างไรเพื่อให้อินสแตนซ์เดียวกัน (แบบสอบถามฐานข้อมูลเดียวเท่านั้น) พร้อมใช้งานทั้งในมิดเดิลแวร์และคอนโทรลเลอร์ เนื่องจากในมิดเดิลแวร์ฉันต้องการตรวจสอบว่าผู้ใช้นั้นได้รับอนุญาตหรือไม่และในตัวควบคุมฉันอาจต้องการนำเสนอข้อมูลเกี่ยวกับผู้ใช้หรือจัดการกับผู้ใช้อย่างใด
alieninlondon

คำตอบ:


140

ฉันเชื่อว่าวิธีที่ถูกต้องในการดำเนินการนี้ (ใน Laravel 5.x) คือการเพิ่มฟิลด์ที่กำหนดเองของคุณลงในคุณสมบัติแอตทริบิวต์

จากความคิดเห็นของซอร์สโค้ดเราสามารถเห็นแอตทริบิวต์ที่ใช้สำหรับพารามิเตอร์ที่กำหนดเอง:

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

ดังนั้นคุณจะใช้สิ่งนี้ดังนี้

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

จากนั้นคุณสามารถดึงแอตทริบิวต์โดยโทร:

\Request::get('myAttribute');

หรือจากออบเจ็กต์คำขอใน laravel 5.5+

 $request->get('myAttribute');

1
คุณจะเข้าถึงแอตทริบิวต์ภายในตัวควบคุมการรับได้อย่างไร?
user985366

1
เพิ่มคลาสคำขอไปยังอาร์กิวเมนต์เมธอดคอนโทรลเลอร์ (คอนเทนเนอร์ IoC) หรือเรียกคลาสแบบคงที่ \ Request
Gaz_Edge

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

4
ว้าววิธีนี้ดูสะอาดมาก!
schellingerht

3
นอกจากนี้คุณยังสามารถใช้$request->request->add(['myAttribute' => 'myValue']);เพื่อให้สามารถใช้ชวเลขเวทมนตร์ได้$request->myAttribute
jonan.pineda

33

แทนที่จะใช้พารามิเตอร์คำขอที่กำหนดเองคุณสามารถทำตามรูปแบบการควบคุมแบบผกผันและใช้การฉีดขึ้นต่อกัน

ในมิดเดิลแวร์ของคุณลงทะเบียนPageอินสแตนซ์ของคุณ:

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

จากนั้นประกาศว่าคอนโทรลเลอร์ของคุณต้องการPageอินสแตนซ์:

class PagesController 
{
    protected $page;

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

Laravel จะแก้ไขการพึ่งพาโดยอัตโนมัติและสร้างอินสแตนซ์คอนโทรลเลอร์ของคุณด้วยPageอินสแตนซ์ที่คุณผูกไว้ในมิดเดิลแวร์ของคุณ


1
นี่เป็นความคิดที่ดีจริงๆฉันได้ดำเนินการสร้างผู้ให้บริการจากนั้นลงทะเบียนคอนเทนเนอร์บริการ ด้วยวิธีนี้เมื่อต้องการคุณสมบัติบางอย่างฉันก็แค่ฉีดการอ้างอิง สะอาดและโปร่งใสมาก ขอบคุณ!
Arian Acosta

1
@ArianAcosta ได้โปรดคุณสามารถอธิบายคำตอบโดยละเอียดได้หรือไม่? ฉันหมายถึงวิธีการใช้การฉีดพึ่งพาและวิธีการที่เกี่ยวข้องกับมิดเดิลแวร์
JCarlosR

4
@JCarlos ชัวร์! แนวคิดคือการมีคลาส Service Container ที่กำหนดเองซึ่งถือเป็นคุณสมบัติภายในของข้อมูลที่คุณต้องการส่งผ่านระหว่างมิดเดิลแวร์และคอนโทรลเลอร์ หากคุณลงทะเบียน Service Container นั้นเป็น singleleton ด้วย $ this-> app-> singleton (... ) มันจะเป็นอินสแตนซ์เดียวกันทุกครั้งที่คุณฉีดเข้าไป ดังนั้นโดยพื้นฐานแล้วคุณจะต้องฉีดมันลงในมิดเดิลแวร์ก่อน (โดยกำหนดให้เป็นอาร์กิวเมนต์) จากนั้นใส่ข้อมูลเข้าไปในนั้นและสุดท้ายต้องใช้ในคอนโทรลเลอร์ที่คุณสามารถเข้าถึงข้อมูลได้ ดูlaravel.com/docs/5.4/container good luck
Arian Acosta

2
นี่คือคำตอบที่ดี ... เรียบร้อย! :)
Pietro

5
หมายเหตุ: ใน __constructor ไม่ทำงานเนื่องจากมิดเดิลแวร์โหลดหลังจากคอนสตรัคเตอร์ของคอนโทรลเลอร์ แต่คุณสามารถใช้ DI ในการทำงานของคอนโทรลเลอร์ได้
Serhii Topolnytskyi

19

ใน laravel> = 5 คุณสามารถใช้$request->mergeในมิดเดิลแวร์:

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

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

    return $next($request);
}

และในตัวควบคุม:

public function index(Request $request)
{

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

11
ทำไมคุณจะเข้าถึงRequest::instance()แบบคงที่มากกว่าการใช้$request?
jjok


8

ดังที่กล่าวไว้ในหนึ่งในความคิดเห็นด้านบนสำหรับ laravel 5.3.x

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

ไม่ทำงาน แต่การตั้งค่าตัวแปรเช่นนี้ในมิดเดิลแวร์ได้ผล

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

ฉันสามารถดึงข้อมูลโดยใช้สิ่งนี้ในตัวควบคุมของฉัน

$request->get('key');

6

ฉันแน่ใจว่าเป็นไปได้หรือไม่ที่จะส่งผ่านข้อมูลจากมิดเดิลแวร์ไปยังคอนโทรลเลอร์ก็จะอยู่ในเอกสารของ Laravel

ลองดูสิ่งนี้และสิ่งนี้อาจช่วยได้

ในระยะสั้นคุณสามารถสำรองข้อมูลของคุณในวัตถุคำขอซึ่งจะถูกส่งผ่านไปยังมิดเดิลแวร์ ซุ้มการรับรองความถูกต้อง Laravel ก็ทำเช่นนั้นเช่นกัน

ดังนั้นในมิดเดิลแวร์ของคุณคุณสามารถมี:

$request->myAttribute = "myValue";

ขอบคุณ @norman - นั่นเป็นทางออกที่ดีและฉันไม่รู้ว่าคุณทำได้ ... ! ฉันกำลังตั้งคำถามว่าฉันควรจะใช้มิดเดิลแวร์ในตอนนี้หรือไม่ แต่ดูเหมือนว่าฉันควร เอกสารประกอบไม่ได้กล่าวถึงการเรียงลำดับใด ๆ ขอบคุณอีกครั้ง
Alex

1
@Alex ใช่ฉันคิดว่าถ้าเป็นโค้ดทั่วไปที่ดำเนินการในทุกการกระทำของคอนโทรลเลอร์ก็ไม่ควรใช้เป็นมิดเดิลแวร์
Noman Ur Rehman

5

มันง่ายมาก:

นี่คือรหัสมิดเดิลแวร์:

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

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

    return $next($request);
}

และนี่คือรหัสควบคุม:

$request->customVar;

2

หากเว็บไซต์ของคุณมีหน้า cms ​​ที่ถูกดึงมาจากฐานข้อมูลและต้องการแสดงชื่อในส่วนหัวและส่วนท้ายของแอปพลิเคชัน laravel ทุกหน้าให้ใช้มิดเดิลแวร์ เขียนโค้ดด้านล่างในมิดเดิลแวร์ของคุณ:

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

}

จากนั้นไปที่ header.blade.php และ footer.blade.php แล้วเขียนโค้ดด้านล่างเพื่อเพิ่มลิงค์ของหน้า 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>

ขอบคุณมากสำหรับทุกคนและสนุกกับรหัส :)


1

ฉันไม่พูดภาษาอังกฤษดังนั้น ... ขออภัยสำหรับข้อผิดพลาดที่อาจเกิดขึ้น

คุณสามารถใช้การเชื่อม IoC สำหรับสิ่งนี้ ในมิดเดิลแวร์ของคุณคุณสามารถทำได้เพื่อผูกอินสแตนซ์ $ page:

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

หลังจากนั้นในคอนโทรลเลอร์ของคุณคุณเรียกอินสแตนซ์นั้น:

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

อินสแตนซ์ App :: ไม่ได้อินสแตนซ์คลาสอีกครั้ง แต่จะส่งคืนอินสแตนซ์ที่มีผลผูกพันอยู่ทั่วไปแทน


1

ฉันสามารถเพิ่มค่าให้กับ Request-object ด้วย:

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

และนำกลับมาในภายหลังด้วย:

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

สิ่งนี้เป็นไปได้เนื่องจากคำขอ laravelsขยายคำขอsymfonysซึ่งมีแอตทริบิวต์ " $ attributes " ประเภทParameterBagที่มีไว้เพื่อเก็บพารามิเตอร์ที่กำหนดเองพารามิเตอร์ที่กำหนดเอง

ฉันคิดว่านี่ควรเป็นแนวทางปฏิบัติที่ดีที่สุดในการส่งผ่านข้อมูลไปยัง Middleware ตัวควบคุมหรือที่อื่น ๆ ที่สามารถเข้าถึง Request-object ได้

ทดสอบด้วยLaravel 5.6แต่อาจใช้งานได้กับเวอร์ชันอื่น ๆ


1

$requestคืออาร์เรย์เพื่อให้เราสามารถเพิ่มค่าและคีย์ให้กับอาร์เรย์และรับ$requestด้วยคีย์นี้ในคอนโทรลเลอร์

$request['id'] = $id;

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.