Laravel 5中另一个控制器的Access Controller方法


162

我有两个控制器SubmitPerformanceControllerPrintReportController

PrintReportController我有一个方法称为getPrintReport

如何在中访问此方法SubmitPerformanceController

Answers:


364

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

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

您可以在这里阅读更多有关特征的信息


10
包含特征的文件应保存在哪里?
Brainmaniac

24
app('App\Http\Controllers\PrintReportController')->getPrintReport();可以转化为app(PrintReportController::class')->getPrintReport()。对我来说干净的解决方案。
文森特·德克斯

特征文件存储在哪里?
埃里克·麦克温内尔

@EricMcWinNEr可以将其存储在您喜欢的任何位置,例如,假设应用\特质。但是请确保在该特征中使用适当的名称空间。
法庭

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

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


就项目结构而言,您将在哪里保存此类?
阿米泰'16

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。

最好改为调用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
尽管您的答案可能是正确的,但最好将其扩展一点并给出更多解释。
斯堪纳

9

首先,从另一个控制器请求控制器的方法是EVIL。这将在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 Webserver必须正在运行。

$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
请编辑更多信息。不建议使用纯代码和“尝试此”答案,因为它们不包含可搜索的内容,并且不解释为什么有人应该“尝试此”。
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();

与App :: make相比,我喜欢这种方法,因为docStor的类型提示在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,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.