YIMP - Panneau de configuration pour Yii 2 sur Bootstrap 4

Je suis sûr que de nombreux développeurs qui préfèrent les frameworks aux CMS prêts à l'emploi ont en stock une solution sur Bootstrap ou ses analogues, qui est utilisée pour créer des interfaces d'administration et d'autres interfaces de back-office. Et je l'ai. Il fonctionne avec succès depuis de nombreuses années, mais il est désespérément dépassé. Il est temps de réécrire.


En travaillant sur la nouvelle version, j'ai essayé de résumer toute mon expérience sur ce sujet, et en conséquence j'ai obtenu YIMP - un vélo que je n'ai pas honte de partager: GitHub , LiveDemo , Documentation API .


YIMP est très simple. Mais derrière cette simplicité, il y a une longue pensée que je veux aussi partager. Cet article n'est donc pas une instruction. Ici, nous parlons d'architecture, de gestion des dépendances, du paradigme MVC et de l'interface utilisateur, bien sûr.


Ainsi, YIMP est un tableau de bord. Ce n'est pas un panneau d'administration prêt à l'emploi, ni un CMS ni même un CMF. Le code de représentation doit être écrit indépendamment ou utiliser Gii (des modèles sont joints). YIMP fournit une disposition qui définit l'emplacement des contrôles, ainsi que l'interface par laquelle l'application transfère les données vers la disposition. Voici à quoi cela ressemble sur les ordinateurs de bureau:



Disposition adaptative. À mesure que l'écran rétrécit, les éléments commencent à disparaître ou à se déplacer sur les boutons de la barre de navigation. En conséquence , la même page sur le téléphone ressemble à ceci:


Mieux vaut le laisser sous le spoiler

Que voit-on dans la mise en page? Titre de l'application, fil d'Ariane, trois menus (gauche, droite et haut), widgets dans les barres latérales, titre de la page. Dans ma pratique, cet ensemble d'éléments était suffisant pour développer toutes les interfaces - des pages d'administration des pages de destination aux systèmes d'information d'entreprise. J'ai essayé de les organiser pour que l'espace soit utilisé le plus efficacement possible. Que dites-vous?


Le balisage est écrit en Bootstrap pur, sans extensions ni personnalisation. Dans la mesure du possible, des classes de Bootstrap ont été utilisées, donc si vous décidez d'utiliser la personnalisation, il ne devrait y avoir aucun problème.


Comme je l'ai dit, YIMP comprend une interface à travers laquelle l'application transfère des données à la mise en page. Voyons comment cela se produit. Ouvrez le capot!


Disposition


Je crois que le développeur devrait avoir un contrôle total sur la mise en page, donc lors de l'installation de YIMP, je recommande de copier son code dans son application. À mon avis, c'est beaucoup mieux que de laisser une mise en page dans le package et de bloquer un tas de paramètres pour cela. Voyons le code de mise en page:


77 lignes de code. Il n'est pas nécessaire de s'y plonger!
<?php use dmitrybtn\yimp\widgets\Alert; use dmitrybtn\yimp\Yimp; use yii\bootstrap4\Html; $yimp = new Yimp(); $yimp->register($this); /** @var string $content Content came from view */ ?> <?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() ?> 

Comme vous pouvez le voir, tout le balisage YIMP est encapsulé dans des méthodes. La plupart de ces méthodes impriment simplement les lignes extraites de ce tableau . Oui, le principe KISS est notre tout.


