Метод контроля доступа с другого контроллера в Laravel 5

162

У меня есть два контроллера SubmitPerformanceControllerи PrintReportController.

У PrintReportControllerменя есть метод, который называется getPrintReport.

Как получить доступ к этому методу в SubmitPerformanceController?

Ифтахарул Алам
источник

Ответы:

366

Вы можете получить доступ к своему методу контроллера следующим образом:

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

Это будет работать, но это плохо с точки зрения организации кода (не забывайте использовать правильное пространство имен для вашего PrintReportController)

Вы можете расширить, PrintReportControllerтак SubmitPerformanceControllerчто наследует этот метод

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

Но это также унаследует все другие методы от PrintReportController.

Наилучшим подходом будет создание trait(например, в app/Traits), реализация логики и указание контроллерам использовать ее:

trait PrintReport {

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

Скажите вашим контроллерам использовать эту черту:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Оба решения позволяют SubmitPerformanceControllerиспользовать getPrintReportметод, чтобы вы могли вызывать его $this->getPrintReport();из контроллера или напрямую как маршрут (если вы отобразили его в routes.php)

Вы можете прочитать больше о чертах здесь .

Sh1d0w
источник
10
где должен быть сохранен файл с чертой?
Brainmaniac
24
app('App\Http\Controllers\PrintReportController')->getPrintReport();может преобразоваться в app(PrintReportController::class')->getPrintReport(). Чистое решение для меня.
Винсент Деко
Где хранится файл признаков?
Эрик Маквиннер
@EricMcWinNEr Он может храниться где угодно, например, предположим, App \ Traits. Но убедитесь, что используете соответствующее пространство имен в этой черте.
Трибунал
1
Просто небольшой пример использования черт в Laravel: develodesign.co.uk/news/…
Эренор Пас
48

Если вам нужен этот метод в другом контроллере, это означает, что вам нужно абстрагировать его и сделать его многоразовым. Переместите эту реализацию в класс обслуживания (ReportingService или что-то подобное) и внедрите его в свои контроллеры.

Пример:

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

Сделайте то же самое для других контроллеров, где вам нужна эта реализация. Достижение методов контроллера от других контроллеров - запах кода.

Складки
источник
Где бы вы сохранили этот класс с точки зрения структуры проекта?
Amitay
1
Либо Servicesпапка, если проект не большой, либо папка с функциями, которая называется Reportingбольшим проектом и использует Folders By Featureструктуру.
Рябит
Вы имеете в виду поставщика услуг (класс обслуживания), как здесь laravel.com/docs/5.7/providers, или контейнер услуг, как здесь laravel.com/docs/5.7/container ?
БАСПА
1
@Baspa Нет, обычный класс PHP.
Складки
27

Вызов контроллера из другого контроллера не рекомендуется, однако, если по какой-либо причине вам придется это сделать, вы можете сделать это:

Laravel 5 совместимый метод

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

Примечание. Это не приведет к обновлению URL-адреса страницы.

Лучше вместо этого вызвать Route и позволить ему вызывать контроллер.

return \Redirect::route('route-name-here');
Махмуд Зальт
источник
2
Почему это не рекомендуется?
Brunouno
Это должен быть главный ответ.
Джастин Винсент
13

Ты не должен. Это анти-паттерн. Если у вас есть метод в одном контроллере, к которому вам нужен доступ в другом контроллере, то это признак того, что вам нужно пересмотреть фактор.

Подумайте о том, чтобы перевести метод в класс обслуживания, который затем можно создать на нескольких контроллерах. Поэтому, если вам нужно предложить отчеты для печати для нескольких моделей, вы можете сделать что-то вроде этого:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}
Мартин Бин
источник
10
\App::call('App\Http\Controllers\MyController@getFoo')
the_hasanov
источник
11
Несмотря на то, что ваш ответ может быть правильным, было бы неплохо немного его расширить и дать некоторые дополнительные пояснения.
Scana
9

Прежде всего, запрос метода контроллера от другого контроллера - ЗЛО. Это вызовет много скрытых проблем в жизненном цикле Laravel.

Во всяком случае, есть много решений для этого. Вы можете выбрать один из этих различных способов.

Случай 1) Если вы хотите позвонить на основе классов

Способ 1) Простой способ

Но вы не можете добавлять параметры или аутентификацию таким способом.

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

Способ 2) Разделить логику контроллера на сервисы.

Вы можете добавить любые параметры и что-то с этим. Лучшее решение для вашей жизни программирования. Вы можете сделать Repositoryвместо этого 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 ...
    }
}

Случай 2) Если вы хотите позвонить на основе маршрутов

Способ 1) Использовать MakesHttpRequestsчерту, использованную в модульном тестировании приложения.

Я рекомендую это, если у вас есть особая причина для создания этого прокси, вы можете использовать любые параметры и пользовательские заголовки . Также это будет внутренний запрос в laravel. (Поддельный HTTP-запрос) Вы можете увидеть более подробную информацию о callметоде здесь .

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

Однако это тоже не «хорошее» решение.

Способ 2) Использование клиента guzzlehttp

Это самое ужасное решение, я думаю. Вы также можете использовать любые параметры и пользовательские заголовки . Но это будет делать внешний дополнительный http-запрос. Так что HTTP Webserver должен быть запущен.

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

Наконец, я использую способ 1 из случая 2. Мне нужны параметры и

kargnas
источник
1
Способ 2 не должен быть записан там, вы никогда не захотите самостоятельно запросить http, даже в плохой структуре кода.
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
Пожалуйста, отредактируйте с дополнительной информацией. Ответы «только код» и «попробуй это» не приветствуются, поскольку они не содержат контента для поиска и не объясняют, почему кто-то должен «попробовать это».
августа
2

Вы можете использовать статический метод в PrintReportController и затем вызывать его из SubmitPerformanceController следующим образом;

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
источник
2

Этот подход также работает с той же иерархией файлов контроллера:

$printReport = new PrintReportController;

$prinReport->getPrintReport();
Джей Марц
источник
Мне нравится этот подход по сравнению с App :: make, потому что подсказки типа блоков документов все еще работают в phpStorm.
Флорис
1

Здесь эта черта полностью эмулирует работу контроллера через маршрутизатор laravel (включая поддержку промежуточного программного обеспечения и внедрение зависимостей). Протестировано только с версией 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();
    }
}

Затем просто добавьте его в свой класс и запустите контроллер. Обратите внимание, что внедрение зависимостей будет назначено вашему текущему маршруту.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}
Антон
источник
Примите во внимание, что выполнение app()->make(......)равно, app(......)поэтому оно короче.
matiaslauriti
1

Вы можете получить доступ к контроллеру, создав его экземпляр и вызвав doAction: (поместите use Illuminate\Support\Facades\App;перед объявлением класса контроллера)

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

Также обратите внимание, что, выполнив это, вы не будете выполнять какие-либо промежуточные программы, объявленные на этом контроллере.

Абхиджит Навгире
источник
-2

Поздний ответ, но я долго искал это. Теперь это возможно очень простым способом.

Без параметров

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

С параметрами

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

Документы: https://laravel.com/docs/5.6/responses#redirecting-controller-actions

Еще в 5.0 для этого требовался весь путь, теперь все намного проще.

Випул Нандан
источник
3
Первоначальный вопрос заключался в том, как получить доступ к методу контроллера из другого контроллера, а не как перенаправить на действие другого конкретного метода, поэтому ваше решение не связано с вопросом.
matiaslauriti