Escenario MVC + vs controladores gordos

Escenario MVC + vs. controladores gruesos


Los marcos PHP modernos (Symphony, Laravel, en adelante en todas partes) muestran de manera convincente que implementar el patrón Modelo-Vista-Controlador no es tan simple. Todas las implementaciones por alguna razón son propensas a Fat Controllers ( fat controllers ), condenados por todos, y los desarrolladores, y los propios frameworks.


¿Por qué es eso así? ¿Y hay alguna forma de manejar esto? Vamos a hacerlo bien.


Terminología


  • Modelo - modelo (modelador de datos solicitados)
  • Ver - ver (decorador de datos modelo)
  • Controlador - controlador (coordinador de vista de modelo según lo solicitado)
  • Plantilla - plantilla de presentación
  • Renderizado: renderizado (formación, diseño de la imagen de presentación)
  • Renderer - renderizador (modelador, diseñador de la imagen de presentación)

Controlador grueso


Aquí hay un controlador de grasa 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(); } } 

Que vemos ¡Vemos vinagreta! En el controlador, todo lo posible es mixto, ¡tanto el modelo como la presentación y, de hecho, el controlador mismo!


Vemos los nombres del modelo y la plantilla firmemente conectados al controlador. Esto no es un zumbido. Vemos manipulaciones con los datos del modelo en el controlador: la formación de un nombre completo a partir del nombre y apellido. Y esto no es un zumbido.


Y una cosa más: no vemos este ejemplo explícitamente, pero es implícito. A saber: ¡solo hay una forma de renderizar (formación de imagen)! Solo uno: ¡según la plantilla en el archivo php! ¿Y si quiero pdf? ¿Y si no quiero en un archivo, sino en una línea php? Tenía diseños con diseños elaborados en cientos de pequeñas plantillas. Tuve que soltar el renderizador de plantillas de cadena yo mismo. No me sobrecalentamiento, por supuesto, pero el asunto es en principio.


Breve resumen:


Los marcos modernos tienen fallas comunes en la implementación de MVC para todos:
  1. Interpretación estrecha de MVC-view (Ver) solo como "Ver con una plantilla en un archivo PHP" en lugar de "Ver con cualquier renderizador" .
  2. Interpretación estrecha del modelo MVC solo como "Dominio del modelo de base de datos" en lugar de "Cualquier compilador de datos para presentación" .
  3. Provocan el uso de los llamados "controladores gruesos" que contienen toda la lógica al mismo tiempo: negocios, presentación e interacción. Esto destruye por completo el objetivo principal de MVC: la división de responsabilidades entre los componentes de la tríada.

Para abordar estas deficiencias, sería bueno echar un vistazo más de cerca a los componentes de MVC.


La vista es un renderizador


Echa un vistazo al primer inconveniente:


  1. Interpretación estrecha de MVC-view (Ver) solo como "Ver con una plantilla en un archivo PHP" en lugar de "Ver con cualquier renderizador" .

Aquí, todo es bastante simple: la solución al problema ya está indicada en la declaración del problema. Solo tenemos que decir que cualquier renderizador puede usar la vista. Para implementar esto, simplemente agregue la nueva propiedad de renderer a la clase View:


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

Por lo tanto, hemos definido una nueva propiedad de renderer para la vista. En el caso más general, el valor de esta propiedad puede ser cualquier función callable que forme una imagen de los datos transferidos mediante la plantilla transmitida.


La mayoría de las aplicaciones usan solo un renderizador, e incluso si usan varias, se prefiere una de ellas. Por lo tanto, el argumento del renderer se define como opcional, suponiendo que haya algún renderizador predeterminado.


Es simple? Simple En realidad no es tan simple. El hecho es que la View que está en MVC no es exactamente la View que está en los marcos. La View que está en el marco no puede vivir sin una plantilla. Pero la View , que en MVC, por alguna razón, no sabe nada sobre estas mismas plantillas. Por qué Sí, porque para MVC View , este es cualquier convertidor de datos del modelo en una imagen , y no solo un motor de plantillas. Cuando escribimos algo como esto en el controlador de solicitudes:


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

o incluso:


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

entonces realmente definimos la View que está en MVC, ¡sin tocar ninguna View que esté en los marcos!


