ventajas inyeccion dependencias php laravel dependency-injection laravel-facade

php - inyeccion de dependencias ventajas



Uso de inyección de dependencia sobre fachadas laravel (6)

He leído varias fuentes que sugieren que la fachada de Laravel en última instancia existe para su conveniencia y que estas clases deberían ser injected para permitir un acoplamiento flexible. Incluso Taylor Otwell tiene una publicación que explica cómo hacer esto. Parece que no soy el único en preguntarse esto .

use Redirect; class Example class { public function example() { return Redirect::route("route.name"); } }

se convertiría

use Illuminate/Routing/Redirector as Redirect; class Example class { protected $redirect; public function __constructor(Redirect $redirect) { $this->redirect = $redirect } public function example() { return $this->redirect->route("route.name"); } }

Esto está bien, excepto que estoy empezando a encontrar que algunos constructores y métodos están comenzando a tomar cuatro + parámetros.

Dado que el IoC de Laravel parece inyectar solo en constructores de clase y ciertos métodos (controladores), incluso cuando tengo funciones y clases bastante escasas, descubro que los constructores de las clases se van completando con las clases necesarias que luego se inyectan en el métodos necesarios

Ahora estoy descubriendo que si continúo con este enfoque, necesitaré mi propio contenedor de IoC, que parece reinventar la rueda si utilizo un marco como laravel.

Por ejemplo, utilizo los servicios para controlar la lógica de negocio / vista en lugar de los controladores que se ocupan de ellos, simplemente enrutan las vistas. Entonces un controlador tomará primero su service correspondiente, luego el parameter en su url. Una función de servicio también necesita verificar los valores de un formulario, entonces necesito Request and Validator . Solo así, tengo cuatro parámetros.

// MyServiceInterface is binded using the laravel container use Interfaces/MyServiceInterface; use Illuminate/Http/Request; use Illuminate/Validation/Factory as Validator; ... public function exampleController(MyServiceInterface $my_service, Request $request, Validator $validator, $user_id) { // Call some method in the service to do complex validation $validation = $my_service->doValidation($request, $validator); // Also return the view information $viewinfo = $my_service->getViewInfo($user_id); if ($validation === ''ok'') { return view("some_view", [''view_info''=>$viewinfo]); } else { return view("another_view", [''view_info''=>$viewinfo]); } }

Este es un solo ejemplo. En realidad, muchos de mis constructores ya tienen múltiples clases inyectadas (Modelos, Servicios, Parámetros, Fachadas). Empecé a ''descargar'' la inyección de constructor (cuando corresponda) a la inyección de método, y hago que las clases que llaman a esos métodos usen sus constructores para inyectar dependencias.

Me han dicho que más de cuatro parámetros para un método o constructor de clase como regla general es mal olor de código / práctica. Sin embargo, no veo cómo puedes evitar esto realmente si eliges la ruta de inyección de las fachadas laravel.

¿Tengo esta idea mal? ¿Mis clases / funciones no son lo suficientemente escasas? ¿Me está faltando el punto del contenedor de laravels o realmente necesito pensar en crear mi propio contenedor de IoC? Some otras answers parecen insinuar que el contenedor de laravel puede eliminar mi problema.

Dicho esto, no parece haber un consenso definitivo sobre el tema ...


Bien tus pensamientos e inquietudes y corregirlos y los tuve también. Hay algunos beneficios de Fachadas (generalmente no los uso), pero si los usa solo sugeriría usarlos solo en los controladores, ya que los controladores son solo puntos de entrada y salida para mí al menos.

Por el ejemplo que me brindó, mostraré cómo generalmente lo manejo:

// MyServiceInterface is binded using the laravel container use Interfaces/MyServiceInterface; use Illuminate/Http/Request; use Illuminate/Validation/Factory as Validator; ... class ExampleController { protected $request; public function __constructor(Request $request) { // Do this if all/most your methods need the Request $this->request = $request; } public function exampleController(MyServiceInterface $my_service, Validator $validator, $user_id) { // I do my validation inside the service I use, // the controller for me is just a funnel for sending the data // and returning response //now I call the service, that handle the "business" //he makes validation and fails if data is not valid //or continues to return the result try { $viewinfo = $my_service->getViewInfo($user_id); return view("some_view", [''view_info''=>$viewinfo]); } catch (ValidationException $ex) { return view("another_view", [''view_info''=>$viewinfo]); } } } class MyService implements MyServiceInterface { protected $validator; public function __constructor(Validator $validator) { $this->validator = $validator; } public function getViewInfo($user_id, $data) { $this->validator->validate($data, $rules); if ($this->validator->fails()) { //this is not the exact syntax, but the idea is to throw an exception //with the errors inside throw new ValidationException($this->validator); } echo "doing stuff here with $data"; return "magic"; } }

