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); ?> <?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.
Navegador
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!