ما الذي تشترك فيه التعليقات على مقالة حبري والخيارات الإضافية عند شراء سيارة؟

من وجهة نظر نمذجة البيانات ، كلاهما كيانان "متداخلان" ليس لهما أهمية مستقلة بمعزل عن الكائن الأصل.
في Yii (
إطار عمل php ) ، يوجد Gii - وهو مُنشئ كود مدمج يسمح لك بإنشاء واجهات CRUD أساسية باستخدام نموذج بيانات بنقرات قليلة من الفأرة ، والتي تسرع عملية التطوير بشكل كبير ، ولكنها تنطبق فقط على الكيانات المستقلة ، مثل المقالة أو الجهاز في الأمثلة أعلاه.
سيكون من الرائع أن تكون قادرًا على إنشاء شيء مشابه لكائنات البيانات "المتداخلة" ، أليس كذلك؟ الآن - يمكنك ، مرحبا بكم في كات للحصول على التفاصيل.
للأكثر نفاد الصبر في نهاية المقالة ، يتم إعطاء تعليمات لبداية سريعة.
للراغبين في المقال ، يتم النظر في الجوانب من تطبيق الأعمال إلى جهاز داخلي:
- حالة العمل: نشر حسب الموضوع
- قائمة المواضيع على الرئيسية
- قائمة الوظائف ذات الصلة
- تحت الغطاء: مولد gii على أساس CRUD
- غي مولد القالب
- الطبقة الأساسية القطعة
- تحكم واجهة متكاملة
- بداية سريعة
حالة العمل: نشر حسب الموضوع
ربما تعليقات على habr ومثالا سيئا منذ ذلك الحين غالبًا ما يكون مفيدًا أكثر من المقالة نفسها ، ولكن على أي حال ، عند تطوير تطبيق ما ، تكون هناك حالات غالبًا ما يكون فيها كائن معين من نموذج البيانات ذا فائدة قليلة للمستخدم ككيان مستقل.
فكر في مهمة أعمال مبسطة: لإنشاء موقع ويب لنشر الرسائل التي تم تجميعها حسب مواضيع مختلفة.
يجب أن يحتوي الموقع على الواجهات التالية:
- يجب أن تدعم الصفحة الرئيسية - عناصر واجهة مستخدم متعددة في المستقبل ، ولكن في المرحلة الحالية للتنفيذ ، هناك واحد فقط: قائمة بالموضوعات التي تمت تصفيتها بواسطة بعض المعايير.
- قائمة كاملة بالمواضيع - قائمة كاملة بالمواضيع في شكل جدول ؛
- صفحة الموضوع - معلومات حول الموضوع وقائمة المنشورات المنشورة فيه.
جميلة القياسية ، أليس كذلك؟
لنلقِ نظرة على نموذج البيانات:

