Accedi al metodo Controller da un altro controller in Laravel 5


162

Ho due controller SubmitPerformanceControllere PrintReportController.

In PrintReportControllerho un metodo chiamato getPrintReport.

Come accedere a questo metodo in SubmitPerformanceController?

Risposte:


365

È possibile accedere al metodo del controller in questo modo:

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

Funzionerà, ma è male in termini di organizzazione del codice (ricordati di usare lo spazio dei nomi giusto per il tuo PrintReportController)

È possibile estendere PrintReportControllercosì SubmitPerformanceControllererediterà quel metodo

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

Ma questo erediterà anche tutti gli altri metodi PrintReportController.

L'approccio migliore sarà quello di creare un trait(es. In app/Traits), implementare la logica lì e dire ai controller di usarlo:

trait PrintReport {

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

Di 'ai tuoi controller di usare questo tratto:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Entrambe le soluzioni SubmitPerformanceControllerhanno un getPrintReportmetodo in modo da poterlo chiamare $this->getPrintReport();dall'interno del controller o direttamente come route (se lo hai mappato nel routes.php)

Puoi leggere di più sui tratti qui .


10
dove dovrebbe essere salvato il file incluso il tratto?
Brainmaniac,

24
app('App\Http\Controllers\PrintReportController')->getPrintReport();può trasformarsi in app(PrintReportController::class')->getPrintReport(). Soluzione pulita per me.
Vincent Decaux,

Dove viene archiviato il file tratto?
Eric McWinNEr

@EricMcWinNEr Può essere archiviato ovunque tu voglia, come supponiamo App \ Traits. Ma assicurati di usare lo spazio dei nomi appropriato in quel tratto.
Tribunale

1
Solo un piccolo esempio per l'uso dei tratti in Laravel: develodesign.co.uk/news/…
Erenor Paz

48

Se hai bisogno di quel metodo in un altro controller, significa che devi estrarlo e renderlo riutilizzabile. Spostare l'implementazione in una classe di servizio (ReportingService o qualcosa di simile) e iniettarla nei controller.

Esempio:

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

Fare lo stesso per gli altri controller in cui è necessaria l'implementazione. Raggiungere metodi di controller da altri controller è un odore di codice.


Dove salveresti questa classe in termini di struttura del progetto?
Entro il

1
O una Servicescartella se il progetto non è grande o una cartella delle caratteristiche chiamata Reportingse è un progetto più grande e utilizza la Folders By Featurestruttura.
Ruffles,

Ti riferisci a un fornitore di servizi (classe di servizio) come qui laravel.com/docs/5.7/providers o a un contenitore di servizi come qui laravel.com/docs/5.7/container ?
Baspa,

1
@Baspa No, una normale classe PHP.
Increspature,

27

Non è consigliabile chiamare un controller da un altro controller, tuttavia se per qualsiasi motivo è necessario farlo, è possibile farlo:

Metodo compatibile Laravel 5

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

Nota: questo non aggiornerà l'URL della pagina.

È meglio chiamare invece il percorso e lasciarlo chiamare il controller.

return \Redirect::route('route-name-here');

2
Perché non è raccomandato?
brunouno,

Questa dovrebbe essere la risposta migliore.
Justin Vincent,

13

Non dovresti. È un anti-schema. Se hai un metodo in un controller a cui devi accedere in un altro controller, allora è un segno che devi ripetere il fattore.

Prendi in considerazione la possibilità di ricodificare il metodo in una classe di servizio, che puoi creare un'istanza in più controller. Quindi, se devi offrire rapporti di stampa per più modelli, puoi fare qualcosa del genere:

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

10
\App::call('App\Http\Controllers\MyController@getFoo')

11
Nonostante il fatto che la tua risposta sia corretta, sarebbe bello estenderla un po 'e dare qualche spiegazione in più.
scana,

9

Innanzitutto, richiedere un metodo di un controller a un altro controller è EVIL. Ciò causerà molti problemi nascosti nel ciclo di vita di Laravel.

Ad ogni modo, ci sono molte soluzioni per farlo. È possibile selezionare uno di questi vari modi.

Caso 1) Se si desidera chiamare in base alle classi

Modo 1) Il modo semplice

Ma non puoi aggiungere alcun parametro o autenticazione in questo modo.

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

Modo 2) Dividere la logica del controller in servizi.

È possibile aggiungere qualsiasi parametro e qualcosa con questo. La migliore soluzione per la tua vita di programmazione. Puoi fare Repositoryinvece 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 ...
    }
}

Caso 2) Se si desidera chiamare in base alle rotte

Modo 1) Utilizzare il MakesHttpRequeststratto utilizzato nel Test unità applicazione.

Lo consiglio se hai un motivo speciale per creare questo proxy, puoi usare qualsiasi parametro e intestazione personalizzata . Anche questa sarà una richiesta interna in laravel. (Richiesta HTTP falsa) Puoi vedere maggiori dettagli per il callmetodo qui .

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

Tuttavia, questa non è una soluzione "buona".

Modo 2) Utilizzare il client guzzlehttp

Questa è la soluzione più terribile che penso. Puoi utilizzare anche qualsiasi parametro e intestazione personalizzata . Ma ciò comporterebbe una richiesta http extra esterna. Quindi HTTP Webserver deve essere in esecuzione.

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

Finalmente sto usando il modo 1 del caso 2. Ho bisogno di parametri e


1
Il modo 2 non dovrebbe essere scritto laggiù, non dovresti mai auto-richiedere te stesso, anche in una struttura di codice errata.
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
Modifica con ulteriori informazioni. Le risposte di solo codice e "prova questo" sono scoraggiate, perché non contengono contenuti ricercabili e non spiegano perché qualcuno dovrebbe "provare questo".
Abarisone,

2

È possibile utilizzare un metodo statico in PrintReportController e quindi chiamarlo da SubmitPerformanceController in questo modo;

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

Questo approccio funziona anche con la stessa gerarchia dei file del controller:

$printReport = new PrintReportController;

$prinReport->getPrintReport();

Mi piace questo approccio rispetto all'App :: make one perché il suggerimento del tipo dei blocchi doc funziona ancora in phpStorm in questo modo.
Floris,

1

Qui il tratto emula completamente il controller in esecuzione dal router laravel (incluso il supporto di middleware e iniezione di dipendenza). Testato solo con la versione 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();
    }
}

Quindi aggiungilo alla tua classe ed esegui il controller. Si noti che l'iniezione di dipendenza verrà assegnata con il percorso corrente.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}

Prendi in considerazione che fare app()->make(......)è uguale a app(......)quindi è più breve.
matiaslauriti,

1

È possibile accedere al controller istanziandolo e chiamando doAction: (inserito use Illuminate\Support\Facades\App;prima della dichiarazione della classe controller)

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

Si noti inoltre che facendo ciò non si eseguirà nessuno dei middleware dichiarati su quel controller.


-2

Risposta in ritardo, ma ho cercato questo per qualche tempo. Questo è ora possibile in un modo molto semplice.

Senza parametri

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

Con parametri

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

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

Nel 5.0 richiedeva l'intero percorso, ora è molto più semplice.


3
La domanda originale era come accedere al metodo di un controller da un altro controller, non come reindirizzare all'azione di un altro metodo specifico, quindi la tua soluzione non è correlata alla domanda.
matiaslauriti,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.