Método Access Controller de outro controlador no Laravel 5

162

Eu tenho dois controladores SubmitPerformanceControllere PrintReportController.

No PrintReportControllereu tenho um método chamado getPrintReport.

Como acessar esse método no SubmitPerformanceController?

Iftakharul Alam
fonte

Respostas:

365

Você pode acessar seu método de controle como este:

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

Isso funcionará, mas é ruim em termos de organização do código (lembre-se de usar o espaço para nome certo para o seu PrintReportController)

Você pode estender o PrintReportControllermodo SubmitPerformanceControllerherdará esse método

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

Mas isso também herdará todos os outros métodos de PrintReportController.

A melhor abordagem será criar trait(por exemplo, in app/Traits), implementar a lógica e dizer aos seus controladores para usá-la:

trait PrintReport {

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

Diga a seus controladores para usar esta característica:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Ambas as soluções criam SubmitPerformanceControllerum getPrintReportmétodo para que você possa chamá-lo $this->getPrintReport();de dentro do controlador ou diretamente como uma rota (se você o mapeou no routes.php)

Você pode ler mais sobre características aqui .

Sh1d0w
fonte
10
onde o arquivo, incluindo a característica, deve ser salvo?
Brainmaniac 4/18
24
app('App\Http\Controllers\PrintReportController')->getPrintReport();pode transformado para app(PrintReportController::class')->getPrintReport(). Solução limpa para mim.
Vincent Decaux
Onde o arquivo de características é armazenado?
Eric McWinNEr 9/08/19
@ EricMcWinNEr Ele pode ser armazenado em qualquer lugar que você quiser, como suponha App \ Traits. Mas certifique-se de usar o espaço para nome apropriado nessa característica.
Tribunal
1
Apenas um pequeno exemplo para usar características no Laravel: develodesign.co.uk/news/…
Erenor Paz
48

Se você precisar desse método em outro controlador, isso significa que você deve abstraí-lo e torná-lo reutilizável. Mova essa implementação para uma classe de serviço (ReportingService ou algo semelhante) e injete-a em seus controladores.

Exemplo:

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

Faça o mesmo para os outros controladores em que você precisa dessa implementação. Chegar a métodos de controle de outros controladores é um cheiro de código.

Babados
fonte
Onde você salvaria essa classe em termos de estrutura do projeto?
25716
1
Uma Servicespasta se o projeto não for grande ou uma pasta de recurso chamada Reportingse for um projeto maior e usar Folders By Featureestrutura.
Ruffles
Você está se referindo a um provedor de serviços (classe de serviço) como aqui laravel.com/docs/5.7/providers ou a um Service Container como aqui laravel.com/docs/5.7/container ?
Baspa 13/02/19
1
@Baspa Não, uma classe PHP normal.
Babados
27

Não é recomendável chamar um controlador de outro controlador; no entanto, se por algum motivo você precisar fazer isso, poderá fazer o seguinte:

Método compatível com Laravel 5

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

Nota: isso não atualizará o URL da página.

É melhor chamar a rota e deixá-la chamar o controlador.

return \Redirect::route('route-name-here');
Mahmoud Zalt
fonte
2
Por que não é recomendado?
Brunouno
Essa deve ser a resposta principal.
Justin Vincent
13

Você não deveria. É um anti-padrão. Se você possui um método em um controlador que precisa acessar em outro controlador, isso é um sinal de que você precisa refatorar.

Considere re-fatorar o método em uma classe de serviço, que você pode instanciar em vários controladores. Portanto, se você precisar oferecer relatórios de impressão para vários modelos, faça algo assim:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}
Martin Bean
fonte
10
\App::call('App\Http\Controllers\MyController@getFoo')
the_hasanov
fonte
11
Apesar de sua resposta estar correta, seria bom estender um pouco e dar mais explicações.
#
9

Primeiro de tudo, solicitar um método de um controlador de outro controlador é MAU. Isso causará muitos problemas ocultos no ciclo de vida do Laravel.

De qualquer forma, existem muitas soluções para isso. Você pode selecionar uma dessas várias maneiras.

Caso 1) Se você deseja ligar com base em Classes

Caminho 1) O caminho simples

Mas você não pode adicionar nenhum parâmetro ou autenticação dessa maneira.

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

Caminho 2) Divida a lógica do controlador em serviços.

Você pode adicionar quaisquer parâmetros e algo com isso. A melhor solução para sua vida de programação. Você pode fazer em seu Repositorylugar 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 você deseja ligar com base em rotas

Caminho 1) Use a MakesHttpRequestscaracterística usada no Teste de Unidade de Aplicativo.

Eu recomendo isso se você tiver um motivo especial para criar esse proxy, poderá usar quaisquer parâmetros e cabeçalhos personalizados . Além disso, este será um pedido interno no laravel. (Solicitação HTTP falsa) Você pode ver mais detalhes sobre o callmétodo aqui .

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

No entanto, essa também não é uma solução 'boa'.

Caminho 2) Use o cliente guzzlehttp

Esta é a solução mais terrível que eu acho. Você também pode usar parâmetros e cabeçalhos personalizados . Mas isso faria uma solicitação http extra externa. Portanto, o servidor Web HTTP deve estar em execução.

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

Finalmente, estou usando o Caminho 1 do Caso 2. Preciso de parâmetros e

kargnas
fonte
1
O Caminho 2 não deve ser escrito lá em baixo, você nunca deseja solicitar HTTP sozinho, mesmo em uma estrutura de código incorreta.
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();   
    }
   }
Ahmed Mahmoud
fonte
7
Edite com mais informações. As respostas somente código e "tente isso" são desencorajadas, porque não contêm conteúdo pesquisável e não explicam por que alguém deveria "tentar fazer isso".
abarisone 19/09/16
2

Você pode usar um método estático no PrintReportController e chamá-lo no SubmitPerformanceController como este;

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
fonte
2

Essa abordagem também funciona com a mesma hierarquia de arquivos do Controller:

$printReport = new PrintReportController;

$prinReport->getPrintReport();
Jay Marz
fonte
Eu gosto dessa abordagem em comparação com a App :: make one porque a dica de tipo dos blocos de documentos ainda funciona no phpStorm dessa maneira.
Floris
1

Aqui, a característica emula totalmente o controlador em execução pelo roteador laravel (incluindo suporte de middlewares e injeção de dependência). Testado apenas com a versão 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();
    }
}

Em seguida, basta adicioná-lo à sua classe e executar o controlador. Observe que a injeção de dependência será atribuída à sua rota atual.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}
Anton
fonte
Leve em consideração que fazer app()->make(......)é igual a, app(......)portanto é mais curto.
Matiaslauriti
1

Você pode acessar o controlador instanciando-o e chamando doAction: (colocado use Illuminate\Support\Facades\App;antes da declaração da classe do controlador)

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

Observe também que, ao fazer isso, você não executará nenhum dos middlewares declarados nesse controlador.

Abhijeet Navgire
fonte
-2

Resposta tardia, mas estou procurando isso há algum tempo. Agora isso é possível de uma maneira muito simples.

Sem parâmetros

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

Com parâmetros

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

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

De volta à versão 5.0, era necessário todo o caminho, agora é muito mais simples.

Vipul Nandan
fonte
3
A pergunta original era como acessar o método de um controlador a partir de outro controlador, não como redirecionar para a ação de outro método específico; portanto, sua solução não está relacionada à pergunta.
Matiaslauriti