أيضا لا مفاجآت. ستحتوي فئتان من النماذج على منطق أعمالنا:
- فئة الموضوع - بيانات عن الموضوع ، والتحقق من الصحة ، وقائمة المنشورات فيه ، فضلاً عن طريقة منفصلة تُرجع قائمة بالموضوعات التي تمت تصفيتها وفقًا لمعايير عنصر واجهة المستخدم في الصفحة الرئيسية.
- فئة المشاركة هي فقط البيانات والتحقق من الصحة.
سيتم تقديم الطلب من قبل جهازي تحكم:
- SiteController - الصفحات القياسية (من نحن ، جهات الاتصال ، وما إلى ذلك) ، إذن (غير مطلوب بالمواصفات الفنية ، لكننا نعرف شيئًا ما) وفهرس - الصفحة الرئيسية. لأن نفترض في المستقبل العديد من الحاجيات المختلفة ، فمن المنطقي أن نترك الصفحة الرئيسية في وحدة التحكم هذه ، وليس نقلها إلى نموذج خاص بواحد.
- TopicController هو مجموعة قياسية من الإجراءات: قائمة ، إنشاء ، تحرير ، عرض وحذف المواضيع.
من المحتمل ، يمكنك أيضًا إنشاء
PostController - لأغراض الإدارة و / أو نسخ لصق أجزاء من التعليمات البرمجية في عناصر واجهة
مستخدم مخصصة ، ولكن اترك هذا خارج نطاق هذه المقالة.
حتى الآن ، يمكن إنشاء معظم الشفرة باستخدام gii ، مما يسرع عملية التطوير ويقلل المخاطر (أقل رمز يدوي = فرصة أقل لارتكاب خطأ).
يبقى سؤالان:
- كيف يتم عرض قائمة مصفاة بالموضوعات على الصفحة الرئيسية؟
- كيفية عرض قائمة المنشورات حسب الموضوع؟
إذا تمكنت من حلها باستخدام مولد تلقائي - فسيكون ذلك إنجازًا قويًا.
قائمة المواضيع على الرئيسية
يجب أن تحتوي الصفحة الرئيسية التي يقدمها عنوان الموقع / الفهرس على قائمة بالموضوعات التي تمت تصفيتها وفقًا لمعيار محدد مسبقًا. معايير التصفية ، كجزء من منطق العمل ، قمنا بإدراجها في النموذج.
للعرض ، هناك العديد من خيارات التنفيذ.
الأول ، القذر والسريع ، هو القيام بكل شيء مباشرةً في ملف العرض (
طرق العرض / الموقع / index.php ):
- إنشاء ActiveDataProvider ؛
- املأها ببيانات من نموذج Topic ؛
- العرض باستخدام عنصر واجهة مستخدم ListView / GridView قياسي ، مع تحديد الحقول المطلوبة يدويًا.
يمكنك الانتقال إلى أبعد من ذلك
وتعبئته جميعًا في ملف عرض منفصل ، مثل طريقة العرض
/ site / _topic-list-widget.php ، مع استدعاء العرض الخاص به من الملف الرئيسي. سيعطي ذلك المزيد من القابلية للإدارة والتوسعة ، ولكنه لا يزال يبدو قذرًا جدًا.
من المحتمل أن يقوم معظمنا بإنشاء عنصر واجهة مستخدم منفصل وفقًا لجميع القواعد ، في مساحة اسم منفصلة (
التطبيق \ الحاجيات أو
التطبيق \ المكونات الخاصة بالقالب الأساسي - اعتمادًا على الإصدار الذي تستخدمه) ، حيث يقومون بتغليف إنشاء
ActiveDataProvider حسب الطراز وعرضه بشكل منفصل التمثيل. كل ما تبقى هو استدعاء هذه القطعة من الصفحة الرئيسية. هذا الحل هو الأصح من حيث تحليل الطبقة ، والتحكم ، وتوسعة الكود.
ولكن هل يبدو أن التعليمات البرمجية لهذه الأداة
ستكرر رمز
TopicController كثيرًا من حيث التعامل مع
actionIndex () ؟ ومن المزعج جدًا كتابة هذا الرمز يدويًا.
سيكون من الأفضل كثيرًا إنشاء هذا الرمز تلقائيًا ثم استدعاء الأداة المصغّرة:
<?= \app\widgets\TopicControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => app\models\Topic::findBySomeSpecificCriteria() ], ]) ?>
قائمة الوظائف ذات الصلة
يجب أن تحتوي صفحة عرض الموضوع الذي يقدمه عنوان
الموضوع / العرض على معلومات حول الموضوع نفسه وقائمة بالرسائل المنشورة فيه. نحصل على قائمة الرسائل الخاصة بالموضوع في النموذج تلقائيًا إذا قمنا بتكوين العلاقات بين الجداول بشكل صحيح ، بحيث يبقى سؤال العرض فقط.
عن طريق القياس مع قائمة الموضوعات التي تمت تصفيتها ، لدينا نفس الخيارات تقريبًا.
الأول هو عمل كل شيء في رمز ملف العرض لعرض الموضوع (
المشاهدات / الموضوع / view.php ):
- إنشاء ActiveDataProvider ؛
- املأه ببيانات من النموذج $ model-> getPosts () ؛
- العرض باستخدام عنصر واجهة مستخدم ListView / GridView قياسي ، مع تحديد الحقول المطلوبة يدويًا.
والثاني هو عزل هذا الرمز في ملف عرض تقديمي منفصل:
طرق العرض
/ الموضوع / _posts-list-widget.php ، حتى لا تكون عينًا - إعادة استخدامها في مكان ما ستفشل.
والثالث عبارة عن عنصر واجهة مستخدم كامل يعمل على تكرار رمز
PostController الشرطي إلى حد كبير في الجزء
actionIndex () ، ولكن كتابته يدويًا.
أو قم بإنشاء الشفرة تلقائيًا واتصل بالبرنامج المصغّر النهائي:
<?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $model->getPosts(), ], ]) ?>
تحت الغطاء: مولد gii على أساس CRUD
يتم تعريف مهمة العمل ، ويتم تحديد متطلبات عنصر واجهة التعامل الذي تم إنشاؤه ، وسنعرف كيف سننشئها بالضبط. يحتوي Gii بالفعل على مولد لوحدة التحكم CRUD. بالنسبة إلى عنصر واجهة تعامل المستخدم (CRUD) ، نحتاج إلى إنشاء مولد جديد استنادًا إلى المكون الحالي.
بضعة روابط للوثائق قبل البدء - سيكون مفيدًا أيضًا إذا قررت كتابة ملحقك الخاص:
من الواضح أن جميع الوظائف يتم تعبئتها في ملحق Yii ، الذي يتم تثبيته من خلال الملحن ويدخل في مجلد البائع في مشروعك.
يتكون التمديد من ثلاثة أجزاء:
- دليل القوالب / الخام الذي يحتوي على قالب مولد gii ؛
- Controller.php file - المدمج في واجهة تحكم للمكالمات القطعة.
- ملف Widget.php هو الفئة الأساسية لجميع عناصر واجهة المستخدم المنشأة.

