Heim >PHP-Framework >Laravel >Bringen Sie Ihnen Schritt für Schritt bei, wie Sie einen Laravel-Abfragefilter implementieren
Abfragefilter... Häufige Fragen bei der Entwicklung von Systemen. Aber wenn man anfängt, Code zu schreiben, stellen sich für jeden Entwickler viele bekannte Fragen: „Wo soll ich diese Abfragelogik platzieren? Wie soll ich sie verwalten, um die Verwendung zu vereinfachen?“ Ehrlich gesagt schreibe ich für jedes Projekt, das ich entwickle, in einem anderen Stil, basierend auf meinen Erfahrungen mit früheren Projekten, die ich erstellt habe. Und jedes Mal, wenn ich ein neues Projekt starte, stelle ich mir dieses Mal die gleiche Frage: Wie ordne ich Abfragefilter an? Dieser Artikel kann als schrittweise Entwicklung eines Abfragefiltersystems mit entsprechenden Problemen betrachtet werden.
Zum Zeitpunkt des Schreibens dieses Artikels verwende ich Laravel 9 auf PHP 8.1 und MySQL 8. Ich glaube, dass der Technologie-Stack kein großes Problem darstellt. Hier konzentrieren wir uns hauptsächlich auf den Aufbau eines Abfragefiltersystems. In diesem Artikel werde ich den Aufbau eines Filters für die Benutzertabelle demonstrieren.
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * 运行迁移 * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->string('gender', 10)->nullable()->index(); $table->boolean('is_active')->default(true)->index(); $table->boolean('is_admin')->default(false)->index(); $table->timestamp('birthday')->nullable(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } /** * 回退迁移 * * @return void */ public function down() { Schema::dropIfExists('users'); } }
Darüber hinaus verwende ich Laravel Telescope, um Abfragen einfach zu überwachen.
An meinen ersten Tagen, an denen ich die Verwendung von Laravel erlernte, rief ich oft Filter direkt auf dem Controller auf. Einfach, keine Zauberei, leicht zu verstehen, aber es gibt Probleme mit dieser Methode:
<?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; class UserController extends Controller { public function __invoke(Request $request) { // /users?name=ryder&email=hartman&gender=male&is_active=1&is_admin=0&birthday=2014-11-30 $query = User::query(); if ($request->has('name')) { $query->where('name', 'like', "%{$request->input('name')}%"); } if ($request->has('email')) { $query->where('email', 'like', "%{$request->input('email')}%"); } if ($request->has('gender')) { $query->where('gender', $request->input('gender')); } if ($request->has('is_active')) { $query->where('is_active', $request->input('is_active') ? 1 : 0); } if ($request->has('is_admin')) { $query->where('is_admin', $request->input('is_admin') ? 1 : 0); } if ($request->has('birthday')) { $query->whereDate('birthday', $request->input('birthday')); } return $query->paginate(); // select * from `users` where `name` like '%ryder%' and `email` like '%hartman%' and `gender` = 'male' and `is_active` = 1 and `is_admin` = 0 and date(`birthday`) = '2014-11-30' limit 15 offset 0 } }
Um die Logik beim Filtern ausblenden zu können, versuchen wir es mit Laravel’s Local Scope. Konvertieren Sie die Abfrage in einen Funktionsumfang im Benutzermodell:
// User.php public function scopeName(Builder $query): Builder { if (request()->has('name')) { $query->where('name', 'like', "%" . request()->input('name') . "%"); } return $query; } public function scopeEmail(Builder $query): Builder { if (request()->has('email')) { $query->where('email', 'like', "%" . request()->input('email') . "%"); } return $query; } public function scopeGender(Builder $query): Builder { if (request()->has('gender')) { $query->where('gender', request()->input('gender')); } return $query; } public function scopeIsActive(Builder $query): Builder { if (request()->has('is_active')) { $query->where('is_active', request()->input('is_active') ? 1 : 0); } return $query; } public function scopeIsAdmin(Builder $query): Builder { if (request()->has('is_admin')) { $query->where('is_admin', request()->input('is_admin') ? 1 : 0); } return $query; } public function scopeBirthday(Builder $query): Builder { if (request()->has('birthday')) { $query->where('birthday', request()->input('birthday')); } return $query; } // UserController.php public function __invoke(Request $request) { // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11 $query = User::query() ->name() ->email() ->gender() ->isActive() ->isAdmin() ->birthday(); return $query->paginate(); // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0 }
Mit diesem Setup haben wir die meisten Datenbankoperationen in die Modellklasse verschoben, es gab jedoch viele Codeduplizierungen. Beispiel 2 hat den gleichen Namen und E-Mail-Bereichsfilter, das gleiche Geschlecht am Geburtstag und die Gruppe „is_active/is_admin“. Wir werden ähnliche Abfragefunktionen gruppieren.
// User.php public function scopeRelativeFilter(Builder $query, $inputName): Builder { if (request()->has($inputName)) { $query->where($inputName, 'like', "%" . request()->input($inputName) . "%"); } return $query; } public function scopeExactFilter(Builder $query, $inputName): Builder { if (request()->has($inputName)) { $query->where($inputName, request()->input($inputName)); } return $query; } public function scopeBooleanFilter(Builder $query, $inputName): Builder { if (request()->has($inputName)) { $query->where($inputName, request()->input($inputName) ? 1 : 0); } return $query; } // UserController.php public function __invoke(Request $request) { // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11 $query = User::query() ->relativeFilter('name') ->relativeFilter('email') ->exactFilter('gender') ->booleanFilter('is_active') ->booleanFilter('is_admin') ->exactFilter('birthday'); return $query->paginate(); // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0 }
An dieser Stelle haben wir die meisten Duplikate gruppiert. Allerdings ist es etwas schwierig, die if-Anweisungen zu entfernen oder diese Filter auf ein anderes Modell zu erweitern. Wir suchen nach einer Möglichkeit, dieses Problem vollständig zu lösen.
Das Pipeline-Entwurfsmuster ist ein Entwurfsmuster, das die Möglichkeit bietet, eine Abfolge von Vorgängen Schritt für Schritt zu erstellen und auszuführen. Laravel verfügt über eine integrierte Pipeline, die es uns erleichtert, dieses Entwurfsmuster in der Praxis anzuwenden, aber aus irgendeinem Grund ist es nicht in der offiziellen Dokumentation aufgeführt. Auch Laravel selbst nutzt Pipelines als Middleware zwischen Anfragen und Antworten. Im einfachsten Fall können wir zur Verwendung von Pipeline in Laravel
app(\Illuminate\Pipeline\Pipeline::class) ->send($intialData) ->through($pipes) ->thenReturn(); // data with pipes applied
verwenden. Für unser Problem können wir die anfängliche Abfrage User:query() über den Filterschritt an die Pipeline übergeben und den Abfrage-Builder zurückgeben, der den Filter anwendet .
// UserController public function __invoke(Request $request) { // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11 $query = app(Pipeline::class) ->send(User::query()) ->through([ // filters ]) ->thenReturn(); return $query->paginate();
Jetzt müssen wir den Pipeline-Filter erstellen:
// File: app/Models/Pipes/RelativeFilter.php <?php namespace App\Models\Pipes; use Illuminate\Database\Eloquent\Builder; class RelativeFilter { public function __construct(protected string $inputName) { } public function handle(Builder $query, \Closure $next) { if (request()->has($this->inputName)) { $query->where($this->inputName, 'like', "%" . request()->input($this->inputName) . "%"); } return $next($query); } } // File: app/Models/Pipes/ExactFilter.php <?php namespace App\Models\Pipes; use Illuminate\Database\Eloquent\Builder; class ExactFilter { public function __construct(protected string $inputName) { } public function handle(Builder $query, \Closure $next) { if (request()->has($this->inputName)) { $query->where($this->inputName, request()->input($this->inputName)); } return $next($query); } } //File: app/Models/Pipes/BooleanFilter.php <?php namespace App\Models\Pipes; use Illuminate\Database\Eloquent\Builder; class BooleanFilter { public function __construct(protected string $inputName) { } public function handle(Builder $query, \Closure $next) { if (request()->has($this->inputName)) { $query->where($this->inputName, request()->input($this->inputName) ? 1 : 0); } return $next($query); } } // UserController public function __invoke(Request $request) { // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11 $query = app(Pipeline::class) ->send(User::query()) ->through([ new \App\Models\Pipes\RelativeFilter('name'), new \App\Models\Pipes\RelativeFilter('email'), new \App\Models\Pipes\ExactFilter('gender'), new \App\Models\Pipes\BooleanFilter('is_active'), new \App\Models\Pipes\BooleanFilter('is_admin'), new \App\Models\Pipes\ExactFilter('birthday'), ]) ->thenReturn(); return $query->paginate(); // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0 }
Indem wir jede Abfragelogik in eine separate Klasse verschieben, erschließen wir Anpassungsmöglichkeiten mithilfe von OOP, einschließlich Polymorphismus, Vererbung, Kapselung und Abstraktion. Sie können beispielsweise in der Handle-Funktion der Pipeline sehen, dass nur die Logik in der if-Anweisung unterschiedlich ist. Ich werde sie trennen und abstrahieren, indem ich eine abstrakte Klasse BaseFilter erstelle Um es zu implementieren und sogar zu erweitern, erstellen Sie einfach eine Pipeline, erweitern Sie BaseFilter und deklarieren Sie die Funktion apply, um sie auf die Pipeline anzuwenden.
Lokalen Bereich mit Pipeline kombinieren//File: app/Models/Pipes/BaseFilter.php <?php namespace App\Models\Pipes; use Illuminate\Database\Eloquent\Builder; abstract class BaseFilter { public function __construct(protected string $inputName) { } public function handle(Builder $query, \Closure $next) { if (request()->has($this->inputName)) { $query = $this->apply($query); } return $next($query); } abstract protected function apply(Builder $query): Builder; } // BooleanFilter class BooleanFilter extends BaseFilter { protected function apply(Builder $query): Builder { return $query->where($this->inputName, request()->input($this->inputName) ? 1 : 0); } } // ExactFilter class ExactFilter extends BaseFilter { protected function apply(Builder $query): Builder { return $query->where($this->inputName, request()->input($this->inputName)); } } // RelativeFilter class RelativeFilter extends BaseFilter { protected function apply(Builder $query): Builder { return $query->where($this->inputName, 'like', "%" . request()->input($this->inputName) . "%"); } }
Benutzer können jetzt von überall aus Filter aufrufen. Aber auch andere Modelle möchten Filterung implementieren. Wir erstellen ein Trait, das den Bereich enthält, und deklarieren die Pipeline, die am Filterprozess innerhalb des Modells teilnimmt.
// User.php public function scopeFilter(Builder $query) { $criteria = $this->filterCriteria(); return app(\Illuminate\Pipeline\Pipeline::class) ->send($query) ->through($criteria) ->thenReturn(); } public function filterCriteria(): array { return [ new \App\Models\Pipes\RelativeFilter('name'), new \App\Models\Pipes\RelativeFilter('email'), new \App\Models\Pipes\ExactFilter('gender'), new \App\Models\Pipes\BooleanFilter('is_active'), new \App\Models\Pipes\BooleanFilter('is_admin'), new \App\Models\Pipes\ExactFilter('birthday'), ]; } // UserController.php public function __invoke(Request $request) { // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11 return User::query() ->filter() ->paginate() ->appends($request->query()); // 将所有当前查询附加到分页链接中 // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0 }
Wir haben das Teilen-und-herrschen-Problem gelöst, jede Datei, jede Klasse, jede Funktion hat jetzt eine klare Verantwortung. Der Code ist auch sauber, intuitiv und einfacher wiederzuverwenden, nicht wahr? Ich habe den Code des gesamten Prozesses dieser Beitragsdemo hier eingefügt.
FazitProgrammiervideoÜbersetzungsadresse: https://learnku.com/laravel/t/68762
Mehr für Programmierkenntnisse finden Sie unter:
Das obige ist der detaillierte Inhalt vonBringen Sie Ihnen Schritt für Schritt bei, wie Sie einen Laravel-Abfragefilter implementieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!