48

laravel ファサードは最終的に利便性のために存在し、代わりにこれらのクラスを注入して疎結合を許可する必要があることを示唆する多くのソースを読みました。Taylor Otwellでさえ、これを行う方法を説明する投稿をしています。不思議に思うのは私だけではないようだ。

use Redirect;

class Example class
{
    public function example()
    {
         return Redirect::route("route.name");
    }
}

になるだろう

use Illuminate\Routing\Redirector as Redirect;

class Example class
{
    protected $redirect;

    public function __constructor(Redirect $redirect)
    {
        $this->redirect = $redirect
    }

    public function example()
    {
         return $this->redirect->route("route.name");
    }
}

これは問題ありませんが、一部のコンストラクターとメソッドが 4 つ以上のパラメーターを取り始めていることがわかり始めています。

Laravel IoCはクラス コンストラクターと特定のメソッド (コントローラー) にのみ注入されるように見えるため、かなり無駄のない関数とクラスを使用している場合でも、クラスのコンストラクターが必要なクラスで詰め込まれていることがわかります。必要な方法。

このアプローチを続けると、laravel のようなフレームワークを使用している場合、車輪の再発明のように感じる独自の IoC コンテナーが必要になることがわかりました。

たとえば、サービスを使用して、それらを処理するコントローラーではなく、ビジネス/ビューロジックを制御します-それらは単にビューをルーティングします。したがって、コントローラーは最初に対応する を取得しservice、次にparameterをその URL で取得します。1 つのサービス関数もフォームからの値をチェックする必要があるため、 と が必要RequestですValidator。そのように、私は4つのパラメータを持っています。

// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;

...

public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id) 
{ 
    // Call some method in the service to do complex validation
    $validation = $my_service->doValidation($request, $validator);

    // Also return the view information
    $viewinfo = $my_service->getViewInfo($user_id);

    if ($validation === 'ok') {
        return view("some_view", ['view_info'=>$viewinfo]);
    } else {
        return view("another_view", ['view_info'=>$viewinfo]);
    }
}

これは一例です。実際には、私のコンストラクターの多くには、すでに複数のクラス (モデル、サービス、パラメーター、ファサード) が注入されています。コンストラクター注入 (該当する場合) をメソッド注入に「オフロード」し、それらのメソッドを呼び出すクラスにコンストラクターを使用して依存関係を注入するようにしました。

経験則として、メソッドまたはクラス コンストラクターの 4 つを超えるパラメーターは、悪い習慣/コードのにおいがすると言われています。ただし、laravel ファサードを注入するパスを選択した場合、これを実際に回避する方法がわかりません。

この考えは間違っていますか?私のクラス/関数は十分に無駄がありませんか? 私はlaravelsコンテナの要点を見逃していますか、それとも独自のIoCコンテナを作成することを本当に考える必要がありますか? 他のいくつかの回答は、laravelコンテナが私の問題を解決できることを示唆しているようです?

とは言っても、この問題について決定的なコンセンサスはないようです...

4

6 に答える 6

26

これは、コンストラクター インジェクションの利点の 1 つです。コンストラクターのパラメーターが大きくなりすぎるため、クラスが多くのことを行っている場合に明らかになります。

最初にすべきことは、責任が多すぎるコントローラーを分割することです。

ページ コントローラーがあるとします。

Class PageController
{

    public function __construct(
        Request $request,
        ClientRepositoryInterface $clientrepo,
        StaffRepositortInterface $staffRepo
        )
    {

     $this->clientRepository = $clientRepo;
     //etc etc

    }

    public function aboutAction()
    {
        $teamMembers = $this->staffRepository->getAll();
        //render view
    }

    public function allClientsAction()
    {
        $clients = $this->clientRepository->getAll();
        //render view
    }

    public function addClientAction(Request $request, Validator $validator)
    {
        $this->clientRepository->createFromArray($request->all() $validator);
        //do stuff
    }
}

ClientControllerこれは、2 つのコントローラーに分割するための最有力候補ですAboutController

それを行っても、まだ依存関係が多すぎる場合は、間接依存関係と呼ぶものを探す時が来ました (それらの適切な名前を考えることができないためです!) - 依存クラスによって直接使用されていない依存関係、しかし代わりに別の依存関係に渡されました。

この例はaddClientAction、リクエストとバリデータを必要とし、clientRepostory.

リクエストからクライアントを作成するための新しいクラスを作成することでリファクタリングできます。これにより、依存関係が減り、コントローラーとリポジトリの両方が簡素化されます。

