Phương thức điều khiển truy cập từ một bộ điều khiển khác trong Laravel 5


160

Tôi có hai bộ điều khiển SubmitPerformanceControllerPrintReportController.

Trong PrintReportControllertôi có một phương pháp gọi là getPrintReport.

Làm thế nào để truy cập phương pháp này trong SubmitPerformanceController?

Câu trả lời:


363

Bạn có thể truy cập phương thức điều khiển của bạn như thế này:

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

Điều này sẽ hoạt động, nhưng nó không tốt về mặt tổ chức mã (hãy nhớ sử dụng không gian tên phù hợp cho bạn PrintReportController )

Bạn có thể mở rộng PrintReportControllervì vậy SubmitPerformanceControllersẽ kế thừa phương thức đó

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

Nhưng điều này cũng sẽ kế thừa tất cả các phương pháp khác từ PrintReportController.

Cách tiếp cận tốt nhất sẽ là tạo một trait(ví dụ trong app/Traits), triển khai logic ở đó và báo cho bộ điều khiển của bạn sử dụng nó:

trait PrintReport {

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

Nói với bộ điều khiển của bạn để sử dụng đặc điểm này:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Cả hai giải pháp SubmitPerformanceControllerđều có getPrintReportphương thức để bạn có thể gọi nó bằng $this->getPrintReport();từ bên trong bộ điều khiển hoặc trực tiếp dưới dạng tuyến đường (nếu bạn ánh xạ nó trongroutes.php )

Bạn có thể đọc thêm về đặc điểm ở đây .


10
tập tin bao gồm các đặc điểm nên được lưu ở đâu?
Brainmaniac

24
app('App\Http\Controllers\PrintReportController')->getPrintReport();có thể biến thành app(PrintReportController::class')->getPrintReport(). Giải pháp sạch cho tôi.
Vincent Decaux

Trường hợp các tập tin đặc điểm được lưu trữ?
Eric McWinNEr

@EricMcWinNEr Nó có thể được lưu trữ ở bất cứ đâu bạn thích, như giả sử Ứng dụng \ Traits. Nhưng hãy chắc chắn sử dụng không gian tên thích hợp trong đặc điểm đó.
Toà án

1
Chỉ là một ví dụ nhỏ cho việc sử dụng các đặc điểm trong Laravel: develodesign.co.uk/news/,
Erenor Paz

48

Nếu bạn cần phương thức đó trong một bộ điều khiển khác, điều đó có nghĩa là bạn cần trừu tượng hóa nó và làm cho nó có thể sử dụng lại. Chuyển việc triển khai đó vào một lớp dịch vụ (ReportingService hoặc một cái gì đó tương tự) và đưa nó vào bộ điều khiển của bạn.

Thí dụ:

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
  }
}

Làm tương tự cho các bộ điều khiển khác mà bạn cần thực hiện. Tiếp cận các phương thức điều khiển từ các bộ điều khiển khác là mùi mã.


Bạn sẽ lưu lớp này ở đâu về mặt cấu trúc dự án?
Amitay

1
Hoặc là một Servicesthư mục nếu dự án không lớn hoặc thư mục tính năng được gọi Reportingnếu đó là một dự án lớn hơn và sử dụng Folders By Featurecấu trúc.
Ruffles

Bạn đang đề cập đến Nhà cung cấp dịch vụ (lớp dịch vụ) như ở đây laravel.com/docs/5.7/providers hoặc Container dịch vụ như ở đây laravel.com/docs/5.7/container ?
Baspa

1
@Baspa Không, một lớp PHP bình thường.
Ruffles

27

Không nên gọi Bộ điều khiển từ Bộ điều khiển khác, tuy nhiên nếu vì bất kỳ lý do gì bạn phải thực hiện, bạn có thể thực hiện việc này:

Phương pháp tương thích với Laravel 5

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

Lưu ý: điều này sẽ không cập nhật URL của trang.

Thay vào đó, tốt hơn là gọi Tuyến và để nó gọi bộ điều khiển.

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

2
Tại sao nó không được khuyến khích?
brunouno

Đây phải là câu trả lời hàng đầu.
Justin Vincent

13

Bạn không nên. Đó là một mô hình chống. Nếu bạn có một phương thức trong một bộ điều khiển mà bạn cần truy cập trong một bộ điều khiển khác, thì đó là một dấu hiệu bạn cần phải xác định lại.

Xem xét việc bao thanh toán lại phương thức trong một lớp dịch vụ, sau đó bạn có thể khởi tạo trong nhiều bộ điều khiển. Vì vậy, nếu bạn cần cung cấp báo cáo in cho nhiều mô hình, bạn có thể làm một cái gì đó như thế này:

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

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

11
Mặc dù thực tế rằng câu trả lời của bạn có thể đúng, nhưng thật tốt khi mở rộng nó ra một chút và đưa ra một số lời giải thích thêm.
Scana

9

