No MVC, a recuperação de dados básicos do modelo pode / deve ser feita no modo de exibição?

10

Dado o conceito de 'controladores magros, modelos gordos' e a aceitação geral de que o Views pode chamar diretamente o Models ao exigir dados para saída, deve-se considerar lidar com as partes de 'obter e exibir' as solicitações no Views e não no Controller? Por exemplo (tentativa de manter o código bastante genérico):

Controlador

<?php

class Invoice extends Base_Controller {

    /**
     * Get all the invoices for this month
     */

    public function current_month() {

        // as there's no user input let's keep the controller very skinny,
        // DON'T get data from the Model here, just load the view

        $this->load->view('invoice/current_month');

    }

}

Visão

<?php

// directly retrieve current month invoices here

$invoices = $this->invoice_model->get_current_month();

// get some other display-only data, e.g. a list of users for a separate list somewhere on the page

$users = $this->user_model->get_users();

?>

<h1>This month's invoices</h1>

<ul>
<?php foreach ($invoices as $invoice) { ?>

<li><?php echo $invoice['ref']; ?></li>

<?php } ?>
</ul>

Para mim, isso faz pelo menos algum sentido nos casos em que uma solicitação é essencialmente apenas uma Visualização. Por que o Controlador deve coletar e repassar os dados para o View quando ele pode apenas recuperá-lo? Isso deixa o Controlador aberto para processamento puramente no "nível de aplicativo" (por exemplo, tratamento de solicitações GET / POST, gerenciamento de direitos e permissões de acesso etc.), além de manter os modelos reutilizáveis ​​e todas as outras coisas boas.

Se este exemplo fosse estendido para permitir que um usuário filtrasse os resultados, o Controlador apenas manipularia o POST do formulário e passaria os filtros para a Visualização, que solicitaria os dados novamente, desta vez com os filtros.

Essa é uma abordagem válida para o desenvolvimento de um aplicativo MVC? Ou estou negligenciando uma parte importante do papel que um Controlador deve desempenhar?

Adam Westbrook
fonte

Respostas:

17

Sim, tecnicamente isso pode ser feito. Não, isso não deve ser feito. E sim, você está perdendo um pouco do objetivo do controlador.

O controlador está lá para desacoplar a vista do modelo. A dissociação é benéfica, porque você deve olhar para a exibição como um código quase descartável. À medida que a tecnologia da interface do usuário muda, você deseja minimizar o retrabalho necessário na geração de uma nova exibição. O controlador permite essa dissociação e fornece um local para o seu código que permanecerá por meio das tecnologias da interface do usuário.

Também funciona ao contrário se você precisar adicionar ou alterar seu modelo. Todas as alterações upstream estarão contidas no Controller e suas Views serão deixadas em paz.

O outro risco é que, embora o View seja muito simples agora , você tem menos garantia de que ele permanecerá tão simples ao longo de sua vida. Ao chamar o Model diretamente da (muito simples) View, você abriu a porta um pouco para permitir que más práticas adicionais entrem mais tarde, quando a View muito simples precisar se tornar não muito simples. Um futuro desenvolvedor ficará tentado a fazer mais chamadas de modelo a partir da exibição não muito simples, em vez de refatorar o código e interagir com um controlador.


fonte
11
Ótima resposta, obrigado. Estendendo um pouco o cenário 'olhando para o futuro'; se houver informações comuns em uma página separadas do que é solicitado (por exemplo, o usuário está visualizando um produto específico, uma lista geral das 'últimas ofertas especiais' é mostrada ao lado) como / onde a chamada deve offers_model->get_latest()ser feita? Adicionar isso a todos os métodos do controlador (como eu tentei tolamente antes) parece exagero e claramente seco.
Adam Westbrook
2
@AdamWestbrook Dê uma olhada no MVVM. A parte do ViewModel pode resolver esse problema específico. Você pode adicionar a offers_model->get_latest()uma ProductViewModelclasse base ou algo semelhante.
Zachary Yates
11
Ótimo, definitivamente vou olhar para o MVVM, obrigado novamente.
Adam Westbrook
Resposta muito boa, desafiadoramente manterá isso estrelado. Pessoalmente, eu também sou um grande fã de MVVM :)
Benjamin Gruenbaum
@BenjaminGruenbaum Você está usando MVVM em PHP? Se sim, você está usando uma estrutura específica para isso?
Adam Westbrook
6

Dado o conceito de 'controladores magros, modelos gordos' e a aceitação geral de que o Views pode chamar diretamente os modelos ao exigir dados para saída

Não, isso não está correto. A exibição não pode chamar diretamente os modelos. As visualizações não devem ter acesso aos objetos Modelo, a menos que, por algum motivo, o programador tenha exposto esses objetos à Visualização.

deve-se considerar lidar com as partes de 'obter e exibir' de solicitações nas Views e não no Controller?

