数日前、不安定なテストを修正していましたが、ファクトリー内で一意と有効な値が必要であることが判明しました。 Laravel は FakerPHP をラップしており、通常は fake() ヘルパーを通じてアクセスします。 FakerPHP には valid() や unique() などの修飾子が付属していますが、一度に 1 つしか使用できないため、fake()->unique()->valid() を実行することはできません。必要です。そこで私は、独自のモディファイアを作成したい場合はどうすればよいのかと考えました。たとえば、uniqueAndValid()、またはその他の修飾子です。どうすればフレームワークを拡張できるでしょうか?
思考回路を放棄します。
過剰に設計されたソリューションに飛び込む前に、よりシンプルなオプションがあるかどうかを常に確認し、自分が何を扱っているのかを理解したいと考えています。それでは、fake() ヘルパーを見てみましょう:
function fake($locale = null) { if (app()->bound('config')) { $locale ??= app('config')->get('app.faker_locale'); } $locale ??= 'en_US'; $abstract = \Faker\Generator::class.':'.$locale; if (! app()->bound($abstract)) { app()->singleton($abstract, fn () => \Faker\Factory::create($locale)); } return app()->make($abstract); }
コードを読むと、Laravel がシングルトンをコンテナにバインドしていることがわかります。ただし、抽象を検査すると、インターフェイスを実装していない通常のクラスであり、オブジェクトはファクトリ経由で作成されています。これは事態を複雑にします。なぜ?
解決策?? 「ポイント 1 で概説したように、新しい発電機を返却する独自の工場を設立することを妨げているのは何だろう?」と考える人もいるかもしれません。まあ、何もできませんが、それはできません。私たちがフレームワークを使用する理由はいくつかありますが、その 1 つはアップデートです。 FakerPHP に新しいプロバイダーが追加されたり、メジャー アップグレードが行われた場合はどうなりますか? Laravel がコードを調整しますが、何も変更していない人は何も気づきません。ただし、私たちは取り残され、コードが壊れる可能性さえあります (おそらく)。ですから、はい、そこまではしたくないのです。
基本的なオプションを検討したので、デザイン パターンなどのより高度なオプションについて考え始めることができます。正確な実装は必要ありません。問題に馴染みのあるものだけで十分です。これが、私が彼らを知ることは良いことだといつも言う理由です。この場合、古い機能を維持しながら新しい機能を追加することで、Generator クラスを「装飾」できます。いいですね?その方法を見てみましょう!
まず、新しいクラス FakerGenerator を作成しましょう:
function fake($locale = null) { if (app()->bound('config')) { $locale ??= app('config')->get('app.faker_locale'); } $locale ??= 'en_US'; $abstract = \Faker\Generator::class.':'.$locale; if (! app()->bound($abstract)) { app()->singleton($abstract, fn () => \Faker\Factory::create($locale)); } return app()->make($abstract); }
これが私たちの「デコレーター」(のようなもの)になります。これは、基本ジェネレーターを依存関係として想定し、新しい修飾子 uniqueAndValid() を導入する単純なクラスです。また、Laravel の ForwardsCalls トレイトを使用し、基本オブジェクトへの呼び出しをプロキシできるようにします。
このトレイトには、forwardCallTo と forwardDecoratedCallTo という 2 つのメソッドがあります。装飾されたオブジェクトにメソッドをチェーンする場合は、後者を使用します。私たちの場合、電話は常に 1 回です。
カスタム修飾子である UniqueAndValidGenerator も実装する必要がありますが、これはこの記事の要点ではありません。実装に興味がある場合、このクラスは基本的に FakerPHP に同梱されている ValidGenerator と UniqueGenerator を組み合わせたもので、ここで見つけることができます。
次に、AppServiceProvider でフレームワークを拡張しましょう:
<?php namespace App\Support; use Closure; use Faker\Generator; use Illuminate\Support\Traits\ForwardsCalls; class FakerGenerator { use ForwardsCalls; public function __construct(private readonly Generator $generator) { } public function uniqueAndValid(Closure $validator = null): UniqueAndValidGenerator { return new UniqueAndValidGenerator($this->generator, $validator); } public function __call($method, $parameters): mixed { return $this->forwardCallTo($this->generator, $method, $parameters); } }
extend() メソッドは、指定された名前に一致するアブストラクトがコンテナにバインドされているかどうかを確認します。存在する場合は、その値がクロージャの結果でオーバーライドされます。見てください:
<?php namespace App\Providers; use Closure; use Faker\Generator; use App\Support\FakerGenerator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->extend( $this->fakerAbstractName(), fn (Generator $base) => new FakerGenerator($base) ); } private function fakerAbstractName(): string { // This is important, it matches the name bound by the fake() helper return Generator::class . ':' . app('config')->get('app.faker_locale'); } }
そのため、fakerAbstractName() メソッドを定義しました。このメソッドは、fake() ヘルパーがコンテナ内でバインドするのと同じ名前を生成します。
上記のコードを見逃した場合は再確認してください。コメントを残しました。
これで、fake() を呼び出すたびに FakerGenerator のインスタンスが返され、導入したカスタム モディファイアにアクセスできるようになります。 FakerGenerator クラスに存在しない呼び出しを呼び出すたびに、__call() がトリガーされ、forwardCallTo() メソッドを使用してベース ジェネレーターにプロキシされます。
それだけです!ついに、fake()->uniqueAndValid()->randomElement() を実行できるようになり、魅力的に機能します!
結論を出す前に、これは純粋なデコレータ パターンではないことを指摘しておきます。ただし、パターンは神聖なテキストではありません。ニーズに合わせて調整し、問題を解決してください。
フレームワークは非常に便利で、Laravel には多くの組み込み機能が付属しています。ただし、プロジェクト内のすべてのエッジケースをカバーできるわけではなく、場合によっては行き詰まりに陥る可能性があります。そのような場合には、いつでもフレームワークを拡張できます。これがいかにシンプルであるかを見てきましたが、この Faker の例以外にも当てはまる主なアイデアを理解していただけたでしょうか。
常にシンプルなことから始めて、問題に対する最も簡単な解決策を探してください。必要に応じて複雑になるため、基本的な継承で十分であれば、デコレーターなどを実装する必要はありません。フレームワークを拡張する場合は、損失が利益を上回るような行き過ぎないように注意してください。フレームワークの一部を自分で保守することになるのは望ましくありません。
以上がLaravel の内部 - フレームワークの拡張の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。