Veuillez noter que les blocs sont utilisés dans la mise en page. Ils sont nécessaires pour afficher les widgets définis dans les vues (c'est ainsi que les contrôles de formulaire sont rendus). Eh bien, si le widget doit se bloquer sur toutes les pages, il est préférable de le déterminer directement dans la mise en page.


Ainsi, la zone principale et les widgets sont définis dans les vues. Et où sont déterminés les titres, les menus et la chapelure? À mon avis, ils sont mieux définis dans les contrôleurs. C'est un point important, car une telle décision contredit le paradigme MVC. Examinons ce problème plus en détail.


Contrôleurs


Donc, qu'il y ait un ProfileController qui peut afficher des informations sur le profil de l'utilisateur actuel et changer le mot de passe. Logiquement, l'action profile/view sera appelée "Mon profil". Il est également logique que le menu principal contienne un élément «Mon profil». Enfin, "Mon profil" devrait être dans le fil d'Ariane: "Accueil / Mon profil / Changer le mot de passe". Je pense que le désir de définir une constante avec les mots "Mon profil" est tout à fait justifié. Vous ne pouvez pas faire cela dans la vue. La sélection d'un calque séparé pour les en-têtes est fastidieuse. Raisonnant comme ça, je suis venu aux contrôleurs.


L'étape suivante a été la décision de définir non seulement les en-têtes d'action dans les contrôleurs, mais aussi les miettes de pain et les menus. Et faites-le pour que YIMP puisse les lire. Et ici, nous avons besoin d'un exemple. Examinons une implémentation possible de la classe ProfileController .


52 lignes de code simple. Mieux vaut regarder attentivement!
 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'); } } 

Les en-têtes et le fil d'Ariane sont définis à l'aide de méthodes statiques, ce qui signifie qu'ils peuvent être utilisés n'importe où. Par exemple, dans le menu principal de l'application, vous pouvez écrire:


 ['label' => ProfileController::titleView(), 'url' => ['/profile/view']], 

Pourquoi exactement les méthodes? Parce que demain, il vous sera demandé à la place des mots "Mon profil" d'afficher la connexion de l'utilisateur actuel.


Avec des miettes de pain pareil. Supposons que notre utilisateur dispose d'une liste d'images dont ImageController est responsable. Ensuite, dans l' image/create action, vous pouvez écrire:


  $this->nav->crumbs = ProfileController::crumbsToView(), 

et obtenez des miettes de pain comme "Accueil / Mon profil / Ajouter une photo". Soit dit en passant, puisque l'action image/create est appelée "Ajouter une image", le menu action profile/view devra être corrigé:


  $this->nav->menuRight = [ ['label' => ''], ['label' => static::titlePassword(), ['password']], ['label' => ImageController::titleCreate(), 'url' => ['/image/create']] ]; 

Je pense que l'idée est compréhensible. À mon avis, il s'agit d'une solution simple et efficace pour laquelle vous pouvez vous éloigner du paradigme MVC. Oui, le code du contrôleur grossit, mais il y a de la place pour lui dans le contrôleur - nous n'y écrivons pas de logique métier, non? Et oui, je serais très intéressé de connaître votre opinion à ce sujet.


Nous allons plus loin. Comme vous l'avez peut-être deviné, la propriété nav , définie comme \dmitrybtn\yimp\Navigator , est utilisée pour transférer les en-têtes, les menus et les fils d'Ariane du contrôleur vers la disposition. Et ceci est une autre caractéristique de YIMP.



Comment les paramètres entrent-ils dans la mise en page? Très simple. Lors de son initialisation, YIMP vérifie la propriété nav du contrôleur actuel. Si cette propriété est lisible et est un navigateur ( instanceof \dmitrybtn\yimp\Navigator ), elle est utilisée pour afficher les informations correspondantes. Le navigateur comprend des propriétés correspondant aux éléments de mise en page, dont une liste complète est plus facile à voir dans la documentation de l' API .


Dans l'application, il est recommandé de créer votre propre navigateur et d'y définir des menus qui ne dépendront pas de l'action en cours (en haut et à gauche). Après cela, dans tous les contrôleurs, vous devez créer la propriété nav et la définir comme un navigateur (vous pouvez utiliser vos mains, vous pouvez hériter ou vous pouvez trait). Si nécessaire, vous pouvez définir plusieurs navigateurs.


Cette approche élimine la relation directe entre YIMP et le contrôleur. Toute interaction se fait à travers un seul objet dont la mise en œuvre est contrôlée par le développeur. Autrement dit, il s'agit du même principe d'inversion de dépendance de SOLID ou de faible couplage de GRASP .


Il serait idéologiquement correct d'utiliser des interfaces, à la fois pour le contrôleur et pour le navigateur. Mais ici, j'ai décidé de suivre la voie la plus simple et de ne pas encombrer le système. Au final, DIP ne parle pas d'interfaces, mais d'abstractions. Et dans ce cas, l'abstraction est un accord sur la présence d'un certain type de propriété dans une classe particulière. Qu'en penses-tu?


L'absence de relation directe entre YIMP et le contrôleur devient importante lorsque des modules apparaissent dans le système qui ne connaissent rien de YIMP. Ou vice versa - les modules écrits sous YIMP sont installés dans un système que YIMP n'utilise pas.


Dans le premier cas, YIMP ne verra pas les propriétés de nav dans le contrôleur. Il n'y aura pas d'erreur, mais vos menus disparaîtront de l'écran et l'ID d'action sera utilisé comme titre. Comment être Très simple - si YIMP ne peut pas prendre le navigateur du contrôleur, il le créera via le conteneur DI en utilisant l'alias yimp-nav . En utilisant cet alias, vous pouvez enregistrer votre propre navigateur par défaut, par exemple en spécifiant dans les paramètres de l'application:


  'container' => [ 'definitions' => [ 'yimp-nav' => [ 'class' => '\your\own\Navigator', ] ] ], 

Dans le second cas, le navigateur du contrôleur le sera, mais il n'y aura personne pour le lire. Dans ce cas, il est recommandé d'écrire un wrapper pour les vues dans le module, qui adaptera le navigateur du contrôleur actuel au format accepté dans Yii. Autrement dit, affichez <h1> dans la zone principale, <title> et le Yii::$app->view Ariane passent par les paramètres Yii::$app->view , et affichez le menu de droite sous forme de boutons.


Conclusion


YIMP est maintenant publié sans version. Je ne vois aucune raison de publier la version préliminaire - tout est trop simple pour cela. Je pense qu'il vaut mieux tester quelques semaines sur un vrai projet et passer immédiatement à la version 1.0.0. Les critiques, commentaires et aide sur GitHub sont donc les bienvenus.


Et je termine un module qui implémente le contrôle d'accès. Comment terminer - je vais écrire.


Comme vous pouvez le voir, il n'y a rien de compliqué ici. Je suis sûr que beaucoup d'entre vous ont quelque chose de similaire en stock. Et je serais très intéressé de savoir comment vous résolvez la tâche d'interface utilisateur dans votre zone d'administration.


Merci à tous!

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


All Articles