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


162

두 개의 컨트롤러 SubmitPerformanceController가 PrintReportController있습니다.

에라 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.