web-dev-qa-db-fra.com

Méthode du contrôleur d'accès à partir d'un autre contrôleur dans Laravel 5

J'ai deux contrôleurs SubmitPerformanceController et PrintReportController.

Dans PrintReportController j'ai une méthode appelée getPrintReport.

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

117
Iftakharul Alam

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 la PrintReportController pour que SubmitPerformanceController hérite de cette méthode

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

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

La meilleure approche consiste à créer une trait, à y implémenter la logique et à dire à vos contrôleurs de l’utiliser:

trait PrintReport {

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

Dites à vos contrôleurs d'utiliser ce trait:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Les deux solutions permettent à SubmitPerformanceController d’avoir la méthode getPrintReport afin que vous puissiez l’appeler avec $this->getPrintReport(); à partir du contrôleur ou directement sous forme de route (si vous l’avez mappée dans le routes.php)

Vous pouvez en savoir plus sur les traits ici .

296
shaddy

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 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 des méthodes de contrôleur à partir d'autres contrôleurs est une odeur de code.

40
Ruffles

Il n'est pas recommandé d'appeler un contrôleur à partir d'un autre contrôleur. Toutefois, 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');

Note: 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');
19
Mahmoud Zalt

Tu ne devrais pas. C’est un anti-modèle. Si vous avez besoin d’une méthode dans un contrôleur auquel vous devez accéder dans un autre contrôleur, c’est un signe que vous devez reformater.

Envisagez de re-factoriser la méthode dans une classe de service, que vous pouvez ensuite instancier dans plusieurs contrôleurs. Ainsi, si vous devez proposer des rapports d'impression pour plusieurs modèles, vous pouvez procéder comme suit:

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

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 le faire. 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 ajouter aucun paramètre ni aucune authentification avec cette méthode.

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

Voie 2) Divisez la logique du contrôleur en services.

Vous pouvez ajouter tous les paramètres et quelque chose avec cela. La meilleure solution pour votre vie de programmation. Vous pouvez créer Repository à la place de 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 MakesHttpRequests le trait utilisé dans les tests d’unités d’application.

Je le recommande 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 . De plus, ceci sera une requête interne en laravel. (Demande HTTP fictive) Vous pouvez voir plus de détails pour la méthode call dans 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.

Way 2) Utiliser le client guzzlehttp

C’est la solution la plus terrible à mon avis. Vous pouvez également utiliser tous les paramètres et en-têtes personnalisés . Mais ce serait faire une requête http externe supplémentaire. Donc, HTTP Webserver doit ê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

8
kargnas
\App::call('App\Http\Controllers\MyController@getFoo')
7
the_hasanov
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();   
    }
   }
4
Ahmed Mahmoud

Ici, le trait émule complètement le contrôleur en cours d’exécution par le routeur laravel (y compris la prise en charge 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 cette injection de dépendance sera affectée à votre itinéraire actuel.

class CustomController extends Controller {
    use RunsAnotherController;

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

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

Réponse tardive, mais je le cherche depuis un moment. Ceci est maintenant possible d'une manière très simple.

Sans paramètres

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

Avec paramètres

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

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

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

0
Vipul Nandan