Vor ein paar Tagen habe ich einen fehlerhaften Test repariert und es stellte sich heraus, dass ich einige einzigartige und gültige Werte in meiner Fabrik benötigte. Laravel umhüllt FakerPHP, auf das wir normalerweise über den Helfer fake() zugreifen. FakerPHP verfügt über Modifikatoren wie valid() und unique(), aber Sie können jeweils nur einen verwenden, sodass Sie nicht fake()->unique()->valid() ausführen können, was genau das ist, was ich benötigt. Das brachte mich zum Nachdenken: Was wäre, wenn wir unseren eigenen Modifikator erstellen wollten? Zum Beispiel uniqueAndValid() oder ein anderer Modifikator. Wie können wir den Rahmen erweitern?
Ich werde meinen Gedankengang fallen lassen.
Bevor ich mich auf eine überentwickelte Lösung einlasse, möchte ich immer prüfen, ob es eine einfachere Option gibt und verstehen, womit ich es zu tun habe. Werfen wir also einen Blick auf den Fake()-Helfer:
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); }
Wenn wir den Code lesen, können wir sehen, dass Laravel einen Singleton an den Container bindet. Wenn wir uns jedoch die Zusammenfassung ansehen, handelt es sich um eine reguläre Klasse, die keine Schnittstelle implementiert, und das Objekt wird über eine Factory erstellt. Das macht die Sache komplizierter. Warum?
Lösungen ?? Man könnte denken: „Was hält uns davon ab, eine eigene Fabrik zu gründen, die den neuen Generator zurückgibt, wie in Punkt 1 beschrieben?“ Nun, nichts, das können wir tun, aber wir werden es nicht tun! Wir verwenden ein Framework aus mehreren Gründen, einer davon sind Updates. Was passiert, wenn FakerPHP einen neuen Anbieter hinzufügt oder ein größeres Upgrade durchführt? Laravel wird den Code anpassen und Leute, die keine Änderungen vorgenommen haben, werden nichts bemerken. Allerdings würden wir außen vor bleiben und unser Code könnte (höchstwahrscheinlich) sogar kaputt gehen. Also, ja, so weit wollen wir nicht gehen.
Nachdem wir nun die grundlegenden Optionen erkundet haben, können wir beginnen, über fortgeschrittenere Optionen nachzudenken, wie z. B. Designmuster. Wir brauchen keine genaue Implementierung, sondern nur etwas, das unserem Problem vertraut ist. Deshalb sage ich immer, dass es gut ist, sie zu kennen. In diesem Fall können wir die Generator-Klasse „dekorieren“, indem wir neue Funktionen hinzufügen und gleichzeitig die alten beibehalten. Klingt gut? Mal sehen wie!
Zuerst erstellen wir eine neue Klasse, 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); }
Das wird (sozusagen) unser „Dekorateur“ sein. Es handelt sich um eine einfache Klasse, die den Basisgenerator als Abhängigkeit erwartet und einen neuen Modifikator einführt: uniqueAndValid(). Es verwendet auch die ForwardsCalls-Eigenschaft von Laravel, die es ermöglicht, Aufrufe an das Basisobjekt weiterzuleiten.
Diese Eigenschaft verfügt über zwei Methoden: ForwardCallTo und ForwardDecoratedCallTo. Verwenden Sie Letzteres, wenn Sie Methoden für das dekorierte Objekt verketten möchten. In unserem Fall haben wir immer einen einzigen Anruf.
Wir müssen auch den UniqueAndValidGenerator implementieren, bei dem es sich um den benutzerdefinierten Modifikator handelt, aber das ist nicht der Sinn des Artikels. Wenn Sie an der Implementierung interessiert sind, ist diese Klasse im Grunde eine Mischung aus ValidGenerator und UniqueGenerator, die mit FakerPHP geliefert werden. Sie können sie hier finden.
Jetzt erweitern wir das Framework im 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); } }
Die Methode „extend()“ prüft, ob ein Abstract, das mit dem angegebenen Namen übereinstimmt, an den Container gebunden wurde. Wenn ja, überschreibt es seinen Wert mit dem Ergebnis des Abschlusses, sehen Sie sich Folgendes an:
<?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'); } }
Deshalb haben wir die Methode fakerAbstractName() definiert, die denselben Namen generiert, den der Fake()-Helfer im Container bindet.
Überprüfen Sie den Code oben noch einmal. Wenn Sie ihn verpasst haben, habe ich einen Kommentar hinterlassen.
Jetzt wird jedes Mal, wenn wir fake() aufrufen, eine Instanz von FakerGenerator zurückgegeben und wir haben Zugriff auf den von uns eingeführten benutzerdefinierten Modifikator. Jedes Mal, wenn wir einen Aufruf aufrufen, der in der FakerGenerator-Klasse nicht vorhanden ist, wird __call() ausgelöst und über die Methode „forwardCallTo()“ an den Basisgenerator weitergeleitet.
Das ist es! Endlich kann ich fake()->uniqueAndValid()->randomElement() ausführen, und es funktioniert wie ein Zauber!
Bevor wir zum Schluss kommen, möchte ich darauf hinweisen, dass es sich hierbei nicht um ein reines Dekorationsmuster handelt. Muster sind jedoch keine heiligen Texte; Passen Sie sie an Ihre Bedürfnisse an und lösen Sie das Problem.
Frameworks sind unglaublich hilfreich und Laravel verfügt über viele integrierte Funktionen. Allerdings können sie nicht alle Grenzfälle in Ihren Projekten abdecken, und manchmal geraten Sie möglicherweise in eine Sackgasse. In diesem Fall können Sie das Framework jederzeit erweitern. Wir haben gesehen, wie einfach es ist, und ich hoffe, Sie haben die Hauptidee verstanden, die über dieses Faker-Beispiel hinaus gilt.
Beginnen Sie immer einfach und suchen Sie nach der einfachsten Lösung für das Problem. Komplexität entsteht dann, wenn sie nötig ist. Wenn also die einfache Vererbung ausreicht, ist es nicht nötig, einen Dekorator oder etwas anderes zu implementieren. Wenn Sie das Framework erweitern, achten Sie darauf, dass Sie nicht zu weit gehen und der Verlust den Gewinn überwiegt. Sie möchten am Ende doch nicht einen Teil des Frameworks selbst pflegen.
Das obige ist der detaillierte Inhalt vonLaravel Under The Hood – Den Rahmen erweitern. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!