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


162

2つのコントローラSubmitPerformanceControllerとがありPrintReportControllerます。

ではPrintReportControllerIと呼ばれる方法がありますgetPrintReport

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

回答:


365

次のようにコントローラーメソッドにアクセスできます。

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

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

特性について詳しくは、こちらをご覧ください


10
トレイトを含むファイルはどこに保存する必要がありますか?
Brainmaniac 2018年

24
app('App\Http\Controllers\PrintReportController')->getPrintReport();に変換できapp(PrintReportController::class')->getPrintReport()ます。私のためのクリーンなソリューション。
ヴィンセントDecaux

特性ファイルはどこに保存されますか?
Eric McWinNEr

@EricMcWinNEr App \ Traitsなど、好きな場所に保存できます。ただし、その特性では必ず適切な名前空間を使用してください。
法廷

1
Laravelに特性を使用するためのほんの少しの例:develodesign.co.uk/news/...
Erenorパズ

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

1
@Baspaいいえ、通常のPHPクラスです。
ラッフルズ

27

別のコントローラーからコントローラーを呼び出すことはお勧めできませんが、何らかの理由でこれを行う必要がある場合は、次のようにできます。

Laravel 5互換メソッド

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

注:これによってページのURLが更新されることはありません。

代わりにルートを呼び出して、コントローラーを呼び出せるようにすることをお勧めします。

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 2016年

9

まず第一に、別のコントローラーからコントローラーのメソッドを要求することは悪です。これは、Laravelのライフサイクルで多くの隠れた問題を引き起こします。

とにかく、それを行うための多くの解決策があります。これらのさまざまな方法のいずれかを選択できます。

ケース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 Webサーバーが実行されている必要があります。

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

最後に、ケース2の方法1を使用しています。パラメーターが必要です。


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
詳細を編集してください。コードのみの回答と「これを試す」の回答は、検索可能なコンテンツが含まれておらず、誰かが「これを試す」必要がある理由を説明していないため、お勧めしません。
アバリゾン

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();

App :: make oneと比較してこのアプローチが好きです。これは、docブロックの型ヒントがphpStormでもこのように機能するためです。
フローリス

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

5.0では、パス全体が必要でしたが、今でははるかに簡単になりました。


3
元の質問は、他の特定のメソッドのアクションにリダイレクトする方法ではなく、他のコントローラーからコントローラーのメソッドにアクセスする方法でした。そのため、ソリューションは質問に関連していません。
matiaslauriti
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.