Pero ahora todo es realmente simple: esas View , que en los marcos, este es un subconjunto de esas View , que están en MVC. Además, un subconjunto muy estrecho, es decir, es solo un motor de plantillas basado en archivos PHP.


Resumen: es el , es decir cualquier decorador de una imagen de datos es la View que está en MVC. Y esas View , que están en los marcos, son solo una especie de .


Modelo de dominio / Modelo de vista (ViewModel / DomainModel)


Ahora mira el segundo inconveniente:


  1. Interpretación estrecha del modelo MVC solo como "Dominio del modelo de base de datos" en lugar de "Cualquier compilador de datos para presentación" .

Es obvio para todos que el modelo MVC es una cosa compleja que consta de otras piezas. La comunidad acuerda descomponer el modelo en dos componentes: un modelo de dominio (DomainModel) y un modelo de presentación (ViewModel).


Un modelo de dominio es lo que se almacena en las bases de datos, es decir. Datos del modelo normalizado. Escriba, 'nombre' y 'apellido' en diferentes campos. Los marcos están ocupados con esta parte particular del modelo simplemente porque el almacenamiento de datos es su propio universo, bien estudiado.


Sin embargo, una aplicación necesita datos agregados en lugar de datos normalizados. Los datos del dominio deben compilarse en imágenes como: "¡Hola, Ivan!", "¡Querido Ivan Petrov!", O incluso "¡Para Ivan a Petrov a !". Estos datos convertidos se refieren a otro modelo: modelo de presentación. Por lo tanto, es esta parte del modelo la que aún ignoran los marcos modernos. Se ignora porque no hay acuerdo sobre cómo tratarlo. Y si los marcos no proporcionan una solución, los programadores siguen el camino más simple: lanzan el modelo de vista al controlador. ¡Y obtienen los odiados pero inevitables controladores de grasa!


Total: para implementar MVC, debe implementar un modelo de vista. No hay otras opciones. Dado que las representaciones y sus datos pueden ser cualquiera, declaramos que tenemos un problema.


Escenario vs. Controladores de grasa


Hay un último inconveniente de los marcos:


  1. Provocan el uso de los llamados "controladores gruesos" que contienen toda la lógica al mismo tiempo: negocios, presentación e interacción. Esto destruye por completo el objetivo principal de MVC: la división de responsabilidades entre los componentes de la tríada.

Aquí llegamos a los conceptos básicos de MVC. Seamos claros. Entonces, MVC asume la siguiente distribución de responsabilidades entre los componentes de la tríada:


  • El controlador es la lógica de interacción , es decir. interacciones tanto con el mundo exterior (solicitud - respuesta) como con el interno (Modelo - Presentación),
  • El modelo es la lógica de negocios , es decir. generar datos para una solicitud específica,
  • La representación es la lógica de la representación , es decir. Decoración de los datos generados por el Modelo.

Adelante Dos niveles de responsabilidades son claramente visibles:


  • El nivel organizacional es el controlador,
  • El nivel ejecutivo es Modelo y Representación.

En términos simples, el controlador dirige, el arado Modelo y Vista. Esto es si de una manera simple. ¿Y si no de una manera simple, sino más específicamente? ¿Cómo se dirige exactamente el controlador? ¿Y cómo aran exactamente el Modelo y la Vista?


El controlador dirige así:


  • Recibe una solicitud de una aplicación,
  • Decide qué modelo y qué vista usar para esta solicitud,
  • Llama al modelo seleccionado y recibe datos de él,
  • Invoca la Vista seleccionada con los datos recibidos del Modelo,
  • Devuelve los datos decorados por la Vista a la aplicación.

Algo asi. Lo esencial en este esquema es que el Modelo y la Representación resultan ser enlaces en la cadena de ejecución de consultas. Además, mediante enlaces sucesivos: primero, el Modelo convierte la solicitud en algunos datos, luego la Vista convierte estos datos del Modelo en una respuesta decorada según sea necesario para una solicitud específica. Al igual que una solicitud humanoide está decorada visualmente con templatizadores, una solicitud de Android está decorada con codificadores JSON.


Ahora tratemos de descubrir cómo exactamente aran los artistas: modelo y presentación. Dijimos anteriormente que hay consenso sobre la descomposición del Modelo en dos subcomponentes: Modelo de Dominio y Modelo de Presentación. Esto significa que puede haber más artistas, no dos, sino tres. En lugar de una cadena de ejecución


>>

