Die Möglichkeit, einen Codegenerator für die API zu erstellen, um die zukünftige Generation vor der Notwendigkeit zu bewahren, ständig dieselben Controller, Modelle, Router, Middleware, Migrationen, Skelette, Filter, Validierungen usw. zu erstellen. manuell (auch im Kontext vertrauter und praktischer Frameworks) schien es mir interessant.
Ich habe die Typisierung und Feinheiten der OpenAPI-Spezifikation untersucht. Ich mochte sie durch ihre Linearität und die Fähigkeit, die Struktur und die Typen von Entitäten auf 1 bis 3 Ebenen zu beschreiben. Da ich zu dieser Zeit bereits mit Laravel (Yii2 verwendete es früher, CI, aber sie waren weniger beliebt) sowie mit dem json-api-Datenausgabeformat vertraut war, ging mir die gesamte Architektur mit einem verbundenen Diagramm in den Kopf.

Kommen wir zu den Beispielen.
Angenommen, wir haben die folgende in OAS beschriebene Entität:
ArticleAttributes: description: Article attributes description type: object properties: title: required: true type: string minLength: 16 maxLength: 256 facets: index: idx_title: index description: required: true type: string minLength: 32 maxLength: 1024 facets: spell_check: true spell_language: en url: required: false type: string minLength: 16 maxLength: 255 facets: index: idx_url: unique show_in_top: description: Show at the top of main page required: false type: boolean status: description: The state of an article enum: ["draft", "published", "postponed", "archived"] facets: state_machine: initial: ['draft'] draft: ['published'] published: ['archived', 'postponed'] postponed: ['published', 'archived'] archived: [] topic_id: description: ManyToOne Topic relationship required: true type: integer minimum: 1 maximum: 6 facets: index: idx_fk_topic_id: foreign references: id on: topic onDelete: cascade onUpdate: cascade rate: type: number minimum: 3 maximum: 9 format: double date_posted: type: date-only time_to_live: type: time-only deleted_at: type: datetime
Wenn wir den Befehl ausführen
php artisan api:generate oas/openapi.yaml --migrations
dann erhalten wir die folgenden generierten Objekte:
1) Entity Controller
<?php namespace Modules\V1\Http\Controllers; class ArticleController extends DefaultController { }
Er kennt bereits GET / POST / PATCH / DELETE, für das er über das Modell an die Tabelle geht, für das auch die Migration generiert wird. DefaultController steht dem Entwickler immer zur Verfügung, so dass Funktionen für alle Controller implementiert werden können.
2) Artikelentitätsmodell
<?php namespace Modules\V2\Entities; use Illuminate\Database\Eloquent\SoftDeletes; use rjapi\extension\BaseModel; class Article extends BaseModel { use SoftDeletes;
Wie Sie sehen können, wurden hier Kommentare angezeigt // >>> Requisiten >>> und // >>> Methoden >>> - sie werden benötigt, um den Code-Raum vom Benutzer-Code-Raum zu trennen. Es gibt auch eine Tag / Topic-Beziehung - belognsToMany / ZugehörigkeitTo -, die die Artikelentität mit Tags / Themes verbindet und die Möglichkeit bietet, mit einer einzigen GET-Anforderung auf sie in den Beziehungen json-api zuzugreifen oder sie durch Aktualisieren des Artikels zu ändern.
3) Entitätsmigration mit Rollback-Unterstützung (Reflexion / Atomizität):
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateArticleTable extends Migration { public function up() { Schema::create('article', function(Blueprint $table) { $table->bigIncrements('id'); $table->string('title', 256); $table->index('title', 'idx_title'); $table->string('description', 1024); $table->string('url', 255); $table->unique('url', 'idx_url');
Der Migrationsgenerator unterstützt alle Arten von Indizes (einschließlich zusammengesetzter Indizes).
4) Router für Bildlaufanforderungen:
Routen wurden nicht nur für grundlegende Abfragen erstellt, sondern auch für Beziehungen zu anderen Entitäten, die von 1 Abfrage und Erweiterungen als Massenoperationen abgerufen werden, um Daten in Stapeln erstellen / aktualisieren / löschen zu können.
5) FormRequest zur Vorverarbeitung / Validierung von Anfragen:
<?php namespace Modules\V1\Http\Requests; use rjapi\extension\BaseFormRequest; class ArticleFormRequest extends BaseFormRequest {
Hier ist alles einfach - die Regeln zum Validieren von Eigenschaften und Beziehungen werden generiert, um die Hauptentität mit Entitäten in der Beziehungsmethode zu verknüpfen.
Der beste Teil sind schließlich die Abfragebeispiele:
http:
Zeigen Sie einen Artikel an, ziehen Sie alle Tags in Relationen und Paginierung auf Seite 2 mit einem Limit von 10 fest und sortieren Sie sie nach Alter.
Wenn wir nicht alle Felder des Artikels anzeigen müssen:
http:
Nach mehreren Feldern sortieren:
http:
Filtern (oder was auch immer in die WHERE-Klausel fällt):
http:
Ein Beispiel für die Erstellung einer Entität (in diesem Fall Artikel):
POST http://laravel.loc/v1/article { "data": { "type":"article", "attributes": { "title":"Foo bar Foo bar Foo bar Foo bar", "description":"description description description description description", "fake_attr": "attr", "url":"title title bla bla bla", "show_in_top":1 } } }
Die Antwort lautet:
{ "data": { "type": "article", "id": "1", "attributes": { "title": "Foo bar Foo bar Foo bar Foo bar", "description": "description description description description description", "url": "title title bla bla bla", "show_in_top": 1 }, "links": { "self": "laravel.loc/article/1" } } }
Siehe den Link in links-> self? Sie können sofort
GET http://laravel.loc/article/1
oder speichern Sie es zum späteren Nachschlagen.
GET http://laravel.loc/v1/article?include=tag&filter=[["updated_at", ">", "2017-01-03 12:13:13"], ["updated_at", "<", "2019-01-03 12:13:15"]] { "data": [ { "type": "article", "id": "1", "attributes": { "title": "Foo bar Foo bar Foo bar Foo bar 1", "description": "The quick brovn fox jumped ower the lazy dogg", "url": "http://example.com/articles_feed 1", "show_in_top": 0, "status": "draft", "topic_id": 1, "rate": 5, "date_posted": "2018-02-12", "time_to_live": "10:11:12" }, "links": { "self": "laravel.loc/article/1" }, "relationships": { "tag": { "links": { "self": "laravel.loc/article/1/relationships/tag", "related": "laravel.loc/article/1/tag" }, "data": [ { "type": "tag", "id": "1" } ] } } } ], "included": [ { "type": "tag", "id": "1", "attributes": { "title": "Tag 1" }, "links": { "self": "laravel.loc/tag/1" } } ] }
Ich habe eine Liste von Objekten zurückgegeben, in denen der Typ dieses Objekts, seine ID, der gesamte Satz von Attributen, dann ein Link zu mir selbst, Beziehungen, die in der URL über
include = tag angefordert werden, angegeben sind. Es gibt keine Einschränkungen für das Einbeziehen von Beziehungen, d.
H. Include = tag, Thema, Stadt und alle von ihnen werden in den
Beziehungsblock aufgenommen , und ihre Objekte werden in
eingeschlossen gespeichert.
Wenn wir 1 Artikel und alle seine Beziehungen / Beziehungen erhalten möchten:
GET http://laravel.loc/v1/article/1?include=tag&data=["title", "description"] { "data": { "type": "article", "id": "1", "attributes": { "title": "Foo bar Foo bar Foo bar Foo bar 123456", "description": "description description description description description 123456", }, "links": { "self": "laravel.loc/article/1" }, "relationships": { "tag": { "links": { "self": "laravel.loc/article/1/relationships/tag", "related": "laravel.loc/article/1/tag" }, "data": [ { "type": "tag", "id": "3" }, { "type": "tag", "id": "1" }, { "type": "tag", "id": "2" } ] } } }, "included": [ { "type": "tag", "id": "3", "attributes": { "title": "Tag 4" }, "links": { "self": "laravel.loc/tag/3" } }, { "type": "tag", "id": "1", "attributes": { "title": "Tag 2" }, "links": { "self": "laravel.loc/tag/1" } }, { "type": "tag", "id": "2", "attributes": { "title": "Tag 3" }, "links": { "self": "laravel.loc/tag/2" } } ] }
Und hier ist ein Beispiel für das Hinzufügen von Beziehungen zu einer vorhandenen Entität - eine Anforderung:
PATCH http://laravel.loc/v1/article/1/relationships/tag { "data": { "type":"article", "id":"1", "relationships": { "tag": { "data": [{ "type": "tag", "id": "2" },{ "type": "tag", "id": "3" }] } } } }
Die Antwort lautet:
{ "data": { "type": "article", "id": "1", "attributes": { "title": "Foo bar Foo bar Foo bar Foo bar 1", "description": "The quick brovn fox jumped ower the lazy dogg", "url": "http://example.com/articles_feed 1", "show_in_top": 0, "status": "draft", "topic_id": 1, "rate": 5, "date_posted": "2018-02-12", "time_to_live": "10:11:12" }, "links": { "self": "laravel.loc/article/1" }, "relationships": { "tag": { "links": { "self": "laravel.loc/article/1/relationships/tag", "related": "laravel.loc/article/1/tag" }, "data": [ { "type": "tag", "id": "2" }, { "type": "tag", "id": "3" } ] } } }, "included": [ { "type": "tag", "id": "2", "attributes": { "title": "Tag 2" }, "links": { "self": "laravel.loc/tag/2" } }, { "type": "tag", "id": "3", "attributes": { "title": "Tag 3" }, "links": { "self": "laravel.loc/tag/3" } } ] }
Sie können zusätzliche Optionen auf den Konsolengenerator übertragen:
php artisan api:generate oas/openapi.yaml --migrations --regenerate --merge=last
Auf diese Weise teilen Sie dem Generator mit, dass Sie einen Code mit Migrationen erstellen (Sie haben ihn bereits gesehen), den Code neu generieren und ihn mit den neuesten Änderungen aus dem gespeicherten Verlauf speichern, ohne die benutzerdefinierten Abschnitte des Codes zu beeinflussen, sondern nur diejenigen, die automatisch generiert wurden (dh nur diese) , die durch spezielle Blöcke mit Kommentaren im Code hervorgehoben werden). Es ist möglich, Schritte zurück anzugeben, zum Beispiel:
--merge = 9 (
Zurücksetzen der Generation 9 Schritte zurück), die Codegenerierungsdaten in der Vergangenheit
--merge = "2017-07-29 11:35:32" .
Einer der Benutzer der Bibliothek schlug vor, Funktionstests für Abfragen zu
generieren. Durch Hinzufügen der Option
--tests können Sie Tests ausführen, um sicherzustellen, dass Ihre API fehlerfrei funktioniert.
Darüber hinaus können Sie viele Optionen verwenden (alle werden flexibel über den Konfigurator konfiguriert, der im generierten Modul enthalten ist - Beispiel:
/Modules/V2/Config/config.php ):
<?php return [ 'name' => 'V2', 'query_params'=> [
Natürlich können alle Konfigurationen bei Bedarf ein- und ausgeschaltet werden. Weitere Informationen zu zusätzlichen Funktionen des Codegenerators finden Sie unter den folgenden Links. Beiträge sind immer willkommen.
Vielen Dank für Ihre Aufmerksamkeit, kreativen Erfolg.
Artikel Ressourcen: