هناك العديد من الطرق لإنشاء تطبيق ويب حديث ، ولكن كل فريق يواجه حتمًا نفس المجموعة من الأسئلة: كيفية توزيع المسؤوليات الأمامية والخلفية ، وكيفية تقليل ظهور المنطق المكرر - على سبيل المثال ، عند التحقق من صحة البيانات ، والمكتبات التي تستخدمها ، وكيفية ضمان موثوقة وشفافية النقل بين الأمام والخلف وتوثيق التعليمات البرمجية.
في رأينا ، تمكنا من تنفيذ مثال جيد على حل متوازن في التعقيد والربح ، والذي نستخدمه بنجاح في الإنتاج بناءً على Symfony و React.
ما نوع تنسيق تبادل البيانات الذي يمكن أن نختاره عند التخطيط لتطوير واجهة برمجة تطبيقات الواجهة الخلفية في منتج ويب مطور بنشاط يحتوي على نماذج ديناميكية مع الحقول ذات الصلة ومنطق الأعمال المعقد؟
- SWAGGER هو خيار جيد ، وهناك وثائق وأدوات تصحيح ملائمة. علاوة على ذلك ، هناك مكتبات لـ Symfony تعمل على أتمتة العملية ، ولكن للأسف تبين أن JSON Schema هو الأفضل ؛
- مخطط JSON - تم تقديم هذا الخيار بواسطة مطوري الواجهة الأمامية. لديهم بالفعل مكتبات تسمح لهم بعرض النماذج. هذا حدد اختيارنا. يتيح لك التنسيق وصف الفحوصات البدائية التي يمكن إجراؤها في المتصفح. هناك أيضًا وثائق تصف جميع الخيارات الممكنة للمخطط ؛
- GraphQL صغير جدًا. ليس هناك الكثير من المكتبات والخوادم الأمامية. في وقت إنشاء النظام ، لم يتم اعتباره في المستقبل - أفضل طريقة لإنشاء واجهة برمجة تطبيقات ، ستكون هناك مقالة منفصلة حول هذا الأمر ؛
- SOAP - لديه كتابة بيانات صارمة ، والقدرة على بناء الوثائق ، ولكن ليس من السهل تكوين صداقات مع واجهة التفاعل. يحتوي SOAP أيضًا على مقدار حمل أكبر لنفس الكمية القابلة للاستخدام من البيانات المرسلة ؛
كل هذه الأشكال لا تغطي احتياجاتنا بالكامل ، لذلك كان علي أن أكتب حصادة خاصة بي. يمكن أن يوفر نهج مماثل حلولًا فعالة للغاية لأي تطبيق معين ، ولكن هذا ينطوي على مخاطر:
- احتمالية عالية للأخطاء ؛
- في كثير من الأحيان لا يتم توثيق 100 ٪ وتغطية الاختبار ؛
- انخفاض "النمطية" بسبب إغلاق واجهة برمجة التطبيقات. عادة ، يتم كتابة هذه الحلول تحت كتلة متجانسة ولا تعني المشاركة بين المشاريع في شكل مكونات ، لأن هذا يتطلب إنشاءًا معماريًا خاصًا (اقرأ تكلفة التطوير) ؛
- مستوى عال من دخول المطورين الجدد. قد يستغرق الأمر وقتًا طويلاً لفهم كل برودة الدراجة ؛
لذلك ، من الممارسات الجيدة استخدام المكتبات العامة والمستقرة (مثل اللوحة اليسرى من npm) حسب القاعدة - أفضل رمز هو الرمز الذي لم تكتبه مطلقًا ، ولكن حل مشكلة الأعمال. يتم تطوير الواجهة الخلفية لتطبيق الويب في تقنيات الإعلان لمجموعة Rambler Group في Symfony. لن نتطرق إلى جميع المكونات المستخدمة للإطار ، سنتحدث أدناه عن الجزء الرئيسي ، الذي يتم على أساسه تنفيذ العمل -
نموذج Symfony . تستخدم الواجهة الأمامية React والمكتبة المقابلة التي تمد مخطط JSON لتفاصيل WEB -
React JSON Schema Form .
مخطط العمل العام:

