\n \n \n\nLo bueno de esto es que el parámetro no afecta al nombre del fichero. Todo lo que vaya después de la interrogación es independiente del nombre. En el ejemplo anterior, siempre se hace referencia al fichero 'miestilo.css', así que no te líes a cambiar el nombre de tus ficheros. Con añadir el parámetro en la URL vale.\n\nDe hecho, si ya eres un profesional más avanzado y has probado React o Angular, te habrás dado cuenta de que los CSS y los JS que obtienes tienen incluidos unos \"codiguitos\" estilo main.54ds6.js. Esto es precisamente para saltarse la caché cuando haya un cambio y solo descargar los contenidos nuevos.\n\nOtra opción que tienes si no quieres añadir parámetros es usando la directiva ****no-cache****. Recuerdas que te dije que no significaba lo que daba a entender por el nombre. Recapitulando, los recursos con la directiva ****no-cache**** permiten al navegador cachear un recurso pero siempre comprobando que no hay una versión más reciente. Lo malo de esta directiva es que siempre hay una petición al servidor. Lo bueno, es que si el recurso no ha cambiado, la respuesta será un 304 Not modified y sin cuerpo; traduciendo, que en vez de kB de tamaño, apenas serán unos bytes. Siempre es más rápido enviar bytes que kilobytes.\n\n¿Y cómo sabe el navegador o el servidor si el recurso está actualizado 🤔? Pues hay varias formas de hacerlo: usando la cabecera If-Modified-Since o usando la cabecera ETag.\n\nLa cabecera If-Modified-Since (que se traduce por \"si no ha sido modificado desde\") la manda el navegador. Es muy útil cuando se está pidiendo estáticos (que existen en el servidor como ficheros). El navegador envía la petición para comprobar si el recurso que tiene en caché es el último igual que con cualquier petición, pero añade esta cabecera indicando la fecha y hora en la que guardó el recurso en la caché. El servidor, comprobará la fecha del fichero y si no ha cambiado desde entonces, devolverá un ****304 Not Modified.**** El navegador cuando lee esta respuesta entiende que sí tiene la última versión, por lo que puede usarla. Si por el contrario el servidor detecta que hay una versión más reciente, devolverá un ****200 Ok**** y el propio recurso en la misma petición. El navegador entiende que debe borrar el recurso que tiene guardado en su caché y reemplazarlo por este nuevo que está enviando el servidor. Este proceso se repetirá siempre tal cual. Según el navegador detecte que necesita ese recurso y comprueba que existe en la caché con la directiva ****no-cache****, hará la petición esperando un 200 o un 304.\n\nLa otra posibilidad que tienes es usar la cabecera ETag. Funciona de forma similar a If-Modified-Since, pero en este caso no contiene fechas, sino cadenas de texto. Cuando el servidor envía un recurso le añade la cabecera ETag cuyo valor es aleatorio pero permite identificar el recurso (la firma de su contenido mediante algún hash, la fecha de generación, la versión, etc). Una vez que el navegador quiere comprobar si el recurso es la última versión envía la petición con la cabecera ETag según la envió el servidor. El servidor comprueba así si el recurso ha cambiado de versión y la cadena de texto coincide con la que envió el cliente. Si coincide, el servidor devuelve un 304; si no, un 200. No deberías tener que preocuparte de generar un ETag, los principales servidores web lo harán por ti. Quédate con el concepto de que el servidor sabe a través de ese ETag a qué versión del fichero se refiere.\n\n## Cómo aplicar la caché HTTP a mi web\n\nPues ya tienes toda la teoría que necesitas para empezar y para volar durante un tiempo. Pero, ¿y la práctica?. Pues depende del servidor web que estés utilizando, aunque seguramente sea Apache o Nginx. Si es Apache, tienes toda la información sobre caché en la [web oficial](http://web.archive.org/web/20201230082813/https://httpd.apache.org/docs/2.4/caching.html) (versión 2.4) o en muchos blogs, pero básicamente es usar el módulo Header de Apache (aunque puedes usar expires, pero no me hace mucha gracia; prefiero establecerlo a mano):\n\n \n Header set Cache-Control \"max-age=63072000, public\"\n \n \n // O...\n \n \n Header set Cache-Control \"max-age=300, private\"\n \n\nEn Nginx creo que es más sencillo (aunque siempre me gustó más Nginx, por lo que mi opinión es subjetiva). Entra en el fichero de configuración de tu sitio y añade cabeceras según el tipo de ruta.\n\n location ~* \\.(js|css|png|jpg|jpeg|gif|svg|ico)$ {\n add_header Cache-Control \"public, max-age=3600, s-maxage=10000\";\n }\n\nEspero que te haya quedado un poco más claro el tema de la caché y cómo puede ayudarte a optimizar tu sitio web y ayudarte con tu sitio web. Es más, estoy pensando que podría añadir caché al [proyecto del Blog en Ghost](http://web.archive.org/web/20201230082813/https://www.juannicolas.eu/montar-un-blog-con-ghost/) que te conté en este artículo. No sé... a lo mejor hacemos un nuevo capítulo de esa serie 😏.\n\nQue tengas un feliz coding !\n","mainEntityOfPage":"https://www.juannicolas.eu/cache-en-http/","datePublished":"2020-10-28T16:00:00.000Z","dateModified":"2022-10-06T16:00:59.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":2},{"@context":"https://schema.org","@type":"Article","headline":"MailHog y cómo probar correos electrónicos en desarrollo","description":"MailHog es una herramienta destinada a desarrolladores que permite hacer pruebas de envío de correos electrónicos usando SMTP y comprobando cómo quedan en una WebUI","url":"https://www.juannicolas.eu/mailhog-como-probar-correos-electronicos-en-desarrollo/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/post-box-pexels-markus-winkler-3703429.DCkb3sBk.jpg","width":1611,"height":1074},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"Una de las partes más comunes que los desarrolladores tenemos que hacer es **enviar correos electrónicos al usuario**: cuando se completa el registro, cuando responden a un hilo que te interesa o cuando hay alguna notificación. Esto puede llegar a ser un dolor de cabeza porque generalmente tienes que hacer varias pruebas, los servidores de correo te pueden calificar como SPAM al intentar enviar varias veces el mismo, etc.\n\n¿Sabías que hay una forma más pro de **enviar correos electrónicos en un entorno local o de pruebas** sin necesitar una cuenta de correo real? En este artículo te voy a **enseñar qué es MailHog y por qué deberías usarlo para pruebas de email** en tu próximo proyecto.\n\nPero antes de eso, vamos a repasar un poco de teoría, para que entiendas cómo funciona MailHog y por qué te puede ahorrar muchos problemas a la hora de hacer pruebas de envío de email y cómo quedan tus plantillas.\n\n## Qué es un servidor SMTP\n\nDesde hace muchos años, existen dos protocolos básicos que usamos los usuarios para leer y recibir correo electrónico. Por un lado, el IMAP (o POP3 si eres un poco viejuno) permite conectarse con tu buzón de correo y descargar tus mensajes. Por el otro lado, el SMTP (que no ha cambiado mucho a lo largo de los años) permite que tú envíes un correo electrónico a otros usuarios.\n\nCuando tu terminas de redactar un correo electrónico y le das a enviar, lo que está pasando por detrás es que estás haciendo una conexión al servidor SMTP de tu proveedor de correo y le estás diciendo quién eres, cuál es tu cuenta y contraseña, a quién quieres enviar el correo y algunos datos más. Si el usuario/contraseña es correcta y no hay errores en el mensaje, el servidor SMTP acepta el encargo y se encargará de hacerle llegar ese correo al usuario destinatario. **Realmente quién se encarga de enviar el correo es el servidor de SMTP, no tú**.\n\n> Es por esto que a veces, a las horas de enviar un correo, te viene devuelto con un error que indica que no se ha podido enviar. Esto se debe a que el servidor SMTP ha estado intentándolo un par de horas pero finalmente no ha podido.\n\nCuando configuramos una aplicación para que envíe correos, el panorama no ha cambiado mucho: en vez de componerlos nosotros y darle al botón de enviar, se lo estamos delegando a un software automatizado. Este software se encarga de componer el mensaje y enviarlo al servidor SMTP que hayamos configurado.\n\nY es aquí cuando empiezan a aparecer los posibles problemas y contratiempos para el desarrollador que quiere que su sistema mande correos automáticos.\n\n- En primer lugar, necesitamos tener conexión a Internet para enviar correos electrónicos reales. Eso significa que si teletrabajas desde el bosque vas a tener problemas de cobertura.\n\n- En algunas plataformas como Gmail, no vale con meter usuario y contraseña para comenzar a enviar correos. Tienes que generar un token y generalmente activar una serie de mecanismos de seguridad que pueden resultar molestos (y muy trabajosos).\n\n- Necesitas usar una cuenta real (personal o de trabajo). Eso significa que si envías muchos correos de prueba, tu SMTP y/o el servidor del destinatario pueden pensar que estás un poco pesado: SPAM temporal.\n\n- Si las pruebas no quedan bien o metes contenido que pueda resultar sospechoso, tu cuenta puede caer en SPAM durante mucho mucho tiempo.\n\n\nPasan muchas cosas cuando envías un email a través de tu sistema. ¿Verdad que estaría bien que existiera un entorno de pruebas para simplificar este proceso? Exacto, aquí entra en escena MailHog. **MailHog se encargará de simplificar el envío de emails con un servidor SMTP falso**.\n\n## Qué es MailHog y por qué te interesa en el desarrollo\n\nYa sabes cómo funciona el envío real de un correo electrónico, ya sea desde un cliente de correo o desde un sistema automatizado. Aquí es donde entra en juego MailHog para hacerte la vida más fácil.\n\nMailHog, en pocas palabras, es una herramienta que **sirve para hacer pruebas de entregabilidad de correos electrónicos y para \"ver cómo quedan los emails\" cuando los envías**. Internamente, crea un servidor de SMTP falso (indistinguible de uno de verdad a primera vista) para que tus aplicaciones puedan enviar el email pensando que es un servidor SMTP normal. Cuando detecta que está entrando un mensaje de correo electrónico, lo captura y no lo envía (como haría un SMTP real).\n\n> Luego, cuando subas tu aplicación a producción, bastará con cambiar los parámetros de conexión con el servidor SMTP real y no hará falta tocar el código de tu aplicación.\n\nMailHog tiene una interfaz web que muestra los mensajes de correo que han ido entrando como si fuese una aplicación de correo electrónico normal. De esta forma, puedes ver qué mensajes han entrado, quién es el remitente y el destinatario y ver cómo queda la plantilla del correo electrónico (si se ve bien, el HTML funciona, etc.). La interfaz web se va actualizando en tiempo real, lo que permite que no tengas que preocuparte por refrescar constantemente.\n\n**La principal ventaja que nos ofrece MailHog es que no tenemos que lidiar con los problemas de la capa de red ni con los problemas de SPAM** que puede generar el uso de un servidor real. Sin embargo, tiene otras ventajas añadidas, como por ejemplo, que está disponible sin conexión a Internet, por lo que ya puedes irte a teletrabajar al bosque. Además, se puede instalar en casi cualquier sistema operativo y no ocupa mucho espacio.\n\nPor último, y para ser un buen desarrollador, MailHog tiene un as escondido bajo la manga: [Jim, el mono del caos](https://github.com/mailhog/MailHog/blob/master/docs/JIM.md). Bajo este cómico nombre se encuentra una funcionalidad muy útil para el desarrollador: probar cómo se comporta tu aplicación cuando el servidor de correos electrónicos da problemas. Cuando activas al mono Jim, de forma aleatoria, MailHog genera problemas comunes de entrega, como conexiones rechazadas o límite de velocidad. De esta manera, puedes probar si tu código soporta el fallo en el envío sin tener que cortar el cable de Internet.\n\n## Cómo instalar MailHog y cómo ponerlo en marcha\n\nSi ya te he convencido de usar MailHog para el envío de correos electrónicos en un entorno de pruebas, el siguiente paso es ver cómo se instala. Tienes toda la documentación de cómo [instalar MailHog en diferentes plataformas](https://github.com/mailhog/MailHog#installation). Por comodidad y sencillez, en este artículo vamos a utilizar la versión de docker. Con tan solo lanzar este comando, tendremos levantada una instancia de MailHog en nuestro equipo:\n\n```shell\ndocker run --name mailhog -p 1025:1025 -p 8025:8025 mailhog/mailhog\n```\n\n> Cuando lo uses con otro proyecto que tengas en docker, asegúrate de que el contenedor de tu aplicación y el contenedor de MailHog están en la misma red, para que puedan conectarse correctamente.\n\n**MailHog permite muchas funcionalidades extra**, como por ejemplo requerir usuario y contraseña, enviar realmente los correos electrónicos a un SMTP real o guardar los correos electrónicos que recibe en una base de datos para asegurar la persistencia. Revista el [documento de configuración a través de las variables de entorno](https://github.com/mailhog/MailHog/blob/master/docs/CONFIG.md) para saber más.\n\n```shell\n$ docker run -p 1025:1025 -p 8025:8025 mailhog/mailhog\n2022/10/06 10:37:59 Using in-memory storage\n2022/10/06 10:37:59 [SMTP] Binding to address: 0.0.0.0:1025\n[HTTP] Binding to address: 0.0.0.0:8025\n2022/10/06 10:37:59 Serving under http://0.0.0.0:8025/\nCreating API v1 with WebPath: \nCreating API v2 with WebPath: \n```\n\nAhora que ya tenemos MailHog funcionando, podemos configurar nuestra aplicación para que envíe correos electrónicos a través de este SMTP. Deberás configurar tu aplicación para que use \"localhost:1025\" si estas en tu local o el nombre del contenedor si estás en docker: \"mailhog:1025\".\n\nNo olvides abrir la interfaz de usuario a través de tu navegador en localhost:8025. Verás una ventana increíblemente sencilla donde irán apareciendo los correos electrónicos a medida que MailHog los vaya recibiendo. También podrás activar al mono Jim para probar si tu aplicación es tolerante a fallos.\n\n![Interfaz web de MailHog](./post-image/mailhog-ui-web.png)\n\n## Conclusión\n\nHasta que descubrí MailHog, yo también tenía configurada mi cuenta de Gmail para hacer las pruebas de envío de correos electrónicos y ver cómo quedaban las plantillas, pero, desde que lo descubrí, siempre tengo un contenedor de MailHog en mi ordenador 🙂. De esta forma, trabajo sin necesidad de Internet y sin tener que esperar a que el mensaje se envíe a otros servidores: no hay nada más rápido que localhost.\n\nEspero que te haya picado el gusanillo y a partir de hoy envíes correos electrónicos en entorno de desarrollo a través de MailHog y no un SMTP real. Así conservarás la credibilidad de tu cuenta de correo.\n\n¡Qué tengas un feliz coding!\n\n\n\n","mainEntityOfPage":"https://www.juannicolas.eu/mailhog-como-probar-correos-electronicos-en-desarrollo/","datePublished":"2022-10-06T16:00:00.000Z","dateModified":"2022-10-06T16:00:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":3},{"@context":"https://schema.org","@type":"Article","headline":"Definiendo librerías externas en Symfony","description":"Symfony nos permiten hacer autowire con librerías de otros desarrolladores. Incluso pasar parámetros extra al inicializarlas. Vemos el componente Dependency Injection con ejemplos","url":"https://www.juannicolas.eu/definir-librerias-externas-symfony/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/close-up-nozzles-diesel-engine-opened-bonnet.D59UBrqX.jpg","width":1612,"height":1075},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"\nMuchas veces, cuando estamos trabajando en un proyecto PHP/Symfony, necesitamos inyectar librerías de terceros. Sin embargo, la función `autowire` del componente de inyección de dependencias de Symfony no funciona correctamente: no encuentra la nueva biblioteca. Esto se debe a que **Symfony no hace autowire con librerías externas**. En el artículo de hoy te voy a enseñar a **definir e inicializar librerías de terceros como un servicio más en un proyecto Symfony** o, si no usas Symfony, al menos inicializarlas usando este genial componente.\n\nPero no todo va a ser Symfony. Desde hace un par de versiones, **Symfony dio un giro bastante aplaudido por los desarrolladores comenzando a romper el monolito de Symfony en componentes**. Esto significa que puedes usarlos en tu proyecto PHP sin tener que usar el núcleo del framework. Y uno de estos componentes es el [Dependency Injection](https://symfony.com/doc/current/components/dependency_injection.html), un componente increíble que permite centralizar y estandarizar las dependencias de las clases que componen tu aplicación. El artículo de hoy habla principalmente de este componente y sobre cómo puedes configurarlo para inicializar las librerías, con o sin parámetros. Así que tanto si utilizas el framework de Symfony o este componente, esto te va a interesar.\n\n## Problema del autowire en Symfony con librerías externas\n\nSymfony es un framework genial para PHP. Podemos criticar si es muy pesado o grande (tema que resolvieron cuando enfocaron la arquitectura de componentes), es muy complejo, etc. Pero que quita muchísimo trabajo al desarrollador es algo en lo que creo que todos estaremos de acuerdo.\n\nEn cada nueva versión añaden algún componente o utilidad (que generalmente puedes utilizar por separado del núcleo de Symfony) que hace que programar en PHP sea más sencillo y podamos ir directos al grano. Uno de esos cambios drásticos que introdujeron fue el **autowire: no tenías que declarar los servicios que hubieses programado en ningún YML**: Symfony los encontraba y los inyectaba de forma automática (o automágica). Los ficheros de servicios adelgazaron bastante al no tener que definir de manera explícita cada uno de los servicios que habíamos creado.\n\nSin embargo, cuando estamos trabajando en un proyecto PHP (y en la mayoría de lenguajes de programación), llega el momento en el que hay que importar y utilizar un módulo, librería, biblioteca o componente de otro desarrollador. Con `composer` es tremendamente fácil importar librerías, pero, cuando la vas a usar en tu proyecto, ¿un momento? Algo no está funcionando. Efectivamente, **el autowire de Symfony no funciona con librerías externas, solo con las que estén dentro de tu propio proyecto**.\n\n`Cannot autowire service \"XXX\\MyService\": argument \"$xx\" of method \"__construct()\" references interface \"XXX\\YYY\" but no such service exists.`\n\n## Inicializar librerías externas en Symfony\n\nQue no funcione de primeras, no quiere decir que no podamos usar librerías externas en nuestro proyecto. Es más, con declarar estas librerías en el fichero `services.yml` bastará para poder utilizarlas. **Symfony necesita que declaremos la librería de terceros que queremos emplear de forma expresa en el fichero de servicios**.\n\nUn ejemplo de definición de librería es el que sigue:\n\n```yml\n# Definición usando alias\nalias.library:\n class: ExternalVendor\\Library\n\n# Defnición usando la clase\nExternalVendor\\Library:\n class: ExternalVendor\\Library\n autowire: true\n\n# Definición de Interfaces\nApp\\Util\\TransformerInterface: '@App\\Util\\Rot13Transformer'\n\n\n```\n\nPodemos definir la librería externa de dos formas diferentes: mediante un alias y mediante la declaración de clase.\n\nLa primera es útil cuando es un servicio público (aunque deberías plantearte si esto es realmente necesario). **Un servicio público es aquel que es global y puede llamarse en casi cualquier parte del código desde el contenedor de dependencias**. No se considera una buena práctica de programación porque las dependencias deberían estar inyectadas desde el constructor, pero Symfony te da la opción de inyectarla de cualquier forma.\n\n```php\n$library = $container->get('alias.library');\n$library->run();\n```\n\nLa mayoría de las veces, usarás un servicio privado. **Un servicio privado es aquel que no puede inyectarse en mitad del código, ya que debe ser inyectado a través de la función que construye la clase**:\n\n```php\nclass MyClass {\n private Library $library;\n\n public function __construct(Library $library) {\n $this->library = $library;\n }\n}\n```\n\nPor último, es posible que necesites _inyectar una interfaz_. Un momento, ¿eso es posible? Rotundamente no, pero **podemos hacer que Symfony inyecte una clase en concreto cada vez que como dependencia declaremos una interfaz**. Esto quiere decir que cada vez que en un constructor declaremos como parámetro una interfaz, Symfony buscará la clase que nosotros hayamos indicado como preferente para ese caso.\n\n```php\nclass S3Storage implements StorageInterface{};\n\nclass MyStorage {\n public function __construct(StorageInterface $storage)\n {\n //Hacer algo super cool !\n }\n}\n\n# the ``App\\Util\\S3Storage`` service will be injected when\n# an ``App\\Util\\StorageInterface`` type-hint is detected\nApp\\Util\\StorageInterface: '@App\\Util\\S3Storage'\n```\n\nCon esto, podrás utilizar en tus proyectos cualquier librería externa que requieras. Bastará con que la definas explícitamente en el fichero `services.yml` de Symfony para que este la importe.\n\n## Pasando parámetros al inicializar la librería\n\nHasta hora hemos visto cómo declarar una librería externa para que Symfony haga el `autowire` en nuestro proyecto. Pero, ¿qué pasa si esta librería necesita algún parámetro antes de ser inicializada? Un ejemplo bastante recurrente es la librería de AWS S3 para proyectos en PHP, una librería que permite subir y descargar archivos de un bucket de Amazon (o MinIO, como hemos visto en este blog).\n\nEl código de demostración que proporciona Amazon para inicializar la librería requiere que le pasemos algunos parámetros como usuario y clave.\n\n```php\n$s3Client = new S3Client([\n 'profile' => 'default',\n 'region' => 'us-west-2',\n 'version' => '2006-03-01',\n 'credentials' => [\n 'key' => AWS_KEY,\n 'secret' => AWS_SECRET_KEY,\n ]\n]);\n\n//Listing all S3 Bucket\n$buckets = $s3Client->listBuckets();\n```\n\nSin embargo, el **Inyector de Dependencias de Symfony es increíblemente flexible** y nos evita tener que escribir este código cada vez que inicializamos la librería. El DI (Dependency Injector en inglés) nos permite pasar parámetros y configuraciones a la hora de inicializar la librería, para que cada vez que la usemos en nuestro código, ya venga inicializada con dichos parámetros. Además, nos da la ventaja de no tener que escribir las claves y demás credenciales sensibles en código puro: nos permite hacer referencia a variables de entorno (que no se adjuntan en el código).\n\nSiguiendo con el ejemplo, para inicializar el AWS de S3, bastaría con pasarle los parámetros en el formato que espera, pero en un fichero YML en vez de hacerlo directamente en el código:\n\n```yml\nAws\\S3\\S3Client:\n arguments:\n - version: 'latest'\n region: '%env(AWS_S3_BUCKET_REGION)%'\n credentials:\n key: '%env(AWS_S3_ACCESS_ID)%'\n secret: '%env(AWS_S3_ACCESS_SECRET)%'\n```\n\n```php\nclass MyStorage {\n public function __construct(S3Client $storage) {\n //Aquí el S3Client ya está inicializado con los\n // parámetros que le hemos pasado.\n\n // Por ejemplo... siguiendo ejemplo de AWS\n $buckets = $storage->listBuckets();\n }\n}\n```\n\n\n\n## Conclusión\n\nEl inyector de dependencias de Symfony es un componente tremendamente útil para PHP, ya sea para usarlo en un proyecto Symfony o en lenguaje PHP puro (ya que al ser un componente, se puede importar sin tener el núcleo de Symfony).\n\nPermite abstraer al desarrollador de usar `require`, importar ficheros externos, y afear el código (y esas cosas que hacían de PHP un lenguaje bastante feo). Gracias a este componente, podemos estandarizar y centralizar la forma en la que inyectamos clases, dejando un código más profesional y legible.\n\nOtra de las ventajas del componente Dependency Injector de Symfony es que nos permite [respetar los principios SOLID](https://www.juannicolas.eu/principios-solid-con-ejemplos-parte-i/) de los que ya hablamos en este blog.\n\nEn cualquier caso, espero haberte enseñado una forma realmente útil de cómo inicializar dependencias externas en tu proyecto Symfony PHP o, si no usas Symfony, cómo inyectar dependencias con este componente.\n\n¡Qué tengas un feliz coding!\n\n\n\n","mainEntityOfPage":"https://www.juannicolas.eu/definir-librerias-externas-symfony/","datePublished":"2022-09-27T14:00:00.000Z","dateModified":"2022-09-27T14:00:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":4},{"@context":"https://schema.org","@type":"Article","headline":"Convierte texto a voz con Javascript. La API speechSynthesis","description":"Gracias a la API speechSynthesis puedes convertir texto a voz (text to speech, TTS) con tan solo un navegador moderno y Javascript.","url":"https://www.juannicolas.eu/convierte-texto-a-voz-con-javascript/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/altavoces-776153-resized.CPnA4Mtb.jpg","width":1612,"height":1075},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"\nCuando estamos diseñando una App en ocasiones necesitamos leerle un texto al usuario (tal vez leer artículos de un blog mientras hace otra cosa) u ofrecerle un conversor de texto a voz, generalmente por cuestiones de accesibilidad. Existen servicios externos que nos proporcionan un resultado bastante realista, pero la mayoría son de pago.\n\nPuede que tal vez no lo sepas porque de momento no es una API muy extendida, pero, ¿sabías que **con Javascript puedes hacer que el navegador convierta texto a voz**? Y todo ello con apenas unas líneas de código y la API de **speechSynthesis**. Este proceso, el de convertir texto a voz se suele llamar **tts o text to speech**. En este artículo te cuento cómo hacer hablar a tu navegador a través de varios ejemplos.\n\nTen en cuenta que esta API depende de las voces de tu navegador o del sistema operativo que use el usuario. Y generalmente, el resultado no es tan natural o tan humano como los servicios en la nube. Pero para salir del paso o no invertir mucho dinero es perfecta.\n\n## La interfaz speechSynthesis\n\nPresta atención al siguiente fragmento de código Javascript. Puede que no te lo termines de creer, pero con apenas estas cuatro líneas se consigue **convertir texto a voz en Javascript**.\n\n```javascript\nconst synth = window.speechSynthesis\nlet text = \"Hello everybody!!!!\"\nconst utterThis = new SpeechSynthesisUtterance(text)\n\nsynth.speak(utterThis)\n```\n\nLo único que estamos haciendo es declarar una variable que contiene una instancia de `SpeechSynthesis`, la puerta a la API Web Speech. En este caso, para convertir texto a voz.\n\nPosteriormente creamos una variable que contiene una instancia de `SpeechSynthesisUtterance`y hacemos que el navegador hable. La interfaz **SpeechSynthesisUtterance** representa una solicitud para que el navegador sea capaz de modular un texto.\n\nCuesta creer, pero parece que los navegadores se han puesto las pilas con el asunto de la accesibilidad y, en este caso, la modulación de voz. Sin embargo, te invito a que [compruebes la compatibilidad](https://caniuse.com/?search=speechSynthesis) con todos los navegadores porque solo funcionarán los más modernos.\n\n## Propiedades de SpeechSynthesisUtterance. Configúralo a tu gusto.\n\n¡ Genial ! Ya hemos conseguido que nuestro navegador convierta texto a voz, pero no queda del todo bien porque no está configurado a nuestro gusto. Lo primero en nuestro caso es establecer el idioma español para que nuestros visitantes lo entiendan:\n\n```javascript\nconst synth = window.speechSynthesis;\nconst utterThis = new SpeechSynthesisUtterance('Hablo en español');\nutterThis.lang = 'es-ES';\nsynth.speak(utterThis);\n```\n\nSi no establecemos por defecto un idioma, se cogerá de forma automática el que figure en la etiqueta ``. Si este tampoco se hubiera definido, cogerá el que tenga por defecto el navegador del usuario.\n\nTambién podemos **cambiar la voz con la que suena el navegador**. Para ello, podemos consultar las voces disponibles con `synth.getVoices()`, que devolverá un listado de voces disponibles. Generalmente dependerá del navegador pero también del sistema operativo: por ejemplo en Chrome solo hay una voz disponible para cada idioma.\n\n> Ten en cuenta que las voces no estarán disponibles cuando termine de cargar el window, ya que es una operación asíncrona. La mejor opción para detectar cuándo están cargadas las voces es usar el evento window.speechSynthesis.onvoiceschanged\n\nY, por último, podemos personalizar el rate (velocidad de lectura) y el pitch (el tono) de la voz. En el caso del `rate`, debemos establecer un valor decimal entre 0.1 (el más bajo posible; leerá despacio) y el 10 (más alto), siendo 1 el valor por defecto. Y, por otro lado, el pitch es un valor decimal entre 0 (más bajo; un auténtico Barry White ) y el 2 (más alto; casi solo lo oyen los felinos), siendo 1 el valor por defecto.\n\n```javascript\nconst synth = window.speechSynthesis;\nconst utterThis = new SpeechSynthesisUtterance('Tengo una voz grave');\nutterThis.pitch = 0.2;\nsynth.speak(utterThis);\n```\n\n## Controles de voz\n\nJavascript nos permite controlar si está sonando o no alguna voz en el navegador. Para ello, **la API de speechSynthesis** expone cuatro métodos que son de utilidad (sobretodo si el contenido que se está leyendo cambia).\n\nEl método speechSynthesis.speak(SpeechSynthesisUtterance) **comenzará el proceso de convertir el texto a voz** (por lo que el texto deberá estar ya establecido antes de llamar a este método). Ten en cuenta que si hay alguna otra instancia convirtiendo ya el texto a voz, la nueva se quedará encolada hasta que finalicen las existentes.\n\nEl resto de métodos, pause(), resume() y cancel() pausará, retomará (desde donde se ha quedado cuando se hizo la pausa) y cancelará la lectura respectivamente. Son útiles para, por ejemplo cuando cambie el texto, cancelar la lectura y recomenzarla desde cero con el texto ya cambiado. Fíjate en que no debemos pasar la instancia de `SpeechSynthesisUtterance`cuando estamos pausando, retomando o cancelando alguna lectura. Solo se debe pasar cuando se desea comenzar a leer.\n\n```javascript\nconst synth = window.speechSynthesis;\nconst utterThis = new SpeechSynthesisUtterance('Lorem ipsum...');\n\nonTextChange = (newText) => {\n synth.cancel();\n utterThis.text = newText;\n synth.speak(utterThis);\n}\n```\n\n## Solo es compatible con navegadores modernos\n\nComo bien sabes, la mayoría de navegadores están haciendo un gran trabajo en el uso de API modernas, permitiendo hacer de forma nativa una gran cantidad de features que de otra forma habría costado mucho implementar. Sin embargo, cuando te dedicas al front, sabes que aunque la mayoría de usuarios utiliza navegadores modernos, siempre existe el usuario que sigue con su Windows XP e Internet Explorer 11.\n\nSi un usuario con un navegador desfasado intenta usar la API de **speechSynthesis**, el código fallará de forma inexorable. Por ello, y cuando uses APIs modernas, siempre es recomendable detectar si el user-agent del usuario tiene disponible la API en cuestión y tener preparado un camino alternativo si no es el caso.\n\n```javascript\nif ('speechSynthesis' in window) {\n //El navegador es moderno. Todo ok!\n window.speechSynthesis.speak(\n new SpeechSynthesisUtterance('A jugar')\n )\n} else {\n //El navegador es antiguo. Camino alternativo\n // Tal vez mostrar una versión reducida o un cartel de información\n alert('Actualízate!');\n}\n```\n\n## Conclusión\n\nYa sea para permitir a los usuarios la lectura automática de posts mientras navegan por otras web o para hacer la vida más fácil a los usuarios aportando accesibilidad, **Javascript permite la conversión de texto a voz** de forma simple y sencilla.\n\nAsí que la próxima vez que necesites implementar un sistema fácil de lectura de texto y de forma nativa, acuérdate de `window.speechSynthesis`. En el siguiente artículo de esta colección, conoceremos cómo se puede [convertir la voz del usuario en texto](https://www.juannicolas.eu/convierte-texto-a-voz-con-javascript), pero hasta entonces...\n\n¡Qué tengas un feliz coding!","mainEntityOfPage":"https://www.juannicolas.eu/convierte-texto-a-voz-con-javascript/","datePublished":"2022-09-13T09:00:00.000Z","dateModified":"2022-09-20T14:00:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":5},{"@context":"https://schema.org","@type":"Article","headline":"Convierte voz a texto con Javascript. La otra API speechSynthesis","description":"En este artículo conoceremos el SpeechRecognition de la API speechSynthesis, con la que puedes convertir voz a texto utilizando únicamente un navegador Javascript.","url":"https://www.juannicolas.eu/convertir-voz-a-texto-con-javascript/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/microfono.ZgZKy1kL.jpg","width":1612,"height":1075},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"\nHace poco hablábamos de [cómo convertir un texto a viva voz](https://www.juannicolas.eu/convierte-texto-a-voz-con-javascript/) usando tan solo un navegador moderno y algo de Javascript. En este artículo vamos a hacer justo lo contrario: **convertir voz a texto utilizando únicamente Javascript**. Esto es realmente útil para ofrecer accesibilidad al usuario o evitar que el usuario tenga que escribir textos largos.\n\nLos navegadores modernos implementan la API Web Speech, pero esta está dividida en dos interfaces, independiente la una de la otra. La primera es la SpeechSynthesis de la que ya hablamos en otro artículo y que permite convertir texto a voz. La que vamos a ver hoy es **la interfaz SpeechRecognition que permite capturar, a través del micrófono, voz humana y convertirla a una cadena de texto**.\n\nSuena bastante bien, pero tiene una parte mala: no es una API soportada por todas las familias de navegadores. De hecho, según [CanIUse](https://caniuse.com/?search=SpeechRecognition), esta API solo está disponible principalmente para los navegadores Firefox, Chrome y Safari y con un porcentaje de uso de poco más del 89%. Si esto no es un impedimento, ¡vamos allá !\n\n> En algunos navegadores como Chrome, el uso de Speech Recognition implica que el audio se procesa en servidores externos, por lo que no funcionará en modo offline o sin conexión.\n\n## Configurar la gramática de nuestro reconocimiento de voz\n\nComo se ha mencionado anteriormente, esta API no está realmente extendida, por lo que cada navegador la implementa como quiere. Este es el caso de Chrome, que lo implementa usando el prefijo webkit-. Por ello, antes de nada, vamos a crear un código que permita utilizar la API en su versión sin prefijo o, si no se encuentra, con prefijo. Esto hará que funcione en un futuro, cuando se le retire el prefijo.\n\n```javascript\nvar SpeechRecognition = SpeechRecognition || webkitSpeechRecognition\nvar SpeechGrammarList = SpeechGrammarList || webkitSpeechGrammarList\nvar SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent\n\nvar recognition = new SpeechRecognition(); //<-- Reconocimiento de voz\n```\n\nHecho esto, vamos a comenzar con la gramática. Tenemos que decirle al navegador en qué palabras se tiene que centrar. Podemos tomar dos opciones:\n\n- Si es un cuadro de texto genérico, donde el usuario puede escribir (o dictar) literalmente cualquier cosa, utilizaremos la gramática que viene por defecto, que generalmente comprende la mayoría de palabras de un idioma.\n\n- Si, por el contrario, el usuario tiene que limitarse a una serie de palabras, como por ejemplo colores, debemos limitar la lista de palabras que el navegador debería \"admitir\". Es aquí cuando establecemos nuestra propia gramática.\n\nSi has decidido que el navegador puede escuchar cualquier cosa, pasa al punto siguiente. Si, por el contrario, necesitas especificar la lista de palabras, sigue leyendo.\n\nCrearemos una gramática concreta que incluye colores. De esta forma, el navegador se centrará en dicha lista de palabras, dando de lado al resto y ofreciendo resultados más precisos.\n\n```javascript\nvar colors = [ 'aqua' , 'azure' , 'beige', 'bisque', 'black', 'blue', 'brown', 'chocolate', 'coral' ... ];\nvar grammar = '#JSGF V1.0; grammar colors; public = ' + colors.join(' | ') + ' ;'\n```\n\nEl array de `colors` contiene las palabras admitidas y la variable `grammar` contiene la definición de la gramática, que está en formato [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/). Una vez definida, tenemos que conectarla a nuestro **reconocimiento de voz en Javascript ofrecido por SpeechRecognition**.\n\nLo haremos con la función `addFromString(gramática, importancia)`. El primer parámetro define la gramática que queremos utilizar; el segundo especifica un número decimal entre 0 y 1 (inclusive), que determina la importancia de esta gramática frente al resto de gramáticas disponibles.\n\n```javascript\nvar speechRecognitionList = new SpeechGrammarList();\nspeechRecognitionList.addFromString(grammar, 1);\nrecognition.grammars = speechRecognitionList;\n```\n\n## Establezcamos las opciones del reconocimiento\n\nEstablecer la gramática es solo una de las opciones de personalización que nos ofrece la API de SpeechRecognition, pero no es la única. Gracias al resto de propiedades podemos moldear el funcionamiento en base a nuestras necesidades. A continuación se definen qué otras propiedades son realmente útiles a la hora de personalizar nuestro reconocimiento de voz.\n\n- [`SpeechRecognition.continuous`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/continuous): Determina si solo se debe capturar un solo resultado (false) o, por el contrario, si se deben capturar resultados de forma continua (true). Si se establece a true, el reconocimiento seguirá escuchando de forma infinita entregando un solo resultado largo.\n- [`SpeechRecognition.lang`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/lang): Determina el idioma en del reconociminiento. Deberías establecerlo para unos mejores resultados.\n- [`SpeechRecognition.interimResults`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/interimResults): Mientras se está procesando el audio, se producen resultados temporales. Gracias a esta propiedad, determinamos si el reconocimiento debe devolver resultados provisionales o no.\n- [`SpeechRecognition.maxAlternatives`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition/maxAlternatives): Determina el número de posibles coincidencias que se deben devolver. Es útil si un resultado no está claro y desea mostrar alternativas para que el usuario elija la correcta.\n\n```javascript\nrecognition.continuous = false;\nrecognition.lang = 'es-ES';\nrecognition.interimResults = false;\nrecognition.maxAlternatives = 1;\n```\n\n## Comenzando a escuchar con el reconocimiento de voz\n\nYa tenemos el reconocimiento de voz preparado, configurado y personalizado; solo falta indicarle que empiece a escuchar y eso lo conseguimos con el siguiente código\n\n```javascript\nrecognition.start();\n```\n\nSin embargo, ten en cuenta que antes de empezar a escuchar realmente, y por política de privacidad, el navegador preguntará al usuario si autoriza a que la web use el micrófono. En cualquier caso, es un proceso transparente para el código: si el usuario no concede permiso se tirará una excepción que deberíamos manejar.\n\n## Recogiendo resultados\n\nExisten un montón de eventos asociados a la API de SpeechRecognition, pero sin duda, los más útiles son `onspeechend` y `onresult`. El primero se detona cuando se ha reconocido un texto y se ha finalizado de hablar. El segundo se detona cuando el reconocimiento de voz tiene algún resultado.\n\n```javascript\nrecognition.onspeechend = function() {\n recognition.stop();\n}\n\nrecognition.onresult = function(event) {\n var color = event.results[0][0].transcript;\n var confianza = event.results[0][0].confidence;\n console.log('Estoy seguro al ' + confianza + ' % que dijo ' + color)\n}\n```\n\nGeneralmente, el reconocimiento de voz devuelve lo que cree que ha escuchado (transcript) y el porcentaje de confiaza; o dicho de otra forma, cuánto de seguro está de lo que ha escuchado (confidence). En este caso y como especificamos colores en la gramática, estamos seguros de que el reconocimiento de voz devolverá un color (o un error si no entendió nada, pero eso lo veremos después).\n\n## Otros eventos y manejando errores del reconocimiento\n\nExisten otros tres eventos que requieren una mención especial: `onstart`, `onnomatch` y `onerror`. Vamos a verlos en detenimiento utilizando el siguiente código (pero como te imaginas, el primero es cuando comienza el reconocimiento, el segundo cuando no hay coincidencia y el tercero es cuando hay un error)\n\n```javascript\nrecognition.onstart = function() {\n // Se activó el reconocimiento. Añadir algún elemento a la UI\n // para que el usuario sepa que le está oyendo.\n}\n\nrecognition.onnomatch = function(event) {\n // No hay coincidencia entre lo escuchado y la gramática\n // Deberá hablar más claro o deberemos añadir palabras a la gramática.\n}\n\nrecognition.onerror = function(event) {\n if(event.error == 'no-speech') {\n // No he escuchado nada. Mejora la calidad de la grabación.\n } else {\n // Error gen\n }\n}\n```\n\n## Conclusión\n\nSi bien el uso de este sistema suele ofrecer buenos resultados, depende mucho del navegador del usuario, pues se trata de una API no muy extendida. Por lo general, se suelen utilizar servicios en la nube que tratan de identificar lo que ha dicho el usuario. Pero si la compatibilidad no es problema o no quieres invertir mucho dinero, esta API merece una mención especial sobre todo para la accesibilidad y para la comodidad del usuario.\n\nHace años, esta API habría sido un sueño inalcanzable. **Hemos convertido voz humana a texto utilizando tan solo Javascript y un navegador**, además de hacerlo sin pagar. Pero sí, el futuro ha llegado. Espero que te haya picado la curiosidad y que te animes a probar esta fantástica feature en tu próximo proyecto.\n\n¡Qué tengas un feliz coding!","mainEntityOfPage":"https://www.juannicolas.eu/convertir-voz-a-texto-con-javascript/","datePublished":"2022-09-20T14:00:00.000Z","dateModified":"2022-09-20T14:00:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":6},{"@context":"https://schema.org","@type":"Article","headline":"Entendiendo Bind, Apply y Call en Javascript","description":"Las funciones bind, call y apply en Javascript son complicadas de entender, así como this y el contexto. Te cuento como funciona con ejemplos.","url":"https://www.juannicolas.eu/bind-call-y-apply-en-js/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/this.JEjf-e3w.jpg","width":1612,"height":1074},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"\n## This en Javascript\n\nAntes de ver qué hacen estas funciones es necesario repasar un poco de teoría. Es raro que Javascript sea el primer lenguaje de programación que aprendes; generalmente habrás aprendido Java, Python, PHP o C#. En la mayoría de ellos hay una regla muy básica que habrás aprendido en Programación Orientada a Objetos: **this** hace referencia a la instancia de la clase que estás codificando.\n\n```java\npublic void setNombre(String x) {\n this.nombre = x;\n this.myFunc();\n}\n```\n\nEl código anterior es claro: dentro de esta instancia de clase, establece la variable `nombre` a lo que valga el argumento `x` y después llama al método (función dentro de la clase) llamado `myFunc`. ¡ Genial ! Lo tenemos.\n\nPero, un segundo. ¿Qué hace **this** en un lenguaje que no es orientado a objetos 🤔? La respuesta es simple pero un poco compleja: this en Javascript hace referencia al **contexto**. La misma respuesta pero un poco más refinada es que this depende de cómo se llame a dicha función; o dicho de otra forma, depende del contexto.\n\n## El contexto en Javascript\n\nUna forma más fácil de entender el contexto en Javascript es usando la siguiente frase: \"**this** es el objeto propietario de la función (el cual la está llamando)\". Puede que a priori no tenga mucho sentido, pero vamos a ver varios ejemplos para entender mejor qué es eso del contexto en Javascript y para entender un poco después, **cómo funciona bind y sus hermanas apply y call**.\n\nVamos a ver el ejemplo más simple: cuando el contexto es el objeto global (el objeto global será `window` en el navegador, o `global` en Node.js). El `this`de la función `myFunction` representa al objeto global porque ha sido el objeto global quién ha llamado a dicha función (no se ha llamado desde una estructura). \n\n```javascript\nfunction myFunction() {\n this.a = 2;\n}\n\nmyFunction();\nconsole.log(a); // Imprime 2 \n```\n\n> Nota: Este ejemplo no funcionaría si usamos el modo estricto. Pero tómatelo como un ejemplo de aprendizaje\n\nHasta aquí bien ¿verdad? Pero vamos a complicarlo un poco más. ¿Qué ocurre si definimos la función dentro de un objeto ? Imagina (o más bien prueba) el siguiente código.\n\n```javascript\nconst persona = {\n name: 'Pepín',\n surname: 'Fernandez',\n fullname: function() {\n console.log(this) // OJO !!\n return this.name + ' ' + this.surname\n }\n}\n\n\npersona.fullname()\n\n// El console.log imprimirá lo siguiente:\n// { name: 'Pepín', surname: 'Fernandez', fullname: [Function: fullname] }\n```\n\nFíjate en que el console.log está imprimiendo el this y ese `this` vale lo mismo que el objeto `persona`. ¿Por qué? Porque el propietario de la función `fullname` es el objeto `persona`. \n\nEste ejemplo es un poco más retorcido, pero para que te des cuenta de una cosa que suele pasar (y en algún momento te pasará según vayas trabajando en Javascript). Está literalmente sacado de MDN. Si te fijas hay dos llamadas a la función getX y, realmente, **las dos llamadas son a la misma función: lo único que cambia es el contexto**. En la primera llamada (la identificada como A), la función devuelve 81 porque estás usando el contexto donde this.x vale 81 (el `this` en getX es el propio objeto). Sin embargo, cuando copias la función en la llamada B cambia el contexto (la función sigue funcionando exactamente igual, pero cambias el contexto, pues this.x = 9 en el contexto global).\n\n```javascript\nthis.x = 9;\nvar module = {\n x: 81,\n getX: function() { return this.x; }\n};\n\nmodule.getX(); // Llamada A: devuelve 81\n\nvar getX = module.getX;\ngetX(); // Llamaba B: devuelve 9, \n //porque en este caso, \"this\" apunta al objeto global\n```\n\n> Las funciones se pueden copiar sin problemas en Javascript porque es un tipo de variable: function. El problema viene cuando copias una función pero cambia el contexto. \n\n> Ten en cuenta que las funciones de flecha gorda ( => ) no crean un nuevo contexto.\n\nMás o menos vamos entendiendo el contexto en Javascript: el objeto propietario de la función a la que se ha llamado (y si no se especifica, el objeto global). Pero, ¿qué ocurre si quiero cambiar el contexto de una función? ¿Hay algúna forma de hacer eso? Exacto, con **las funciones bind, call y apply**.\n\n## La función Bind en Javascript\n\n`fun.bind(thisArg[, arg1[, arg2[, ...]]])`\n\nLa función **bind()** de Javascript básicamente hace una copia de la función original y la deja igual salvo porque cambia el contexto al que apunta `this`. Está disponible en todas las variables que representan funciones. \n\nRescatemos el anterior ejemplo. Declaramos el objeto que contiene a la función y posteriormente usamos **bind()** sobre el método getX del objeto module. Ese bind le está diciendo a Javascript: \"Oye! Copia la función pero `this` va a valer `{x: 5}`\".\n\n```javascript\nvar module = {\n x: 81,\n getX: function() { return this.x; }\n};\n\n\nconst getX = module.getX.bind({x: 5});\ngetX(); //Devuelve 5\n```\n\n> Fíjate en que no se usa getX().bind(). Eso significaría que ejecutase getX y sobre el resultado que devuelva hacemos el bind.\n\nAdicionalmente, es posible pasarle argumentos a una función clonada con bind. En el siguiente código vemos como `saludo` es una copia de `obj.saludaConEdad` pero cambiando el contexto para que el nombre sea el de fulanito. Sin embargo, saludo2 es una copia de `obj.saludaConEdad` donde también se establece que como primer argumento (edad) siempre se llamará con 10. Esto no es tan útil en **la función bind** como en las demás, pero está disponible para que la copia incluya ya el argumento o argumentos. Como **la función bind** copia la función, generalmente solo se le establece el primer parámetro (el que define el this). El resto de parámetros se establecen cuando se llama a la copia.\n\n```javascript\nvar obj = {\n nombre: 'Pepín',\n saludaConEdad: function(edad) {\n return 'Soy ' + this.nombre + ' y tengo ' + edad + ' años';\n }\n}\n\n\nobj.saludaConEdad(6); //'Soy Pepín y tengo 6 años'\nconst saludo = obj.saludaConEdad.bind({nombre: 'fulanito'})\nsaludo(7); //'Soy fulanito y tengo 7 años'\nconst saludo2 = obj.saludaConEdad.bind({nombre: 'fulanito'}, 10);\nsaludo2(); //'Soy fulanito y tengo 10 años'\n```\n\n## La función Call en Javascript\n\n`function.call(thisArg[, arg1[, arg2[, ...]]])`\n\n**La función call** es parecida a bind salvo por una cosa: call no hace una copia de la función. Es decir, mientras que **la función bind** hace una copia de la función que vamos a copiar (que se puede guardar en una variable para usarla posteriormente), call ejecuta directamente la función (pero antes establece el `this` personalizado). Cuando se usa **call()**, sí se suelen definir el segundo argumento y sucesivos, porque no hay otra forma de indicarlos a la hora de llamar a la función que estamos clonando.\n\n```javascript\nobj.saludaConEdad(6); //'Soy Pepín y tengo 6 años'\n\n// Soy fulanito y tengo undefined años.\n// Como no se ha pasado primer argumento, la función no lo puede leer\nobj.saludaConEdad.call({nombre: 'fulanito'})\n\n// Soy fulanito y tengo 66 años\nobj.saludaConEdad.bind({nombre: 'fulanito'}, 66);\n```\n\n## La función Apply en Javascript\n\n`fun.apply(thisArg[, argsArray])`\n\n**La función Apply es exactamente igual que Call** salvo por un pequeño detalle: los argumentos de la función original se pasan por array (no como una lista de argumentos). Esto es útil cuando tenemos los argumentos guardados en un arreglo y no tenemos que preocuparnos por ellos. Quitando esa diferencia, técnicamente es igual que call.\n\n```javascript\nobj.saludaConEdad(6); //'Soy Pepín y tengo 6 años'\n\n// Soy fulanito y tengo undefined años.\n// Como no se ha pasado array, la función no lo puede leer\nobj.saludaConEdad.apply({nombre: 'fulanito'})\n\n// Soy fulanito y tengo 66 años\nobj.saludaConEdad.apply({nombre: 'fulanito'}, [66]);\n```\n\n## Conclusión\n\n**Las funciones bind(), call() y apply() establecen un contexto**, reemplazando el valor de this dentro de la función que están llamando (o copiando). Pero hay algunas sutiles diferencias:\n\n - Bind copia la función\n - Call ejecuta directamente la función sin copiarla\n - Apply es como Call, pero entrega a la función original los parámetros en forma de array.\n\nSin duda, el contexto y el uso de la palabra reservada this es una de las cosas más complicadas de aprender de Javascript. Sin embargo, espero que después de estos apuntes te haya quedado un poco más claro cómo usarlo correctamente y evites pasarte horas y horas depurando funciones que \"no encuentran las variables\". Claro que no las encuentran: no estás usando el contexto adecuado. \n\nQue tengas un feliz coding 😀\n","mainEntityOfPage":"https://www.juannicolas.eu/bind-call-y-apply-en-js/","datePublished":"2022-09-01T20:00:00.000Z","dateModified":"2022-09-01T20:00:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":7},{"@context":"https://schema.org","@type":"Article","headline":"Componer tuberías (pipe) en Javascript","description":"Las tuberías (del inglés pipe) te ayudan a combinar funciones de una forma más elegante y ayudando a crear un código legible y profesional.","url":"https://www.juannicolas.eu/tuberias-pipe-en-javascript/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/pipes-magda-ehlers-2569842.DUaDtLPf.jpg","width":1612,"height":1075},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"El concepto de **tubería o pipe** es básicamente conectar múltiples funciones (o programas) de tal forma que la salida del primer comando es la entrada del segundo. La salida del segundo es la entrada del tercero, y así sucesivamente. Este concepto puede ayudarte a crear un código más mantenible y legible.\n\n## Qué es una tubería y porqué es útil\nLas tuberías son tremendamente útiles en programación desde hace mucho tiempo, y si no que se lo digan a los usuarios de Linux. Y con el auge de la programación funcional, esta tendencia se está llevando a cada vez más lenguajes porque simplifican mucho el código, tanto para escribirlo como para leerlo. Para entendernos, el concepto de **tubería en programación** es como las tuberías que tienes en tu casa: conectar el final de un tramo al principio del otro tantas veces como sea necesario. Sin embargo, en programación lo que hacemos es unir el resultado que devuelve un método o función a la entrada de otra. De esta forma, cuando necesitamos aplicar una serie de procesos (cálculos o transformaciones por ejemplo) creamos una tubería que se encargará de que el dato original vaya pasando por la primera función; el resultado de la primera función será entregado como entrada de la segunda; la salida de la segunda se entregará a la tercera como entrada y así todas las veces que necesitemos. Cuando no haya más procesos conectados a dicha tubería, el resultado de la última función nos será devuelto y podremos dar el proceso (o suma de procesos mejor dicho) como terminado.\n\n## Empecemos con un ejemplo sin tubería\n\nLo vamos a ver mucho más claro con un ejemplo práctico que es posible que te haya pasado. Imagina que tenemos un dato, en este caso, un string que representa el nombre de usuario. El nombre del usuario no sabemos como nos llega (mayúsculas, minúsculas, signos) y por ello, queremos hacer una función que \"limpie\" y deje bonito el nombre de usuario para pintarlo en nuestra aplicación. Este proceso de limpieza se compone de los siguientes pasos:\n- Poner la primera letra en mayúsculas y el resto en minúsculas (capitalizar),\n- Eliminar los espacios en blanco,\n- Como máximo, solo se permiten 10 caracteres, así que cortamos a partir del 11 (truncar).\n\nLas tres funciones anteriores las podemos declarar de cualquier forma, por ejemplo:\n```javascript\nconst capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();\nconst removeSpaces = (string) => string.replace(/\\s/g, '');\nconst truncate = (string) => string.substring(0,10);\n```\n\nHasta aquí nada nuevo, ¿no? Qué es lo que haríamos para devolver el nombre de usuario de forma \"bonita\", pues a primera vista crear una función tal que así:\n```javascript\nconst coolUsername = (name) => truncate(removeSpaces(capitalize(name)));\n```\nComo sabemos, se ejecuta primero el comando que está más dentro de las funciones. La salida de \"capitalize\" se pasa como entrada de \"removeSpaces\". Y a su vez la salida de \"removeSpaces\" se pasa como entrada de \"truncate\". Así tenemos nuestro nombre de usuario de la forma que nos han pedido. Y realmente no está mal, pero...\n\nPara a pensar una cosa 🤔, ¿si el proceso de transformar el nombre de usuario en vez de tres funciones requiriese 5, 7, 9, 12 o 15 funciones? Encadenaríamos 15 funciones tal que \"a(b(c(d(e(....)))))\". El código sería una línea de muchos caracteres; los paréntesis de cierre serían imposibles de contar, habría que leerlo de derecha a izquierda... ¿No te empieza a parecer un código un poco feo?\n\n## Cómo usar tuberías o pipes en Javascript\nSi has seguido leyendo es que estás de acuerdo conmigo en que debe haber una forma más cool 😎 de escribir el código. Y la respuesta es \"Sí, con una tubería\". Quédate con la siguiente línea. Luego la repasamos paso a paso:\n```javascript\nconst pipe = (...fns) => (arguments) => fns.reduce((value, fn) => fn(value), arguments);\n```\nComo sabes, en Javascript una función puede devolver otra función. Así que la función anterior \"pipe\" puede explicarse como una función que va ejecutando funciones (declaradas de izquierda a derecha) de tal forma que la salida de la anterior será la entrada de la siguiente mediante el método \"reduce\". Al final, la función \"pipe\" devolverá un valor \"simple\" que es el que nos interesa.\n\nCon esto en mente, qué te parece redefinir nuestra función para limpiar el nombre de usuario de la siguiente manera:\n```javascript\nconst coolUsername = (nombreUsuario) =>\n pipe(\n capitalize,\n removeSpaces,\n truncate,\n )(nombreUsuario);\n```\nMás simple, se lee más rápido, se modifica más rápido si hiciese falta cambiar algún paso. Bueno, bonito y barato.\nSi te fijas, la parte donde se especifican las funciones que quieres ejecutar (\"capitalize, removeSpaces, truncate\") es el argumento primero de pipe llamado `fns` y \"nombreUsuario\" es el segundo argumento de pipe llamado `arguments` .\n\nLa ventaja de usar la función `pipe` que hemos definido es que puedes reutilizarla las veces que necesites para crear todas las tuberías que necesites: calcular un precio aplicando tasas o descuentos, procesar un array de alguna forma, lo que necesites. Además, por cómo es Javascript, puedes definir las funciones dentro de la propia tubería:\n```javascript\nconst calcularPrecio = (precio) =>\n pipe(\n sumarIVA,\n sumarOtrosImpuestos,\n precio => precio * 0.90, //Aplicar descuento custom.\n )(nombreUsuario);\n```\n\nEspero haberte ayudado a aprender un mecanismo interesante a la hora de crear funciones que se basan en aplicar de forma sistemática otras funciones. Usar tuberías es una forma de crear un código bueno, profesional y bastante limpio y legible.\n\n¡Que tengas un feliz coding!\n","mainEntityOfPage":"https://www.juannicolas.eu/tuberias-pipe-en-javascript/","datePublished":"2022-05-30T20:04:00.000Z","dateModified":"2022-05-30T20:04:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":8},{"@context":"https://schema.org","@type":"Article","headline":"El método mágico invoke en PHP. La función aliada en el clean code","url":"https://www.juannicolas.eu/el-metodo-invoke-en-php/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/elephant.BJ1GGRwO.jpg","width":1602,"height":1074},"publisher":{"@type":"Organization","name":"Juan Nicolás","url":"https://www.juannicolas.eu/","logo":{"@type":"ImageObject","url":"https://www.juannicolas.eu/favicon/android-icon-192x192.png","width":192,"height":192}},"articleBody":"\nEl método __invoke forma parte de los métodos mágicos y de PHP desde la versión 5.3, hace un par de años ya. Sin embargo, aunque otros métodos mágicos (como toString) están muy presentes, __invoke no se suele ver mucho en código ajeno. Supongo que por desconocimiento general 🤔. Vamos a intentar que eso no ocurra; permíteme presentarte algunos casos extremadamente útiles para el uso del método __invoke y porqué es tremendamente útil cuando quieres hacer código limpio (clean code, que si no sabes lo que es, básicamente es entender un código de un vistazo sin pensar \"WTF?...\")\n\n## Qué es __invoke en PHP\n\n**El método inkove es invocado cada vez que se llama a un objeto como si se tratase de una función**. Es decir, en otras palabras, este método se llama cada vez que escribimos lo siguiente...\n\n```php\nmétodo();\n\nDe esta forma, sólo hay que buscar un buen nombre para la clase, que permita definir bien qué hace. Ya no es necesario buscar también un nombre para la función (que al final va a ser redundante).\n\n## Usar una función o usar __invoke\n\nPartamos de la base que, según SOLID y mas concretamente la S, cada unidad de codigo tiene una sola responsabilidad. Esto significa que una clase o función debería resolver una sola tarea. Si por ejemplo tenemos que calcular un dato y luego en base a ese dato, enviar por mail, realmente hay tres clases o funciones asociadas: la que obtiene el dato, la que lo compara y la que envía el mail.\n\nImagina que necesitamos realizar un proceso que se repite a lo largo de nuestro código, un proceso más o menos simple como obtener un valor en base a otro. Pongamos por ejemplo determinar si un usuario es residente en Madrid o no a partir de un código postal (que por sencillez, se guarda como string).\n\nVolviendo a nuestro caso, podríamos tener una función o clousure tal que\n\n```php\nfunction viveEnMadrid(User $user) {\n return substr($user->codigoPostal, 0, 2) === \"28\";\n}\n```\n\nY esto en sí no está mal; es un ejemplo práctico bueno. Dado que el código anterior no tiene dependencias y a partir del objeto pasado como parámetro podemos calcular el valor deseado. Simplemente funciona ¯\\_(ツ)_/¯.\n\nSin embargo, qué pasaría si a lo largo del proceso, necesitásemos alguna dependencia. Pongamos que ahora necesitamos una función para saber si un usuario es mayor de edad a partir de los años que tiene. Como (insisto) intentamos ser buenos programadores y como es posible que nuestra aplicación se use en varios países, no deberíamos usar un literal y realizar una comparación con 18 años (mayoría legal en España). Por ende, necesitamos un comparador. De esta forma, le pasaremos la edad a nuestro comparador y él (en base a la ubicación del usuario por ejemplo) calcule si es mayor de edad.\n\nPodría quedar algo así.\n\n```php\nfunction esMayorDeEdad(ValidadorEdadLegal $validador, User $user): bool {\n return $validador->validarEdadLegal($user->edad);\n}\n```\n\nNo acaba de ser bonito. Si tenemos que instanciar la dependencia de nuestra función o clase fuera de la propia entidad, se rompe la encapsulación y abstracción. Es el usuario quien tiene que instanciar el comparador fuera de la propia unidad de código y estamos delegando en él la responsabilidad.\n\nEsto ocurre porque las funciones o métodos de PHP no admiten la inyección de dependencias externas como tal. Por ello, habría que recurrir a una clase (o a un clousure, que junto con el fuctor, si permitirá una inyección externa de dependencia pero rompería nuestra abstracción y encapsulación).\n\nPor lo tanto, en funciones no podemos usar el método __invoke ni nada que se le parezca, pues es un método mágico aplicado solo a objetos; instancias de clases. Si bien nuestro código es sencillo y no tiene dependencias, podemos usar una función. Si por el contrario sí que tiene dependencias, no deberíamos usar funciones.\n\n## Usando una clase con __invoke\n\nCuando una función no puede hacer algo, siempre podemos recurrir a una clase.\nCreamos una clase que defina un constructor en el que se inyecte nuestro validador. Dentro de esta clase, que se llama ValidadorEdadLegal, una función que reciba el objeto $user. Esta función pública se llamaría... ¿prácticamente igual que la clase? ¿validarEdadLegal()?. Y para invocarla, habría que escribir \n\n```php\nfunction ... (ValidadorEdadLegal $validador) {\n $validador->validarEdadLegal(x)\n}\n```\n\nEstamos escribiendo dos veces el mismo concepto, uno para la clase y otro para el método. Cuando se elije un buen nombre para la clase, es prácticamente autoexplicativo. Esto quedaría más simple y más legible con el método invoke.\n\n```php\nfunction .... (ValidadorEdadLegal $validador) {\n $validador(x)\n}\n```\n\nAnalicemos las ventajas del código anterior.\n\n- Es simple de leer. Sin pararnos a mirar en cómo funciona la clase por dentro, sabemos que valida que algo o alguien tenga la edad legal.\n- Sumado a esto, no tenemos que ver el mapa de la clase para ver qué método público podemos usar. Siempre usamos invoke.\n- Si te acostumbras a usar invoke, cada vez te será más difícil declarar varios métodos por clase (lo que al final da a lugar a las terribles clases XManager o XServices, que aglutinan 2000 funciones).\n- Junto con Solid y CleanCode, los test te quedarán también más legibles.\n\n## ¿ Y cuándo lo tengo que usar con $this ?\n\n```php\nclass X() {\n public function __construct(ClaseConInvoke $x) {\n $this->$x = $x;\n }\n\n public function myMethod() {\n //¿ Cómo llamo al __invoke de ClaseConInvoke x ?\n $this->x(); // ❌\n\n // Mejor así ...\n $this->x->__invoke(); // ✅\n\n }\n}\n```\n\nLas primeras veces que recurras al método mágico __invoke de una clase A y tengas que usarlo en otra clase B junto con this (porque la has inyectado como dependencia), te surgirá la duda: ¿como la invoco? Normalmente usamos $this->algo->método(), pero ¿con invoke funciona igual?\n\nCuando inyectamos la clase con __invoke definido, podemos usarlo como $objeto(), pero cuando lo necesitamos llamar con this, no funciona exactamente igual. \n\nSi usamos $this->x(), PHP intentará buscar un método llamado x en la propia clase y no lo encontrará. De alguna forma hay que hacerle saber que no estamos buscando un método en la propia clase sino que estamos llamando al método mágico __invoke de otra. Esto lo conseguimos con:\n\n```php\n$this->x->__invoke($params);\n```\n\n## Conclusión. Invoke está para ayudar\n\nComo decía al principio de este artículo, no he visto de forma tan cotidiana, como __constrcut o __toString, el método invoke en el código de otros programadores. Es posible que parte de ellos no conocieran este útil método. Pero desde luego, desde que lo conozco, lo intento usar todo lo posible por las ventajas que he comentado a lo largo de este documento.\n\nEspero haberte convencido de que el método mágico invoke está desde PHP 5.3 para hacernos la vida más fácil y dar más legibilidad al código. Al fin y al cabo, con pensar un buen nombre para la clase, basta. Piensa que en informática solo hay dos cosas realmente difíciles: pensar un buen nombre para algo e invalidar la caché: para que vamos a pensar dos nombres buenos pudiendo pensar solo uno.\n","mainEntityOfPage":"https://www.juannicolas.eu/el-metodo-invoke-en-php/","datePublished":"2022-02-04T16:04:00.000Z","dateModified":"2022-02-04T16:04:00.000Z","author":{"@type":"Person","name":"Juan Nicolás","url":"https://www.juannicolas.eu/author/juan/","image":{"@type":"ImageObject","url":"https://www.juannicolas.eu/_astro/juan.BBZ4iYXg.jpg","width":460,"height":460}},"position":9}]}
Desarrollador full stack, javascriptero, phpista y bebedor profesional de café. Desarrollador Senior en @estrategiastv. Escribo tanto como puedo en mi blog
En este artículo de la colección de Recursos para tu web te dejo un par de herramientas para crear o usar directamente animaciones CSS. Listas para copiar y pegar en tu web o para aprender sobre ellas.
La caché de HTTP nos permite servir una página web mucho más rápido, evitando latencias y retrasos en el cliente y reduciendo el uso de nuestros servidores.
MailHog permite a los desarrolladores hacer pruebas de envío de correo electrónico a través de un SMTP falso y ver cómo quedan los emails a través de su sencilla interfaz web.
Symfony permite utilizar librerías de terceros si las definimos en el fichero services.yml, incluso con parámetros. Hoy vamos a conocer el componente Dependency Injection para conseguir usarlas en nuestro proyecto con ejemplos.
La accesibilidad es uno de los aspectos más importantes cuando estamos diseñando una APP. Ahora puedes aportar más valor al usuario convirtiendo cadenas de texto a voz que se escucha por el altavoz con un par de líneas de Javascript.
En este documento vamos a conocer la otra cara de speechSynthesis, SpeechRecognition, que nos va a permitir convertir la voz de nuestros visitantes en texto a la hora de usar formularios, evitando que el usuario tenga que escribir.
Una de las cosas más complicadas de Javascript, sobretodo para los usuarios que acaban de llegar, es el uso de this y, por ende, de las funciones bind, apply y call. Te intento explicar qué hacen estas funciones y cómo usarlas correctamente con ejemplos.
El concepto de tubería (del inglés pipe) es básicamente conectar múltiples procesos de tal forma que la salida del primero es la entrada del segundo; la salida del segundo es la entrada del tercero, etc. Este concepto puede ayudarte a crear un código más mantenible y legible. Te lo cuento con ejemplos.
Los métodos mágicos de PHP hacen la vida del programador más fácil. Hoy te voy a explicar porqué el método mágico __invoke es tu aliado en multitud de casos y te va a permitir escribir un código más legible y mantenible. Y te lo demuestro con ejemplos.