Login to bookmark this video
Buy Access to Course
20.

Optimización del rendimiento 1: Memoización

|

Share this awesome video!

|

Lucky you! You found an early release chapter - it will be fully polished and published shortly!

This Chapter isn't quite ready...

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com

Nuestro filtro Twig funciona como esperábamos, y lo estamos utilizando aquí en nuestro índice de artículos. Ahora mismo, estamos sobreescribiendo la variable artículo con su versión traducida al inicio del bucle. Creo que hay otra forma válida de utilizar este filtro: sólo cuando lo necesitemos.

Utilizar el filtro sólo cuando sea necesario

En primer lugar, elimina la modificación de set article. Luego, aquí abajo, donde realmente necesitamos la versión traducida - para el título y el contenido. Cambia article.titlepor article|translate_object.title y haz lo mismo para el contenido:article|translate_object.content:

88 lines | templates/article/index.html.twig
// ... lines 1 - 2
{% block body %}
// ... line 4
<div class="">
// ... line 6
<div>
{% for article in articles %}
<div class="bg-white my-2 rounded-2xl p-5">
<a href="{{ path('app_article_show', {slug: article.slug}) }}">
<div class="flex flex-col md:flex-row">
// ... lines 12 - 14
<div class="text-5xl font-medium mt-4">{{ article|translate_object.title }}</div>
<div class="line-clamp-3 my-5">{{ article|translate_object.content|markdown_to_html }}</div>
// ... lines 17 - 21
</div>
</a>
</div>
{% endfor %}
</div>
// ... lines 27 - 85
</div>
{% endblock %}

Vuelve a nuestro navegador: estamos en la página de inicio en francés. Antes de actualizar, observa el recuento de consultas en la barra de herramientas de depuración web. 4, una consulta para obtener todos los artículos, y luego una consulta por artículo para obtener las traducciones. Ahora actualiza la página. Vale, todo sigue funcionando... pero mira el número de consultas: ¡7! Una consulta para todos los artículos, y ahora dos consultas por artículo. Los valores traducidos se están cargando dos veces, aunque sean idénticos.

Arreglemos esto con una técnica llamada memoización. Es una palabra elegante, y me gusta mucho decirla, pero sólo significa almacenar datos en memoria para evitar el procesamiento redundante.

WeakMap

Implementaremos esto en nuestro ObjectTranslator, así que ábrelo. Podríamos simplemente añadir una propiedad array para almacenar objetos traducidos por ID o algo así, pero esto podría provocar fugas de memoria. En procesos de larga duración, este array podría crecer indefinidamente.

En su lugar, utilizaremos un objeto especial del núcleo de PHP llamado WeakMap. Añade una nueva propiedad,private \WeakMap $translatedObjects:

// ... lines 1 - 9
final class ObjectTranslator
{
private \WeakMap $translatedObjects;
// ... lines 13 - 80
}

En PHP, cuando se crea y se pasa un objeto, en realidad es una referencia a ese objeto. Mientras algo esté utilizando ese objeto, permanece en memoria. Cuando ya nada lo utiliza, PHP lo limpia automáticamente. Si utilizáramos una matriz estándar para almacenar objetos traducidos, esa referencia nunca se eliminaría. Un WeakMapes diferente, guarda el objeto pero no impide que se limpie: es una referencia débil. ¡Esto es perfecto para nuestro caso de uso!

Para utilizarlo, primero tenemos que instanciarlo, así que en el constructor,$this->translatedObjects = new \WeakMap():

// ... lines 1 - 9
final class ObjectTranslator
{
// ... lines 12 - 13
public function __construct(
// ... lines 15 - 18
) {
$this->translatedObjects = new \WeakMap();
}
// ... lines 22 - 80
}

Abajo, en el método translate(), esta llamada a translationFor() es cara, está haciendo una consulta a la base de datos. Así que vamos a guardar todo este TranslatedObject en nuestro WeakMap. Justo después de return, escribe $this->translatedObjects - ¿con qué clave? El $object original! Ahora, utiliza el operador de asignación de coalescencia de nulos ??= y luego crea el TranslatedObject:

// ... lines 1 - 9
final class ObjectTranslator
{
// ... lines 12 - 29
public function translate(object $object): object
{
// ... lines 32 - 37
return $this->translatedObjects[$object] ??= new TranslatedObject($object, $this->translationsFor($object, $locale));
}
// ... lines 40 - 80
}

Esto comprueba si el objeto traducido ya está en el mapa débil (para el objeto original). Si es así, lo devuelve. En caso contrario, crea un nuevo TranslatedObject, lo almacena en el mapa débil y lo devuelve. Si el objeto original es limpiado por PHP, se eliminará automáticamente del mapa débil.

¡Veámoslo en acción! De vuelta en tu navegador, vemos las 7 consultas. Ahora actualiza... sigue funcionando... y mira el recuento de consultas: ¡vuelve a ser 4! ¡La memoización funciona!

A continuación, vamos a continuar este proceso de optimización del rendimiento con el almacenamiento en caché sin memoria.