هذا النهج له مزايا عديدة:
- يتم إنشاء الوثائق خارج الصندوق ، كما هو الحال مع القدرة على بناء اختبارات تلقائية - مرة أخرى وفقًا للمخطط ؛
- تتم كتابة جميع البيانات المرسلة ؛
- من الممكن إرسال معلومات حول قواعد التحقق الأساسية ؛
تكامل سريع لطبقة النقل في React - بسبب مكتبة Mozilla React JSON Schema ؛ - القدرة على توليد مكونات الويب الأمامية من الصندوق من خلال تكامل bootstrap ؛
- يتم التحكم في التجميع المنطقي ، ومجموعة من عمليات التحقق والقيم المحتملة لعناصر HTML ، بالإضافة إلى كل منطق الأعمال في نقطة واحدة - في الخلفية ، لا يوجد تكرار للشفرة ؛
- إنه بسيط بقدر الإمكان لنقل التطبيق إلى أنظمة أساسية أخرى - يتم فصل جزء العرض عن عنصر التحكم (انظر الفقرة السابقة) ، بدلاً من React والمتصفح ، يمكن لتطبيق Android أو iOS تقديم ومعالجة طلبات المستخدم ؛
دعونا نلقي نظرة على مكونات ومخطط تفاعلهم بمزيد من التفصيل.
مبدئيًا ، يسمح لك
مخطط JSON بوصف عمليات الفحص البدائية التي يمكن إجراؤها على العميل ، مثل ربط أو كتابة أجزاء مختلفة من المخطط:
const schema = { "title": "A registration form", "description": "A simple form example.", "type": "object", "required": [ "firstName", "lastName" ], "properties": { "firstName": { "type": "string", "title": "First name" }, "lastName": { "type": "string", "title": "Last name" }, "password": { "type": "string", "title": "Password", "minLength": 3 }, "telephone": { "type": "string", "title": "Telephone", "minLength": 10 } } }
للعمل مع المخططات الأمامية ، توجد مكتبة
React JSON Schema Form Library الشهيرة التي توفر الوظائف الإضافية اللازمة لـ
JSON Schema لتطوير الويب:
uiSchema - يحدد مخطط JSON نفسه نوع المعلمات المطلوب تمريرها ، ولكن هذا لا يكفي لإنشاء تطبيق ويب. على سبيل المثال ، يمكن تمثيل حقل من نوع String كـ <input ... /> أو كـ <textarea ... /> ، فهذه فروق دقيقة مهمة ، مع مراعاة ما تحتاجه لرسم رسم تخطيطي للعميل بشكل صحيح. تعمل UiSchema أيضًا على نقل هذه الفروق الدقيقة ، على سبيل المثال ، لمخطط JSON المعروض أعلاه ، يمكنك تحديد مكون الويب المرئي لـ uiSchema التالي:
const uiSchema = { "firstName": { "ui:autofocus": true, "ui:emptyValue": "" }, "age": { "ui:widget": "updown", "ui:title": "Age of person", "ui:description": "(earthian year)" }, "bio": { "ui:widget": "textarea" }, "password": { "ui:widget": "password", "ui:help": "Hint: Make it strong!" }, "date": { "ui:widget": "alt-datetime" }, "telephone": { "ui:options": { "inputType": "tel" } } }
يمكن رؤية مثال ملعب مباشر
هنا .
باستخدام هذا المخطط ، سيتم تنفيذ التقديم الأمامي بواسطة مكونات التمهيد القياسية في عدة خطوط:
render(( <Form schema={schema} uiSchema={uiSchema} /> ), document.getElementById("app"));
إذا كانت الحاجيات القياسية التي تأتي مع bootstrap لا تناسبك وتحتاج إلى التخصيص - بالنسبة لبعض أنواع البيانات ، يمكنك تحديد قوالبك الخاصة في uiSchema ، في وقت الكتابة ، يتم دعم
السلسلة ،
الرقم ،
العدد الصحيح ،
المنطقي .
FormData - يحتوي على بيانات النموذج ، على سبيل المثال:
{ "firstName": "Chuck", "lastName": "Norris", "age": 78, "bio": "Roundhouse kicking asses since 1940", "password": "noneed" }
بعد العرض ، سيتم ملء الأدوات بهذه البيانات - مفيدة لتحرير النماذج ، وكذلك لبعض الآليات المخصصة التي أضفناها للحقول ذات الصلة والنماذج المعقدة ، المزيد عن ذلك أدناه.
يمكنك قراءة المزيد حول جميع الفروق الدقيقة في إعداد واستخدام الأقسام الموضحة أعلاه في
صفحة المكون الإضافي .
خارج الصندوق ، تسمح لك المكتبة بالعمل فقط مع هذه الأقسام الثلاثة ، ولكن بالنسبة لتطبيق ويب كامل ، تحتاج إلى إضافة عدد من الميزات:
الأخطاء - من الضروري أيضًا أن تكون قادرًا على نقل أخطاء عمليات التحقق من الواجهة الخلفية المختلفة لتقديمها إلى المستخدم ، ويمكن أن تكون الأخطاء إما أخطاء بسيطة للتحقق - على سبيل المثال ، تفرد تسجيل الدخول عند تسجيل المستخدم ، أو أكثر تعقيدًا بناءً على منطق الأعمال - أي يجب أن نكون قادرين على تخصيص رقم (أخطاء) ونصوص الإشعارات المعروضة. للقيام بذلك ، بالإضافة إلى تلك المذكورة أعلاه ، تمت إضافة قسم الأخطاء إلى مجموعة البيانات المرسلة - لكل حقل ، يتم تحديد قائمة بأخطاء العرض هنا
الإجراء ،
الطريقة - لإرسال البيانات التي أعدها المستخدم إلى الواجهة الخلفية ، تمت إضافة سمتين تحتويان على عنوان URL للواجهة الخلفية لجهاز التحكم الذي يقوم بالمعالجة وطريقة تسليم HTTP
ونتيجة لذلك ، للتواصل بين الأمام والخلف ، حصلنا على json مع الأقسام التالية:
{ "action": "https://...", "method": "POST", "errors":{}, "schema":{}, "formData":{}, "uiSchema":{} }
ولكن كيف يتم إنشاء هذه البيانات على الواجهة الخلفية؟ في وقت إنشاء النظام ، لم تكن هناك مكتبات جاهزة تسمح لك بتحويل نموذج Symfony إلى مخطط JSON. لقد ظهروا الآن بالفعل ، ولكن لديهم
عيوبهم - على سبيل المثال ، يفسر
LiformBundle مخطط JSON بحرية كبيرة ويغير المعيار وفقًا لتقديره ، لذلك ، لسوء الحظ ، كان علي كتابة التنفيذ الخاص بي.
يتم استخدام
نموذج Symfony القياسي كأساس للجيل. يكفي استخدام الباني وإضافة الحقول الضرورية:
مثال على النموذج $builder ->add('title', TextType::class, [ 'label' => 'label.title', 'attr' => [ 'title' => 'title.title', ], ]) ->add('description', TextareaType::class, [ 'label' => 'label.description', 'attr' => [ 'title' => 'title.description', ], ]) ->add('year', ChoiceType::class, [ 'choices' => range(1981, 1990), 'choice_label' => function ($val) { return $val; }, 'label' => 'label.year', 'attr' => [ 'title' => 'title.year', ], ]) ->add('genre', ChoiceType::class, [ 'choices' => [ 'fantasy', 'thriller', 'comedy', ], 'choice_label' => function ($val) { return 'genre.choice.'.$val; }, 'label' => 'label.genre', 'attr' => [ 'title' => 'title.genre', ], ]) ->add('available', CheckboxType::class, [ 'label' => 'label.available', 'attr' => [ 'title' => 'title.available', ], ]);
عند الإخراج ، يتم تحويل هذا النموذج إلى دائرة من النموذج:
مثال JsonSchema { "action": "//localhost/create.json", "method": "POST", "schema": { "properties": { "title": { "maxLength": 255, "minLength": 1, "type": "string", "title": "label.title" }, "description": { "type": "string", "title": "label.description" }, "year": { "enum": [ "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990" ], "enumNames": [ "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990" ], "type": "string", "title": "label.year" }, "genre": { "enum": [ "fantasy", "thriller", "comedy" ], "enumNames": [ "genre.choice.fantasy", "genre.choice.thriller", "genre.choice.comedy" ], "type": "string", "title": "label.genre" }, "available": { "type": "object", "title": "label.available" } }, "required": [ "title", "description", "year", "genre", "available" ], "type": "object" }, "formData": { "title": "", "description": "", "year": "", "genre": "" }, "uiSchema": { "title": { "ui:help": "title.title", "ui:widget": "text" }, "description": { "ui:help": "title.description", "ui:widget": "textarea" }, "year": { "ui:widget": "select", "ui:help": "title.year" }, "genre": { "ui:widget": "select", "ui:help": "title.genre" }, "available": { "ui:help": "title.available", "ui:widget": "checkbox" }, "ui:widget": "mainForm" } }
يتم إغلاق جميع الرموز التي تحول النماذج إلى JSON ويتم استخدامها فقط في مجموعة Rambler Group ، إذا كان المجتمع مهتمًا بهذا الموضوع ، فسنقوم بإعادة تشكيله بتنسيق الحزم في
مستودع github الخاص بنا.
دعونا نلقي نظرة على بعض الجوانب الأخرى التي بدونها يصعب إنشاء تطبيق ويب حديث:
التحقق من صحة الحقل
يتم تعيينه باستخدام
مدقق symfony ، الذي يصف قواعد التحقق من كائن ما ، مثال على المدقق:
<property name="title"> <constraint name="Length"> <option name="min">1</option> <option name="max">255</option> <option name="minMessage">title.min</option> <option name="maxMessage">title.max</option> </constraint> <constraint name="NotBlank"> <option name="message">title.not_blank</option> </constraint> </property>
في هذا المثال ، تقوم قيود من نوع NotBlank بتعديل النظام عن طريق إضافة حقل إلى مصفوفة الحقول المطلوبة للمخطط ، ويضيف قيد من نوع Length السمات السمات-> الخصائص-> title-> maxLength والمخطط-> الخصائص-> title-> minLength ، التي يجب أن تأخذها عملية التحقق بالفعل في الاعتبار في الواجهة الأمامية.
عناصر التجميع
في الحياة الواقعية ، تكون الأشكال البسيطة على الأرجح استثناء للقاعدة. على سبيل المثال ، قد يحتوي المشروع على نموذج يحتوي على عدد كبير من الحقول وإعطاء كل شيء في قائمة صلبة ليس الخيار الأفضل - يجب أن نهتم بمستخدمي تطبيقنا:

