YIMP - Painel de Controle para o Yii 2 no Bootstrap 4

Estou certo de que muitos desenvolvedores que preferem estruturas ao CMS pronto têm em estoque uma solução no Bootstrap ou seus análogos, que é usada para criar interfaces de administração e outras interfaces de back-office. E eu tenho. Ele trabalha com sucesso há muitos anos, mas está irremediavelmente desatualizado. É hora de reescrever.


Enquanto trabalhava na nova versão, tentei resumir toda a minha experiência neste tópico e, como resultado, adquiri o YIMP - uma bicicleta que não tenho vergonha de compartilhar: GitHub , LiveDemo , API Documentation .


YIMP é muito simples. Mas por trás dessa simplicidade há um longo pensamento, que eu também quero compartilhar. Portanto, este artigo não é uma instrução. Aqui falamos sobre arquitetura, gerenciamento de dependências, o paradigma MVC e a interface do usuário, é claro.


Portanto, o YIMP é um painel. Este não é um painel de administração pronto, nem um CMS ou mesmo um CMF. O código de representação precisa ser escrito independentemente ou usar o Gii (os modelos estão anexados). O YIMP fornece um layout que define onde os controles devem estar localizados, bem como a interface através da qual o aplicativo transfere dados para o layout. É assim que parece nos desktops:



Layout adaptável. À medida que a tela diminui, os elementos começam a desaparecer ou mover-se sobre os botões na barra de navegação. Como resultado , a mesma página no telefone fica assim:


Melhor deixá-lo sob o spoiler

O que vemos no layout? Título do aplicativo, trilha de navegação, três menus (esquerdo, direito e superior), widgets nas barras laterais, título da página. Na minha prática, esse conjunto de elementos foi suficiente para desenvolver quaisquer interfaces - de páginas de administração de páginas de entrada a sistemas de informações corporativas. Tentei organizá-los para que o espaço fosse usado da maneira mais eficiente possível. O que você diz


A marcação é escrita no Bootstrap puro, sem extensões e personalização. Sempre que possível, foram usadas classes do Bootstrap; portanto, se você decidir usar a personalização, não haverá problemas.


Como eu disse, o YIMP inclui uma interface através da qual o aplicativo transfere dados para o layout. Vamos ver como isso acontece. Abra o capô!


Layout


Acredito que o desenvolvedor deve ter controle total sobre o layout, portanto, ao instalar o YIMP, recomendo copiar o código dele para o aplicativo. Na minha opinião, isso é muito melhor do que deixar um layout no pacote e bloquear várias configurações para ele. Vamos ver o código do layout:


77 linhas de código. Não é necessário se aprofundar!
<?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() ?> 

Como você pode ver, toda a marcação YIMP é agrupada em métodos. A maioria desses métodos simplesmente imprime as linhas que são obtidas dessa matriz . Sim, o princípio do KISS é o nosso tudo.


Observe que os blocos são usados ​​no layout. Eles são necessários para exibir os widgets definidos nas visualizações (é assim que os controles do formulário são renderizados). Bem, se o widget travar em todas as páginas, é melhor determiná-lo diretamente no layout.


Portanto, a área principal e os widgets são definidos nas visualizações. E onde são determinados os títulos, menus e migalhas de pão? Na minha opinião, eles são melhor definidos nos controladores. Este é um ponto importante, pois essa decisão contradiz o paradigma MVC. Vejamos esse problema com mais detalhes.


Controladores


Portanto, permita que um ProfileController possa exibir informações sobre o perfil do usuário atual e alterar a senha. Logicamente, a ação de profile/view será chamada "Meu perfil". Também é lógico que no menu principal deve haver um item "Meu perfil". Por fim, "Meu perfil" deve estar na trilha de navegação: "Página inicial / Meu perfil / Alterar senha". Eu acho que o desejo de definir uma constante com as palavras "Meu perfil" é bastante justificado. Você não pode fazer isso na exibição. Selecionar uma camada separada para cabeçalhos é complicado. Raciocínio assim, cheguei aos controladores.


O próximo passo foi a decisão de definir não apenas os cabeçalhos de ação nos controladores, mas também as migalhas de pão e os menus. E faça-o para que o YIMP possa lê-los. E aqui precisamos de um exemplo. Vejamos uma possível implementação da classe ProfileController .


52 linhas de código simples. Melhor olhar com cuidado!
 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'); } } 

