Laravel 5 中另一个控制器的访问控制器方法
Access Controller method from another controller in Laravel 5
我有两个控制器 SubmitPerformanceController
和 PrintReportController
。
在 PrintReportController
中,我有一个名为 getPrintReport
的方法。
如何在SubmitPerformanceController
中访问这个方法?
您可以像这样访问您的控制器方法:
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。
如果您需要在另一个控制器中使用该方法,则意味着您需要将其抽象化并使其可重用。将该实现移至服务 class(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
}
}
对需要该实现的其他控制器执行相同的操作。从其他控制器获取控制器方法是一种代码味道。
你不应该。这是一个反模式。如果您在一个控制器中有一个方法需要在另一个控制器中访问,那么这就是您需要重构的标志。
考虑将方法重构为服务 class,然后您可以在多个控制器中实例化该服务。因此,如果您需要为多个模型提供打印报告,您可以这样做:
class ExampleController extends Controller
{
public function printReport()
{
$report = new PrintReport($itemToReportOn);
return $report->render();
}
}
\App::call('App\Http\Controllers\MyController@getFoo')
Calling a Controller from another Controller is not recommended, however if for any reason you have to do it, you can do this:
Laravel 5兼容方法
return \App::call('bla\bla\ControllerName@functionName');
注意:这不会更新页面的URL。
不如调用Route,让它调用controller
return \Redirect::route('route-name-here');
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();
}
}
首先,从另一个控制器请求一个控制器的方法是EVIL。这样会造成Laravel的life-cycle.
的很多隐藏问题
无论如何,有很多解决方案可以做到这一点。您可以 select 这些不同的方式之一。
案例1)如果要根据类
调用
方式一)简单的方式
但是您无法通过这种方式添加任何参数或身份验证。
app(\App\Http\Controllers\PrintReportContoller::class)->getPrintReport();
方式2)将controller逻辑拆分成services
您可以添加任何参数和一些东西。适合您编程生活的最佳解决方案。您可以 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 ...
}
}
Case 2)如果要基于Routes调用
方式 1) 使用 MakesHttpRequests
在应用程序单元测试中使用的特征。
如果你有特殊原因做这个代理我推荐这个,你可以使用任何参数和自定义headers。此外,此 将是 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 ...
}
}
然而,这也不是 'good' 解决方案。
方式 2) 使用 guzzlehttp 客户端
这是我认为最糟糕的解决方案。您 也可以使用任何参数和自定义 headers。但这将发出外部额外的 http 请求。所以 HTTP Webserver 必须是 运行.
$client = new Client([
'base_uri' => request()->getSchemeAndhttpHost(),
'headers' => request()->header()
]);
$a = $client->get('/performance/submit')->getBody()->getContents()
这里的 trait 通过 laravel 路由器完全模拟了 运行ning 控制器(包括对中间件和依赖注入的支持)。仅使用 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 和 运行 控制器。请注意,依赖注入将分配给您当前的路由。
class CustomController extends Controller {
use RunsAnotherController;
public function someAction()
{
$controller = app()->make('App\Http\Controllers\AnotherController');
return $this->runController($controller, 'doSomething');
}
}
回复晚了,但我已经找了一段时间了。这现在可以通过一种非常简单的方式实现。
无参数
return redirect()->action('HomeController@index');
带参数
return redirect()->action('UserController@profile', ['id' => 1]);
文档:https://laravel.com/docs/5.6/responses#redirecting-controller-actions
在 5.0 中它需要整个路径,现在简单多了。
您可以在 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();
}
}
您可以通过实例化控制器并调用 doAction 来访问控制器:(在控制器 class 声明之前放置 use Illuminate\Support\Facades\App;
)
$controller = App::make('\App\Http\Controllers\YouControllerName');
$data = $controller->callAction('controller_method', $parameters);
另请注意,这样做您将不会执行在该控制器上声明的任何中间件。
此方法也适用于相同层次结构的控制器文件:
$printReport = new PrintReportController;
$prinReport->getPrintReport();
//In Controller A <br >
public static function function1(){
}
//In Controller B, View or anywhere <br>
A::function1();
尝试在 SubmitPerformanceController 中创建一个新的 PrintReportController 对象并直接调用 getPrintReport 方法。
例如,假设我在 SubmitPerformanceController 中有一个名为“Test”的函数,那么我可以这样做:
public function test() {
$prc = new PrintReportController();
$prc->getPrintReport();
}
- 嗯,当然,你可以实例化另一个控制器并调用你想要的方法。可能这不是一个好习惯,但我不知道为什么:
$otherController = new OtherController();
$otherController->methodFromOtherController($param1, $param2 ...);
但是,这样做,你会遇到一个问题:另一个方法 return 类似于 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,您可以区分每种情况并return一个对应的值。
我有两个控制器 SubmitPerformanceController
和 PrintReportController
。
在 PrintReportController
中,我有一个名为 getPrintReport
的方法。
如何在SubmitPerformanceController
中访问这个方法?
您可以像这样访问您的控制器方法:
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。
如果您需要在另一个控制器中使用该方法,则意味着您需要将其抽象化并使其可重用。将该实现移至服务 class(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
}
}
对需要该实现的其他控制器执行相同的操作。从其他控制器获取控制器方法是一种代码味道。
你不应该。这是一个反模式。如果您在一个控制器中有一个方法需要在另一个控制器中访问,那么这就是您需要重构的标志。
考虑将方法重构为服务 class,然后您可以在多个控制器中实例化该服务。因此,如果您需要为多个模型提供打印报告,您可以这样做:
class ExampleController extends Controller
{
public function printReport()
{
$report = new PrintReport($itemToReportOn);
return $report->render();
}
}
\App::call('App\Http\Controllers\MyController@getFoo')
Calling a Controller from another Controller is not recommended, however if for any reason you have to do it, you can do this:
Laravel 5兼容方法
return \App::call('bla\bla\ControllerName@functionName');
注意:这不会更新页面的URL。
不如调用Route,让它调用controller
return \Redirect::route('route-name-here');
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();
}
}
首先,从另一个控制器请求一个控制器的方法是EVIL。这样会造成Laravel的life-cycle.
的很多隐藏问题无论如何,有很多解决方案可以做到这一点。您可以 select 这些不同的方式之一。
案例1)如果要根据类
调用方式一)简单的方式
但是您无法通过这种方式添加任何参数或身份验证。
app(\App\Http\Controllers\PrintReportContoller::class)->getPrintReport();
方式2)将controller逻辑拆分成services
您可以添加任何参数和一些东西。适合您编程生活的最佳解决方案。您可以 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 ...
}
}
Case 2)如果要基于Routes调用
方式 1) 使用 MakesHttpRequests
在应用程序单元测试中使用的特征。
如果你有特殊原因做这个代理我推荐这个,你可以使用任何参数和自定义headers。此外,此 将是 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 ...
}
}
然而,这也不是 'good' 解决方案。
方式 2) 使用 guzzlehttp 客户端
这是我认为最糟糕的解决方案。您 也可以使用任何参数和自定义 headers。但这将发出外部额外的 http 请求。所以 HTTP Webserver 必须是 运行.
$client = new Client([
'base_uri' => request()->getSchemeAndhttpHost(),
'headers' => request()->header()
]);
$a = $client->get('/performance/submit')->getBody()->getContents()
这里的 trait 通过 laravel 路由器完全模拟了 运行ning 控制器(包括对中间件和依赖注入的支持)。仅使用 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 和 运行 控制器。请注意,依赖注入将分配给您当前的路由。
class CustomController extends Controller {
use RunsAnotherController;
public function someAction()
{
$controller = app()->make('App\Http\Controllers\AnotherController');
return $this->runController($controller, 'doSomething');
}
}
回复晚了,但我已经找了一段时间了。这现在可以通过一种非常简单的方式实现。
无参数
return redirect()->action('HomeController@index');
带参数
return redirect()->action('UserController@profile', ['id' => 1]);
文档:https://laravel.com/docs/5.6/responses#redirecting-controller-actions
在 5.0 中它需要整个路径,现在简单多了。
您可以在 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();
}
}
您可以通过实例化控制器并调用 doAction 来访问控制器:(在控制器 class 声明之前放置 use Illuminate\Support\Facades\App;
)
$controller = App::make('\App\Http\Controllers\YouControllerName');
$data = $controller->callAction('controller_method', $parameters);
另请注意,这样做您将不会执行在该控制器上声明的任何中间件。
此方法也适用于相同层次结构的控制器文件:
$printReport = new PrintReportController;
$prinReport->getPrintReport();
//In Controller A <br >
public static function function1(){
}
//In Controller B, View or anywhere <br>
A::function1();
尝试在 SubmitPerformanceController 中创建一个新的 PrintReportController 对象并直接调用 getPrintReport 方法。
例如,假设我在 SubmitPerformanceController 中有一个名为“Test”的函数,那么我可以这样做:
public function test() {
$prc = new PrintReportController();
$prc->getPrintReport();
}
- 嗯,当然,你可以实例化另一个控制器并调用你想要的方法。可能这不是一个好习惯,但我不知道为什么:
$otherController = new OtherController();
$otherController->methodFromOtherController($param1, $param2 ...);
但是,这样做,你会遇到一个问题:另一个方法 return 类似于 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,您可以区分每种情况并return一个对应的值。