القرار الواضح هو تقسيم النموذج إلى مجموعات منطقية من عناصر التحكم بحيث يسهل على المستخدم التنقل وارتكاب أخطاء أقل:

كما تعلمون ، فإن قدرات نموذج Symfony خارج الصندوق كبيرة جدًا - على سبيل المثال ، يمكن وراثة النماذج من أشكال أخرى ، وهذا مناسب ، ولكن في حالتنا هناك عيوب. في التنفيذ الحالي ، يحدد الترتيب في مخطط JSON الترتيب الذي يتم فيه رسم عنصر النموذج في المتصفح ، وقد ينتهك الميراث هذا الطلب. كان أحد الخيارات هو تجميع العناصر ، على سبيل المثال:
مثال على النموذج المتداخل $info = $builder ->create('info',FormType::class,['inherit_data'=>true]) ->add('title', TextType::class, [ 'label' => 'label.title', 'attr' => [ 'title' => 'title.title', ], ]) ->add('description', TextareaType::class, [ 'label' => 'label.description', 'attr' => [ 'title' => 'title.description', ], ]); $builder ->add($info) ->add('year', ChoiceType::class, [ 'choices' => range(1981, 1990), 'choice_label' => function ($val) { return $val; }, 'label' => 'label.year', 'attr' => [ 'title' => 'title.year', ], ]) ->add('genre', ChoiceType::class, [ 'choices' => [ 'fantasy', 'thriller', 'comedy', ], 'choice_label' => function ($val) { return 'genre.choice.'.$val; }, 'label' => 'label.genre', 'attr' => [ 'title' => 'title.genre', ], ]) ->add('available', CheckboxType::class, [ 'label' => 'label.available', 'attr' => [ 'title' => 'title.available', ], ]);
سيتم تحويل هذا النموذج إلى دائرة من النموذج:
مثال متداخل JsonSchema "schema": { "properties": { "info": { "properties": { "title": { "type": "string", "title": "label.title" }, "description": { "type": "string", "title": "label.description" } }, "required": [ "title", "description" ], "type": "object" }, "year": { "enum": [ "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990" ], "enumNames": [ "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990" ], "type": "string", "title": "label.year" }, "genre": { "enum": [ "fantasy", "thriller", "comedy" ], "enumNames": [ "genre.choice.fantasy", "genre.choice.thriller", "genre.choice.comedy" ], "type": "string", "title": "label.genre" }, "available": { "type": "object", "title": "label.available" } }, "required": [ "info", "year", "genre", "available" ], "type": "object" }
و uiSchema المقابلة "uiSchema": { "info": { "title": { "ui:help": "title.title", "ui:widget": "text" }, "description": { "ui:help": "title.description", "ui:widget": "textarea" }, "ui:widget": "form" }, "year": { "ui:widget": "select", "ui:help": "title.year" }, "genre": { "ui:widget": "select", "ui:help": "title.genre" }, "available": { "ui:help": "title.available", "ui:widget": "checkbox" }, "ui:widget": "group" }
لم تلائمنا طريقة التجميع هذه نظرًا لأن نموذج البيانات يبدأ في الاعتماد على العرض التقديمي ولا يمكن استخدامه ، على سبيل المثال ، في واجهة برمجة التطبيقات أو النماذج الأخرى. تقرر استخدام معلمات إضافية في uiSchema دون كسر معيار JSON Schema القياسي. ونتيجة لذلك ، تمت إضافة خيارات إضافية من النوع التالي إلى نموذج السمفونية:
'fieldset' => [ 'groups' => [ [ 'type' => 'base', 'name' => 'info', 'fields' => ['title', 'description'], 'order' => ['title', 'description'] ] ], 'type' => 'base' ]
سيتم تحويل هذا إلى المخطط التالي:
"ui:group": { "type": "base", "groups": [ { "type": "group", "name": "info", "title": "legend.info", "fields": [ "title", "description" ], "order": [ "title", "description" ] } ], "order": [ "info" ] },
النسخة الكاملة من المخطط و uiSchema "schema": { "properties": { "title": { "maxLength": 255, "minLength": 1, "type": "string", "title": "label.title" }, "description": { "type": "string", "title": "label.description" }, "year": { "enum": [ "1989", "1990" ], "enumNames": [ "1989", "1990" ], "type": "string", "title": "label.year" }, "genre": { "enum": [ "fantasy", "thriller", "comedy" ], "enumNames": [ "genre.choice.fantasy", "genre.choice.thriller", "genre.choice.comedy" ], "type": "string", "title": "label.genre" }, "available": { "type": "boolean", "title": "label.available" } }, "required": [ "title", "description", "year", "genre", "available" ], "type": "object" }
"uiSchema": { "title": { "ui:help": "title.title", "ui:widget": "text" }, "description": { "ui:help": "title.description", "ui:widget": "textarea" }, "year": { "ui:widget": "select", "ui:help": "title.year" }, "genre": { "ui:widget": "select", "ui:help": "title.genre" }, "available": { "ui:help": "title.available", "ui:widget": "checkbox" }, "ui:group": { "type": "base", "groups": [ { "type": "group", "name": "info", "title": "legend.info", "fields": [ "title", "description" ], "order": [ "title", "description" ] } ], "order": [ "info" ] }, "ui:widget": "fieldset" }
نظرًا لأن
مكتبة React التي نستخدمها في الواجهة الأمامية لا تدعم هذا من خارج الصندوق ، كان علي أن أضيف هذه الوظيفة بأنفسنا. مع إضافة عنصر جديد "ui: group" نحصل على فرصة التحكم الكامل في عملية تجميع العناصر والنماذج باستخدام واجهة برمجة التطبيقات الحالية.
أشكال ديناميكية
ماذا لو كان أحد الحقول يعتمد على حقل آخر ، على سبيل المثال ، قائمة منسدلة من الفئات الفرعية تعتمد على الفئة المحددة؟

