Access Controller method from another controller i

2019-01-04 18:09发布

I have two controllers SubmitPerformanceController and PrintReportController.

In PrintReportController I have a method called getPrintReport.

How to access this method in SubmitPerformanceController?

9条回答
可以哭但决不认输i
2楼-- · 2019-01-04 18:36

You can access your controller method like this:

app('App\Http\Controllers\PrintReportController')->getPrintReport();

This will work, but it's bad in terms of code organisation (remember to use the right namespace for your PrintReportController)

You can extend the PrintReportController so SubmitPerformanceController will inherit that method

class SubmitPerformanceController extends PrintReportController {
     // ....
}

But this will also inherit all other methods from PrintReportController.

The best approach will be to create a trait, implement the logic there and tell your controllers to use it:

trait PrintReport {

    public function getPrintReport() {
        // .....
    }
}

Tell your controllers to use this trait:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Both solutions make SubmitPerformanceController to have getPrintReport method so you can call it with $this->getPrintReport(); from within the controller or directly as a route (if you mapped it in the routes.php)

You can read more about traits here.

查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-04 18:38

If you need that method in another controller, that means you need to abstract it and make it reusable. Move that implementation into a service class (ReportingService or something similar) and inject it into your controllers.

Example:

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

Do the same for the other controllers where you need that implementation. Reaching for controller methods from other controllers is a code smell.

查看更多
一纸荒年 Trace。
4楼-- · 2019-01-04 18:43

First of all, request method of controller from another controller is EVIL. This will cause many hidden problems of lifecycle in Laravel.

Anyway, there are many solutions for doing that. You can select one of these various ways.

Case 1) If you want to call based on Classes

Way 1) The simple way

But you can't any parameters or authentication with this way.

app(\App\Http\Controllers\PrintReportContoller::class)->getPrintReport();

Way 2) Make devide the service logics in controllers.

You can any parameters and something with this. The best solution for your programming life. You can make Repository instead 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) If you want to call based on Route

Way 1) Use MakesHttpRequests trait that used in Application Unit Testing.

I recommend if you have special reason for making this proxy. You can use any parameters and custom headers. Also this will be internal request in laravel. (Fake HTTP Request) You can see more detail manuals for call method in here.

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

However this is not 'good' solution, too.

Way 2) Use guzzlehttp client

The most terrible solution I think. You can use any parameters and custom headers, too. But this making external extra http request. So HTTP Webserver must be running.

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

Finally I am using Way 1 of Case 2. I need parameters and

查看更多
SAY GOODBYE
5楼-- · 2019-01-04 18:51

Here the trait fully emulates running controller by laravel router (including support of middlewares and dependency injection). Tested only with 5.4 version

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

Then just add it to your class and run the controller. Note, that dependency injection will be assigned with your current route.

class CustomController extends Controller {
    use RunsAnotherController;

    public function someAction() 
    {
        $controller = app()->make('App\Http\Controllers\AnotherController');

        return $this->runController($controller, 'doSomething');
    }
}
查看更多
萌系小妹纸
6楼-- · 2019-01-04 18:52

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 compatible method

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

Note: this will not update the URL of the page.

It's better to call the Route instead and let it call the controller.

return \Redirect::route('route-name-here');
查看更多
smile是对你的礼貌
7楼-- · 2019-01-04 18:56

Late reply, but I have been looking for this for sometime. This is now possible in a very simple way.

Without parameters

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

With Parameters

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

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

Back in 5.0 it required the entire path, now it's much simpler.

查看更多
登录 后发表回答