Metode Access Controller dari pengontrol lain di Laravel 5

162

Saya memiliki dua pengendali SubmitPerformanceControllerdan PrintReportController.

Dalam PrintReportControllersaya punya metode yang disebut getPrintReport.

Bagaimana cara mengakses metode ini SubmitPerformanceController?

Iftakharul Alam
sumber

Jawaban:

365

Anda dapat mengakses metode pengontrol Anda seperti ini:

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

Ini akan berhasil, tetapi buruk dalam hal pengaturan kode (ingat untuk menggunakan namespace yang tepat untuk Anda PrintReportController)

Anda dapat memperpanjang PrintReportControllersehingga SubmitPerformanceControllerakan mewarisi metode itu

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

Tetapi ini juga akan mewarisi semua metode lain dari PrintReportController.

Pendekatan terbaik adalah dengan membuat trait(misalnya dalam app/Traits), mengimplementasikan logika di sana dan memberi tahu pengendali Anda untuk menggunakannya:

trait PrintReport {

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

Beri tahu pengontrol Anda untuk menggunakan sifat ini:

class PrintReportController extends Controller {
     use PrintReport;
}

class SubmitPerformanceController extends Controller {
     use PrintReport;
}

Kedua solusi membuat SubmitPerformanceControllermemiliki getPrintReportmetode sehingga Anda dapat memanggilnya $this->getPrintReport();dari dalam controller atau langsung sebagai rute (jika Anda memetakannya dalam routes.php)

Anda dapat membaca lebih lanjut tentang ciri-ciri di sini .

Sh1d0w
sumber
10
di mana file termasuk sifat harus disimpan?
Brainmaniac
24
app('App\Http\Controllers\PrintReportController')->getPrintReport();dapat ditransformasikan menjadi app(PrintReportController::class')->getPrintReport(). Solusi bersih untuk saya.
Vincent Decaux
Di mana file sifat disimpan?
Eric McWinNEr
@EricMcWinNEr Ini dapat disimpan di mana saja Anda suka, seperti misalkan App \ Traits. Tetapi pastikan untuk menggunakan namespace yang sesuai dalam sifat itu.
Pengadilan
1
Hanya sedikit contoh untuk menggunakan ciri-ciri di Laravel: develodesign.co.uk/news/…
Erenor Paz
48

Jika Anda memerlukan metode itu di pengontrol lain, itu artinya Anda perlu mengabstraksikannya dan membuatnya bisa digunakan kembali. Pindahkan implementasi itu ke kelas layanan (ReportingService atau yang serupa) dan suntikkan ke controller Anda.

Contoh:

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

Lakukan hal yang sama untuk pengontrol lain di mana Anda memerlukan implementasi itu. Mencapai metode pengontrol dari pengontrol lain adalah bau kode.

Ruffles
sumber
Di mana Anda akan menyelamatkan kelas ini dalam hal struktur proyek?
Amitay
1
Entah Servicesfolder jika proyek tidak besar atau folder fitur disebut Reportingapakah itu proyek yang lebih besar dan menggunakan Folders By Featurestruktur.
Ruffles
Apakah Anda merujuk ke Penyedia Layanan (kelas layanan) seperti di sini laravel.com/docs/5.7/providers atau Wadah Layanan seperti di sini laravel.com/docs/5.7/container ?
Baspa
1
@ Baspa Tidak, kelas PHP normal.
Ruffles
27

Memanggil Pengendali dari Pengontrol lain tidak disarankan, namun jika karena alasan apa pun Anda harus melakukannya, Anda dapat melakukan ini:

Metode kompatibel Laravel 5

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

Catatan: ini tidak akan memperbarui URL halaman.

Lebih baik untuk memanggil Rute dan membiarkannya memanggil controller.

return \Redirect::route('route-name-here');
Mahmoud Zalt
sumber
2
Kenapa tidak direkomendasikan?
brunouno
Ini harus menjadi jawaban teratas.
Justin Vincent
13

Anda seharusnya tidak. Ini anti-pola. Jika Anda memiliki metode dalam satu pengontrol yang perlu Anda akses di pengontrol lain, maka itu pertanda Anda perlu faktor ulang.

Pertimbangkan untuk memfaktorkan ulang metode keluar ke kelas layanan, yang kemudian dapat Anda instantiate di beberapa pengontrol. Jadi, jika Anda perlu menawarkan laporan cetak untuk banyak model, Anda dapat melakukan sesuatu seperti ini:

class ExampleController extends Controller
{
    public function printReport()
    {
        $report = new PrintReport($itemToReportOn);
        return $report->render();
    }
}
Martin Bean
sumber
10
\App::call('App\Http\Controllers\MyController@getFoo')
the_hasanov
sumber
11
Terlepas dari kenyataan bahwa jawaban Anda mungkin benar, alangkah baiknya memperpanjang sedikit dan memberikan penjelasan lebih lanjut.
scana
9

Pertama-tama, meminta metode pengontrol dari pengontrol lain adalah JAHAT. Ini akan menyebabkan banyak masalah tersembunyi dalam siklus hidup Laravel.

Bagaimanapun, ada banyak solusi untuk melakukan itu. Anda dapat memilih salah satu dari berbagai cara ini.

Kasus 1) Jika Anda ingin menelepon berdasarkan Kelas

Cara 1) Cara sederhana