يمنحنا Symfony FORM الفرصة لإنشاء
نماذج ديناميكية بمساعدة الأحداث ، ولكن للأسف ، في وقت التنفيذ ، لم تكن هذه الميزة مدعومة من JSON Schema ، على الرغم من ظهور
هذه الميزة في الإصدارات الأخيرة. في البداية ، كانت الفكرة هي إعطاء القائمة بالكامل لكائن Enum و EnumNames ، بناءً على تصفية القيم:
{ "properties": { "genre": { "enum": [ "fantasy", "thriller", "comedy" ], "enumNames": [ "genre.choice.fantasy", "genre.choice.thriller", "genre.choice.comedy" ], "type": "string", "title": "label.genre" }, "sgenre": { "enum": [ "eccentric", "romantic", "grotesque" ], "enumNames": [ { "title": "sgenre.choice.eccentric", "genre": "comedy" }, { "title": "sgenre.choice.romantic", "genre": "comedy" }, { "title": "sgenre.choice.grotesque", "genre": "comedy" } ], "type": "string", "title": "label.genre" } }, "type": "object" }
ولكن مع هذا النهج ، من الضروري لكل عنصر من هذا النوع كتابة معالجته الخاصة على الواجهة الأمامية ، ناهيك عن حقيقة أن كل شيء يصبح معقدًا للغاية عندما يكون هناك العديد من هذه العناصر أو يعتمد عنصر واحد على عدة قوائم. بالإضافة إلى ذلك ، فإن كمية البيانات المرسلة إلى الواجهة الأمامية تنمو بشكل كبير من أجل المعالجة الصحيحة وتقديم جميع التبعيات. على سبيل المثال ، تخيل رسمًا يتكون من ثلاثة حقول مترابطة - البلدان والمدن والشوارع. يمكن أن يزعج مقدار البيانات الأولية التي يجب إرسالها إلى الواجهة الخلفية إلى الواجهة العملاء الرقيقين ، وكما تتذكر ، يجب علينا رعاية مستخدمينا. لذلك ، تقرر تنفيذ الديناميكيات بإضافة سمات مخصصة:
- SchemaID - سمة من سمات النظام ، تحتوي على عنوان وحدة التحكم لمعالجة FormData التي تم إدخالها حاليًا وتحديث مخطط النموذج الحالي ، إذا لزم الأمر منطق الأعمال ؛
- إعادة تحميل - سمة تخبر الواجهة الأمامية أن التغيير في هذا المجال يبدأ تحديثًا للدائرة عن طريق إرسال بيانات النموذج إلى الواجهة الخلفية ؛
قد يبدو وجود
SchemaID بمثابة تكرار - بعد كل شيء ، هناك سمة
إجراء ، ولكن هنا نتحدث عن تقسيم المسؤولية - وحدة تحكم
SchemaID مسؤولة عن التحديث المتوسط
للمخطط و
UISchema ، وتقوم وحدة التحكم
بالإجراء بإجراء العمل الضروري - على سبيل المثال ، إنشاء أو تحديث كائن ولا تسمح بإرسال جزء من النموذج كما تنتج عمليات التحقق من الصحة. مع هذه الإضافات ، يبدأ المخطط في الظهور بالشكل التالي:
{ "schemaId": "//localhost/schema.json", "properties": { "genre": { "enum": [ "fantasy", "thriller", "comedy" ], "enumNames": [ "genre.choice.fantasy", "genre.choice.thriller", "genre.choice.comedy" ], "type": "string", "title": "label.genre" }, "sgenre": { "enum": [], "enumNames": [], "type": "string", "title": "label.sgenre" } }, "uiSchema": { "genre": { "ui:options": { "reload": true }, "ui:widget": "select", "ui:help": "title.genre" }, "sgenre": { "ui:widget": "select", "ui:help": "title.sgenre" }, "ui:widget": "mainForm" }, "type": "object" }
في حالة تغيير حقل "النوع" ، ترسل الواجهة الأمامية النموذج بالكامل مع البيانات الحالية إلى الواجهة الخلفية ، وتتلقى استجابةً مجموعة من الأقسام اللازمة لعرض النموذج:
{ action: “https://...”, method: "POST", schema:{} formData:{} uiSchema:{} }
وتقديمه بدلاً من النموذج الحالي. ما سيتم تغييره بالضبط بعد الإرسال يتم تحديده من الخلف ، قد يتغير تكوين أو عدد الحقول ، إلخ. - أي تغيير يتطلبه منطق العمل في التطبيق.
الخلاصة
نظرًا للتمديد الصغير للنهج القياسي ، حصلنا على عدد من الميزات الإضافية التي تسمح لنا بالتحكم الكامل في تشكيل وسلوك مكونات التفاعل الأمامية ، وبناء دوائر ديناميكية استنادًا إلى منطق الأعمال ، ولديها نقطة واحدة لتشكيل قواعد التحقق ، والقدرة على إنشاء أجزاء VIEW جديدة بسرعة ومرونة - على سبيل المثال ، المحمول أو سطح المكتب التطبيقات. عند الدخول في مثل هذه التجارب الجريئة ، تحتاج إلى تذكر المعيار الذي تعمل على أساسه والحفاظ على التوافق مع ذلك. بدلاً من React ، يمكن استخدام أي مكتبة أخرى على الواجهة الأمامية ، والشيء الرئيسي هو كتابة محول النقل إلى مخطط JSON وتوصيل بعض مكتبة تقديم النماذج. عمل Bootstrap بشكل جيد مع React لأن لدينا خبرة في العمل مع مجموعة التكنولوجيا هذه ، لكن النهج الذي تحدثنا عنه لا يحد من اختيارك للتقنيات. بدلاً من Symfony ، قد يكون هناك أيضًا أي إطار عمل آخر يسمح لك بتحويل النماذج إلى تنسيق JSON Schema.
تحديث: يمكنك مشاهدة تقريرنا عن لقاء
Symfony Moscow Meetup # 14 حول هذا من 1:15:00.