วิธีการควบคุมการเข้าถึงจากตัวควบคุมอื่นใน Laravel 5


162

ฉันมีสองตัวควบคุมและSubmitPerformanceControllerPrintReportController

ในฉันมีวิธีที่เรียกว่าPrintReportControllergetPrintReport

จะเข้าถึงวิธีนี้ได้SubmitPerformanceControllerอย่างไร

คำตอบ:


364

คุณสามารถเข้าถึงวิธีการควบคุมของคุณเช่นนี้:

app('App\Http\Controllers\PrintReportController')->getPrintReport();

สิ่งนี้จะใช้งานได้ แต่ไม่ดีในแง่ของการจัดระเบียบโค้ด (อย่าลืมใช้เนมสเปซที่เหมาะสมสำหรับคุณPrintReportController)

คุณสามารถขยายPrintReportControllerดังนั้นSubmitPerformanceControllerจะสืบทอดวิธีนั้น

class SubmitPerformanceController extends PrintReportController {
     // ....
}

แต่นี้ยังจะได้รับมรดกวิธีการอื่น ๆ PrintReportControllerทั้งหมดจาก

วิธีที่ดีที่สุดคือการสร้างtrait(เช่นในapp/Traits) ใช้ตรรกะที่นั่นและบอกให้ผู้ควบคุมของคุณใช้:

trait PrintReport {

    public function getPrintReport() {
        // .....
    }
}

บอกผู้ควบคุมของคุณให้ใช้คุณลักษณะนี้:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

ทั้งสองวิธีSubmitPerformanceControllerมีgetPrintReportวิธีการเพื่อให้คุณสามารถเรียกใช้$this->getPrintReport();จากภายในตัวควบคุมหรือโดยตรงเป็นเส้นทาง (ถ้าคุณแมปในroutes.php)

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับลักษณะที่นี่


10
ไฟล์ที่รวมถึงลักษณะที่ควรจะถูกบันทึกไว้ที่ไหน?
Brainmaniac

24
app('App\Http\Controllers\PrintReportController')->getPrintReport();app(PrintReportController::class')->getPrintReport()สามารถเปลี่ยนเป็น น้ำยาทำความสะอาดสำหรับฉัน
Vincent Decaux

ไฟล์ลักษณะที่เก็บอยู่ที่ไหน?
Eric McWinNEr

@EricMcWinNEr สามารถจัดเก็บได้ทุกที่ที่คุณต้องการเช่นสมมติว่า App \ Traits แต่ให้แน่ใจว่าใช้เนมสเปซที่เหมาะสมในลักษณะนั้น
ศาล

1
ตัวอย่างเล็ก ๆ น้อย ๆ สำหรับการใช้คุณลักษณะใน Laravel: develodesign.co.uk/news/…
Erenor Paz

48

หากคุณต้องการวิธีการนั้นในตัวควบคุมอื่นนั่นหมายความว่าคุณจำเป็นต้องทำให้เป็นนามธรรมและทำให้มันสามารถใช้ซ้ำได้ ย้ายการนำไปใช้นั้นลงในคลาสบริการ (ReportingService หรือสิ่งที่คล้ายกัน) แล้วแทรกลงในคอนโทรลเลอร์ของคุณ

ตัวอย่าง:

class ReportingService
{
  public function getPrintReport()
  {
    // your implementation here.
  }
}
// don't forget to import ReportingService at the top (use Path\To\Class)
class SubmitPerformanceController extends Controller
{
  protected $reportingService;
  public function __construct(ReportingService $reportingService)
  {
     $this->reportingService = $reportingService;
  }

  public function reports() 
  {
    // call the method 
    $this->reportingService->getPrintReport();
    // rest of the code here
  }
}

ทำเช่นเดียวกันสำหรับคอนโทรลเลอร์อื่นที่คุณต้องการใช้งาน การเข้าถึงวิธีการควบคุมจากคอนโทรลเลอร์อื่นเป็นกลิ่นรหัส


คุณจะบันทึกคลาสนี้ในแง่ของโครงสร้างโครงการที่ไหน
Amitay

1
อาจเป็นServicesโฟลเดอร์หากโปรเจ็กต์ไม่ใหญ่หรือมีฟีเจอร์ที่เรียกว่าReportingเป็นโปรเจ็กต์ที่ใหญ่กว่าและใช้Folders By Featureโครงสร้าง
Ruffles

คุณอ้างถึงผู้ให้บริการ (ระดับบริการ) เช่นที่นี่laravel.com/docs/5.7/providersหรือ Service Container เช่นที่นี่laravel.com/docs/5.7/containerหรือไม่
Baspa

1
@Baspa ไม่คลาส PHP ปกติ
Ruffles

27

ไม่แนะนำให้เรียกตัวควบคุมจากตัวควบคุมอื่นอย่างไรก็ตามถ้าด้วยเหตุผลใดก็ตามที่คุณต้องทำคุณสามารถทำสิ่งนี้ได้:

วิธีที่เข้ากันได้ของ Laravel 5

return \App::call('bla\bla\ControllerName@functionName');

หมายเหตุ:สิ่งนี้จะไม่อัปเดต URL ของหน้า

มันจะดีกว่าที่จะเรียกเส้นทางแทนและปล่อยให้มันเรียกตัวควบคุม

return \Redirect::route('route-name-here');

2
ทำไมถึงไม่แนะนำ
brunouno

นี่ควรเป็นคำตอบที่ดีที่สุด
Justin Vincent

13

คุณไม่ควร มันเป็นรูปแบบการต่อต้าน หากคุณมีวิธีในตัวควบคุมหนึ่งที่คุณต้องการเข้าถึงในตัวควบคุมอื่นนั่นเป็นสัญญาณที่คุณต้องพิจารณาปัจจัยอีกครั้ง

พิจารณาการคำนวณหาวิธีอื่นในคลาสบริการซึ่งคุณสามารถสร้างอินสแตนซ์ในคอนโทรลเลอร์หลายตัวได้ ดังนั้นหากคุณต้องการเสนอรายงานการพิมพ์สำหรับหลายรุ่นคุณสามารถทำสิ่งนี้:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}