bien puede haber una cadena


>> >>

La pregunta se plantea: ¿por qué solo dos o tres? ¿Y si necesitas más? La respuesta natural es, por el amor de Dios, ¡toma todo lo que necesites!


Otros artistas útiles son inmediatamente visibles: validadores, redirectores, varios renderizadores y, en general, todo lo que es impredecible, pero agradable.


Recapitulemos:


  • El nivel ejecutivo MVC ( - ) se puede implementar como una cadena de enlaces, donde cada enlace convierte la salida del enlace anterior en la entrada para el siguiente.
  • La entrada del primer enlace es la solicitud de la aplicación.
  • La salida del último enlace es la respuesta de la aplicación a la solicitud.

Llamé a esta cadena Scenario , pero para los eslabones de la cadena aún no he decidido el nombre. Las opciones actuales son una escena (como parte de un script), un filtro (como convertidor de datos), una acción de script. En términos generales, el nombre del enlace no es tan importante, hay algo más significativo.


Las consecuencias de la aparición del escenario son significativas. A saber: el escenario asumió la responsabilidad principal del controlador: determinar el modelo y la presentación necesarios para la solicitud y lanzarlos. Por lo tanto, el controlador solo tiene dos responsabilidades: interactuar con el mundo exterior (solicitud-respuesta) y ejecutar el script. Y esto es bueno en el sentido de que todos los componentes de la tríada MVC se descomponen secuencialmente y se vuelven más específicos y manejables. Y sigue siendo bueno en otro aspecto: el controlador MVCS se convierte en una clase inmutable puramente interna y, por lo tanto, incluso en principio, no puede engordar.


El uso de escenarios conduce a otra variación del patrón MVC; llamé a esta variación MVCS - Model-View-Controller-Scenario .


Y un par de líneas más sobre la descomposición de MVC. Los marcos modernos, donde todas las funciones típicas se descomponen al límite, naturalmente, le quitaron a la parte conceptual MVC de las responsabilidades para interactuar con el mundo exterior. Por lo tanto, las clases especialmente entrenadas como HTTP y el participan en el procesamiento de la solicitud del usuario. Como resultado, el Controlador no recibe la solicitud inicial del usuario, sino una refinada, y esto permite aislar al controlador de los detalles de la solicitud. Del mismo modo, se realiza un aislamiento de los detalles de la respuesta HTTP, lo que permite que el módulo MVC defina su propio tipo de respuesta. Además, los marcos implementaron completamente los dos componentes de MVC: el modelo de dominio y la plantilla de presentación, sin embargo, ya lo discutimos. Estoy todo esto en el hecho de que el refinamiento y la concreción de MVC es continuo y continuo, y esto es emocionante.


Ejemplo de MVCS


Ahora veamos cómo se puede implementar el ejemplo de Fat Cortroller al comienzo de este artículo en MVCS.


Comenzamos creando un controlador MVCS:


 $mvcs = new MvcsController(); 

El controlador MVCS recibe una solicitud de un enrutador externo. Deje que el enrutador convierta el URI del formulario 'usuario / hola / XXX' en tales parámetros de acción y solicitud:


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

Teniendo en cuenta que el controlador MVCS acepta scripts en lugar de URI, debemos asignar algunos scripts a la acción de la solicitud. Esto se hace mejor en el contenedor MVCS:


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

Echemos un vistazo más de cerca a este escenario. Esta es una cadena de tres convertidores de datos separados por un '>':


  • 'UserModel' es el nombre del modelo de dominio 'Usuario', la entrada del modelo serán los parámetros de solicitud, la salida serán los datos reales del modelo,
  • 'UserViewModel' es el nombre del modelo de vista que convierte los datos del dominio en datos de vista,
  • 'view, hello' es la 'plantilla' de la vista del sistema para una plantilla PHP llamada 'hello'.

Ahora solo necesitamos agregar dos transformadores involucrados en el script como una función de cierre al contenedor 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']]; }); 

¡Y eso es todo! Para cada solicitud, es necesario determinar el guión correspondiente y todas sus escenas (excepto las del sistema, como 'vista'). Y nada mas.


Y ahora estamos listos para probar MVCS para diferentes solicitudes:


 //        $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); 

La implementación de PHP MVCS está alojada en github.com .
Este ejemplo está en el directorio MVCS de example .

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


All Articles