En esta última charla de Laracon Online 2017, Matt Stauffer, creador del libro Laravel Up & Running, nos explica la «magia» detrás del framework Laravel, es decir cómo funciona el contenedor Illuminate\Container y cómo se relaciona con los demás servicios y componentes de Laravel.
¿Qué es el contenedor?
El contenedor es como la espina dorsal de Laravel. Es lo que permite atar todos componentes y clases o servicios de Laravel, como por ejemplo: enrutador, middleware, solicitudes, resolución e inyección de dependencias automática, facades, etc.
El contenedor y sus alias:
Nosotros solemos referirnos al contenedor de Laravel como simplemente el contenedor (The container) pero también como:
- La clase Application
- El IoC Container
- DI Container
¿Qué significan las siglas IoC y DI?
DI viene del inglés dependency injection o inyección de dependencias: se refiere a la acción de suministrar o inyectar dependencias (objetos) a través del constructor o métodos setter de otro objeto. Lo cuál suele contribuir bastante a la creación de código desacoplado y a un buen diseño orientado a objetos.
IoC viene del inglés inversión de control o inversión de control: con este principio de diseño definimos dentro del framework, una sola vez, cómo queremos que se implementen ciertos features, por ejemplo qué drivers vamos a usar para manejar la sesión o la base de datos, etc.
Puedes aprender sobre inyección de dependencias, contenedores, inversión de control y más en nuestro Curso de creación de componentes con PHP y Laravel.
El contenedor es el pegamento
Por lo tanto, Matt concluye que el contenedor es como el «pegamento» que une los diferentes componentes, servicios y clases del framework.
Cómo atar y resolver clases con el contenedor
Las dos funciones principales del contenedor son “bind” y “resolve”, es decir incluir y resolver dependencias.
Se puede acceder al contenedor de Laravel haciendo uso del helper app()
.
<?php $container = app();
Lo cual no es más que una forma fácil y corta de escribir:
<?php $container = Container::getInstance();
Esto puede parecer confuso ¿Por qué el helper se llama app
pero parece que obtenemos una instancia de Container?
Luego de investigar un poco, descubrí que la clase Illuminate\Foundation\Application
tiene esta línea:
// Linea 174 static::setInstance($this);
y resulta que la clase Application
extiende de la clase contenedor (Illuminate\Container\Container
). Lo que quiere decir que al llamar al helper app()
o al ejecutar Container::getInstance()
obtendremos nada más y nada menos que una instancia del objeto Application
de Laravel. De allí puedes ver porqué se conoce como «Contenedor» o «Aplicación». Application
no es más que una implementación de Container
en Laravel.
El componente Container
puede ser usado como un componente en proyectos fuera del framework Laravel.
¿Qué hace el contenedor de Laravel?
Este objeto nos permite resolver de forma automática cualquier clase, esté registrada o no dentro del contenedor:
<?php class SomeClass { Public function doSomething() { // do stuff } } $container = Container::getInstance(); $someClass = $container->make(SomeClass::class); $someClass->doSomething(); // o en una sola linea: Container::getInstance()->make(SomeClass::class)->doSomething();
Este ejemplo por supuesto no tiene ninguna «ganancia» puesto que nuestra clase SomeClass
no tiene ninguna dependencia. Pero si la tuviera:
<?php class SomeClass { public function __construct(SomeDependency $dependency) { $this->dependency = $dependency; } //... }
El contenedor, por lo general, será capaz de resolver e inyectar todas las dependencias de forma recursiva al momento de crear la instancia de la clase.
Dentro de Laravel, el contenedor nos permite obtener todos los servicios y clases del framework de forma muy sencilla usando:
<?php $container = app(); $logger = $container->make(Illuminate\Log\Writer::class); // or $logger = app()->make(Illuminate\Log\Writer::class);
O aún más corto, puesto que el helper app()
como muchos otros permiten múltiples propósitos:
<?php app(Illuminate\Log\Writer::class);
Aprende más sobre el uso de helpers en Laravel vs Facades e inyección de dependencias.
Autowiring
Matt también nos habló del concepto de autowiring: la capacidad que tiene un framework o componente de instanciar una clase sin recibir instrucciones explícitas de cómo hacerlo. Para lograr esto hace falta usar Reflection.
Puedes aprender cómo se logra la resolución automática de clases con Reflection en nuestro Curso de creación de componentes.
Asociar clases al contenedor de forma manual
Para casos donde requiramos más personalización, también podemos «atar» una o más clases de forma manual y específica:
app()->bind(SomeClass::class, function () { return new SomeClass(new SomeDependency); });
Esto nos devolverá siempre una nueva instancia de la clase SomeClass
.
Uso de «Singleton» en el contenedor
¿Qué sucede si queremos reusar una misma instancia de una clase? Para lograr esto tenemos 2 opciones:
Podemos utilizar el método singleton:
app()->singleton(SomeClass::class, function () { return new SomeClass(new SomeDependency); });
Ahora se creará una instancia de SomeClass
la primera vez que se llame a app(SomeClass::class)
y la referencia a esta clase quedará grabada en el contenedor y será retornada la segunda, tercera, cuarta… vez que se llame a app(SomeClass::class)
.
Aprende más sobre el tema en nuestro Curso de creación de componentes:
También podemos utilizar el método «instance» para instanciar la clase en cualquier lugar de la aplicación y registrarla luego en el contenedor, para obtenerla y usarla en cualquier parte del proyecto:
<?php app()->instance(SomeClass::class, new SomeClass(new SomeDependency));
Uso de alias en el contenedor
Cuando trabajamos con el contenedor no estamos obligados a usar nombres de clases, puesto que también nos permite definir «alias» los pueden crear referencias a interfaces o simples cadenas de texto, ejemplo:
<?php app()->alias(SomeClass::class, 'some-class'); app('some-class'); // retorna la instancia de SomeClass
Todos los servicios del núcleo de Laravel tienen alias o «apodos» como podemos observar en la documentación de Laravel o en el archivo config/app.php
donde están registrados.
Aprende más sobre interfaces y polimorfismo en nuestro Curso de programación orientada a objetos con PHP.
Inyección de dependencias en métodos
Como quizás habrás notado ya, Laravel también nos permite el uso de inyección de dependencias con resolución automática en métodos:
<?php //... public function store(Request $request) { //... }
Otros tipos de inyección de dependencias en Laravel
Matt también nos habló de otros tipos de inyección de dependencias que podemos encontrar en Laravel, como lo son:
- Form requests: nos permite definir clases encargadas de validar datos en las peticiones de forma automática.
- Route model binding: nos permite asociar y atara modelos a nuestras rutas de forma automática.
Luego, Matt Stauffer, continuó con dos conceptos claves de Laravel de los cuales te hemos hablado bastante en Styde:
Service Providers
Son clases que permiten atar servicios al contenedor en una ubicación central, generalmente los agrupamos por funcionalidad. Laravel es configurado dentro de Service Providers y típicamente los componentes y tu aplicación también poseen y usan service providers.
- ¿Cómo funcionan los service providers en Laravel 5.1?
- ¿Qué son y cómo crear tus propios services providers?
Facades
Quizás el feature más controversial de Laravel.
Las facades dan la impresión de ser clases con un montón de métodos estáticos, por ejemplo: Route::get()
o View::make()
o Auth::user()
lo cual sería una mala práctica porque nos llevaría a tener un framework altamente acoplado como explicamos en la lección dependencias y código acoplado del curso de componentes.
Sin embargo, las facades no son más que «proxies» que nos una interfaz muy conveniente para llamar de forma «estática» a métodos no estáticos de clases registradas en el contenedor de Laravel.
Por ejemplo: View::make('mi-vista')
es equivalente a escribir app(Illuminate\View\Factory::class)->make('mi-vista')
o app('view')->make('mi-vista')
. Es decir, con esto estaremos accediendo al contenedor con todos los beneficios que esto trae (clases desacopladas, posibilidad de reemplazar implementaciones, escribir pruebas unitarias, etc.).
Aprende más sobre facades:
- Qué son los facades y cómo implementarlos en tu proyecto
- Facades personalizados en Laravel 5.1
- Facades vs helpers vs inyección de dependencias en Laravel
- Facades automáticos en Laravel 5.4
- Qué son los facades y cómo implementarlos en tus proyectos de PHP (nivel básico)
- Implementación de facades en un proyecto de PHP (nivel avanzado)
Más sobre Laracon Online
Aquí puedes encontrar un resumen de cada charla del evento
- Jeffrey Way “Laravel Mix.”
- Evan You “Something Vue.js 2.2+ related”
- Rachel Andrew “CSS Grid and Flexbox.”
- Adam Wathan “You Might Not Need a Mocking Framework.”
- Nick Canzoneri “What developers should know about email.”
- Taylor Otwell «Laravel Internals»
- Jason McCreary «You don’t know Git»
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.