10
\App::call('App\Http\Controllers\MyController@getFoo')

11
แม้ว่าคำตอบของคุณอาจถูกต้อง แต่ก็เป็นการดีที่จะขยายออกไปเล็กน้อยและให้คำอธิบายเพิ่มเติม
scana

9

ก่อนอื่นการร้องขอวิธีการควบคุมจากคอนโทรลเลอร์อื่นคือ EVIL สิ่งนี้จะทำให้เกิดปัญหาที่ซ่อนอยู่มากมายในวงจรชีวิตของ Laravel

อย่างไรก็ตามมีวิธีแก้ไขมากมายสำหรับการทำเช่นนั้น คุณสามารถเลือกได้หลายวิธี

กรณีที่ 1) ถ้าคุณต้องการโทรตามคลาส

วิธีที่ 1) วิธีที่ง่าย

แต่คุณไม่สามารถเพิ่มพารามิเตอร์หรือการรับรองความถูกต้องด้วยวิธีนี้

app(\App\Http\Controllers\PrintReportContoller::class)->getPrintReport();

วิธีที่ 2) แบ่งลอจิกคอนโทรลเลอร์เป็นบริการ

คุณสามารถเพิ่มพารามิเตอร์และอะไรก็ได้ด้วยสิ่งนี้ ทางออกที่ดีที่สุดสำหรับชีวิตการเขียนโปรแกรมของคุณ คุณสามารถทำแทนRepositoryService

class PrintReportService
{
    ...
    public function getPrintReport() {
        return ...
    }
}

class PrintReportController extends Controller
{
    ...
    public function getPrintReport() {
        return (new PrintReportService)->getPrintReport();
    }
}

class SubmitPerformanceController
{
    ...
    public function getSomethingProxy() {
        ...
        $a = (new PrintReportService)->getPrintReport();
        ...
        return ...
    }
}

กรณีที่ 2) หากคุณต้องการโทรตามเส้นทาง

วิธีที่ 1) ใช้MakesHttpRequestsคุณลักษณะที่ใช้ในการทดสอบหน่วยแอปพลิเคชัน

ฉันแนะนำสิ่งนี้หากคุณมีเหตุผลพิเศษในการสร้างพร็อกซีนี้คุณสามารถใช้พารามิเตอร์และส่วนหัวที่กำหนดเองได้ นอกจากนี้จะเป็นการร้องขอภายในเป็น laravel (ขอ HTTP ปลอม) ท่านสามารถดูรายละเอียดเพิ่มเติมสำหรับcallวิธีการในที่นี่

class SubmitPerformanceController extends \App\Http\Controllers\Controller
{
    use \Illuminate\Foundation\Testing\Concerns\MakesHttpRequests;

    protected $baseUrl = null;
    protected $app = null;

    function __construct()
    {
        // Require if you want to use MakesHttpRequests
        $this->baseUrl = request()->getSchemeAndHttpHost();
        $this->app     = app();
    }

    public function getSomethingProxy() {
        ...
        $a = $this->call('GET', '/printer/report')->getContent();
        ...
        return ...
    }
}

อย่างไรก็ตามนี่ไม่ใช่วิธีที่ 'ดี' เช่นกัน

วิธีที่ 2) ใช้ไคลเอนต์ guzzlehttp

นี่เป็นทางออกที่น่ากลัวที่สุดที่ฉันคิด คุณสามารถใช้พารามิเตอร์และส่วนหัวที่กำหนดเองได้เช่นกัน แต่นี่จะเป็นการร้องขอ http พิเศษภายนอก ดังนั้น HTTP Webserver จึงต้องทำงาน

$client = new Client([
    'base_uri' => request()->getSchemeAndhttpHost(),
    'headers' => request()->header()
]);
$a = $client->get('/performance/submit')->getBody()->getContents()

ในที่สุดฉันก็ใช้วิธีที่ 1 ของกรณีที่ 2 ฉันต้องการพารามิเตอร์และ