Isso basicamente apaga o Controlador e derrota o ponto de tê-los.

Por que o Controlador deve coletar e repassar os dados para o View quando ele pode apenas recuperá-lo?

O controlador não coleta os dados. O Modelo faz a coleta dos dados. O controlador decide se esses dados devem ser passados ​​para a visualização. O View apenas faz a apresentação dos dados.

Se este exemplo fosse estendido para permitir que um usuário filtrasse os resultados, o Controlador apenas manipularia o POST do formulário e passaria os filtros para a Visualização, que solicitaria os dados novamente, desta vez com os filtros.

Não.

O Controlador verifica se os dados do POST são válidos, depois passa esses dados como opções para o Modelo, que consultaria a fonte de dados e retornaria os dados, e o Controlador os passaria para a Visualização.

Essa é uma abordagem válida para o desenvolvimento de um aplicativo MVC? Ou estou negligenciando uma parte importante do papel que um Controlador deve desempenhar?

O Controller opera como um manipulador de solicitações do navegador. Um despachante envia a solicitação para a ação de um controlador, que, por sua vez, espalha a solicitação para os Modelos. Os modelos contêm toda a lógica de negócios (essa é a parte mais importante) e devolvem os dados ao controlador. O controlador pode simplificar e ajustar os dados para facilitar a exibição do View.

O objetivo da Visualização é dissociar a estrutura e a dependência entre a apresentação do HTML e o DataSource. Enquanto isso pode ser difícil. As visualizações nem sempre apresentam dados que vieram diretamente de um modelo. O controlador geralmente adiciona dados extras relevantes.

Tenho certeza de que existem muitos tutoriais no MVC. Eu recomendo a leitura de alguns deles.

Reactgular
fonte
Obrigado, Mathew. Para fins de esclarecimento, até agora eu sempre desacoplei a View and Model com o Controller como lido e sugerido. No entanto, desde que comecei a ler sobre como manter os controladores 'magros', fiquei pensando o que deveria / poderia ser retirado deles, parece que o processo de pensamento que me levou a essa pergunta foi um passo ou dois longe demais!
Adam Westbrook
Quando você começa a usar os modelos por muitos controladores. A necessidade de serem gordas fica muito clara. Quando o View começa a conter muito PHP, você sabe que seu controlador está diminuindo. Quando seus controladores são muito gordos. É difícil fazer com que outros controladores operem da mesma maneira (por exemplo, adicionando um serviço de API).
Reactgular 31/01
3

Achei sua pergunta muito interessante porque encontrei o mesmo problema ao aprender Python recentemente.

Enquanto as respostas dadas fazem um argumento convincente, pensei em acrescentar outra opinião que me deparei, na qual o View obtém o estado do Modelo sem passar pelo Controlador.

MVC

É importante observar que a visualização e o controlador dependem do modelo. No entanto, o modelo não depende da visualização nem do controlador. Esse é um dos principais benefícios da separação. Essa separação permite que o modelo seja construído e testado independentemente da apresentação visual. A separação entre visualização e controlador é secundária em muitos aplicativos rich-client e, de fato, muitas estruturas de interface com o usuário implementam as funções como um objeto. Em aplicativos da Web, por outro lado, a separação entre visualização (o navegador) e controlador (os componentes do servidor que manipulam a solicitação HTTP) é muito bem definida.

O Model-View-Controller é um padrão de design fundamental para a separação da lógica da interface do usuário da lógica de negócios. Infelizmente, a popularidade do padrão resultou em várias descrições defeituosas. Em particular, o termo "controlador" tem sido usado para significar coisas diferentes em diferentes contextos. Felizmente, o advento dos aplicativos da Web ajudou a resolver parte da ambiguidade, porque a separação entre a visualização e o controlador é muito aparente.

Em Programação de aplicativos no Smalltalk-80: Como usar o MVC (Model-View-Controller) [Burbeck92], Steve Burbeck descreve duas variações do MVC: um modelo passivo e um modelo ativo.

O modelo passivo é empregado quando um controlador manipula o modelo exclusivamente. O controlador modifica o modelo e informa a exibição de que o modelo foi alterado e deve ser atualizado (consulte a Figura 2). O modelo neste cenário é completamente independente da visualização e do controlador, o que significa que não há meios para o modelo relatar alterações em seu estado. O protocolo HTTP é um exemplo disso. Não há uma maneira simples no navegador de obter atualizações assíncronas do servidor. O navegador exibe a visualização e responde à entrada do usuário, mas não detecta alterações nos dados no servidor. Somente quando o usuário solicita explicitamente uma atualização é que o servidor é interrogado quanto a alterações.

MVC - Modelo Passivo

Não estou em posição de dizer qual das opiniões é "correta" e, para ser sincera, fico um pouco mais confusa depois de ler as respostas aqui e o artigo vinculado.

