[laravel] Access Controller method from another controller in Laravel 5

I have two controllers SubmitPerformanceController and PrintReportController.

In PrintReportController I have a method called getPrintReport.

How to access this method in SubmitPerformanceController?

This question is related to laravel laravel-5

The answer is


You can use a static method in PrintReportController and then call it from the SubmitPerformanceController like this;

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

    }

}

//In Controller A
public static function function1(){

}

In Controller B, View or anywhere
A::function1();


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

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.


\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 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');

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

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.


Try creating a new PrintReportController object in SubmitPerformanceController and calling getPrintReport method directly.

For example lets say I have a function called "Test" in SubmitPerformanceController then I can do something like this:

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

You can access the controller by instantiating it and calling doAction: (put use Illuminate\Support\Facades\App; before the controller class declaration)

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

Also note that by doing this you will not execute any of the middlewares declared on that controller.


This approach also works with same hierarchy of Controller files:

$printReport = new PrintReportController;

$prinReport->getPrintReport();

  1. Well, of course, you can instantiate the other controller and call the method you want. Probably it's not a good practice but I don't know why:
$otherController = new OtherController();
$otherController->methodFromOtherController($param1, $param2 ...);
  1. But, doing this, you will have a problem: the other method returns something like response()->json($result), and is not it what you want.

  2. To resolve this problem, define the first parameter of the other controller's method as:

public function methodFromOtherController(Request $request = null, ...
  1. When you call methodFromOtherController from the main controller, you will pass null as first parameter value:
$otherController = new OtherController();
$otherController->methodFromOtherController(null, $param1, $param2 ...);
  1. Finally, create a condition at the end of the methodFromOtherController method:
public function methodFromOtherController(Request $request = null, ...) 
{
  ...
  if (is_null($request)) {
    return $result;
  } else {
    return response()->json($result);
  }
}
  1. Once Laravel you ever set $request when it is called by direct route, you can differentiate each situation and return a correspondent value.

First of all, requesting a method of a controller from another controller is EVIL. This will cause many hidden problems in Laravel's life-cycle.

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 add any parameters or authentication with this way.

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

Way 2) Divide the controller logic into services.

You can add 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 Routes

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

I recommend this if you have special reason for making this proxy, you can use any parameters and custom headers. Also this will be an internal request in laravel. (Fake HTTP Request) You can see more details for the 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 a 'good' solution, too.

Way 2) Use guzzlehttp client

This is the most terrible solution I think. You can use any parameters and custom headers, too. But this would be making an 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


You shouldn’t. It’s an anti-pattern. If you have a method in one controller that you need to access in another controller, then that’s a sign you need to re-factor.

Consider re-factoring the method out in to a service class, that you can then instantiate in multiple controllers. So if you need to offer print reports for multiple models, you could do something like this:

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

Examples related to laravel

Parameter binding on left joins with array in Laravel Query Builder Laravel 4 with Sentry 2 add user to a group on Registration Target class controller does not exist - Laravel 8 Visual Studio Code PHP Intelephense Keep Showing Not Necessary Error The POST method is not supported for this route. Supported methods: GET, HEAD. Laravel How to fix 'Unchecked runtime.lastError: The message port closed before a response was received' chrome issue? Post request in Laravel - Error - 419 Sorry, your session/ 419 your page has expired Expected response code 250 but got code "530", with message "530 5.7.1 Authentication required How can I run specific migration in laravel Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory

Examples related to laravel-5

Expected response code 250 but got code "530", with message "530 5.7.1 Authentication required Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory Can't install laravel installer via composer Including a css file in a blade template? No Application Encryption Key Has Been Specified How to Install Font Awesome in Laravel Mix Laravel Migration Error: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes Laravel 5.4 redirection to custom url after login How to set the default value of an attribute on a Laravel model laravel 5.3 new Auth::routes()