La possibilité de créer un générateur de code pour l'API afin de sauver la génération future de la nécessité de créer constamment les mêmes contrôleurs, modèles, routeurs, middleware, migrations, squelettes, filtres, validations, etc. manuellement (même dans le cadre de frameworks familiers et pratiques), cela m'a paru intéressant.
J'ai étudié la typification et les subtilités de la spécification OpenAPI, je l'ai aimé par sa linéarité et sa capacité à décrire la structure et les types de toute entité aux niveaux 1 à 3. Puisqu'à cette époque, je connaissais déjà Laravel (Yii2, CI le faisait auparavant, mais ils étaient moins populaires), ainsi que le format de sortie de données json-api - toute l'architecture est tombée dans ma tête avec un graphique connecté.

Passons aux exemples.
Supposons que nous ayons l'entité suivante décrite dans l'OEA:
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
Si nous exécutons la commande
php artisan api:generate oas/openapi.yaml --migrations
nous obtenons ensuite les objets générés suivants:
1) Contrôleur d'entité
<?php namespace Modules\V1\Http\Controllers; class ArticleController extends DefaultController { }
Il connaît déjà GET / POST / PATCH / DELETE, pour lequel il ira à la table à travers le modèle, dont la migration sera également générée. DefaultController est toujours disponible pour le développeur, de sorte qu'il est possible d'implémenter des fonctionnalités pour tous les contrôleurs.
2) Modèle d'entité article
<?php namespace Modules\V2\Entities; use Illuminate\Database\Eloquent\SoftDeletes; use rjapi\extension\BaseModel; class Article extends BaseModel { use SoftDeletes;
Comme vous pouvez le voir, des commentaires sont apparus ici // >>> accessoires >>> et // >>> méthodes >>> - ils sont nécessaires pour séparer l'espace de code de l'espace de code utilisateur. Il existe également une relation balise / sujet - belognsToMany / appartientTo, respectivement, qui connectera l'entité Article aux balises / sujets, offrant la possibilité d'y accéder dans les relations json-api avec une seule demande GET ou de les modifier en mettant à jour l'article.
3) Migration d'entité, avec support de restauration (réflexion / atomicité):
<?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');
Le générateur de migration prend en charge tous les types d'index (y compris les index composites).
4) Routeur pour les requêtes de défilement:
Les routes ont été créées non seulement pour les requêtes de base, mais aussi pour les relations avec d'autres entités, qui seront tirées par 1 requête et des extensions en tant qu'opérations en bloc pour la possibilité de créer / mettre à jour / supprimer des données par lots.
5) FormRequest pour le prétraitement / la validation des demandes:
<?php namespace Modules\V1\Http\Requests; use rjapi\extension\BaseFormRequest; class ArticleFormRequest extends BaseFormRequest {
Ici, tout est simple: les règles de validation des propriétés et des relations sont générées pour lier l'entité principale aux entités de la méthode des relations.
Enfin, la meilleure partie est les exemples de requĂŞte:
http:
Affichez un article, resserrez toutes ses balises dans les relations, la pagination à la page 2, avec une limite de 10, et triez par âge.
Si nous n'avons pas besoin d'afficher tous les champs de l'article:
http:
Trier par plusieurs champs:
http:
Filtrage (ou tout ce qui tombe dans la clause WHERE):
http:
Un exemple de création d'une entité (dans ce cas, des articles):
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 } } }
La réponse est:
{ "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" } } }
Voir le lien dans links-> self? vous pouvez immédiatement
GET http://laravel.loc/article/1
ou enregistrez-le pour référence future.
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" } } ] }
Il a renvoyé une liste d'objets, dans chacun desquels le type de cet objet, son id, l'ensemble des attributs, puis un lien vers lui-même, les relations demandées dans l'URL via
include = tag, selon la spécification, il n'y a pas de restrictions sur l'inclusion de relations, c'est-à -dire, par exemple,
include = tag, sujet, ville et tous seront inclus dans le bloc des
relations , et leurs objets seront stockés dans
inclus .
Si nous voulons obtenir 1 article et toutes ses relations / relations:
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" } } ] }
Et voici un exemple d'ajout de relations à une entité existante - une demande:
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" }] } } } }
La réponse est:
{ "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" } } ] }
Vous pouvez transférer des options supplémentaires au générateur de console:
php artisan api:generate oas/openapi.yaml --migrations --regenerate --merge=last
Ainsi, vous dites au générateur - créer un code avec des migrations (vous l'avez déjà vu) et régénérer le code, le stocker avec les dernières modifications de l'historique enregistré, sans affecter les sections personnalisées du code, mais uniquement celles qui ont été générées automatiquement (c'est-à -dire, juste celles , qui sont mis en évidence par des blocs spéciaux avec des commentaires dans le code). Il est possible de spécifier des étapes en arrière, par exemple:
--merge = 9 (restaurer la génération 9 étapes en arrière), les dates de génération de code dans le passé
--merge = "2017-07-29 11:35:32" .
L'un des utilisateurs de la bibliothèque a suggéré de générer des tests fonctionnels pour les requêtes - en ajoutant l'option
--tests , vous pouvez exécuter des tests pour vous assurer que votre API fonctionne sans erreur.
De plus, vous pouvez utiliser de nombreuses options (elles sont toutes configurées de manière flexible via le configurateur, qui se trouve dans le module généré - exemple:
/Modules/V2/Config/config.php ):
<?php return [ 'name' => 'V2', 'query_params'=> [
Naturellement, toutes les configurations peuvent être activées / désactivées si nécessaire. Pour plus d'informations sur les fonctionnalités supplémentaires du générateur de code, consultez les liens ci-dessous. Les contributions sont toujours les bienvenues.
Merci pour votre attention, succès créatif.
Ressources de l'article: