أنا متأكد من أن العديد من المطورين الذين يفضلون الأطر على CMS الجاهزة لديهم في المخزون حل على Bootstrap أو نظائره ، والذي يستخدم لإنشاء واجهات مسؤول وغيرها من واجهات المكتب الخلفي. ولدي ذلك. لقد كانت تعمل بنجاح لسنوات عديدة ، لكنها عفا عليها الزمن بشكل يائس. حان الوقت لإعادة الكتابة.
أثناء العمل على الإصدار الجديد ، حاولت تلخيص كل تجربتي في هذا الموضوع ، ونتيجة لذلك حصلت على YIMP - دراجة لا أخجل من مشاركتها: GitHub ، LiveDemo ، وثائق API .
YIMP بسيط جدا. لكن وراء هذا البساطة هو الفكر الطويل ، الذي أود أيضًا مشاركته. لذلك هذه المقالة ليست تعليمات. هنا نتحدث عن الهندسة المعمارية ، وإدارة التبعية ، ونموذج MVC ، وواجهة المستخدم ، بالطبع.
لذلك ، YIMP هي لوحة القيادة. هذه ليست لوحة إدارة جاهزة ، وليست CMS أو حتى CMF. يجب كتابة رمز التمثيل بشكل مستقل أو استخدام Gii (يتم إرفاق القوالب). يوفر YIMP تخطيطًا يحدد مكان وجود عناصر التحكم ، بالإضافة إلى الواجهة التي ينقل التطبيق من خلالها البيانات إلى التخطيط. هذه هي الطريقة التي يبدو بها على أجهزة الكمبيوتر المكتبية:
تخطيط التكيف. أثناء تقلص الشاشة ، تبدأ العناصر في الاختفاء أو التحرك فوق الأزرار في شريط التنقل. نتيجة لذلك ، تبدو نفس الصفحة على الهاتف كما يلي:
من الأفضل أن يكون تحت المفسد ماذا نرى في التخطيط؟ عنوان التطبيق ، فتات التصفح ، ثلاثة قوائم (يسار ، يمين ، أعلى) ، عناصر واجهة مستخدم في الأشرطة الجانبية ، عنوان الصفحة. في ممارستي ، كانت هذه المجموعة من العناصر كافية لتطوير أي واجهات - من صفحات المسؤول للصفحات المقصودة إلى أنظمة معلومات الشركة. حاولت ترتيبها حتى يتم استخدام المساحة بأكبر قدر ممكن من الكفاءة. ماذا تقول؟
يتم كتابة العلامات في Bootstrap الخالص ، دون ملحقات والتخصيص. كلما كان ذلك ممكنًا ، تم استخدام فئات من Bootstrap ، لذلك إذا قررت استخدام التخصيص ، فلن تكون هناك أية مشكلات.
كما قلت ، يتضمن YIMP واجهة ينقل التطبيق من خلالها البيانات إلى التخطيط. دعونا نرى كيف يحدث هذا. افتح الغطاء
تصميم
أعتقد أن المطور يجب أن يكون لديه السيطرة الكاملة على التصميم ، لذلك عند تثبيت YIMP ، أوصي بنسخ الكود الخاص به إلى تطبيقه. في رأيي ، هذا أفضل بكثير من ترك تخطيط في الحزمة وحظر مجموعة من الإعدادات لذلك. دعونا نرى رمز التخطيط:
77 سطور من التعليمات البرمجية. ليس من الضروري الخوض في!<?php use dmitrybtn\yimp\widgets\Alert; use dmitrybtn\yimp\Yimp; use yii\bootstrap4\Html; $yimp = new Yimp(); $yimp->register($this); ?> <?php $this->beginPage() ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= Yii::$app->charset ?>"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <?php echo Html::csrfMetaTags() ?> <title><?php echo Html::encode($yimp->nav->getTitle()) ?></title> <?php $this->head() ?> </head> <body> <?php $this->beginBody() ?> <?php echo $yimp->navbar() ?> <?php echo $yimp->beginSidebars() ?> <?php echo $yimp->beginLeftSidebar() ?> <?php echo $yimp->beginLeftSidebarMenu() ?> <?php echo $yimp->menuLeft([ 'options' => ['class' => 'nav-pills flex-column border rounded py-2'] ]) ?> <?php echo $yimp->endLeftSidebarMenu() ?> <?php if (isset($this->blocks[$yimp::SIDEBAR_LEFT])): ?> <?php echo $this->blocks[$yimp::SIDEBAR_LEFT] ?> <?php endif ?> <?php echo $yimp->endLeftSidebar() ?> <?php echo $yimp->beginRightSidebar() ?> <?php echo $yimp->beginRightSidebarMenu() ?> <?php echo $yimp->menuRight([ 'options' => ['class' => 'nav-pills flex-column border rounded py-2'] ]) ?> <?php echo $yimp->endRightSidebarMenu() ?> <?php if (isset($this->blocks[$yimp::SIDEBAR_RIGHT])): ?> <?php echo $this->blocks[$yimp::SIDEBAR_RIGHT] ?> <?php endif ?> <?php echo $yimp->endRightSidebar() ?> <?php echo $yimp->endSidebars() ?> <?php echo $yimp->beginContent() ?> <?php echo $yimp->headerDesktop() ?> <?php echo Alert::widget() ?> <?php echo $content ?> <?php echo $yimp->endContent() ?> <?php if (isset($this->blocks[$yimp::FOOTER])): ?> <?php echo $this->blocks[$yimp::FOOTER] ?> <?php endif ?> <?php $this->endBody() ?> </body> </html> <?php $this->endPage() ?>
كما ترون ، فإن كل علامات YIMP يتم لفها بالطرق. معظم هذه الطرق ببساطة طباعة الخطوط التي تؤخذ من هذه المجموعة . نعم ، مبدأ KISS هو كل شيء لدينا.
يرجى ملاحظة أن كتل تستخدم في التخطيط. إنها ضرورية لعرض عناصر واجهة تعامل محددة في طرق العرض (هذه هي الطريقة التي يتم بها تقديم عناصر تحكم النموذج). حسنًا ، إذا كان يجب أن يتم تعليق التطبيق المصغّر على جميع الصفحات ، فمن الأفضل تحديده مباشرةً في التخطيط.
لذلك ، يتم تعريف المنطقة الرئيسية والحاجيات في وجهات النظر. وأين يتم تحديد العناوين والقوائم وفتات الخبز؟ في رأيي ، يتم تعريفها بشكل أفضل في وحدات التحكم. هذه نقطة مهمة ، لأن مثل هذا القرار يتناقض مع نموذج MVC. لنلقِ نظرة على هذه المشكلة بمزيد من التفصيل.
التحكم
لذلك ، يجب أن يكون هناك ProfileController
يمكنه عرض معلومات حول ملف تعريف المستخدم الحالي وتغيير كلمة المرور. منطقياً ، سيتم استدعاء إجراء profile/view
"ملفي الشخصي". ومن المنطقي أيضًا وجود عنصر "ملفي الشخصي" في القائمة الرئيسية. أخيرًا ، يجب أن يكون "ملفي الشخصي" في فتات التصفح: "الصفحة الرئيسية / ملفي الشخصي / تغيير كلمة المرور." أعتقد أن الرغبة في تحديد ثابت بكلمات "ملفي الشخصي" لها ما يبررها تمامًا. لا يمكنك القيام بذلك في طريقة العرض. اختيار طبقة منفصلة للعناوين أمر مرهق. التفكير مثل هذا ، جئت إلى وحدات التحكم.
كانت الخطوة التالية هي قرار تحديد ليس فقط رؤوس الإجراءات في وحدات التحكم ، ولكن أيضًا فتات الخبز والقوائم. وقم بذلك حتى يتمكن YIMP من قراءتها. وهنا نحتاج إلى مثال. دعنا ننظر إلى تطبيق ممكن للفئة ProfileController
.
52 سطور من الكود البسيط. من الأفضل أن ننظر بعناية! class ProfileController extends \yii\web\Controller { public $nav; public function init() { parent::init(); $this->nav = new \dmitrybtn\yimp\Navigator; } public static function titleView() { return ' '; } public static function titlePassword() { return ' '; } public static function crumbsToView() { return [ ['label' => static::titleView(), 'url' => ['/profile/view']] ]; } public function actionView() { $this->nav->title = static::titleView(); $this->nav->menuRight = [ ['label' => ''], ['label' => static::titlePassword(), ['password']], ]; ... return $this->render('view'); } public function actionPassword() { $this->nav->title = static::titlePassword(); $this->nav->crumbs = static::crumbsToView(); ... return $this->render('password'); } }
يتم تعريف الرؤوس والفتات باستخدام طرق ثابتة ، مما يعني أنه يمكن استخدامها في أي مكان. على سبيل المثال ، في القائمة الرئيسية للتطبيق ، يمكنك الكتابة:
['label' => ProfileController::titleView(), 'url' => ['/profile/view']],
لماذا بالضبط الطرق؟ لأنه سيُطلب منك غدًا بدلاً من الكلمات "ملفي الشخصي" عرض تسجيل دخول المستخدم الحالي.
مع فتات الخبز نفسه. افترض أن ImageController
لديه قائمة بالصور التي يكون ImageController
مسؤولاً عنها. ثم في image/create
إجراء يمكنك الكتابة:
$this->nav->crumbs = ProfileController::crumbsToView(),
واحصل على فتات الخبز مثل "الصفحة الرئيسية / ملفي الشخصي / أضف صورة". بالمناسبة ، بما أن image/create
إجراء يسمى "إضافة صورة" ، فإن قائمة إجراء profile/view
تحتاج إلى تصحيح:
$this->nav->menuRight = [ ['label' => ''], ['label' => static::titlePassword(), ['password']], ['label' => ImageController::titleCreate(), 'url' => ['/image/create']] ];
أعتقد أن الفكرة مفهومة. في رأيي ، هذا حل بسيط وفعال يمكنك من خلاله الابتعاد عن نموذج MVC. نعم ، رمز وحدة التحكم يزداد حجمًا ، ولكن يوجد مكان في وحدة التحكم - لا نكتب منطق العمل هناك ، أليس كذلك؟ ونعم ، سأكون مهتمًا جدًا بمعرفة رأيك في هذا الأمر.
نحن نذهب أبعد من ذلك. كما كنت قد خمنت ، يتم استخدام خاصية nav
، التي تم تعريفها على أنها \dmitrybtn\yimp\Navigator
، لنقل الرؤوس والقوائم وفتات التصفح من وحدة التحكم إلى التخطيط. وهذه ميزة أخرى من YIMP.
ملاح
كيف تدخل الإعدادات في التصميم؟ بسيط جدا أثناء التهيئة ، يتحقق YIMP من خاصية nav
لوحدة التحكم الحالية. إذا كانت هذه الخاصية قابلة للقراءة وكانت عبارة عن instanceof \dmitrybtn\yimp\Navigator
( instanceof \dmitrybtn\yimp\Navigator
) ، يتم استخدامها لعرض المعلومات ذات الصلة. يتضمن المستكشف خصائص متوافقة مع عناصر التخطيط ، والتي يسهل رؤيتها في وثائق واجهة برمجة التطبيقات .
في التطبيق ، يوصى بإنشاء متصفح خاص بك وتحديد القوائم فيه والتي لن تعتمد على الإجراء الحالي (الأعلى واليسار). بعد ذلك ، في جميع وحدات التحكم ، تحتاج إلى إنشاء خاصية nav
وتعريفها كمستكشف (يمكنك استخدام يديك ، أو يمكنك أن ترث ، أو يمكنك أن تريك). إذا لزم الأمر ، يمكنك تحديد العديد من الملاحين.
هذا النهج يلغي العلاقة المباشرة بين YIMP ووحدة التحكم. يتم تنفيذ كل التفاعل من خلال كائن واحد ، يتم التحكم في تنفيذه بواسطة المطور. وهذا هو ، وهذا هو نفس مبدأ انعكاس التبعية من SOLID أو منخفضة اقتران من GRASP .
سيكون من الصحيح أيديولوجيًا استخدام الواجهات ، سواء بالنسبة لوحدة التحكم أو المستكشف. لكنني قررت هنا أن أسير في أبسط الطرق وليس الفوضى في النظام. في النهاية ، لا يتحدث DIP عن واجهات ، ولكن عن تجريدات. وفي هذه الحالة ، التجريد هو اتفاق على وجود نوع معين من الممتلكات في فئة معينة. ما رايك
يصبح غياب علاقة مباشرة بين YIMP ووحدة التحكم مهمًا عند ظهور الوحدات النمطية في النظام التي لا تعرف أي شيء عن YIMP. أو العكس - يتم تثبيت الوحدات النمطية المكتوبة تحت YIMP في نظام لا يستخدمه YIMP.
في الحالة الأولى ، لن ترى YIMP خصائص nav
في وحدة التحكم. لن يكون هناك أي خطأ ، لكن قوائمك ستختفي من الشاشة ، وسيتم استخدام معرف الإجراء كعنوان. كيف تكون بسيط جدًا - إذا لم يستطع YIMP أخذ المستكشف من وحدة التحكم ، فسيتم yimp-nav
خلال حاوية DI باستخدام الاسم المستعار yimp-nav
. باستخدام هذا الاسم المستعار ، يمكنك تسجيل المستكشف الافتراضي الخاص بك ، على سبيل المثال عن طريق تحديد في إعدادات التطبيق:
'container' => [ 'definitions' => [ 'yimp-nav' => [ 'class' => '\your\own\Navigator', ] ] ],
في الحالة الثانية ، سيكون المستكشف في وحدة التحكم ، ولكن لن يكون هناك أحد لقراءته. في هذه الحالة ، يوصى بكتابة مجمّع في الوحدة النمطية للمشاهدات ، مما يؤدي إلى ضبط المستكشف من وحدة التحكم الحالية إلى التنسيق المقبول في Yii. بمعنى ، عرض <h1>
في المنطقة الرئيسية ، <title>
وفتات التنقل عبر المعلمات Yii::$app->view
، وعرض القائمة اليمنى في شكل أزرار.
استنتاج
تم نشر YIMP الآن بدون إصدار. لا أرى أي سبب لنشر الإصدار التجريبي - كل شيء بسيط جدًا لذلك. أعتقد أنه من الأفضل اختبار أسبوعين في مشروع حقيقي والتحول الفوري إلى الإصدار 1.0.0. لذا فإن النقد والتعليقات والمساعدة على جيثب مرحب به للغاية.
أنا أكمل وحدة نمطية تنفذ التحكم في الوصول. كيف تنتهي - سأكتب.
كما ترون ، لا يوجد شيء معقد هنا. أنا متأكد من أن الكثير منكم لديه شيء مماثل في الأوراق المالية. وسأكون مهتمًا جدًا بمعرفة كيفية حل مهمة واجهة المستخدم في منطقة المشرف.
شكرا للجميع!