Pembuat kode untuk Laravel - untuk input OAS, untuk output JSON-API

Kemampuan untuk membuat generator kode untuk API guna menyelamatkan generasi mendatang dari kebutuhan untuk terus-menerus membuat pengontrol, model, router, middleware, migrasi, kerangka yang sama, filter, validasi, dll. Yang sama. secara manual (bahkan dalam konteks kerangka kerja yang akrab dan nyaman), sepertinya menarik bagi saya.

Saya mempelajari tipifikasi dan seluk-beluk spesifikasi OpenAPI, saya menyukainya karena linearitas dan kemampuannya untuk menggambarkan struktur dan jenis entitas apa pun pada kedalaman 1-3 tingkat. Karena pada waktu itu saya sudah terbiasa dengan Laravel (Yii2 digunakan untuk menggunakannya, CI tetapi mereka kurang populer), serta dengan format output data json-api - seluruh arsitektur turun di kepala saya dengan grafik yang terhubung.



Mari kita beralih ke contoh.

Misalkan kita memiliki entitas berikut yang dijelaskan dalam OAS:

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 

Jika kita menjalankan perintah

 php artisan api:generate oas/openapi.yaml --migrations 

maka kita mendapatkan objek yang dihasilkan berikut:

1) Pengendali Entitas

 <?php namespace Modules\V1\Http\Controllers; class ArticleController extends DefaultController { } 

Dia sudah tahu GET / POST / PATCH / DELETE, yang dia akan pergi ke tabel melalui model, migrasi yang juga akan dihasilkan. DefaultController selalu tersedia untuk pengembang sehingga dimungkinkan untuk mengimplementasikan fungsionalitas untuk semua pengontrol.

2) Model entitas artikel

 <?php namespace Modules\V2\Entities; use Illuminate\Database\Eloquent\SoftDeletes; use rjapi\extension\BaseModel; class Article extends BaseModel { use SoftDeletes; // >>>props>>> protected $dates = ['deleted_at']; protected $primaryKey = 'id'; protected $table = 'article'; public $timestamps = false; public $incrementing = false; // <<<props<<< // >>>methods>>> public function tag() { return $this->belongsToMany(Tag::class, 'tag_article'); } public function topic() { return $this->belongsTo(Topic::class); } // <<<methods<<< } 


Seperti yang Anda lihat, komentar muncul di sini // >>> alat peraga >>> dan // >>> metode >>> - mereka diperlukan untuk memisahkan ruang kode dari ruang kode pengguna. Ada juga hubungan tag / topik - belognsToMany / milikTo, masing-masing, yang akan menghubungkan entitas Artikel dengan tag / topik, memberikan kesempatan untuk mengaksesnya dalam hubungan json-api dengan permintaan GET tunggal atau mengubahnya dengan memperbarui artikel.

3) Migrasi entitas, dengan dukungan rollback (refleksi / atomicity):

 <?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'); // Show at the top of main page $table->unsignedTinyInteger('show_in_top'); $table->enum('status', ["draft","published","postponed","archived"]); // ManyToOne Topic relationship $table->unsignedInteger('topic_id'); $table->foreign('topic_id', 'idx_fk_topic_id')->references('id')->on('topic')->onDelete('cascade')->onUpdate('cascade'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('article'); } } 

Generator migrasi mendukung semua jenis indeks (termasuk yang komposit).

4) Router untuk permintaan bergulir:

 // >>>routes>>> // Article routes Route::group(['prefix' => 'v2', 'namespace' => 'Modules\\V2\\Http\\Controllers'], function() { // bulk routes Route::post('/article/bulk', 'ArticleController@createBulk'); Route::patch('/article/bulk', 'ArticleController@updateBulk'); Route::delete('/article/bulk', 'ArticleController@deleteBulk'); // basic routes Route::get('/article', 'ArticleController@index'); Route::get('/article/{id}', 'ArticleController@view'); Route::post('/article', 'ArticleController@create'); Route::patch('/article/{id}', 'ArticleController@update'); Route::delete('/article/{id}', 'ArticleController@delete'); // relation routes Route::get('/article/{id}/relationships/{relations}', 'ArticleController@relations'); Route::post('/article/{id}/relationships/{relations}', 'ArticleController@createRelations'); Route::patch('/article/{id}/relationships/{relations}', 'ArticleController@updateRelations'); Route::delete('/article/{id}/relationships/{relations}', 'ArticleController@deleteRelations'); }); // <<<routes<<< 

Rute dibuat tidak hanya untuk permintaan dasar, tetapi juga hubungan dengan entitas lain, yang akan ditarik oleh 1 kueri dan ekstensi sebagai operasi massal untuk kemampuan membuat / memperbarui / menghapus data dalam batch.

