Dans MVC, la récupération des données de base à partir du modèle peut / doit-elle être effectuée dans la vue?

10

Compte tenu du concept de `` contrôleurs maigres, de gros modèles '' et de l'acceptation générale que les vues peuvent appeler directement les modèles lors de la demande de données pour la sortie, devrait-on envisager de gérer les parties `` obtenir et afficher '' des demandes dans les vues et non le contrôleur? Par exemple (a tenté de garder le code assez générique):

Manette

<?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');

    }

}

Vue

<?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>

Pour moi, cela a au moins un certain sens dans les cas où une demande est essentiellement juste une vue. Pourquoi le contrôleur devrait-il collecter et transmettre les données à la vue alors qu'il peut simplement les récupérer lui-même? Cela laisse le contrôleur ouvert pour un traitement purement «au niveau de l'application» (par exemple, la gestion des demandes GET / POST, la gestion des droits d'accès et des autorisations, etc.) ainsi que la conservation des modèles réutilisables et toutes les autres bonnes choses.

Si cet exemple était étendu pour permettre à un utilisateur de filtrer les résultats, le contrôleur manipulerait simplement le POST du formulaire et transmettrait les filtres à la vue, qui demanderait à nouveau les données, cette fois avec les filtres.

Est-ce une approche valable pour développer une application MVC? Ou est-ce que je néglige une partie importante du rôle qu'un contrôleur devrait jouer?

Adam Westbrook
la source

Réponses:

17

Oui, cela peut techniquement être fait. Non, cela ne devrait pas être fait. Et oui, vous manquez un peu de ce pour quoi le contrôleur est là.

Le contrôleur est là pour découpler la vue du modèle. Le découplage est avantageux car vous devez considérer la vue comme du code presque jetable. Au fur et à mesure que votre technologie d'interface utilisateur change, vous souhaitez minimiser les retouches nécessaires pour générer une nouvelle vue. Le contrôleur permet ce découplage et fournit un emplacement pour votre code qui vivra grâce aux technologies d'interface utilisateur.

Cela fonctionne également en sens inverse si vous devez ajouter ou modifier votre modèle. Toutes les modifications en amont seront contenues dans le contrôleur et vos vues seront laissées seules.

L'autre risque est que même si la vue est très simple maintenant , vous avez moins de garantie qu'elle restera aussi simple tout au long de sa vie. En appelant le modèle directement à partir de la vue (très simple), vous avez un peu ouvert la porte pour permettre à de mauvaises pratiques supplémentaires de s'introduire plus tard lorsque la vue très simple doit devenir pas si simple. Un futur développeur sera tenté de faire plus d'appels de modèle à partir de la vue pas si simple qu'au lieu de refactoriser le code et d'interagir avec un contrôleur.


la source
1
Excellente réponse, merci. Prolonger légèrement votre scénario «d'anticipation»; s'il existe des informations communes sur une page distincte de ce qui est demandé (par exemple, l'utilisateur consulte un produit spécifique, une liste générale des «dernières offres spéciales» est affichée sur le côté) comment / où l'appel doit-il offers_model->get_latest()être effectué? L'ajout de cela à chaque méthode du contrôleur (comme je l'ai déjà bêtement tenté) semble exagéré et nettement non SEC.
Adam Westbrook
2
@AdamWestbrook Jetez un œil à MVVM. La partie ViewModel de cela peut résoudre ce problème spécifique. Vous pouvez ajouter le offers_model->get_latest()à une ProductViewModelclasse de base ou quelque chose de similaire.
Zachary Yates
1
Très bien, je vais certainement regarder MVVM, merci encore.
Adam Westbrook
Très bonne réponse, gardera sans conteste cette étoile. Personnellement, je suis aussi un grand fan de MVVM :)
Benjamin Gruenbaum
@BenjaminGruenbaum Utilisez-vous MVVM en PHP? Si oui, utilisez-vous un cadre particulier pour cela?
Adam Westbrook
6

Étant donné le concept de `` contrôleurs maigres, modèles lourds '' et l'acceptation générale que les vues peuvent appeler directement les modèles lorsqu'ils nécessitent des données pour la sortie

Non, ce n'est pas correct. La vue ne peut pas appeler directement les modèles. Les vues ne doivent pas avoir accès aux objets du modèle, sauf si, pour une raison quelconque, le programmeur a exposé ces objets à la vue.

Doit-on envisager de traiter les parties «obtenir et afficher» des demandes dans les vues et non dans le contrôleur?

Cela efface fondamentalement le contrôleur et défait le point de les avoir.

Pourquoi le contrôleur devrait-il collecter et transmettre les données à la vue alors qu'il peut simplement les récupérer lui-même?

Le contrôleur ne collecte pas les données. Le modèle effectue la collecte des données. Le contrôleur décide si ces données doivent être transmises à la vue. La vue ne fait que la présentation des données.