غي مولد القالب
يجب أن ينشئ الامتداد رمزًا ، لذلك يكون الجزء المركزي هو مولد Gii.
في البداية ، كان من المفترض أنه لتنفيذ الامتداد ، ستكون كافية لكتابة القالب الخاص بك لمولد CRUD-Controller المدمج. بالمناسبة ، هذا هو السبب في أن الدليل يسمى القوالب ، وليس المولدات. لكن اتضح أن مولد CRUD-Controller يقوم بالتحقق المكثف للغاية لبيانات الإدخال ، والتي لم تسمح بتنفيذ العديد من المتطلبات ، على سبيل المثال ، تغيير فئة الميراث. لذلك ، يحتوي الملحق على منشئ كامل ، وليس فقط على قالب.
يتكون مولد gii من الأجزاء التالية (كلها داخل دليل القوالب / الخام):
- الدليل الافتراضي هو قالب يحدث فيه كل السحر: سيتوافق كل ملف في هذا الدليل مع ملف تم إنشاؤه في مشروعك ؛
- File form.php - كما قد تخمن من الاسم ، هذا نموذج لإدخال معلمات الجيل (أسماء الفئات ، وما إلى ذلك) ؛
- File Generator.php - أوركسترا جيل تتلقى البيانات من النموذج ، تقوم بالتحقق من صحتها ، ثم تقوم باستدعاء ملفات القوالب بالتسلسل لإنشاء النتيجة.
تحتوي
ملفات Generator.php و
form.php على تغييرات تجميلية في الغالب بالنسبة لتلك الأصلية من مولد CRUD: أسماء الملفات ، التحقق من الصحة ، نصوص تلميح الأدوات ، إلخ.
ملفات القوالب هي المسؤولة عن طريقة العرض التي تم إنشاؤها ورمز القطعة نفسه. بادئ ذي بدء ، تعد
قوالب الملفات
/ crud / default / controller.php مهمة ، وهي المسؤولة عن إنشاء فئة عنصر واجهة المستخدم مباشرةً ، والتي تتوافق مع فئة وحدة التحكم من المولد الأصلي.
يجب أن يكون لعنصر واجهة المستخدم نفس الإجراءات مثل وحدة التحكم CRUD ، ولكن يتم إنشاؤها بشكل مختلف قليلاً. توضح الأمثلة أدناه نتيجة الجيل مع التعليقات:
- actionIndex - بدلاً من الإخراج غير المشروط لجميع النماذج ، تقبل الطريقة معلمة استعلام $؛
public function actionIndex($query) { $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); }
- actionCreate و actionUpdate - في حالة النجاح ، فبدلاً من إعادة التوجيه ، يقومون ببساطة بإرجاع رمز النجاح ، ويتم توفير مزيد من المعالجة بواسطة وحدة تحكم الواجهة المدمجة ؛
public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return 'success'; } return $this->render('create', [ 'model' => $model, ]); }
- actionDelete - يدعم طريقة GET لعرض أداة الحذف (افتراضيا - زر واحد) و POST لأداء الإجراء ؛ إذا نجحت ، فإنه لا يؤدي أيضًا عملية إعادة توجيه ، ولكنه يعرض رمزًا.
public function actionDelete($id) { $model = $this->findModel($id); if (Yii::$app->request->method == 'GET') { return $this->render('delete', [ 'model' => $model, ]); } else { $model->delete(); return 'success'; } }
أخيرًا ، تحتوي ملفات العرض على التعديلات الأساسية التالية:
- تمت ترجمة جميع الرؤوس إلى h2 بدلاً من h1 ؛
- أزلت الكود المسؤول عن عرض عنوان الصفحة وعن فتات التنقل - يجب ألا تؤثر الأداة على هذه الأشياء ؛
- يتم إنشاء النماذج وتحريرها باستخدام نافذة مشروطة (أداة Modal مدمجة) ؛
- وأضاف حذف قالب القطعة - مع زر واحد أحمر كبير.
الطبقة الأساسية القطعة
عندما ينتهي المولد من عمله ، سينشئ فئة عنصر واجهة مستخدم في مساحة اسم التطبيق. تبدو سلسلة الميراث كما يلي: يتم توريث عناصر واجهة المستخدم التي تم إنشاؤها للتطبيق من عنصر واجهة المستخدم الأساسي ، الفئة
\ ianikanov \ wce \ Widget ، والتي بدورها تُورث من عنصر واجهة المستخدم الأساسي الأساسي ، الفئة
\ yii \ base \ Widget .
تحل الفئة الأساسية لعنصر واجهة المستخدم المهام التالية:
- يحدد مجالين رئيسيين: $ action و $ params ، يتم من خلاله نقل السيطرة إلى عنصر واجهة المستخدم من مشاهدة الاتصال ؛
- يحدد عددًا من المعلمات القياسية التي يمكن تجاوزها في الفئة التي تم إنشاؤها ، مثل المسار إلى ملفات عرض عنصر واجهة المستخدم والاسم والمسار إلى وحدة تحكم الواجهة (حولها أدناه) ورسائل الخطأ ؛
- يعرّف المعلمات القياسية عند تقديم طرق العرض: render and renderFile؛
- يوفر بنية أساسية لحدث يشبه بنية التحكم الأساسية بحيث تعمل عوامل التصفية القياسية مثل AccessControl و VerbFilter ؛
- يحدد طريقة التشغيل التي تجمع كل هذا معًا.
تحكم واجهة متكاملة
لا توجد مشاكل في عرض هذه البيانات - الأدوات المصممة لهذا الغرض مخصصة. ولكن للتحرير ، على أي حال ، تحتاج إلى وحدة تحكم. إنشاء وحدة تحكم فريدة لكل عنصر واجهة مستخدم - يتم فقد جوهرها بالكامل. استخدام CRUD القياسي ليس مناسبًا دائمًا ، ولا أريد الاعتماد على الإطلاق الإضافي لـ gii. لذلك ، تم استخدام الخيار مع واجهة تحكم شاملة ومتكاملة.
وحدة التحكم هذه مسجلة في خريطة التطبيق من خلال ملف التكوين وتحتوي على طريقة واحدة فقط - actionIndex ، والتي تنفذ الإجراءات التالية:
- يقبل طلبًا من عميل ؛
- ينقل التحكم إلى فئة القطعة المقابلة ؛
- يتعامل مع أخطاء العمل نتيجة لعنصر واجهة المستخدم ؛
- يعيد التوجيه مرة أخرى إلى التطبيق الرئيسي.
ربما يكون من المهم الإشارة إلى ما لا تفعله وحدة التحكم هذه:
- لا يتحقق من مستويات الوصول - هذا المنطق ينتمي إلى عناصر واجهة تعامل محددة.
- لا يؤدي أي معالجة الإدخال - يتم تمرير المعلمات إلى القطعة كما هي ؛
- لا يعالج الإخراج إلا للتحقق من رمز نجاح محدد مسبقًا.
يتيح لك هذا النهج الحفاظ على براعة الواجهة ، تاركًا تنفيذ متطلبات العمل ، بما في ذلك متطلبات الأمان ، رمز تطبيق التطبيق.
بداية سريعة
تحدي الأعمال واضح وجاهز للبدء؟ استخدام الملحق له أربع خطوات:
- تركيب.
- التكوين.
- جيل.
- التطبيق.
يتم تثبيت الامتداد باستخدام الملحن:
php composer.phar require --prefer-dist ianikanov/yii2-wce "dev-master"
بعد ذلك ، تحتاج إلى إجراء العديد من التغييرات على ملف تكوين التطبيق.
أولاً ، أضف إشارة إلى مولد gii:
if (YII_ENV_DEV) { $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', 'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'], 'generators' => [
ثانياً ، أضف وحدة التحكم في الواجهة المدمجة إلى الخريطة:
$config = [ ... 'controllerMap' => [ 'wce-embed' => '\ianikanov\wce\Controller', ], ... ];
هذا يكمل التثبيت والتكوين.
لإنشاء عنصر واجهة مستخدم:
- فتح gii.
- حدد "القطعة تحكم CRUD" ؛
- ملء حقول النموذج ؛
- عرض وإنشاء رمز.
علاوة على ذلك ، لاستخدام عنصر واجهة المستخدم ، يجب أن يتم استدعاؤه عن طريق تحديد الإجراء والمعاملات - نفس الشيء تقريبًا باسم وحدة التحكم.
القطعة لعرض قائمة النماذج:
<?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $otherModel->getPosts(), ], ]) ?>
القطعة لمشاهدة نموذج واحد:
<?= app\widgets\PostControllerWidget::widget(['action' => 'view', 'params' => ['id' => $post_id]]) ?>
عنصر واجهة إنشاء النموذج (زر + نموذج ملفوف في Modal):
<?= app\widgets\PostControllerWidget::widget(['action' => 'create']) ?>
تغيير نموذج القطعة (زر + نموذج ملفوف في مشروط):
<?= app\widgets\PostControllerWidget::widget(['action' => 'update', 'params'=>['id' => $post_id]]) ?>
القطعة إزالة النموذج (زر):
<?= app\widgets\PostControllerWidget::widget(['action' => 'delete', 'params'=>['id' => $post_id]]) ?>
ينتمي رمز عنصر واجهة المستخدم وجميع طرق العرض إلى التطبيق ويمكن تغييره بسهولة - كل شيء هو نفسه تمامًا عند إنشاء وحدة التحكم.
حول الدعم والتطوير
بضع كلمات حول كيفية دعم التوسع وتطويره. لديّ العمل الرئيسي وبعض مشاريعي "الجانبية" (مشاريع الحيوانات الأليفة). لذلك ، هذا الامتداد هو مشروع جانبي من مشاريعي الجانبية ، لذلك سأقوم بتطوير تحسينات عليه لتلبية احتياجات مشاريعي فقط.
في أفضل تقاليد المصدر المفتوح ، يتوفر الكود على
github ،
وسأدعمه فيما يتعلق بإصلاح الخلل ، وسأحاول أيضًا إجراء مراجعات في الوقت المناسب إذا أراد أي شخص إرسال طلب سحب ، لذلك من المهتمين ، للانضمام.