【问题标题】:Access Controller method from another controller in Laravel 5Laravel 5 中另一个控制器的访问控制器方法
【发布时间】:2015-08-02 14:46:10
【问题描述】:

我有两个控制器 SubmitPerformanceControllerPrintReportController

PrintReportController我有一个方法叫getPrintReport

如何在SubmitPerformanceController中访问该方法?

【问题讨论】:

    标签: php laravel laravel-5 controller


    【解决方案1】:

    您可以像这样访问您的控制器方法:

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

    两种解决方案都使SubmitPerformanceController 具有getPrintReport 方法,因此您可以在控制器内使用$this->getPrintReport(); 或直接作为路由调用它(如果您将其映射到routes.php

    你可以阅读更多关于特质here

    【讨论】:

    • 包含特征的文件应该保存在哪里?
    • app('App\Http\Controllers\PrintReportController')->getPrintReport(); 可以转换为app(PrintReportController::class')->getPrintReport()。为我清洁解决方案。
    • 在 Laravel 中使用 trait 的一个小例子:develodesign.co.uk/news/…
    • @Brainmaniac 在app/Traits。更多关于它here.
    • @VincentDecaux 你忘了' :-P。删除它,否则它将不起作用
    【解决方案2】:

    如果您需要在另一个控制器中使用该方法,这意味着您需要将其抽象化并使其可重用。将该实现移动到服务类(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
      }
    }
    

    对需要该实现的其他控制器执行相同操作。从其他控制器获取控制器方法是一种代码味道。

    【讨论】:

    • 就项目结构而言,您会将此类保存在哪里?
    • 如果项目不大,则使用 Services 文件夹;如果项目较大并使用 Folders By Feature 结构,则使用名为 Reporting 的功能文件夹。
    • 你指的是像这里laravel.com/docs/5.7/providers这样的服务提供者(服务类)还是像这里laravel.com/docs/5.7/container这样的服务容器?
    • @Baspa 不,一个普通的 PHP 类。
    【解决方案3】:

    你不应该。这是一种反模式。如果您在一个控制器中有一个方法需要在另一个控制器中访问,那么这表明您需要重构。

    考虑将方法重构到服务类中,然后您可以在多个控制器中实例化该方法。因此,如果您需要为多个模型提供打印报告,您可以这样做:

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

    【讨论】:

      【解决方案4】:
      \App::call('App\Http\Controllers\MyController@getFoo')
      

      【讨论】:

      • 尽管您的答案可能是正确的,但最好对其进行一点扩展并提供更多解释。
      【解决方案5】:

      不建议从另一个控制器调用控制器,但是如果出于任何原因必须这样做,您可以这样做:

      Laravel 5 兼容方法

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

      注意:这不会更新页面的 URL。

      最好还是调用Route,让它调用控制器。

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

      【讨论】:

      • 为什么不推荐?
      • @Mahmoud Zalt 引用的链接在哪里??
      • 调用控制器动作和重定向不一样,所以不是“更好”。
      • 关于不推荐,我的看法是因为你“跳过”了许多初始化或内部 Laravel 逻辑(现在可能不存在,但将来会存在)。确实不应该。
      • @KatLimRuiz 即使它不跳过初始化步骤,与直接实例化类相比,这种方式调用控制器也比较慢,因为内部调用太多。相反,应该将逻辑分成更小的类并调用它们。
      【解决方案6】:
      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】:

      首先,向另一个控制器请求一个控制器的方法是邪恶的。这会在 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)如果你想基于Routes调用

      方式 1) 使用在应用程序单元测试中使用的 MakesHttpRequests trait。

      如果您有特殊原因制作此代理,我建议您这样做,您可以使用任何参数和自定义标头。此外,这 将是 laravel 中的内部请求。 (虚假 HTTP 请求)您可以在 here 中查看 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 Webserver 必须正在运行。

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

      最后我使用案例2的方式1。我需要参数和

      【讨论】:

      • 方式 2 不应该写在那里,你永远不想自己 http-request 自己,即使在一个糟糕的代码结构中。
      【解决方案8】:

      这里的 trait 完全模拟了 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()-&gt;make(......) 等于 app(......) 所以它更短。
      【解决方案9】:

      回复晚了,但我一直在寻找这个。这现在可以通过一种非常简单的方式实现。

      无参数

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

      带参数

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

      文档:https://laravel.com/docs/5.6/responses#redirecting-controller-actions

      在 5.0 中它需要整个路径,现在它更简单了。

      【讨论】:

      • 最初的问题是如何从其他控制器访问控制器的方法,而不是如何重定向到其他特定方法的操作,因此您的解决方案与问题无关。
      【解决方案10】:

      您可以在 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();
      
          }
      
      }
      

      【讨论】:

        【解决方案11】:

        您可以通过实例化控制器并调用doAction来访问控制器:(在控制器类声明之前放置use Illuminate\Support\Facades\App;

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

        还要注意,这样做不会执行在该控制器上声明的任何中间件。

        【讨论】:

        • 清洁解决方案,谢谢!不应该是普通 Laravel 应用程序中的方式,但在 Themosis 中它很棒。请注意$parameters 必须是一个数组,即使controller_method 只有一个参数或没有参数。
        【解决方案12】:

        这种方法也适用于相同层次的控制器文件:

        $printReport = new PrintReportController;
        
        $prinReport->getPrintReport();
        

        【讨论】:

        • 与 App::make one 相比,我更喜欢这种方法,因为 doc 块的类型提示在 phpStorm 中仍然以这种方式工作。
        • @Floris 使用类型提示,像这样使用它App::make(\App\Http\Controllers\YouControllerName::class)
        【解决方案13】:
        //In Controller A <br >
        public static function function1(){
        
        }
        
        
        //In Controller B, View or anywhere <br>
        A::function1();
        

        【讨论】:

        • 欢迎来到 SO!感谢您花时间回答这个问题。您能否提供有关您的解决方案的更多详细信息?例如,为什么您的解决方案比公认的答案更好?此外,这个问题是在 5 年前提出并回答的。回答时请务必查看原始问题的日期。请阅读How to Answer
        【解决方案14】:

        尝试在SubmitPerformanceController中新建一个PrintReportController对象,直接调用getPrintReport方法。

        例如,假设我在 SubmitPerformanceController 中有一个名为“Test”的函数,那么我可以执行以下操作:

        public function test() { 
          $prc = new PrintReportController();
          $prc->getPrintReport();
         }
        

        【讨论】:

        • 就更好的可维护性和灵活性而言,Trait & Service contract 应该是最好的方法。
        【解决方案15】:
        1. 嗯,当然,你可以实例化另一个控制器并调用你想要的方法。可能这不是一个好习惯,但我不知道为什么:
        $otherController = new OtherController();
        $otherController->methodFromOtherController($param1, $param2 ...);
        
        1. 但是,这样做,你会遇到一个问题:另一个方法返回类似 response()->json($result) 的东西,而不是你想要的。

        2. 要解决这个问题,定义另一个控制器方法的第一个参数为:

        public function methodFromOtherController(Request $request = null, ...
        
        1. 当您从主控制器调用 methodFromOtherController 时,您将传递 null 作为第一个参数值:
        $otherController = new OtherController();
        $otherController->methodFromOtherController(null, $param1, $param2 ...);
        
        1. 最后在methodFromOtherController方法的最后创建一个条件:
        public function methodFromOtherController(Request $request = null, ...) 
        {
          ...
          if (is_null($request)) {
            return $result;
          } else {
            return response()->json($result);
          }
        }
        
        1. 一旦 Laravel 在直接路由调用时会设置 $request,您可以区分每种情况并返回对应的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-30
          • 1970-01-01
          • 2014-04-16
          • 2015-10-05
          • 1970-01-01
          相关资源
          最近更新 更多