Méthode Access Controller depuis un autre contrôleur dans Laravel 5

162

J'ai deux contrôleurs SubmitPerformanceControlleret PrintReportController.

Dans PrintReportControllerj'ai une méthode appelée getPrintReport.

Comment accéder à cette méthode dans SubmitPerformanceController?

Iftakharul Alam
la source

Réponses:

366

Vous pouvez accéder à votre méthode de contrôleur comme ceci:

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

Cela fonctionnera, mais c'est mauvais en termes d'organisation du code (n'oubliez pas d'utiliser le bon espace de noms pour votre PrintReportController)

Vous pouvez étendre le PrintReportControllerdonc SubmitPerformanceControllerhéritera de cette méthode

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

Mais cela héritera également de toutes les autres méthodes de PrintReportController.

La meilleure approche sera de créer un trait(par exemple dans app/Traits), d'y implémenter la logique et de dire à vos contrôleurs de l'utiliser:

trait PrintReport {

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

Dites à vos contrôleurs d'utiliser cette caractéristique:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Les deux solutions font SubmitPerformanceControllerpour avoir une getPrintReportméthode afin que vous puissiez l'appeler $this->getPrintReport();depuis le contrôleur ou directement comme route (si vous l'avez mappée dans le routes.php)

Vous pouvez en savoir plus sur les traits ici .

Sh1d0w
la source
10
où le fichier contenant le trait doit-il être enregistré?
Brainmaniac
24
app('App\Http\Controllers\PrintReportController')->getPrintReport();peut être transformé en app(PrintReportController::class')->getPrintReport(). Solution propre pour moi.
Vincent Decaux
Où le fichier de caractéristiques est-il stocké?
Eric McWinNEr
@EricMcWinNEr Il peut être stocké où vous le souhaitez, comme supposons App \ Traits. Mais assurez-vous d'utiliser l'espace de noms approprié dans ce trait.
Tribunal
1
Juste un petit exemple d'utilisation des traits dans Laravel: develodesign.co.uk/news
...
48

Si vous avez besoin de cette méthode dans un autre contrôleur, cela signifie que vous devez la résumer et la rendre réutilisable. Déplacez cette implémentation dans une classe de service (ReportingService ou quelque chose de similaire) et injectez-la dans vos contrôleurs.

Exemple:

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

Faites de même pour les autres contrôleurs pour lesquels vous avez besoin de cette implémentation. Atteindre les méthodes de contrôleur d'autres contrôleurs est une odeur de code.

Froufrous
la source
Où sauvegarderiez-vous cette classe en termes de structure de projet?
Amitay
1
Soit un Servicesdossier si le projet n'est pas volumineux, soit un dossier de fonctionnalités appelé Reportings'il s'agit d'un projet plus grand et utilise une Folders By Featurestructure.
Volants
Faites-vous référence à un fournisseur de services (classe de service) comme ici laravel.com/docs/5.7/providers ou à un conteneur de services comme ici laravel.com/docs/5.7/container ?
Baspa
1
@Baspa Non, une classe PHP normale.
Ruffles
27

L'appel d'un contrôleur depuis un autre contrôleur n'est pas recommandé, mais si pour une raison quelconque vous devez le faire, vous pouvez le faire:

Méthode compatible Laravel 5

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

Remarque: cela ne mettra pas à jour l'URL de la page.

Il est préférable d'appeler la Route à la place et de la laisser appeler le contrôleur.

return \Redirect::route('route-name-here');
Mahmoud Zalt
la source
2
Pourquoi n'est-ce pas recommandé?
brunouno
Cela devrait être la meilleure réponse.
Justin Vincent
13

Tu ne devrais pas. C'est un anti-pattern. Si vous avez une méthode dans un contrôleur à laquelle vous devez accéder dans un autre contrôleur, c'est un signe que vous devez re-factoriser.

Envisagez de refactoriser la méthode dans une classe de service, que vous pouvez ensuite instancier dans plusieurs contrôleurs. Donc, si vous devez proposer des rapports d'impression pour plusieurs modèles, vous pouvez faire quelque chose comme ceci:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}
Martin Bean
la source
10
\App::call('App\Http\Controllers\MyController@getFoo')
the_hasanov
la source
11
Bien que votre réponse soit peut-être correcte, ce serait bien de l'étendre un peu et de donner plus d'explications.
scana
9

Tout d'abord, demander une méthode d'un contrôleur à un autre contrôleur est EVIL. Cela causera de nombreux problèmes cachés dans le cycle de vie de Laravel.

Quoi qu'il en soit, il existe de nombreuses solutions pour y parvenir. Vous pouvez sélectionner l'une de ces différentes manières.

Cas 1) Si vous souhaitez appeler en fonction des classes

Voie 1) La manière simple

Mais vous ne pouvez pas ajouter de paramètres ou d'authentification de cette manière.

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

Méthode 2) Divisez la logique du contrôleur en services.

