Access Controller method from another controller in Laravel 5

339,010

Solution 1

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 (e.g. in app/Traits), 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.

Solution 2

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.

Solution 3

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

Solution 4

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

Solution 5

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();
    }
}
Share:
339,010

Related videos on Youtube

Iftakharul Alam
Author by

Iftakharul Alam

Updated on July 08, 2022

Comments

  • Iftakharul Alam
    Iftakharul Alam almost 2 years

    I have two controllers SubmitPerformanceController and PrintReportController.

    In PrintReportController I have a method called getPrintReport.

    How to access this method in SubmitPerformanceController?

  • scana
    scana about 8 years
    Despite the fact that your answer might be correct, it would be nice to extend it a little and give some more explanation.
  • Amitay
    Amitay almost 8 years
    Where would you save this class in terms of project structure?
  • Davor Minchorov
    Davor Minchorov almost 8 years
    Either a Services folder if the project is not big or a feature folder called Reporting if it's a bigger project and uses Folders By Feature structure.
  • abarisone
    abarisone over 7 years
    Please edit with more information. Code-only and "try this" answers are discouraged, because they contain no searchable content, and don't explain why someone should "try this".
  • Sw0ut
    Sw0ut about 6 years
    Way 2 should not be written down there, you never want to self http-request yourself, even in a bad code structure.
  • Brainmaniac
    Brainmaniac over 5 years
    where should the file including the trait be saved?
  • Vincent Decaux
    Vincent Decaux over 5 years
    app('App\Http\Controllers\PrintReportController')->getPrintR‌​eport(); can transformed to app(PrintReportController::class')->getPrintReport(). Clean solution for me.
  • Baspa
    Baspa about 5 years
    Are you referring to a Service Provider (service class) like here laravel.com/docs/5.7/providers or a Service Container like here laravel.com/docs/5.7/container?
  • Davor Minchorov
    Davor Minchorov about 5 years
    @Baspa No, a normal PHP class.
  • matiaslauriti
    matiaslauriti almost 5 years
    The original question was how to access a controller's method from other controller, not how to redirect to other specific method's action, so your solution is not related to the question.
  • matiaslauriti
    matiaslauriti almost 5 years
    Take in consideration that doing app()->make(......) is equals to app(......) so it is shorter.
  • brunouno
    brunouno over 4 years
    Why is it not recommended?
  • Erenor Paz
    Erenor Paz almost 4 years
    Just a little example for using traits in Laravel: develodesign.co.uk/news/…
  • Floris
    Floris almost 4 years
    I like this approach compared to the App::make one because the type hinting of the doc blocks still work in phpStorm this way.
  • above_c_level
    above_c_level over 3 years
    Welcome to SO! Thank you for your time in answering this question. Can you please give more details about your solution? For example, why is your solution better than the accepted answer? Also, the question was asked and answered 5 years ago. Be sure to look at the date of the original question when answering. Please read How to Answer.
  • Iftakharul Alam
    Iftakharul Alam over 3 years
    As far as better maintainability and flexibility Trait & Service contract should be the best approach.
  • sbnc.eu
    sbnc.eu over 3 years
    Clean solution, thanks! Shouldn't be the way in a normal Laravel app, but in Themosis it is brilliant. Note that $parameters must be an array, even if there's only one or no parameters to the controller_method.
  • Tiago Martins Peres
    Tiago Martins Peres about 3 years
    @Brainmaniac in app/Traits. More about it here.
  • Binar Web
    Binar Web over 2 years
    @Floris to use type hinting, use it like this App::make(\App\Http\Controllers\YouControllerName::class)
  • francisco
    francisco over 2 years
    @Mahmoud Zalt where is the link of cite??
  • Kat Lim Ruiz
    Kat Lim Ruiz over 2 years
    calling a controller action is not the same as redirect, so it is not "better".
  • Kat Lim Ruiz
    Kat Lim Ruiz over 2 years
    About the not recommended, my opinion is because you are "skipping" many initialization or internal Laravel logic (which may not exist now, but in the future it will). Indeed you should not.
  • Artur Müller Romanov
    Artur Müller Romanov over 2 years
    @VincentDecaux You forgot that ' :-P. Remove it or it won't work
  • Binar Web
    Binar Web over 2 years
    @KatLimRuiz Even if it doesn't skip initialization steps, calling the controller this way is slower compared to a direct instantiation of a class, because of so many internal calls. Instead, one should chunk the logic into smaller classes and call those instead.