ChatGPT解决这个技术问题 Extra ChatGPT

Laravel 5 中另一个控制器的访问控制器方法

我有两个控制器 SubmitPerformanceControllerPrintReportController

PrintReportController 中,我有一个名为 getPrintReport 的方法。

如何在 SubmitPerformanceController 中访问此方法?


m
mustaccio

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

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 中使用特征的小例子:develodesign.co.uk/news/…
app/Traits 中的@Brainmaniac。更多关于它here
@VincentDecaux 你忘了' :-P。删除它,否则它将不起作用
m
matiaslauriti

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

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

Laravel 5 兼容方法

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

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

最好改为调用 Route 并让它调用控制器。

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

为什么不推荐?
@Mahmoud Zalt 引用的链接在哪里?
调用控制器操作与重定向不同,因此它不是“更好”。
关于不推荐,我的观点是因为您“跳过”了许多初始化或内部 Laravel 逻辑(现在可能不存在,但将来会存在)。确实你不应该。
@KatLimRuiz即使它不跳过初始化步骤,与直接实例化类相比,以这种方式调用控制器也会更慢,因为内部调用如此之多。相反,应该将逻辑分成更小的类并调用它们。
A
AdHorger

首先,向另一个控制器请求一个控制器的方法是邪恶的。这会在 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 请求)您可以在 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 不应该写在那里,你永远不想自己 http-request 自己,即使在一个糟糕的代码结构中。
M
Martin Bean

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

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

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

h
hichris123
\App::call('App\Http\Controllers\MyController@getFoo')

尽管您的答案可能是正确的,但最好对其进行一点扩展并提供更多解释。
J
Jay Marz

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

$printReport = new PrintReportController;

$prinReport->getPrintReport();

与 App::make one 相比,我喜欢这种方法,因为 doc 块的类型提示在 phpStorm 中仍然以这种方式工作。
@Floris 使用类型提示,像这样使用它App::make(\App\Http\Controllers\YouControllerName::class)
m
matiaslauriti
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();   
    }
   }

请编辑更多信息。不鼓励使用纯代码和“试试这个”的答案,因为它们不包含可搜索的内容,也没有解释为什么有人应该“试试这个”。
T
TheLastCodeBender

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

    }

}

A
Abhijeet Navgire

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

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

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


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

这里的 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()->make(......) 等于 app(......) 所以它更短。
A
Abdullah Aman

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

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

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

就更好的可维护性和灵活性而言Trait & Service contract 应该是最好的方法。
S
Srilal Sachintha
//In Controller A <br >
public static function function1(){

}


//In Controller B, View or anywhere <br>
A::function1();

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

好吧,当然,您可以实例化另一个控制器并调用您想要的方法。可能这不是一个好习惯,但我不知道为什么:

$otherController = new OtherController();
$otherController->methodFromOtherController($param1, $param2 ...);

但是,这样做,你会遇到一个问题:另一个方法返回类似 response()->json($result) 的东西,这不是你想要的。要解决此问题,请将其他控制器方法的第一个参数定义为:

public function methodFromOtherController(Request $request = null, ...

当您从主控制器调用 methodFromOtherController 时,您将传递 null 作为第一个参数值:

$otherController = new OtherController();
$otherController->methodFromOtherController(null, $param1, $param2 ...);

最后,在 methodFromOtherController 方法的末尾创建一个条件:

public function methodFromOtherController(Request $request = null, ...) 
{
  ...
  if (is_null($request)) {
    return $result;
  } else {
    return response()->json($result);
  }
}

一旦 Laravel 在被直接路由调用时设置了 $request,你就可以区分每种情况并返回一个对应的值。


V
Vipul Nandan

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

无参数

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

带参数

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

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

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


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