Estoy usando el patrón MVC en mi aplicación web creada con PHP.
Siempre me cuesta determinar si necesito un nuevo controlador dedicado para un conjunto de acciones o si debo ubicarlos dentro de un controlador ya existente.
¿Hay alguna buena regla a seguir al crear controladores?
Por ejemplo, puedo tener:
AuthenticationController
con acciones:
index()
para mostrar el formulario de inicio de sesión.submit()
para manejar el envío de formularios.logout()
, Autoexplicativo.
O
LoginController
con acciones:
index()
para mostrar el formulario de inicio de sesión.submit()
para manejar el envío de formularios.
LogoutController
con acción:
index()
para manejar el cierre de sesión.
O
AccountController
con acciones:
loginGet()
para mostrar el formulario de inicio de sesión.loginPost()
para manejar el envío del formulario de inicio de sesión.logoutGet()
para manejar el cierre de sesión.registerGet()
para mostrar el formulario de registro.registerPost()
para manejar el envío de formularios.Y cualquier otra acción relacionada con una cuenta.
Respuestas:
Para encontrar la agrupación correcta para los controladores, piense en las pruebas .
(Incluso si en realidad no realiza ninguna prueba, pensar en cómo realizaría la prueba de sus controladores le dará una muy buena idea de cómo estructurarlos).
Un
AuthenticationController
no es comprobable por sí mismo, porque solo contiene funcionalidades para iniciar y cerrar sesión, pero su código de prueba necesitará crear cuentas falsas para propósitos de prueba antes de que pueda probar un inicio de sesión exitoso. Puede omitir el subsistema bajo prueba e ir directamente a su modelo para la creación de las cuentas de prueba, pero luego tendrá una prueba frágil en sus manos: si el modelo cambia, tendrá que modificar no solo el código de las pruebas el modelo, pero también el código que prueba el controlador, a pesar de que la interfaz y el comportamiento del controlador no han cambiado. Eso no es razonable.A
LoginController
no es adecuado por las mismas razones: no puede probarlo sin crear cuentas primero, y hay aún más cosas que no puede probar, como por ejemplo evitar inicios de sesión duplicados pero luego permitir que un usuario inicie sesión después de haber cerrado sesión. (Dado que este controlador no tiene funcionalidad de cierre de sesión).Y
AccountController
le dará todo lo que necesita para hacer su prueba: puede crear una cuenta de prueba y luego intentar iniciar sesión, puede eliminar la cuenta y luego asegurarse de que ya no puede iniciar sesión, puede cambiar la contraseña y asegurarse de que la contraseña correcta debe usarse para iniciar sesión, etc.Para concluir: para escribir incluso el conjunto de pruebas más pequeño, necesitará poner a su disposición toda la funcionalidad del
AccountController
mismo. Subdividirlo en controladores más pequeños parece estar produciendo controladores para discapacitados con una funcionalidad insuficiente para una prueba adecuada. Esta es una muy buena indicación de que la funcionalidad deAccountController
es la subdivisión más pequeña que tiene sentido.Y en términos generales, el enfoque de "pensar en la prueba" funcionará no solo en este escenario en particular, sino en cualquier escenario similar que se encuentre en el futuro.
fuente
La respuesta no es tan obvia
Permítanme aclarar algunas cosas antes de hacer cualquier declaración de respuesta. Ante todo:
¿Qué es el controlador?
El controlador es una parte del sistema que controla la solicitud, después del despacho. Por lo tanto, podemos definirlo como un conjunto de acciones relacionadas con ... ¿qué?
¿Cuál es el alcance del controlador?
Y eso es más o menos parte cuando tendremos alguna respuesta. ¿Qué piensas? ¿Es un controlador de cosas (por ejemplo, una cuenta) o un controlador de acciones? Por supuesto, es un controlador de algún modelo o algo más abstracto que proporciona acciones sobre él.
La respuesta es...
No, la autenticación es un proceso. No vayas por ese camino.
Igual que aquí. Inicio de sesión - acción. Mejor no cree un controlador de acción (no tiene un modelo correlacionado).
Bastante bien, pero no estoy convencido de que valga la pena construir ese controlador de bajo nivel (el controlador es la abstracción en sí mismo). De todos modos, la creación de métodos con * Get o * Post no está clara.
¿Cualquier sugerencia?
Sí, considérelo:
AccountController:
Y modelo relacionado con él, ofc Clase de cuenta. Le dará la oportunidad de mover su par modelo-controlador a otro lugar (si es necesario) y crear un código claro (es obvio lo
login()
que significa el método). Apestar al modelo es realmente famoso, especialmente con las aplicaciones CRUD y tal vez sea una forma para ti.fuente
Los controladores generalmente se crean para un determinado recurso (una clase de entidad, una tabla en la base de datos), pero también se pueden crear para agrupar acciones que son responsables de una determinada parte de la aplicación. En sus ejemplos, ese sería un controlador que maneja la seguridad de la aplicación:
Nota : no coloque las acciones relacionadas con la seguridad y las acciones del perfil de usuario en el mismo controlador; puede tener sentido porque están relacionados con el usuario, pero uno debe manejar la autenticación y el otro debe manejar las actualizaciones de correo electrónico, nombre, etc.
Con los controladores creados para los recursos (digamos
Task
), tendría las acciones CRUD habituales :Por supuesto, tiene la posibilidad de agregar recursos relacionados al mismo controlador. Digamos, por ejemplo, que tiene la entidad
Business
, y cada una tiene variasBusinessService
entidades. Un controlador para esto podría verse así:Este enfoque tiene sentido cuando las entidades secundarias relacionadas no pueden existir sin la entidad principal.
Estas son mis recomendaciones:
Subscription
entidad que tiene una disponibilidad basada en un número limitado de entradas, puede agregar una nueva acción al controlador nombradouse()
que tiene el único propósito de restar una entrada de laSubscription
)Algunos recursos para leer más aquí .
fuente
Veo dos "fuerzas" de diseño antagónicas (que no son exclusivas de los controladores):
Desde el punto de vista de la cohesión, las tres acciones (inicio de sesión, cierre de sesión, registro) están relacionadas, pero el inicio y cierre de sesión es mucho más que el registro. Están relacionados semánticamente (uno es una inversión del otro) y posiblemente también usarán los mismos objetos de servicio (sus implementaciones también son coherentes).
Mi primer instinto sería agrupar el inicio y cierre de sesión en un controlador. Pero si las implementaciones del controlador de inicio y cierre de sesión no son tan simples (por ejemplo, el inicio de sesión tiene captcha, más métodos de autenticación, etc.), no tendría ningún problema en dividirlos en LoginController y LogoutController para mantener la simplicidad. Donde se encuentra este umbral de complejidad (cuando debería comenzar a dividir el controlador) es un poco personal.
Recuerde también que, independientemente de lo que diseñe su código inicialmente, puede (y debería) refactorizarlo a medida que cambia. En este caso, es bastante típico comenzar con un diseño simple (tener un AuthenticationController) y con el tiempo recibirá más requisitos que complicarán el código. Una vez que cruza el umbral de complejidad, debe refactorizarlo a dos controladores.
Por cierto, su código sugiere que está cerrando la sesión del usuario con la solicitud GET. Esa es una mala idea ya que HTTP GET debería ser nullipotente (no debería modificar el estado de la aplicación).
fuente
Aquí hay algunas reglas generales:
Organice por tema o tema, siendo el nombre del controlador el nombre del tema.
Recuerde que el nombre del controlador aparecerá en la URL, visible para sus usuarios, por lo que preferiblemente debería tener sentido para ellos.
En la situación que menciona (autenticación), el equipo de MVC ya ha escrito el controlador para usted. Abra Visual Studio 2013 y luego haga clic en
AccountController.cs contiene todos los métodos para administrar cuentas de usuario:
Entonces se han organizado por tema "Cuentas de usuario y autenticación", con el nombre visible del tema "Cuenta".
fuente
Terminología
Creo que es una gran idea errónea llamar a una clase que contiene algunos métodos relacionados con HTTP un "controlador".
El controlador es un método que maneja la solicitud, pero no una clase que contenga dichos métodos . Así,
index()
,submit()
,logout()
son controladores.La clase que contiene ese tipo de métodos se denomina "controlador" simplemente porque constituye un grupo de controladores y desempeña un papel de espacio de nombres de "nivel inferior". En lenguaje FP (como Haskell) sería solo un módulo. Es una buena práctica mantener esas clases de "controlador" lo más sin estado posible en los lenguajes OOP, excepto las referencias a servicios y otras cosas de todo el programa.
La respuesta
Con la terminología ordenada, la pregunta es "¿cómo debemos separar los controladores en espacios de nombres / módulos?" Creo que la respuesta es: los controladores dentro de un solo espacio de nombres / módulo deben tratar con el mismo tipo de datos . Por ejemplo,
UserController
trata principalmente con instancias deUser
clase, pero ocasionalmente toca otras cosas relacionadas, si es necesario.Dado que
login
,logout
y otras acciones similares se ocupan principalmente de la sesión, probablemente sea mejor ponerlas dentroSessionController
, y elindex
controlador, que solo imprime un formulario, debe colocarseLoginPageController
, ya que obviamente se trata de la página de inicio de sesión. Tiene un poco de sentido incluir el procesamiento de HTML y la gestión de sesiones en una sola clase, y eso violaría SRP y probablemente un montón de otras buenas prácticas.Principio general
Cuando tenga problemas para decidir dónde colocar un fragmento de código, comience con los datos (y tipos) con los que trata.
fuente