Si cet exemple était étendu pour permettre à un utilisateur de filtrer les résultats, le contrôleur manipulerait simplement le POST du formulaire et transmettrait les filtres à la vue, qui demanderait à nouveau les données, cette fois avec les filtres.

Non.

Le contrôleur vérifie si les données POSTées sont valides, il transmet ensuite ces données en tant qu'options au modèle, qui interroge ensuite la source de données et renvoie les données, et le contrôleur les transmet à la vue.

Est-ce une approche valable pour développer une application MVC? Ou est-ce que je néglige une partie importante du rôle qu'un contrôleur devrait jouer?

Le contrôleur fonctionne comme un gestionnaire des demandes du navigateur. Un répartiteur envoie la demande à l'action d'un contrôleur, qui à son tour, diffuse la demande aux modèles. Les modèles contiennent toute la logique métier (c'est la partie grasse) et rendent les données au contrôleur. Le contrôleur peut alors simplifier et ajuster les données afin qu'il soit plus facile pour la vue de les présenter.

Le point de vue est de découpler la structure et la dépendance entre la présentation du HTML et le DataSource. Bien que cela puisse être difficile. Les vues ne présentent pas toujours des données provenant directement d'un modèle. Le contrôleur ajoute souvent des données supplémentaires pertinentes.

Je suis sûr qu'il existe de nombreux tutoriels sur MVC. Je recommanderais de lire certains d'entre eux.

Reactgular
la source
Merci Mathew. Pour plus de précision, jusqu'à présent, j'ai toujours découplé la vue et le modèle avec le contrôleur comme lu et suggéré. Cependant, depuis que j'ai commencé à lire sur le maintien des contrôleurs «maigres», je me demandais simplement ce qui devrait / pourrait être retiré d'eux, le processus de réflexion qui m'a conduit à cette question était un pas ou deux trop loin!
Adam Westbrook
Lorsque vous commencez à obtenir des modèles utilisés par de nombreux contrôleurs. La nécessité pour eux d'être gras devient très claire. Lorsque la vue commence à contenir beaucoup de PHP, vous savez que votre contrôleur est trop mince. Lorsque vos manettes sont très grasses. Il est difficile de faire fonctionner les autres contrôleurs de la même manière (par exemple, en ajoutant un service API).
Reactgular
3

J'ai trouvé votre question très intéressante car j'ai rencontré le même problème lors de mon apprentissage de Python récemment.

Bien que les réponses données constituent un argument convaincant, j'ai pensé ajouter une autre opinion que j'ai rencontrée dans laquelle la vue obtient l'état du modèle sans passer par le contrôleur.

MVC

Il est important de noter que la vue et le contrôleur dépendent du modèle. Cependant, le modèle ne dépend ni de la vue ni du contrôleur. C'est l'un des principaux avantages de la séparation. Cette séparation permet au modèle d'être construit et testé indépendamment de la présentation visuelle. La séparation entre la vue et le contrôleur est secondaire dans de nombreuses applications client riche et, en fait, de nombreux frameworks d'interface utilisateur implémentent les rôles comme un seul objet. Dans les applications Web, en revanche, la séparation entre la vue (le navigateur) et le contrôleur (les composants côté serveur gérant la requête HTTP) est très bien définie.

Model-View-Controller est un modèle de conception fondamental pour la séparation de la logique de l'interface utilisateur de la logique métier. Malheureusement, la popularité du modèle a entraîné un certain nombre de descriptions erronées. En particulier, le terme "contrôleur" a été utilisé pour signifier différentes choses dans différents contextes. Heureusement, l'avènement des applications Web a permis de résoudre une partie de l'ambiguïté car la séparation entre la vue et le contrôleur est tellement apparente.

Dans Application Programming in Smalltalk-80: Comment utiliser Model-View-Controller (MVC) [Burbeck92], Steve Burbeck décrit deux variantes de MVC: un modèle passif et un modèle actif.

Le modèle passif est utilisé lorsqu'un contrôleur manipule exclusivement le modèle. Le contrôleur modifie le modèle et informe ensuite la vue que le modèle a changé et doit être actualisé (voir Figure 2). Le modèle dans ce scénario est complètement indépendant de la vue et du contrôleur, ce qui signifie qu'il n'y a aucun moyen pour le modèle de signaler les changements dans son état. Le protocole HTTP en est un exemple. Il n'y a pas de moyen simple dans le navigateur pour obtenir des mises à jour asynchrones du serveur. Le navigateur affiche la vue et répond aux entrées de l'utilisateur, mais il ne détecte pas les modifications des données sur le serveur. Ce n'est que lorsque l'utilisateur demande explicitement une actualisation que le serveur est interrogé pour des modifications.

MVC - Modèle passif

Je ne suis pas en mesure de dire laquelle des opinions est "correcte", et pour être honnête, je suis un peu plus confus après avoir lu les réponses ici et l'article lié.

Texte complet de l'article ici .

alnafie
la source
Oui, et l'autre chose qui ajoute de la confusion est client-serveur, ce que le SmallTalk MVC original ne tenait pas vraiment compte. Dans client-serveur (par exemple avec javascript), il existe des modèles, des vues et des contrôleurs orientés présentation sur le client, et des vues et contrôleurs orientés domaine sur le serveur, bien que le serveur effectue également un traitement orienté présentation ajoutant de la confusion. De plus, nous voulons parfois qu'une vue de domaine ait une certaine persistance, ce qui signifie que les paramètres de vue forment leur propre modèle, qui ne fait pas nécessairement partie du modèle de domaine.
Erik Eidt
Merci pour le lien, je savais que je n'étais pas fou de penser ça! C'est essentiellement ce que je faisais avant de pousser l'idée un peu trop loin, tant que le modèle ne dépend de rien, qu'importe comment / où il est accédé? Je n'ai pas encore décidé quelle approche je vais adopter pour mon prochain développement, mais cela aide certainement.
Adam Westbrook
1

Une autre chose à considérer est que vous semblez avoir chargé automatiquement le user_modelet invoice_modelpermettre à la vue d'y accéder. Pour que cela fonctionne de manière fiable, vous devez probablement charger automatiquement tous vos modèles (car cela $this->load->model()ne semble pas correct dans une vue, n'est-ce pas ...)