Simplemente recuerde dividir su código en pequeñas piezas individuales que cada uno maneja su propia responsabilidad. Cuando se rompe correctamente el código, en la mayoría de los casos no tendrá tantos parámetros de constructor, y el código será fácilmente comprobable y burlado.

Solo una última nota, si está construyendo una aplicación pequeña o incluso una página en una gran aplicación, por ejemplo, una "página de contacto" y una "página de contacto enviada", seguramente puede hacer todo en el controlador con fachadas, simplemente depende de la complejidad del proyecto.


Este es uno de los beneficios de la inyección de constructor: se vuelve obvio cuando su clase está haciendo mucho, porque los parámetros del constructor crecen demasiado.

Lo primero que hay que hacer es dividir los controladores que tienen demasiadas responsabilidades.

Digamos que tienes un controlador de página:

Class PageController { public function __construct( Request $request, ClientRepositoryInterface $clientrepo, StaffRepositortInterface $staffRepo ) { $this->clientRepository = $clientRepo; //etc etc } public function aboutAction() { $teamMembers = $this->staffRepository->getAll(); //render view } public function allClientsAction() { $clients = $this->clientRepository->getAll(); //render view } public function addClientAction(Request $request, Validator $validator) { $this->clientRepository->createFromArray($request->all() $validator); //do stuff } }

Este es un candidato ideal para dividir en dos controladores, ClientController y AboutController .

Una vez que haya hecho eso, si todavía tiene demasiadas * dependencias, es hora de buscar lo que llamaré dependencias indirectas (¡porque no puedo pensar en el nombre correcto para ellas!) - dependencias que no son utilizadas directamente por la clase dependiente , sino que pasó a otra dependencia.

Un ejemplo de esto es addClientAction : requiere una solicitud y un validador, solo para pasarlos al clientRepostory del clientRepostory .

Podemos volver a factor creando una nueva clase específicamente para crear clientes a partir de solicitudes, reduciendo así nuestras dependencias y simplificando tanto el controlador como el repositorio:

//think of a better name! Class ClientCreator { public function __construct(Request $request, validator $validator){} public function getClient(){} public function isValid(){} public function getErrors(){} }

Nuestro método ahora se convierte en:

public function addClientAction(ClientCreator $creator) { if($creator->isValid()){ $this->clientRepository->add($creator->getClient()); }else{ //handle errors } }

No existe una regla rígida en cuanto a la cantidad de dependencias que son demasiadas. La buena noticia es que si ha creado su aplicación utilizando acoplamiento libre, el refaccionamiento es relativamente simple.

Preferiría ver un constructor con 6 o 7 dependencias distintas a una sin parámetros y un montón de llamadas estáticas ocultas en todos los métodos.


Los métodos de clase que forman parte del mecanismo de enrutamiento en Laravel (middleware, controladores, etc.) también tienen sus sugerencias de tipo usadas para inyectar dependencias , no todas necesitan ser inyectadas en el constructor. Esto puede ayudar a mantener su constructor delgado, aunque no estoy familiarizado con ninguna regla de cuatro límites de parámetros; PSR-2 permite que la definición del método se estire sobre múltiples líneas, presumiblemente porque no es raro requerir más de cuatro parámetros.

En su ejemplo, puede inyectar los servicios de Request y Validator en el constructor como un compromiso, ya que a menudo son utilizados por más de un método.

En cuanto a establecer un consenso, Laravel debería ser más obstinado para que las aplicaciones sean lo suficientemente similares como para utilizar un enfoque único para todos. Sin embargo, una llamada más fácil es que creo que las fachadas seguirán el camino del dodo en una versión futura.


Me encanta el laravel debido a su hermosa arquitectura. Ahora, desde mi enfoque, no inyectaría todas las fachadas en el método del controlador, ¿por qué? Inyectar las fachadas Redirect solo en las prácticas incorrectas del controlador que pueda necesitar en otro. Y principalmente las cosas que se usan en su mayoría deben declararse para todos, mientras que para aquellos que usan algunas o solo sus mejores prácticas para inyectarlas a través del método, como cuando declaras en la parte superior, obstaculizará la optimización de tu memoria y la velocidad de tu código. Espero que esto ayude


No tanto una respuesta sino algo de reflexión después de hablar con mis colegas que han hecho algunos puntos muy válidos;

  1. Si la estructura interna de laravel se cambia entre versiones (aparentemente en el pasado), inyectar las rutas de la clase de fachada resuelta rompería todo en una actualización, mientras que usar las fachadas predeterminadas y los métodos de ayuda en su mayoría (si no completamente) evita este problema .

  2. Aunque el código de desacoplamiento generalmente es bueno, la sobrecarga de inyectar estas rutas de clase de fachada resueltas hace que las clases estén desordenadas: para los desarrolladores que se encargan del proyecto, se gasta más tiempo tratando de seguir el código que podría emplearse mejor para corregir errores o pruebas. Los nuevos desarrolladores deben recordar qué clases inyectadas son desarrolladores y cuáles son laravels. Los desarrolladores que no estén familiarizados con laravel under the hood deben pasar tiempo buscando la API. En última instancia, aumenta la probabilidad de introducir errores o faltar la funcionalidad clave.

  3. El desarrollo se ralentiza y la capacidad de prueba no se mejora realmente, ya que las fachadas ya se pueden probar. El desarrollo rápido es un punto fuerte para usar laravel en primer lugar. El tiempo siempre es una restricción.

  4. La mayoría de los otros proyectos usan fachadas laravel. La mayoría de las personas con experiencia en el uso de laravel usan fachadas. Crear un proyecto que no sigue las tendencias existentes de proyectos anteriores ralentiza las cosas en general. Los futuros desarrolladores inexpertos (o perezosos) pueden ignorar la inyección de fachada y el proyecto puede terminar con un formato mixto. (Incluso los revisores de código son humanos)


Un problema con las fachadas es que se debe escribir un código adicional para respaldarlas cuando se realizan pruebas unitarias automáticas.

En cuanto a soluciones:

1. Resolviendo dependencias manualmente

Una forma de resolver dependencias, si no desea hacerlo a través de. constructores o métodos de inyección, es llamar directamente a app ():

/* @var $email_services App/Contracts/EmailServicesContract $email_services = app(''App/Contracts/EmailServicesContract'');

2. Refactorización

A veces, cuando me encuentro pasando demasiados servicios o dependencias a una clase, tal vez violé el Principio de Responsabilidad Individual. En esos casos, tal vez se necesite un rediseño, rompiendo el servicio o la dependencia en clases más pequeñas. Usaría otro servicio para concluir un grupo relacionado de clases para servir algo como fachada. En esencia, será una jerarquía de servicios / clases de lógica.

Ejemplo: Tengo un servicio que genera productos recomendados y lo envía a los usuarios por correo electrónico. Llamo al servicio WeeklyRecommendationServices , y toma otros 2 servicios como dependencia: un servicio de Recommendation que es un recuadro negro para generar las recomendaciones (y tiene sus propias dependencias, tal vez un repositorio para productos, un ayudante o dos), y un EmailService que tal vez tenga Mailchimp como dependencia). Algunas dependencias de nivel inferior, como redirecciones, validadores, etc. estarán en esos servicios secundarios en lugar del servicio que actúa como punto de entrada.

3. Utiliza las funciones globales de Laravel

Algunas de las fachadas están disponibles como llamadas a funciones en Laravel 5. Por ejemplo, puede usar redirect()->back() lugar de Redirect::back() , así como también view(''some_blade) lugar de View::make(''some_blade'') . Creo que es lo mismo para el dispatch y algunas otras fachadas de uso común.

(Editado para agregar) 4. Uso de rasgos Como estaba trabajando actualmente en trabajos en cola, también observo que otra forma de inyectar dependencias es mediante el uso de rasgos. Por ejemplo, el rasgo DispathcesJobs en Laravel tiene las siguientes líneas:

protected function dispatch($job) { return app(''Illuminate/Contracts/Bus/Dispatcher'')->dispatch($job); }

Cualquier clase que use los rasgos tendrá acceso al método protegido y acceso a la dependencia. Es más útil que tener muchas dependencias en el constructor o en las firmas de métodos, es más claro (acerca de qué dependencias están involucradas) que las globales y más fácil de personalizar que las llamadas manuales al contenedor DI. El inconveniente es que cada vez que invocas la función tienes que recuperar la dependencia del contenedor DI,