1
ไม่ควรเขียนวิธีที่ 2 ลงไปที่นั่นคุณไม่ต้องการ http ด้วยตนเองแม้แต่ในโครงสร้างโค้ดที่ไม่ดี
Sw0ut

5
namespace App\Http\Controllers;

//call the controller you want to use its methods
use App\Http\Controllers\AdminController;

use Illuminate\Http\Request;

use App\Http\Requests;

class MealController extends Controller
   {
      public function try_call( AdminController $admin){
         return $admin->index();   
    }
   }

7
โปรดแก้ไขด้วยข้อมูลเพิ่มเติม รหัสเท่านั้นและคำตอบ "ลองนี้" จะหมดกำลังใจเพราะพวกเขาไม่มีเนื้อหาที่ค้นหาได้และไม่อธิบายว่าทำไมคนควร "ลองนี้"
abarisone

2

คุณสามารถใช้วิธีการคงที่ใน PrintReportController แล้วเรียกมันจาก SubmitPerformanceController ดังนี้

namespace App\Http\Controllers;

class PrintReportController extends Controller
{

    public static function getPrintReport()
    {
      return "Printing report";
    }


}



namespace App\Http\Controllers;

use App\Http\Controllers\PrintReportController;

class SubmitPerformanceController extends Controller
{


    public function index()
    {

     echo PrintReportController::getPrintReport();

    }

}

2

วิธีการนี้ยังใช้งานได้กับลำดับชั้นของไฟล์คอนโทรลเลอร์:

$printReport = new PrintReportController;

$prinReport->getPrintReport();

ฉันชอบวิธีการนี้เมื่อเทียบกับแอพ :: สร้างเพราะวิธีการบอกประเภทบล็อก doc ยังคงทำงานใน phpStorm ด้วยวิธีนี้
Floris

1

นี่คือลักษณะที่จำลองการทำงานของคอนโทรลเลอร์อย่างสมบูรณ์โดยเราเตอร์ laravel (รวมถึงการสนับสนุนของมิดเดิลแวร์และการฉีดพึ่งพา) ทดสอบกับรุ่น 5.4 เท่านั้น

<?php

namespace App\Traits;

use Illuminate\Pipeline\Pipeline;
use Illuminate\Routing\ControllerDispatcher;
use Illuminate\Routing\MiddlewareNameResolver;
use Illuminate\Routing\SortedMiddleware;

trait RunsAnotherController
{
    public function runController($controller, $method = 'index')
    {
        $middleware = $this->gatherControllerMiddleware($controller, $method);

        $middleware = $this->sortMiddleware($middleware);

        return $response = (new Pipeline(app()))
            ->send(request())
            ->through($middleware)
            ->then(function ($request) use ($controller, $method) {
                return app('router')->prepareResponse(
                    $request, (new ControllerDispatcher(app()))->dispatch(
                    app('router')->current(), $controller, $method
                )
                );
            });
    }

    protected function gatherControllerMiddleware($controller, $method)
    {
        return collect($this->controllerMidlleware($controller, $method))->map(function ($name) {
            return (array)MiddlewareNameResolver::resolve($name, app('router')->getMiddleware(), app('router')->getMiddlewareGroups());
        })->flatten();
    }

    protected function controllerMidlleware($controller, $method)
    {
        return ControllerDispatcher::getMiddleware(
            $controller, $method
        );
    }

    protected function sortMiddleware($middleware)
    {
        return (new SortedMiddleware(app('router')->middlewarePriority, $middleware))->all();
    }
}

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

class CustomController extends Controller {
    use RunsAnotherController;

    public function someAction() 
    {
        $controller = app()->make('App\Http\Controllers\AnotherController');

        return $this->runController($controller, 'doSomething');
    }
}

คำนึงถึงว่าการทำapp()->make(......)นั้นมีค่าเท่ากับapp(......)ดังนั้นจึงสั้นกว่า
matiaslauriti

1

คุณสามารถเข้าถึงคอนโทรลเลอร์ได้โดยการสร้างอินสแตนซ์และเรียกใช้ doAction: (ใส่use Illuminate\Support\Facades\App;ก่อนการประกาศคลาสคอนโทรลเลอร์)

$controller = App::make('\App\Http\Controllers\YouControllerName');
$data = $controller->callAction('controller_method', $parameters);

โปรดทราบด้วยการทำเช่นนี้คุณจะไม่ใช้งานมิดเดิลแวร์ใด ๆ ที่ประกาศในตัวควบคุมนั้น


-2

ตอบกลับช้า แต่ฉันได้ค้นหาสิ่งนี้มาระยะหนึ่งแล้ว ตอนนี้เป็นไปได้ในวิธีที่ง่ายมาก

ไม่มีพารามิเตอร์

return redirect()->action('HomeController@index');

ด้วยพารามิเตอร์

return redirect()->action('UserController@profile', ['id' => 1]);

เอกสาร: https://laravel.com/docs/5.6/responses#redirecting-controller- ปฏิกิริยา

ย้อนกลับไปใน 5.0 มันต้องการเส้นทางทั้งหมดตอนนี้มันง่ายกว่ามาก


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