Faire cela gonfle inutilement votre pile en chargeant un tas de choses qui pourraient ne jamais être utilisées. Une partie de la raison d'avoir plusieurs modèles est de vous permettre d'encapsuler la logique associée et de charger uniquement ce dont vous avez besoin pour une tâche donnée.

Cela ressemble à CodeIgniter. J'ai fait beaucoup de développement de CI et je peux partager par expérience personnelle que vous ne voulez vraiment pas charger automatiquement plus que vous ne l'avez vraiment. Essayez d'ajouter $this->output->enable_profiler(TRUE);le constructeur d'un contrôleur et de jouer avec les chargements automatiques (y compris les aides comme database): vous verrez probablement un changement significatif dans les temps de chargement et d'exécution, mais surtout dans l'allocation de mémoire.

msanford
la source
1
Bon point, vous avez raison, cela est basé sur CI, bien que j'aie supprimé une partie de la syntaxe spécifique pour plus de clarté. J'ai pris l'habitude de `` charger automatiquement '' à peu près tout pour une grande partie du temps et des raisons SECHES, semblait un peu fou d'avoir beaucoup de la même chose load->modeldans la plupart des contrôleurs et des méthodes. Ne pas utiliser une fonction de chargement automatique appropriée est l'une des choses que je n'aime pas le plus dans la rétrocompatibilité de CI, mais c'est une toute autre discussion ...
Adam Westbrook
0

La réponse courte est que la forme de votre exemple de code est trompeusement intuitive. Il semblerait que ce soit une solution «facile à l'esprit».


Problème n ° 1

Vos objets Modelet Viewseront étroitement liés.

Si vous devez ajouter ou supprimer des méthodes dans le Model, vous devrez peut-être modifier le en Viewconséquence.

Fondamentalement, MVC est dérivé des modèles de commandement et d' observation . Vous voulez un «modèle» indépendant qui est manipulé via une interface / API à laquelle le Controllerpeut se connecter (c'est-à-dire la délégation).

Fréquemment, cela signifie injecter Model et Viewinstances dans un Controlleret les stocker en tant que propriétés dudit Controller. Ensuite, en utilisant une méthode de Controller(c'est-à-dire une commande) comme zone de travail, transmettez les données à un View de Model (une fois que le `Modèle a terminé la mise à jour de l'état de l'application ).

La transmission de données (tableaux, objets itérables, peu importe) maintient le couplage entre les instances Modelet lâche . Si vous injectez l' instance dans le , consultez le problème n ° 1 ci-dessus.ViewModelView

Rappelez-vous, Viewspourrait être HTML, JSON, texte, XML, en-têtes HTTP, YAML ou presque n'importe quoi, suivant une méthodologie de transfert d'état de représentation (REST) .

Ainsi, la clé pour comprendre comment gérer la relation entre le Modelet Viewsest de voir la relation pour ce qu'elle est, un à plusieurs (potentiellement)! C'est exactement ce que le modèle Observer a été conçu pour accomplir.

Alors que la plupart des configurations n'ont qu'une seule vue à traiter à la fois, rien n'empêche le modèle architectural MVC de mettre à jour plusieurs vues à la fois! Travailler avec des applications Web traditionnelles CRUD fait réfléchir les gens dans un one-to-one chemin, mais qui est le plus petit exemple de la façon dont le modèle d'observateur pourrait travailler ( un à plusieurs étant l'autre ).

Ainsi, si vous en aviez un Modelet plusieurs Views, le mal de tête potentiel de la mise à jour de tout le Views'code d'implémentation parce que vous avez changé quelque chose dans l' Model'sAPI / méthodes devient maintenant aigu .

Passez les données à Views , pas les instances de Models .

Anthony Rutledge
la source