//think of a better name!
Class ClientCreator 
{
    public function __construct(Request $request, validator $validator){}

    public function getClient(){}
    public function isValid(){}
    public function getErrors(){}
}

メソッドは次のようになります。

public function addClientAction(ClientCreator $creator)
{ 
     if($creator->isValid()){
         $this->clientRepository->add($creator->getClient());
     }else{
         //handle errors
     }
}

依存関係の数が多すぎるという厳密なルールはありません。幸いなことに、疎結合を使用してアプリを構築した場合、リファクタリングは比較的簡単です。

パラメーターのないコンストラクターや、メソッド全体に隠されている一連の静的呼び出しよりも、6 つまたは 7 つの依存関係を持つコンストラクターの方がはるかに望ましいと思います。

于 2016-02-05T00:19:10.650 に答える
1

Laravel のルーティング メカニズムの一部を形成するクラス メソッド (ミドルウェア、コントローラーなど)にも、依存関係を注入するために使用される型ヒントがあります。それらすべてをコンストラクターに注入する必要はありません。これは、コンストラクターをスリムに保つのに役立つかもしれませんが、私は 4 つのパラメーター制限の経験則に精通していません。PSR-2 では、メソッド定義を複数行に拡張することができます。おそらく、5 つ以上のパラメーターが必要になることは珍しくないためです。

あなたの例では、コンストラクターにサービスRequestValidatorサービスを挿入することができます。これは、複数のメソッドで使用されることが多いためです。

コンセンサスの確立に関しては、Laravelは、アプリケーションが画一的なアプローチを利用するのに十分なほど類似しているために、より強い意見を持つ必要があります。もっと簡単に言うと、将来のバージョンではファサードがドードーのようになると思います。

于 2016-01-27T00:45:03.830 に答える
1

あなたの考えや懸念は正しいし、私もそれらを持っていました。ファサードにはいくつかの利点があります (私は通常は使用しません) が、使用する場合は、コントローラーでのみ使用することをお勧めします。コントローラーは少なくとも私にとっては入口と出口のポイントに過ぎないためです。

あなたが与えた例について、私が一般的にそれをどのように処理するかを示します:

// MyServiceInterface is binded using the laravel container
use Interfaces\MyServiceInterface;
use Illuminate\Http\Request;
use Illuminate\Validation\Factory as Validator;

...
class ExampleController {

    protected $request;

    public function __constructor(Request $request) {
        // Do this if all/most your methods need the Request
        $this->request = $request;
    }

    public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id) 
    { 
        // I do my validation inside the service I use,
        // the controller for me is just a funnel for sending the data
        // and returning response

        //now I call the service, that handle the "business"
        //he makes validation and fails if data is not valid
        //or continues to return the result

        try {
            $viewinfo = $my_service->getViewInfo($user_id);
            return view("some_view", ['view_info'=>$viewinfo]);
        } catch (ValidationException $ex) {
            return view("another_view", ['view_info'=>$viewinfo]);
        }
    }
}



class MyService implements MyServiceInterface {

    protected $validator;

    public function __constructor(Validator $validator) {
        $this->validator = $validator;
    }

    public function getViewInfo($user_id, $data) 
    { 

        $this->validator->validate($data, $rules);
        if  ($this->validator->fails()) {
            //this is not the exact syntax, but the idea is to throw an exception
            //with the errors inside
            throw new ValidationException($this->validator);
        }

        echo "doing stuff here with $data";
        return "magic";
    }
}

コードを個々の小さな断片に分割し、それぞれが自分の責任を処理することを忘れないでください。コードを適切に分割すると、ほとんどの場合、コンストラクターのパラメーターがそれほど多くなくなり、コードのテストとモックが容易になります。

最後に 1 つだけ注意してください。小さなアプリケーション、または「連絡先ページ」や「連絡先ページの送信」などの巨大なアプリケーションのページを作成している場合、ファサードを使用してコントローラーですべてを確実に実行できます。プロジェクトの複雑さ。

于 2016-02-03T07:35:01.410 に答える
1

私はその美しいアーキテクチャのためにlaravelが大好きです.今、私のアプローチから、すべてのファサードをコントローラーメソッドに注入しないのはなぜですか? 他のコントローラーで必要になる可能性があるため、コントローラーの間違ったプラクティスでのみ Redirect ファサードを注入します。そして主に、ほとんど使用されるものは常に宣言する必要がありますが、一部またはのみを使用してメソッドを介してそれらを注入するベストプラクティスを使用する場合は、一番上で宣言するとメモリの最適化と速度が妨げられます。コード。これが役立つことを願っています

于 2016-02-04T10:26:19.640 に答える