5) FormRequest untuk pra-pemrosesan / validasi permintaan:

 <?php namespace Modules\V1\Http\Requests; use rjapi\extension\BaseFormRequest; class ArticleFormRequest extends BaseFormRequest { // >>>props>>> public $id = null; // Attributes public $title = null; public $description = null; public $url = null; public $show_in_top = null; public $status = null; public $topic_id = null; public $rate = null; public $date_posted = null; public $time_to_live = null; public $deleted_at = null; // <<<props<<< // >>>methods>>> public function authorize(): bool { return true; } public function rules(): array { return [ 'title' => 'required|string|min:16|max:256|', 'description' => 'required|string|min:32|max:1024|', 'url' => 'string|min:16|max:255|', // Show at the top of main page 'show_in_top' => 'boolean', // The state of an article 'status' => 'in:draft,published,postponed,archived|', // ManyToOne Topic relationship 'topic_id' => 'required|integer|min:1|max:6|', 'rate' => '|min:3|max:9|', 'date_posted' => '', 'time_to_live' => '', 'deleted_at' => '', ]; } public function relations(): array { return [ 'tag', 'topic', ]; } // <<<methods<<< } 

Semuanya sederhana di sini - aturan untuk memvalidasi properti dan relasi dibuat untuk menghubungkan entitas utama dengan entitas dalam metode relasi.

Akhirnya, bagian terbaik adalah contoh kueri:

 http://example.com/v1/article?include=tag&page=2&limit=10&sort=asc 

Tampilkan artikel, kencangkan semua tag-nya dalam hubungan, pagination pada halaman 2, dengan batas 10, dan urutkan berdasarkan usia.

Jika kita tidak perlu menampilkan semua bidang artikel:

 http://example.com/v1/article/1?include=tag&data=["title", "description"] 

Urutkan berdasarkan beberapa bidang:

 http://example.com/v1/article/1?include=tag&order_by={"title":"asc", "created_at":"desc"} 

Pemfilteran (atau apa pun yang termasuk dalam klausa WHERE):

 http://example.com/v1/article?include=tag&filter=[["updated_at", ">", "2018-01-03 12:13:13"], ["updated_at", "<", "2018-09-03 12:13:15"]] 

Contoh membuat entitas (dalam hal ini, 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 } } } 

Jawabannya adalah:

 { "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" } } } 

Lihat tautan di tautan-> mandiri? kamu bisa segera
 GET http://laravel.loc/article/1 
atau simpan untuk referensi di masa mendatang.

 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" } } ] } 

Saya mengembalikan daftar objek, di mana masing-masing jenis objek ini, id-nya, seluruh rangkaian atribut, lalu tautan ke diri saya sendiri, hubungan yang diminta dalam url melalui include = tag, dengan spesifikasi, tidak ada batasan untuk menyertakan relasi, yaitu, misalnya, termasuk = tag, topik, kota, dan semuanya akan dimasukkan dalam blok hubungan , dan objek mereka akan disimpan di dalamnya.

Jika kita ingin mendapatkan 1 artikel dan semua hubungannya / hubungan:

 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" } } ] } 


Dan di sini adalah contoh menambahkan hubungan ke entitas yang ada - permintaan:

 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" }] } } } } 

Jawabannya adalah:

 { "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" } } ] } 

Anda dapat mentransfer opsi tambahan ke generator konsol:

 php artisan api:generate oas/openapi.yaml --migrations --regenerate --merge=last 

Dengan demikian, Anda memberi tahu generator - membuat kode dengan migrasi (Anda sudah melihatnya) dan membuat ulang kode, menyimpannya dengan perubahan terbaru dari riwayat yang disimpan, tanpa memengaruhi bagian khusus dari kode, tetapi hanya yang dihasilkan secara otomatis (yaitu, hanya yang , yang disorot oleh blok khusus dengan komentar dalam kode). Dimungkinkan untuk menentukan langkah mundur, misalnya: --merge = 9 (memutar kembali generasi 9 langkah mundur), tanggal pembuatan kode di masa lalu --merge = "2017-07-29 11:35:32" .

Salah satu pengguna perpustakaan menyarankan pembuatan tes fungsional untuk kueri - dengan menambahkan opsi - tes Anda dapat menjalankan tes untuk memastikan bahwa API Anda berfungsi tanpa kesalahan.

Selain itu, Anda dapat menggunakan banyak opsi (semuanya dikonfigurasi secara fleksibel melalui konfigurator, yang terletak pada modul yang dibuat - contoh: /Modules/V2/Config/config.php ):

 <?php return [ 'name' => 'V2', 'query_params'=> [ //    - 'limit' => 15, 'sort' => 'desc', 'access_token' => 'db7329d5a3f381875ea6ce7e28fe1ea536d0acaf', ], 'trees'=> [ //      'menu' => true, ], 'jwt'=> [ // jwt  'enabled' => true, 'table' => 'user', 'activate' => 30, 'expires' => 3600, ], 'state_machine'=> [ // finite state machine 'article'=> [ 'status'=> [ 'enabled' => true, 'states'=> [ 'initial' => ['draft'], 'draft' => ['published'], 'published' => ['archived', 'postponed'], 'postponed' => ['published', 'archived'], 'archived' => [''], ], ], ], ], 'spell_check'=> [ //       'article'=> [ 'description'=> [ 'enabled' => true, 'language' => 'en', ], ], ], 'bit_mask'=> [ //     permissions ( true/false /  ) 'user'=> [ 'permissions'=> [ 'enabled' => true, 'flags'=> [ 'publisher' => 1, 'editor' => 2, 'manager' => 4, 'photo_reporter' => 8, 'admin' => 16, ], ], ], ], 'cache'=> [ //    'tag'=> [ 'enabled' => false, //    tag  'stampede_xfetch' => true, 'stampede_beta' => 1.1, 'ttl' => 3600, ], 'article'=> [ 'enabled' => true, //    article  'stampede_xfetch' => true, 'stampede_beta' => 1.5, 'ttl' => 300, ], ], ]; 

Secara alami, semua konfigurasi dapat dihidupkan / dimatikan jika perlu. Untuk informasi lebih lanjut tentang fitur tambahan pembuat kode, lihat tautan di bawah ini. Kontribusi selalu diterima.

Terima kasih atas perhatian Anda, kesuksesan kreatif.

Sumber Daya Artikel:

Source: https://habr.com/ru/post/id427741/


All Articles