منشئ الكود لـ Laravel - لإدخال OAS ، لإخراج JSON-API

القدرة على إنشاء مولد رمز لواجهة برمجة التطبيقات من أجل إنقاذ الجيل المستقبلي من الحاجة إلى إنشاء نفس وحدات التحكم والنماذج والموجهات والبرامج الوسيطة والترحيل والهياكل العظمية والمرشحات وعمليات التحقق ، وما إلى ذلك باستمرار. يدويًا (حتى في سياق الأطر المألوفة والمريحة) ، بدا لي مثيرًا للاهتمام.

لقد درست مواصفات ودقة مواصفات OpenAPI ، وقد أحببتها بخطتها وقدرتها على وصف هيكل وأنواع أي كيان بمستويات تتراوح بين 1-3. منذ ذلك الوقت كنت على دراية بالفعل بـ Laravel (Yii2 كانت تستخدمه ، CI لكنها كانت أقل شيوعًا) ، وكذلك مع تنسيق إخراج بيانات json-api - سقطت العمارة بأكملها في رأسي مع رسم بياني متصل.



دعنا ننتقل إلى الأمثلة.

لنفترض أن لدينا الكيان التالي الموضح في 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 

إذا قمنا بتشغيل الأمر

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

ثم نحصل على الكائنات التي تم إنشاؤها التالية:

1) مراقب الكيانات

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

إنه يعرف بالفعل GET / POST / PATCH / DELETE ، حيث سيذهب إلى الجدول من خلال النموذج ، وسيتم إنشاء الترحيل له أيضًا. يتوفر DefaultController دائمًا للمطور ، بحيث يمكن تنفيذ الوظائف لجميع وحدات التحكم.

2) نموذج مادة المادة

 <?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<<< } 


كما ترى ، ظهرت التعليقات هنا // >>> props >>> و // >>> طرق >>> - وهي ضرورية من أجل فصل مساحة التعليمات البرمجية عن مساحة رمز المستخدم. هناك أيضًا علاقة بين العلامة / الموضوع - belognsToMany / ينتسب إلى ، على التوالي ، والتي ستربط كيان المقالة بالعلامات / المواضيع ، مما يوفر الفرصة للوصول إليها في العلاقات json-api مع طلب GET واحد أو تغييرها عن طريق تحديث المقالة.

3) ترحيل الكيان ، مع دعم التراجع (الانعكاس / الذرية):

 <?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'); } } 

يدعم منشئ الترحيل جميع أنواع الفهارس (بما في ذلك الفهارس المركبة).

4) جهاز التوجيه لطلبات التمرير:

 // >>>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<<< 

تم إنشاء المسارات ليس فقط للاستعلامات الأساسية ، ولكن أيضًا العلاقات مع الكيانات الأخرى ، والتي سيتم سحبها بواسطة استعلام واحد وإضافات كعمليات مجمعة للقدرة على إنشاء / تحديث / حذف البيانات على دفعات.

5) FormRequest للتجهيز المسبق / التحقق من صحة الطلبات:

 <?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<<< } 

كل شيء بسيط هنا - يتم إنشاء قواعد التحقق من الخصائص والعلاقات لربط الكيان الرئيسي بالكيانات في طريقة العلاقات.

أخيرًا ، أفضل جزء هو أمثلة الاستعلام:

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

قم بعرض مقال وشد جميع علاماته في العلاقات وترقيم الصفحات في الصفحة 2 بحد أقصى ١٠ وقم بالفرز حسب العمر.

إذا لم نكن بحاجة إلى عرض جميع حقول المقالة:

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

فرز حسب حقول متعددة:

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

التصفية (أو أي شيء يقع في بند 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"]] 

مثال على إنشاء كيان (في هذه الحالة ، مقالات):

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

الجواب:

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

انظر الرابط في الروابط> الذات؟ يمكنك على الفور
 GET http://laravel.loc/article/1 
أو حفظه كمرجع مستقبلي.

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

أرجع قائمة من الكائنات ، في كل منها نوع هذا الكائن ، معرفه ، مجموعة السمات بالكامل ، ثم رابط إلى نفسه ، العلاقات المطلوبة في عنوان url عبر include = tag ، وفقًا للمواصفات ، لا توجد قيود على تضمين العلاقات ، على سبيل المثال ، include = tag ، سيتم تضمين الموضوع والمدينة وجميعها في كتلة العلاقات ، وسيتم تخزين كائناتها.

إذا أردنا الحصول على مقال واحد وكل علاقاته / علاقاته:

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


وإليك مثال على إضافة علاقات إلى كيان قائم - طلب:

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

الجواب:

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

يمكنك نقل خيارات إضافية إلى منشئ وحدة التحكم:

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

وبالتالي ، تخبر المولد - قم بإنشاء رمز مع عمليات الترحيل (التي رأيتها بالفعل) وقم بإعادة إنشاء الرمز ، وتخزينه بأحدث التغييرات من السجل المحفوظ ، دون التأثير على الأقسام المخصصة من الرمز ، ولكن فقط تلك التي تم إنشاؤها تلقائيًا (أي تلك فقط ، والتي يتم تمييزها بواسطة كتل خاصة مع تعليقات في الكود). من الممكن تحديد خطوات إلى الوراء ، على سبيل المثال: - دمج = 9 (استرجاع الجيل 9 خطوات إلى الوراء) ، تواريخ إنشاء التعليمات البرمجية في الماضي - دمج = "2017-07-29 11:35:32" .

اقترح أحد مستخدمي المكتبة إنشاء اختبارات وظيفية للاستعلامات - عن طريق إضافة خيار - اختبارات يمكنك تشغيل الاختبارات للتأكد من أن API الخاص بك يعمل بدون أخطاء.

بالإضافة إلى ذلك ، يمكنك استخدام العديد من الخيارات (يتم تكوينها جميعًا بمرونة من خلال أداة التهيئة ، والتي تكمن في الوحدة النمطية التي تم إنشاؤها - مثال: /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, ], ], ]; 

وبطبيعة الحال ، يمكن تشغيل / إيقاف جميع التكوينات إذا لزم الأمر. لمزيد من المعلومات حول الميزات الإضافية لمولد الرمز ، راجع الارتباطات أدناه. المساهمات مرحب بها دائما

شكرا لكم على اهتمامكم والنجاح الإبداعي.

موارد المادة:

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


All Articles