Cabeçalhos e trilhas de navegação são definidos usando métodos estáticos, o que significa que podem ser usados ​​em qualquer lugar. Por exemplo, no menu principal do aplicativo, você pode escrever:


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

Por que exatamente os métodos? Porque amanhã você será solicitado, em vez das palavras "Meu perfil", para exibir o login do usuário atual.


Com migalhas de pão o mesmo. Suponha que nosso usuário tenha uma lista de imagens pelas quais o ImageController é responsável. Em seguida, na image/create ação de image/create , você pode escrever:


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

e obtenha migalhas de pão como "Casa / Meu perfil / Adicionar uma foto". A propósito, como a ação de image/create é chamada de "Adicionar uma imagem", o menu de ação de profile/view precisará ser corrigido:


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

Eu acho que a ideia é compreensível. Na minha opinião, esta é uma solução simples e eficaz para a qual você pode se afastar do paradigma MVC. Sim, o código do controlador está ficando maior, mas há um lugar para ele no controlador - não escrevemos lógica de negócios lá, certo? E sim, eu ficaria muito interessado em saber sua opinião sobre esse assunto.


Nós estamos indo além. Como você deve ter adivinhado, a propriedade nav , definida como \dmitrybtn\yimp\Navigator , é usada para transferir cabeçalhos, menus e trilhas de navegação do controlador para o layout. E esse é outro recurso do YIMP.



Como as configurações entram no layout? Muito simples Durante sua inicialização, o YIMP verifica a propriedade nav do controlador atual. Se essa propriedade estiver legível e for um navegador ( instanceof \dmitrybtn\yimp\Navigator ), ela será usada para exibir as informações correspondentes. O navegador inclui propriedades correspondentes aos elementos de layout, cuja lista completa é mais fácil de ver na documentação da API .


No aplicativo, é recomendável criar seu próprio navegador e definir menus nele que não dependerão da ação atual (superior e esquerda). Depois disso, em todos os controladores, você precisa criar a propriedade nav e defini-la como um navegador (você pode usar suas mãos, herdar ou traçar). Se necessário, você pode definir vários navegadores.


Essa abordagem elimina o relacionamento direto entre o YIMP e o controlador. Toda interação é realizada através de um objeto, cuja implementação é controlada pelo desenvolvedor. Ou seja, este é o mesmo Princípio de Inversão de Dependência do SOLID ou Baixo Acoplamento do GRASP .


Seria ideologicamente correto usar interfaces, tanto para o controlador quanto para o navegador. Mas aqui decidi seguir o caminho mais simples e não bagunçar o sistema. No final, o DIP não fala sobre interfaces, mas sobre abstrações. E, nesse caso, abstração é um acordo sobre a presença de um certo tipo de propriedade em uma classe específica. O que você acha?


A ausência de um relacionamento direto entre o YIMP e o controlador se torna importante quando aparecem módulos no sistema que não sabem nada sobre o YIMP. Ou vice-versa - os módulos escritos no YIMP são instalados em um sistema que o YIMP não usa.


No primeiro caso, o YIMP não verá as propriedades de nav no controlador. Não haverá erro, mas seus menus desaparecerão da tela e o ID da ação será usado como título. Como ser Muito simples - se o YIMP não puder tirar o navegador do controlador, ele será criado através do contêiner DI usando o apelido yimp-nav . Usando esse alias, você pode registrar seu próprio navegador padrão, por exemplo, especificando nas configurações do aplicativo:


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

No segundo caso, o navegador no controlador será, mas não haverá ninguém para lê-lo. Nesse caso, é recomendável escrever um wrapper para as visualizações no módulo, que adaptará o navegador do controlador atual ao formato aceito no Yii. Ou seja, exiba <h1> na área principal, <title> e as trilhas de navegação passam pelos parâmetros Yii::$app->view e exibem o menu direito na forma de botões.


Conclusão


O YIMP agora é publicado sem uma versão. Não vejo motivo para publicar a versão de pré-lançamento - tudo é simples demais para isso. Acho melhor testar algumas semanas em um projeto real e mudar imediatamente para a versão 1.0.0. Portanto, críticas, comentários e ajuda no GitHub são muito bem-vindos.


E estou concluindo um módulo que implementa o controle de acesso. Como terminar - vou escrever.


Como você pode ver, não há nada complicado aqui. Tenho certeza que muitos de vocês têm algo semelhante em estoque. E eu ficaria muito interessado em saber como você resolve a tarefa da interface do usuário na sua área de administração.


Obrigado a todos!

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


All Articles