Texto completo do artigo aqui .

alnafie
fonte
Certo, e a outra coisa que acrescenta confusão é o cliente-servidor, que o SmallTalk MVC original não contava realmente. No cliente-servidor (por exemplo, com javascript), existem modelos, visualizações e controladores orientados a apresentação no cliente e visualizações e controladores orientados a domínio no servidor, embora o servidor também faça algum processamento orientado a apresentação, adicionando confusão. Além disso, às vezes queremos que as visualizações de domínio tenham alguma persistência, o que significa que os parâmetros de visualização formam seu próprio modelo, que não é necessariamente parte do modelo de domínio.
Erik Eidt 02/02
Obrigado pelo link, eu sabia que não estava com raiva de pensar nisso! Isso é basicamente o que eu estava saindo antes de levar a idéia um pouco longe demais, desde que o Modelo não dependa de nada, o que importa como / onde é acessado? Ainda não decidi qual abordagem vou adotar no meu próximo desenvolvimento, mas isso definitivamente ajuda.
Adam Westbrook
1

Outra coisa a considerar é que você parece ter carregado automaticamente user_modele invoice_modelpermitir que a visualização os acesse. Para que isso funcione de maneira confiável, você provavelmente carrega automaticamente todos os seus modelos (porque $this->load->model()apenas parece errado em uma exibição, não é ...)

Fazer isso incha sua pilha desnecessariamente, carregando um monte de coisas que talvez nunca sejam usadas. Parte do motivo de ter vários modelos é permitir que você encapsule a lógica relacionada e carregue apenas o necessário para uma determinada tarefa.

Isso se parece com o CodeIgniter. Desenvolvi muito o desenvolvimento de CI e posso compartilhar por experiência pessoal que você realmente não deseja carregar automaticamente mais do que realmente precisa. Tente adicionar $this->output->enable_profiler(TRUE);o construtor de um controlador e mexer com carregamentos automáticos (incluindo auxiliares como database): você provavelmente verá uma mudança significativa nos tempos de carregamento e execução, mas principalmente na alocação de memória.

msanford
fonte
11
Bons pontos, você está certo, isso se baseia no IC, embora eu tenha removido algumas das sintaxes específicas para fins de clareza. Eu adquiri o hábito de 'carregar automaticamente' praticamente tudo, por grande parte do tempo e por razões secas, parecia um pouco louco por ter o mesmo load->modelna maioria dos controladores e métodos. Não usando uma função autoload adequada é uma das coisas que mais gostam sobre compatibilidade com versões anteriores do CI, mas isso é toda uma outra discussão ...
Adam Westbrook
0

A resposta curta é que a forma do seu exemplo de código é enganosamente intuitiva. Parece que este é um caminho "fácil de entender".


Problema # 1

Seus objetos Modele Viewserão fortemente acoplados.

Se você precisar adicionar ou remover métodos no Model, poderá ser necessário alterar o mesmo View.

Fundamentalmente, o MVC é derivado dos padrões Command e Observer . Você deseja um 'Modelo' independente que seja manipulado por meio de uma interface / API na qual ele Controllerpossa se conectar (ou seja, delegação).

Freqüentemente, isso significa injetar Model e Viewinstâncias no Controllere armazená-los como propriedades do referido Controller. Em seguida, usando um método da Controller(ou seja, um comando) como área de trabalho, passe os dados para a a View partir de Model ( depois que o `Modelo terminar de atualizar o estado do aplicativo ).

Dados passam (arrays, objectos iteráveis, qualquer que seja) mantém acoplamento entre Modele Viewinstâncias solta . Se você injetar a Modelinstância no View, consulte o Problema nº 1 acima.

Lembre-se, Viewspode ser HTML, JSON, Texto, XML, cabeçalhos HTTP, YAML ou quase qualquer coisa, seguindo uma metodologia de transferência de estado de representação (REST) .

Assim, a chave para compreender como gerir a relação entre o Modele Viewsé ver a relação para o que é, um-para-muitos (potencialmente)! É exatamente isso que o padrão Observer foi projetado para realizar.

Embora a maioria das configurações tenha apenas uma visão para se preocupar por vez, não há nada que impeça o padrão arquitetural do MVC de atualizar várias visualizações ao mesmo tempo! Trabalhar com aplicações CRUD web tradicionais faz com que as pessoas pensam em um one-to-one way, mas isso é o menor exemplo de como o padrão Observer poderia trabalhar ( um-para-muitos sendo o outro ).

Portanto, se você tivesse um Modele vários Views, a dor de cabeça em potencial de atualizar todo o Views'código de implementação porque você alterou algo na Model'sAPI / métodos agora fica aguda .

Passe dados para Views , não instâncias de Models .

Anthony Rutledge
fonte