Vous pouvez ajouter des paramètres et quelque chose avec ça. La meilleure solution pour votre vie de programmation. Vous pouvez faire à la Repositoryplace 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 ...
    }
}

Cas 2) Si vous souhaitez appeler en fonction des itinéraires

Méthode 1) Utilisez le MakesHttpRequeststrait utilisé dans les tests unitaires d'application.

Je recommande cela si vous avez une raison particulière de créer ce proxy, vous pouvez utiliser tous les paramètres et en-têtes personnalisés . Ce sera également une demande interne dans laravel. (Fake HTTP Request) Vous pouvez voir plus de détails sur la callméthode ici .

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

Cependant, ce n'est pas non plus une «bonne» solution.

Méthode 2) Utilisez le client guzzlehttp

C'est la solution la plus terrible que je pense. Vous pouvez également utiliser tous les paramètres et en-têtes personnalisés . Mais ce serait faire une demande externe http supplémentaire. Le serveur Web HTTP doit donc être en cours d'exécution.

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

Enfin, j'utilise la voie 1 du cas 2. J'ai besoin de paramètres et

kargnas
la source
1
La manière 2 ne doit pas être écrite là-bas, vous ne voulez jamais vous-même demander http, même dans une mauvaise structure de code.
Sw0ut le
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();   
    }
   }
Ahmed Mahmoud
la source
7
Veuillez modifier avec plus d'informations. Les réponses basées uniquement sur le code et «essayez ceci» sont déconseillées, car elles ne contiennent aucun contenu interrogeable et n'expliquent pas pourquoi quelqu'un devrait «essayer».
abarisone le
2

Vous pouvez utiliser une méthode statique dans PrintReportController, puis l'appeler à partir de SubmitPerformanceController comme ceci;

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

    }

}
TheLastCodeBender
la source
2

Cette approche fonctionne également avec la même hiérarchie de fichiers Controller:

$printReport = new PrintReportController;

$prinReport->getPrintReport();
Jay Marz
la source
J'aime cette approche par rapport à App :: make one car l'indication de type des blocs doc fonctionne toujours de cette façon dans phpStorm.
Floris le
1

Ici, le trait émule entièrement le contrôleur en cours d'exécution par le routeur laravel (y compris le support des middlewares et l'injection de dépendances). Testé uniquement avec la version 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();
    }
}

Ensuite, ajoutez-le simplement à votre classe et exécutez le contrôleur. Notez que l'injection de dépendances sera affectée à votre route actuelle.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}
Anton
la source
Prenez en considération que faire app()->make(......)est égal à app(......)donc c'est plus court.
matiaslauriti
1

Vous pouvez accéder au contrôleur en l'instanciant et en appelant doAction: (mis use Illuminate\Support\Facades\App;avant la déclaration de classe du contrôleur)

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

Notez également qu'en faisant cela, vous n'exécuterez aucun des middlewares déclarés sur ce contrôleur.

Abhijeet Navgire
la source
-2

Réponse tardive, mais je le cherche depuis un certain temps. C'est désormais possible de manière très simple.

Sans paramètres

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

Avec des paramètres

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

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

Dans la version 5.0, il fallait tout le chemin, maintenant c'est beaucoup plus simple.

Vipul Nandan
la source
3
La question initiale était de savoir comment accéder à la méthode d'un contrôleur à partir d'un autre contrôleur, et non comment rediriger vers l'action d'une autre méthode spécifique, de sorte que votre solution n'est pas liée à la question.
matiaslauriti