Tetapi Anda tidak dapat menambahkan parameter atau autentikasi dengan cara ini.

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

Cara 2) Bagi logika pengontrol ke dalam layanan.

Anda dapat menambahkan parameter dan sesuatu dengan ini. Solusi terbaik untuk kehidupan pemrograman Anda. Anda dapat membuat Repositorysebagai gantinya 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 ...
    }
}

Kasus 2) Jika Anda ingin menelepon berdasarkan Rute

Cara 1) Gunakan MakesHttpRequestssifat yang digunakan dalam Pengujian Unit Aplikasi.

Saya merekomendasikan ini jika Anda memiliki alasan khusus untuk membuat proksi ini, Anda dapat menggunakan parameter dan header khusus apa pun . Juga ini akan menjadi permintaan internal di laravel. (Permintaan HTTP Palsu) Anda dapat melihat detail lebih lanjut untuk callmetode ini di sini .

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

Namun ini bukan solusi yang 'baik' juga.

Cara 2) Gunakan klien guzzlehttp

Ini adalah solusi paling mengerikan yang saya pikir. Anda dapat menggunakan parameter dan tajuk khusus apa pun. Tapi ini akan membuat permintaan http eksternal tambahan. Jadi HTTP Webserver harus dijalankan.

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

Akhirnya saya menggunakan Way 1 dari Kasus 2. Saya perlu parameter dan

kargnas
sumber
1
Cara 2 tidak boleh dituliskan di sana, Anda tidak ingin sendiri http-request sendiri, bahkan dalam struktur kode yang buruk.
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
sumber
7
Harap edit dengan informasi lebih lanjut. Jawaban khusus kode dan "coba ini" tidak disarankan, karena tidak mengandung konten yang dapat ditelusuri, dan jangan jelaskan mengapa seseorang harus "mencoba ini".
abarisone
2

Anda dapat menggunakan metode statis di PrintReportController dan kemudian memanggilnya dari SubmitPerformanceController seperti ini;

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

Pendekatan ini juga berfungsi dengan hierarki file Controller yang sama:

$printReport = new PrintReportController;

$prinReport->getPrintReport();
Jay Marz
sumber
Saya suka pendekatan ini dibandingkan dengan App :: make one karena tipe petunjuk dari blok doc masih berfungsi di phpStorm dengan cara ini.
Floris
1

Di sini sifat sepenuhnya mengemulasi menjalankan controller oleh router laravel (termasuk dukungan middlewares dan injeksi ketergantungan). Diuji hanya dengan versi 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();
    }
}

Kemudian tambahkan saja ke kelas Anda dan jalankan controller. Perhatikan, injeksi ketergantungan akan diberikan dengan rute Anda saat ini.

class CustomController extends Controller {
    use RunsAnotherController;

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

        return $this->runController($controller, 'doSomething');
    }
}
Anton
sumber
Pertimbangkan bahwa melakukan app()->make(......)sama dengan app(......)sehingga lebih pendek.
matiaslauriti
1

Anda dapat mengakses controller dengan membuat instance dan memanggil doAction: (diletakkan use Illuminate\Support\Facades\App;sebelum deklarasi kelas controller)

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

Perhatikan juga bahwa dengan melakukan ini, Anda tidak akan menjalankan middlewares yang dideklarasikan pada pengontrol itu.

Abhijeet Navgire
sumber
-2

Terlambat membalas, tetapi saya telah mencari ini untuk beberapa waktu. Ini sekarang mungkin dengan cara yang sangat sederhana.

Tanpa parameter

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

Dengan Parameter

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

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

Kembali di 5.0 diperlukan seluruh path, sekarang jauh lebih sederhana.

Vipul Nandan
sumber
3
Pertanyaan aslinya adalah bagaimana mengakses metode pengontrol dari pengontrol lain, bukan bagaimana mengarahkan ke tindakan metode tertentu lainnya, sehingga solusi Anda tidak terkait dengan pertanyaan.
matiaslauriti