Cenário MVC + vs Controladores de gordura

Cenário MVC + vs. controladores espessos


As estruturas PHP modernas (Symphony, Laravel, daqui para a frente em todos os lugares) mostram de forma convincente que a implementação do padrão Model-View-Controller não é tão simples. Todas as implementações, por algum motivo, são propensas a Fat Controllers ( fat controllers ), condenadas por todos, pelos desenvolvedores e pelas próprias estruturas.


Por que é isso? E existe alguma maneira de lidar com isso? Vamos acertar.


Terminologia


  • Modelo - modelo (modelador de dados solicitados)
  • View - view (decorador de dados do modelo)
  • Controller - controller (coordenador de visualização de modelo, conforme solicitado)
  • Template - modelo de apresentação
  • Renderização - renderização (formação, design da imagem da apresentação)
  • Renderer - renderizador (modelador, designer da imagem da apresentação)

Controlador grosso


Aqui está um controlador de gordura típico:


 class UserController { /** *   *      ID */ public function actionUserHello($userId) { //         ( ) $user = UserModel::find($userId); //       -   $name = $user->firstName.' '.$user->lastName; //         $view = new View('hello', ['name' => $name]); //  ( )     return $view->render(); } } 

O que nós vemos? Nós vemos vinagrete! No controlador, tudo o que é possível é misto - tanto o modelo quanto a apresentação e, de fato, o próprio controlador!


Vemos os nomes do modelo e do modelo firmemente conectados ao controlador. Este não é um burburinho. Vemos manipulações com os dados do modelo no controlador - a formação de um nome completo a partir do nome e sobrenome. E isso não é um burburinho.


E mais uma coisa: não vemos este exemplo explicitamente, mas está implícito. Ou seja: existe apenas uma maneira de renderizar (formação de imagem)! Apenas um: de acordo com o modelo no arquivo php! E se eu quiser pdf? E se eu não quiser em um arquivo, mas em uma linha php? Eu tinha desenhos com desenhos elaborados em centenas de pequenos modelos. Eu tive que deixar escapar o renderizador para os modelos de string. Não superaqueço, é claro, mas o assunto é em princípio.


Breve resumo:


As estruturas modernas têm falhas comuns na implementação do MVC para todos:
  1. Interpretação restrita da MVC-view (View) apenas como "Visualizar com um modelo em um arquivo PHP" em vez de "Visualizar com qualquer renderizador" .
  2. Interpretação restrita do modelo MVC apenas como "Domínio do Modelo de Banco de Dados" em vez de "Qualquer compilador de dados para apresentação" .
  3. Eles provocam o uso dos chamados "controladores grossos", que contêm toda a lógica ao mesmo tempo: negócios, apresentação e interação. Isso destrói completamente o objetivo principal do MVC - a divisão de responsabilidades entre os componentes da tríade.

Para resolver essas deficiências, seria bom dar uma olhada nos componentes do MVC.


View é um renderizador


Veja a primeira desvantagem:


  1. Interpretação restrita da MVC-view (View) apenas como "Visualizar com um modelo em um arquivo PHP" em vez de "Visualizar com qualquer renderizador"

Aqui, tudo é bem simples - a solução para o problema já está indicada na própria formulação do problema. Nós apenas temos que dizer que qualquer renderizador pode usar a visualização. Para implementar isso, basta adicionar a nova propriedade do renderer à classe View:


 class View { public $template, $data, $renderer; public function __costruct($template, $data, $renderer = NULL) {} } 

Portanto, definimos uma nova propriedade de renderer para a visualização. No caso mais geral, o valor dessa propriedade pode ser qualquer função que possa callable que forma uma imagem dos dados transferidos para ela usando o modelo transmitido.


A maioria dos aplicativos usa apenas um renderizador e, mesmo que use vários, um deles é o preferido. Portanto, o argumento do renderer é definido como opcional, assumindo que haja algum renderizador padrão.


É simples? Simples. Na verdade não é tão simples. O fato é que a View que está no MVC não é exatamente a View que está nas estruturas. A View que está na estrutura não pode viver sem um modelo. Mas o View , que no MVC, por algum motivo, não sabe nada sobre esses mesmos modelos. Porque Sim, porque para o MVC View , este é qualquer conversor de dados do modelo em uma imagem , e não apenas um mecanismo de modelo. Quando escrevemos algo parecido com isto no manipulador de solicitações:


 $name = ' '; return "Hello, {$name}!"; 

ou até:


 $return json_encode($name); // Ajax response 

então nós realmente definimos a View que está no MVC, sem tocar em nenhuma View que esteja nos frameworks!


Mas agora tudo é realmente simples: aqueles View , que nos frameworks - este é um subconjunto desses View , que estão no MVC. Além disso, um subconjunto muito restrito, a saber, é apenas um mecanismo de modelo baseado em arquivos PHP.


Resumo: é o , ou seja, qualquer decorador de uma imagem de dados é o View que está no MVC. E esses View , que estão nas estruturas, são apenas um tipo de .


Modelo de domínio / modelo de exibição (ViewModel / DomainModel)


Agora veja a segunda desvantagem:


  1. Interpretação restrita do modelo MVC apenas como "Domínio do Modelo de Banco de Dados" em vez de "Qualquer compilador de dados para apresentação" .

É óbvio para todos que o modelo MVC é uma coisa complexa que consiste em outras partes. A comunidade concorda em decompor o modelo em dois componentes: um modelo de domínio (DomainModel) e um modelo de apresentação (ViewModel).


Um modelo de domínio é o que é armazenado nos bancos de dados, ou seja, dados normalizados do modelo. Digite, 'primeiro nome' e 'sobrenome' em diferentes campos. As estruturas estão ocupadas com essa parte específica do modelo simplesmente porque o armazenamento de dados é seu próprio universo, bem estudado.


No entanto, um aplicativo precisa de dados agregados, e não normalizados. Os dados do domínio devem ser compilados em imagens como: "Olá, Ivan!", Ou "Caro Ivan Petrov!", Ou mesmo "Para Ivan a Petrov a !". Esses dados convertidos são referidos a outro modelo - modelo de apresentação. Portanto, é essa parte do modelo que ainda é ignorada pelas estruturas modernas. É ignorado porque não há acordo sobre como lidar com isso. E se as estruturas não fornecem uma solução, os programadores seguem o caminho mais simples - lançam o modelo de visualização no controlador. E eles recebem os controladores de gordura odiados, mas inevitáveis!


Total: para implementar o MVC, você precisa implementar um modelo de visualização. Não há outras opções. Dado que as representações e seus dados podem ser quaisquer, afirmamos que temos um problema.


Cenário vs. controladores de gordura


Há uma última desvantagem das estruturas:


  1. Eles provocam o uso dos chamados "controladores grossos", que contêm toda a lógica ao mesmo tempo: negócios, apresentação e interação. Isso destrói completamente o objetivo principal do MVC - a divisão de responsabilidades entre os componentes da tríade.

Aqui chegamos ao básico do MVC. Sejamos claros. Portanto, o MVC assume a seguinte distribuição de responsabilidades entre os componentes da tríade:


  • O controlador é a lógica de interação , ou seja, interações com o mundo externo (solicitação - resposta) e o interno (Modelo - Apresentação),
  • O modelo é lógica de negócios , ou seja, gerar dados para uma solicitação específica,
  • Representação é a lógica da representação , ou seja, decoração dos dados gerados pelo Modelo.

Vá em frente. Dois níveis de responsabilidades são claramente visíveis:


  • O nível organizacional é o controlador,
  • O nível executivo é Modelo e Representação.

Em termos simples, o controlador dirige, Model e View. Isto é se de uma maneira simples. E se não de uma maneira simples, mas mais especificamente? Como exatamente o controlador dirige? E como exatamente o Model e o View funcionam?


O controlador dirige assim:


  • Recebe uma solicitação de um aplicativo,
  • Decide qual modelo e qual exibição usar para esta solicitação,
  • Chama o modelo selecionado e recebe dados dele,
  • Invoca a vista selecionada com os dados recebidos do modelo,
  • Retorna os dados decorados pela exibição de volta para o aplicativo.

Algo assim. O essencial neste esquema é que o Modelo e Representação acabem sendo links na cadeia de execução da consulta. Além disso, por links sucessivos: primeiro, o Modelo converte a solicitação em alguns dados, depois esses dados do Modelo são convertidos pela Visualização em uma resposta decorada conforme necessário para uma solicitação específica. Assim, uma solicitação humanóide é decorada visualmente com templatizators, uma solicitação android é decorada com codificadores JSON.


Agora vamos tentar descobrir exatamente como os artistas atuam - Modelo e Apresentação. Dissemos acima que há consenso sobre a decomposição do Modelo em dois subcomponentes: Modelo de Domínio e Modelo de Apresentação. Isso significa que pode haver mais artistas - não dois, mas três. Em vez de uma cadeia de execução


>>

pode muito bem haver uma corrente


>> >>

A pergunta se coloca: por que apenas dois ou três? E se você precisar de mais? A resposta natural é, pelo amor de Deus, pegue o quanto você precisar!


Outros artistas úteis são imediatamente visíveis: validadores, redirecionadores, vários renderizadores e, em geral, tudo o que é imprevisível, mas agradável.


Vamos recapitular:


  • O nível executivo MVC ( - ) pode ser implementado como uma cadeia de links, em que cada link converte a saída do link anterior na entrada para o próximo.
  • A entrada do primeiro link é a solicitação do aplicativo.
  • A saída do último link é a resposta do aplicativo à solicitação.

Chamei essa cadeia de Scenario , mas, para os elos da cadeia, ainda não decidi o nome. As opções atuais são uma cena (como parte de um script), um filtro (como um conversor de dados), uma ação de script. De um modo geral, o nome do link não é tão importante, há uma coisa mais significativa.


As consequências da aparência do cenário são significativas. A saber: O cenário assumiu a responsabilidade primária do Controlador - determinar o Modelo e a Apresentação necessários para a solicitação e iniciá-los. Portanto, o controlador tem apenas duas responsabilidades: interagir com o mundo externo (solicitação-resposta) e executar o script. E isso é bom no sentido de que todos os componentes da tríade MVC são decompostos sequencialmente e se tornam mais específicos e gerenciáveis. E ainda é bom em outro aspecto - o controlador MVCS se torna uma classe imutável puramente interna e, portanto, mesmo em princípio, não pode engordar.


O uso de cenários leva a outra variação do padrão MVC; chamei essa variação MVCS - Model-View-Controller-Scenario .


E mais algumas linhas sobre a decomposição do MVC. Estruturas modernas, onde todas as funções típicas são decompostas até o limite, naturalmente tiraram da parte conceitual do MVC as responsabilidades de interagir com o mundo exterior. Portanto, classes especialmente treinadas, como HTTP e estão envolvidas no processamento de solicitação do usuário. Como resultado, o Controlador recebe não a solicitação inicial do usuário, mas algumas refinadas, e isso permite isolar a controladora das especificidades da solicitação. Da mesma forma, é feito isolamento das especificidades da resposta HTTP, permitindo que o módulo MVC defina seu próprio tipo de resposta. Além disso, as estruturas implementaram totalmente os dois componentes do MVC - o Modelo de Domínio e o Modelo de Apresentação, no entanto, já discutimos isso. Sou tudo isso porque o refinamento e a concretização do MVC são contínuos e contínuos, e isso é movimentado.


Exemplo MVCS


Agora vamos ver como o exemplo do Fat Cortroller no início deste artigo pode ser implementado no MVCS.


Começamos criando um controlador MVCS:


 $mvcs = new MvcsController(); 

O controlador MVCS recebe uma solicitação de um roteador externo. Permita que o roteador converta o URI do formulário 'user / hello / XXX' em uma ação e solicite parâmetros:


 $requestAction = 'user/hello'; //   $requestParams = ['XXX']; //   -   

Considerando que o controlador MVCS aceita scripts em vez de URIs, precisamos mapear alguns scripts para a ação da solicitação. É melhor fazer isso no contêiner MVCS:


 //   MVCS  URI  $mvcs->set('scenarios', [ 'user/hello' => 'UserModel > UserViewModel > view, hello', ..., ]); 

Vamos dar uma olhada mais de perto neste cenário. Esta é uma cadeia de três conversores de dados separados por um '>':


  • 'UserModel' é o nome do modelo de domínio 'User', a entrada do modelo será os parâmetros de solicitação, a saída será os dados reais do modelo,
  • 'UserViewModel' é o nome do modelo de exibição que converte dados de domínio em dados de exibição,
  • 'view, olá' é o 'modelo' da visualização do sistema para um modelo PHP chamado 'olá'.

Agora, apenas precisamos adicionar dois transformadores envolvidos no script como uma função de fechamento no contêiner MVCS:


 //   UserModel $mvcs->set('UserModel', function($id) { $users = [ 1 => ['first' => '', 'last' => ''], 2 => ['first' => '', 'last' => ''], ]; return isset($users[$id]) ? $users[$id] : NULL; }); //   UserViewModel $mvcs->set('UserViewModel', function($user) { //    PHP  : 'echo "Hello, $name!"'; return ['name' => $user['first'].' '.$user['last']]; }); 

E isso é tudo! Para cada solicitação, é necessário determinar o script correspondente e todas as suas cenas (exceto as do sistema, como 'visualização'). E nada mais.


E agora estamos prontos para testar o MVCS para diferentes solicitações:


 //        $scenarios = $mvcs->get('scenarios'); $scenario = $scenarios[$requestAction]; //      ... //   'user/hello/1'  ' '   'hello' $requestParams = ['1']; $response = $mvcs->play($scenario, $requestParams); //   'user/hello/2'  ' '   'hello' $requestParams = ['2']; $response = $mvcs->play($scenario, $requestParams); 

A implementação do PHP MVCS está hospedada no github.com .
Este exemplo está no diretório MVCS de example .

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


All Articles