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 { public function actionUserHello($userId) {
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:
- Interpretação restrita da MVC-view (View) apenas como "Visualizar com um modelo em um arquivo PHP" em vez de "Visualizar com qualquer renderizador" .
- 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" .
- 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:
- 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);
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:
- 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:
- 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';
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:
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:
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:
A implementação do PHP MVCS está hospedada no github.com .
Este exemplo está no diretório MVCS de example
.