Laravel 5의 다른 컨트롤러에서 액세스 컨트롤러 방법


162

두 개의 컨트롤러 SubmitPerformanceControllerPrintReportController있습니다.

에라 PrintReportController는 메소드가 getPrintReport있습니다.

에서이 방법에 액세스하는 방법은 SubmitPerformanceController무엇입니까?

답변:


364

다음과 같이 컨트롤러 메소드에 액세스 할 수 있습니다.

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

이것은 작동하지만 코드 구성 측면에서 나쁩니다 (에 적합한 네임 스페이스를 사용해야 함 PrintReportController)

당신은 확장 할 수 있습니다 PrintReportController그래서 SubmitPerformanceController그 방법을 상속합니다

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

그러나 이것은에서 다른 모든 방법을 상속합니다 PrintReportController.

가장 좋은 방법은 trait(예 : in 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구조.
프릴

여기 laravel.com/docs/5.7/providers 와 같은 서비스 제공자 (서비스 클래스) 또는 laravel.com/docs/5.7/container 와 같은 서비스 컨테이너를 언급하고 있습니까?
Baspa 2019

1
@Baspa No, 일반적인 PHP 클래스입니다.
Ruffles

27

다른 컨트롤러에서 컨트롤러를 호출하는 것은 권장되지 않지만 어떤 이유로해야 할 경우 다음을 수행 할 수 있습니다.

Laravel 5 호환 방법

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

참고 : 페이지의 URL은 업데이트되지 않습니다.

대신 Route를 호출하고 컨트롤러를 호출하도록하는 것이 좋습니다.

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

2
권장하지 않는 이유는 무엇입니까?
brunouno

이것이 최고의 답변이되어야합니다.
저스틴 빈센트

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입니다. 이것은 라 라벨의 라이프 사이클에서 많은 숨겨진 문제를 일으킬 것입니다.

어쨌든, 그렇게하는 많은 솔루션이 있습니다. 이러한 다양한 방법 중 하나를 선택할 수 있습니다.

사례 1) 수업을 기준으로 전화를 걸려면

방법 1) 간단한 방법

그러나이 방법으로는 매개 변수 나 인증추가수 없습니다 .

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

방법 2) 컨트롤러 로직을 서비스로 나누십시오.

이것으로 매개 변수와 무언가추가 할있습니다 . 프로그래밍 수명을위한 최고의 솔루션. Repository대신 만들 수 있습니다 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 ...
    }
}

사례 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 웹 서버가 실행 중이어야합니다.

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

마지막으로 Case 2의 Way 1을 사용하고 있습니다.


1
방법 2는 거기에 작성해서는 안되며, 코드 구조가 나쁜 경우에도 직접 http-request를 원하지 않습니다.
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

이 방법은 동일한 계층의 Controller 파일에서도 작동합니다.

$printReport = new PrintReportController;

$prinReport->getPrintReport();

doc 블록의 유형 힌트가 여전히 phpStorm에서 작동하므로 App : :: make와 비교 하여이 접근법을 좋아합니다.
플로리스

1

여기서 특성은 라 라벨 라우터 (미들웨어 및 종속성 주입 지원 포함)에 의해 실행중인 컨트롤러를 완전히 에뮬레이트합니다. 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-actions

5.0으로 돌아 가면 전체 경로가 필요했지만 훨씬 간단 해졌습니다.


3
원래 질문은 다른 특정 메소드의 조치로 경로 재지 정하는 방법이 아니라 다른 제어기에서 제어기의 메소드에 액세스하는 방법이었습니다. 따라서 솔루션은 질문과 관련이 없습니다.
matiaslauriti 19
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.