ホームページ > バックエンド開発 > PHPチュートリアル > Laravel の内部 - フレームワークの拡張

Laravel の内部 - フレームワークの拡張

Susan Sarandon
リリース: 2024-12-10 18:05:12
オリジナル
972 人が閲覧しました

Laravel Under The Hood - Extending the framework

こんにちは ?

数日前、不安定なテストを修正していましたが、ファクトリー内で一意有効な値が必要であることが判明しました。 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. それがインターフェースであれば、基本 FakerGenerator クラスを拡張する新しいクラスを作成し、いくつかの新しい機能を追加して、それをコンテナーに再バインドするだけで済みます。しかし、私たちにはそんな贅沢はありません。
  2. 工場が関係しています。これは、単純なインスタンス化ではなく、何らかのロジックが実行されていることを意味します。この場合、ファクトリはいくつかのプロバイダー (PhoneNumber、Text、UserAgent など) を追加します。したがって、再バインドしようとしても、元の FakerGenerator を返すファクトリを使用する必要があります。

解決策?? 「ポイント 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 サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート