Método de controlador de acceso desde otro controlador en Laravel 5

162

Tengo dos controladores SubmitPerformanceControllery PrintReportController.

En PrintReportControllertengo un método llamado getPrintReport.

¿Cómo acceder a este método en SubmitPerformanceController?

Iftakharul Alam
fuente

Respuestas:

365

Puede acceder a su método de controlador de esta manera:

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

Esto funcionará, pero es malo en términos de organización del código (recuerde usar el espacio de nombres correcto para su PrintReportController )

Puede extender el PrintReportControllermodo SubmitPerformanceControllerheredará ese método

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

Pero esto también heredará todos los demás métodos de PrintReportController.

El mejor enfoque será crear un trait(por ejemplo, en app/Traits), implementar la lógica allí y decirles a sus controladores que lo usen:

trait PrintReport {

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

Dígale a sus controladores que usen este rasgo:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Ambas soluciones SubmitPerformanceControllertienen un getPrintReportmétodo para que pueda llamarlo $this->getPrintReport();desde dentro del controlador o directamente como una ruta (si lo asignó en elroutes.php )

Puedes leer más sobre los rasgos aquí .

Sh1d0w
fuente
10
¿Dónde debe guardarse el archivo, incluido el rasgo?
Brainmaniac
24
app('App\Http\Controllers\PrintReportController')->getPrintReport();puede transformarse a app(PrintReportController::class')->getPrintReport(). Solución limpia para mi.
Vincent Decaux el
¿Dónde se almacena el archivo de rasgos?
Eric McWinNEr
@EricMcWinNEr Se puede almacenar en cualquier lugar que desee, como supongamos que App \ Traits. Pero asegúrese de usar el espacio de nombres apropiado en ese rasgo.
Tribunal
1
Solo un pequeño ejemplo para usar rasgos en Laravel: Developdesign.co.uk/news/…
Erenor Paz
48

Si necesita ese método en otro controlador, eso significa que debe abstraerlo y hacerlo reutilizable. Mueva esa implementación a una clase de servicio (ReportingService o algo similar) e inyéctela en sus controladores.

Ejemplo:

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

Haga lo mismo para los otros controladores donde necesita esa implementación. Alcanzar métodos de controlador desde otros controladores es un olor a código.

Volantes
fuente
¿Dónde guardarías esta clase en términos de estructura del proyecto?
Amitay
1
Una Servicescarpeta si el proyecto no es grande o una carpeta de características llamada Reportingsi es un proyecto más grande y usa Folders By Featureestructura.
Ruffles
¿Se refiere a un proveedor de servicios (clase de servicio) como aquí laravel.com/docs/5.7/providers o un contenedor de servicios como aquí laravel.com/docs/5.7/container ?
Baspa
1
@Baspa No, una clase PHP normal.
Ruffles
27

No se recomienda llamar a un controlador desde otro controlador, sin embargo, si por alguna razón tiene que hacerlo, puede hacerlo:

Método compatible con Laravel 5

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

Nota: esto no actualizará la URL de la página.

Es mejor llamar a la ruta y dejar que llame al controlador.

return \Redirect::route('route-name-here');
Mahmoud Zalt
fuente
2
¿Por qué no se recomienda?
brunouno
Esta debería ser la mejor respuesta.
Justin Vincent
13

No deberías Es un antipatrón. Si tiene un método en un controlador al que necesita acceder en otro controlador, entonces esa es una señal que necesita re-factorizar.

Considere volver a factorizar el método en una clase de servicio, que luego puede instanciar en múltiples controladores. Entonces, si necesita ofrecer informes impresos para varios modelos, podría hacer algo como esto:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}
Frijol Martin
fuente
10
\App::call('App\Http\Controllers\MyController@getFoo')
the_hasanov
fuente
11
A pesar de que su respuesta puede ser correcta, sería bueno extenderla un poco y dar más explicaciones.
scana
9

En primer lugar, solicitar un método de un controlador de otro controlador es MAL. Esto causará muchos problemas ocultos en el ciclo de vida de Laravel.

De todos modos, hay muchas soluciones para hacerlo. Puede seleccionar una de estas diversas formas.

Caso 1) Si desea llamar según las clases

Camino 1) El camino simple

Pero no puede agregar ningún parámetro o autenticación de esta manera.

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

Forma 2) Divida la lógica del controlador en servicios.

Puede agregar cualquier parámetro y algo con esto. La mejor solución para tu vida de programación. Puedes hacer en su 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) Si desea llamar por rutas

Forma 1) Use el MakesHttpRequestsrasgo que utilizó en la Prueba de la Unidad de Aplicación.

Recomiendo esto si tiene una razón especial para hacer este proxy, puede usar cualquier parámetro y encabezado personalizado . También será una solicitud interna en laravel. (Solicitud HTTP falsa) Puede ver más detalles para el callmétodo aquí .

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

Sin embargo, esta tampoco es una "buena" solución.

Manera 2) Usar el cliente guzzlehttp

Esta es la solución más terrible, creo. También puede usar cualquier parámetro y encabezados personalizados . Pero esto sería hacer una solicitud http externa adicional. Por lo tanto, el servidor web HTTP debe estar ejecutándose.

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

Finalmente estoy usando el camino 1 del caso 2. Necesito parámetros y

kargnas
fuente
1
Way 2 no debe escribirse allí, nunca querrás auto-solicitar http, incluso en una estructura de código incorrecta.
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
fuente
77
Edite con más información. Se desaconsejan las respuestas de solo código y "pruebe esto", ya que no contienen contenido que se pueda buscar y no explican por qué alguien debería "probar esto".
abarisone
2

Puede usar un método estático en PrintReportController y luego llamarlo desde SubmitPerformanceController de esta manera;

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

Este enfoque también funciona con la misma jerarquía de archivos del controlador:

$printReport = new PrintReportController;

$prinReport->getPrintReport();
Jay Marz
fuente
Me gusta este enfoque en comparación con App :: make one porque la sugerencia de tipo de los bloques doc todavía funciona en phpStorm de esta manera.
Floris
1

Aquí el rasgo emula completamente el controlador en ejecución mediante un enrutador laravel (incluido el soporte de middlewares y la inyección de dependencia). Probado solo con la versión 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();
    }
}

Luego solo agrégalo a tu clase y ejecuta el controlador. Tenga en cuenta que esa inyección de dependencia se asignará con su ruta actual.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}
Anton
fuente
Tenga en cuenta que hacer app()->make(......)es igual a app(......)lo que es más corto.
matiaslauriti
1

Puede acceder al controlador instanciando y llamando a doAction: (poner use Illuminate\Support\Facades\App;antes de la declaración de clase del controlador)

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

También tenga en cuenta que al hacer esto no ejecutará ninguno de los middlewares declarados en ese controlador.

Abhijeet Navgire
fuente
-2

Respuesta tardía, pero he estado buscando esto por algún tiempo. Esto ahora es posible de una manera muy simple.

Sin parámetros

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

Con parámetros

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

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

En 5.0, requería todo el camino, ahora es mucho más simple.

Vipul Nandan
fuente
3
La pregunta original era cómo acceder al método de un controlador desde otro controlador, no cómo redirigir a la acción de otro método específico, por lo que su solución no está relacionada con la pregunta.
matiaslauriti