web-dev-qa-db-ja.com

Laravel 5内の別のコントローラーからコントローラーメソッドにアクセスする

私は2つのコントローラSubmitPerformanceControllerPrintReportControllerを持っています。

PrintReportControllerにはgetPrintReportというメソッドがあります。

SubmitPerformanceControllerでこのメソッドにアクセスする方法?

113
Iftakharul Alam

あなたはこのようにあなたのコントローラメソッドにアクセスすることができます:

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

これはうまくいくでしょうが、コード構成の点では悪いです(あなたのPrintReportControllerには正しい名前空間を使うことを忘れないでください)

PrintReportControllerを拡張して、SubmitPerformanceControllerがそのメソッドを継承するようにすることができます。

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

しかし、これは他のすべてのメソッドもPrintReportControllerから継承します。

最善の方法はtraitを作成し、そこにロジックを実装してそれを使用するようにあなたのコントローラに指示することです。

trait PrintReport {

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

この特色を使うようにコントローラーに伝えてください:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

どちらのソリューションもSubmitPerformanceControllergetPrintReportメソッドにするため、コントローラ内から$this->getPrintReport();で呼び出すことも、ルートとして直接呼び出すこともできます(routes.phpにマップした場合)。

あなたはここで特性についてもっと読むことができます。

279
shaddy

そのメソッドが別のコントローラで必要な場合は、それを抽象化して再利用可能にする必要があります。その実装をサービスクラス(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
  }
}

その実装が必要な他のコントローラについても同じことをしてください。他のコントローラーからコントローラーメソッドに手を伸ばすのはコードの匂いです。

35
Ruffles

他のコントローラからコントローラを呼び出すことはお勧めできませんが、何らかの理由でそれを行わなければならない場合は、次のようにします。

Laravel 5互換方式

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

注:これはページのURLを更新しません。

代わりにRouteを呼び出して、それをコントローラに呼び出させる方が良いです。

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

しないでください。これはアンチパターンです。あるコントローラに別のコントローラでアクセスする必要があるメソッドがある場合は、それが見直す必要がある兆候です。

メソッドをサービスクラスにリファクタリングすることを検討してください。これにより、複数のコントローラでインスタンス化できます。あなたが複数のモデルのために印刷報告書を提供する必要があるならば、あなたはこのような何かをすることができました:

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

まず、他のコントローラからのコントローラの要求方法はEVILです。これはLaravelにおけるライフサイクルの多くの隠れた問題を引き起こすでしょう。

とにかく、それをするための多くの解決策があります。あなたはこれらの様々な方法の1つを選ぶことができます。

ケース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特性を使用します。

このプロキシを作成する特別な理由がある場合はお勧めします。あなたは任意のパラメータとカスタムヘッダを使うことができます。またこれは内部要求になります。 (偽の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 Webサーバーは稼働している必要があります。

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

最後に、ケース2の方法1を使用しています。

7
kargnas
\App::call('App\Http\Controllers\MyController@getFoo')
7
the_hasanov
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();   
    }
   }
3
Ahmed Mahmoud

ここでは特性は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');
    }
}
1
Anton

返信が遅くなりましたが、しばらくの間これを探していました。これは非常に簡単な方法で可能になりました。

パラメータなし

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

パラメータあり

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

ドキュメント: https://laravel.com/docs/5.6/responses#redirecting-controller-actions

5.0に戻って、それは全体のパスを必要としました、今でははるかに簡単です。

0
Vipul Nandan