Trước hết, yêu cầu một phương thức của bộ điều khiển từ bộ điều khiển khác là EVIL. Điều này sẽ gây ra nhiều vấn đề tiềm ẩn trong vòng đời của Laravel.

Dù sao, có nhiều giải pháp để làm điều đó. Bạn có thể chọn một trong những cách khác nhau.

Trường hợp 1) Nếu bạn muốn gọi dựa trên Lớp học

Cách 1) Cách đơn giản

Nhưng bạn không thể thêm bất kỳ tham số hoặc xác thực theo cách này.

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

Cách 2) Phân chia logic bộ điều khiển thành các dịch vụ.

Bạn có thể thêm bất kỳ tham số và một cái gì đó với điều này. Giải pháp tốt nhất cho cuộc sống lập trình của bạn. Bạn có thể thực hiện Repositorythay thế Service.

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 ...
    }
}

Trường hợp 2) Nếu bạn muốn gọi dựa trên các tuyến

Cách 1) Sử dụng MakesHttpRequestsđặc điểm được sử dụng trong Kiểm tra đơn vị ứng dụng.

Tôi khuyên bạn nên điều này nếu bạn có lý do đặc biệt để tạo proxy này, bạn có thể sử dụng bất kỳ tham số và tiêu đề tùy chỉnh nào . Ngoài ra đây sẽ là một yêu cầu nội bộ trong laravel. (Yêu cầu HTTP giả) Bạn có thể xem thêm chi tiết cho callphương thức tại đây .

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 ...
    }
}

Tuy nhiên, đây cũng không phải là một giải pháp 'tốt'.

Cách 2) Sử dụng ứng dụng khách gu Muffhttp

Đây là giải pháp khủng khiếp nhất tôi nghĩ. Bạn cũng có thể sử dụng bất kỳ tham số và tiêu đề tùy chỉnh nào . Nhưng điều này sẽ làm cho một yêu cầu http thêm bên ngoài. Vì vậy, HTTP Webserver phải được chạy.

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

Cuối cùng tôi đang sử dụng Cách 1 của Trường hợp 2. Tôi cần các tham số và


1
Cách 2 không nên được viết ra ở đó, bạn không bao giờ muốn tự yêu cầu http, ngay cả trong một cấu trúc mã xấu.
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
Vui lòng chỉnh sửa với nhiều thông tin hơn. Các câu trả lời chỉ dành cho mã và "thử cái này" không được khuyến khích, vì chúng không chứa nội dung có thể tìm kiếm và không giải thích lý do tại sao ai đó nên "thử cái này".
abarisone

2

Bạn có thể sử dụng một phương thức tĩnh trong PrintReportControll và sau đó gọi nó từ SendPerformanceControll như thế này;

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

Cách tiếp cận này cũng hoạt động với cùng phân cấp các tệp Trình điều khiển:

$printReport = new PrintReportController;

$prinReport->getPrintReport();

Tôi thích cách tiếp cận này so với Ứng dụng :: tạo một vì kiểu gợi ý của các khối tài liệu vẫn hoạt động trong phpStorm theo cách này.
Floris

1

Ở đây, đặc điểm mô phỏng hoàn toàn bộ điều khiển đang chạy bằng bộ định tuyến laravel (bao gồm hỗ trợ phần mềm trung gian và tiêm phụ thuộc). Chỉ được thử nghiệm với phiên bản 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();
    }
}

Sau đó, chỉ cần thêm nó vào lớp của bạn và chạy bộ điều khiển. Lưu ý, tiêm phụ thuộc sẽ được chỉ định với tuyến đường hiện tại của bạn.

class CustomController extends Controller {
    use RunsAnotherController;

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

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

Hãy xem xét rằng làm app()->make(......)là bằng để app(......)nó ngắn hơn.
matiaslauriti

1

Bạn có thể truy cập bộ điều khiển bằng cách khởi tạo nó và gọi doAction: (đặt use Illuminate\Support\Facades\App;trước khai báo lớp của bộ điều khiển)

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

Cũng lưu ý rằng bằng cách này, bạn sẽ không thực thi bất kỳ phần mềm trung gian nào được khai báo trên bộ điều khiển đó.


-2

Trả lời muộn, nhưng đôi khi tôi đã tìm kiếm điều này. Điều này bây giờ có thể theo một cách rất đơn giản.

Không có tham số

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

Với thông số

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

Tài liệu: https://laravel.com/docs/5.6/responses#redirecting-controll-ilities

Trở lại 5.0, nó yêu cầu toàn bộ đường dẫn, bây giờ nó đơn giản hơn nhiều.


3
Câu hỏi ban đầu là làm thế nào để truy cập phương thức của bộ điều khiển từ bộ điều khiển khác, không phải cách chuyển hướng đến hành động của phương thức cụ thể khác, vì vậy giải pháp của bạn không liên quan đến câu hỏi.
matiaslauriti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.