~/bloc/dev/notas/2 — Lecciones aprendidas

Manu Pijierro
5 min readOct 29, 2017

--

Antes de comenzar he de decir que he cambiado el nombre de esta serie de artículos con resúmenes semanales sobre lo más relevante que me voy encontrando en el día a día. Pensando en que estos artículos serían más bien un bloc de notas, he pasado del poco o nada original ‘Lecciones aprendidas #1’ al poco, o mucho más, freak~/bloc/dev/notas’, ojo al detalle ‘dev’ (development :p)

Bueno, vamos al lío.

Model Factories de LARAVEL para testing.

Las model factories son una herramienta que tiene Laravel para generar registros de prueba en una base de datos. Se suelen utilizar para realizar cargas iniciales de registros en una base de datos. Las había utilizado en alguna ocasión anterior de manera muy puntual pero no ha sido hasta esta semana cuando he exprimido todo su potencial.

En mi caso las he utilizado para hacer testing funcional contra la base de datos, de manera que para un test creo siempre los mismos datos y tiro los tests contra ellos. Nada del otro mundo.

El funcionamiento de estas model factories es muy sencillo. Para crear una factoría basta crear un archivo en la ruta “/database/factories” y añadir el siguiente código (ejemplo de la documentación oficial que recomiendo leer):

$factory->define(App\User::class, function (Faker\Generator $faker) {
static $password;

return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
})

Es bastante sencillo seguir aún no sabiendo realmente para que sirve. Hay varias cosas relevantes. La primera es indicar el modelo sobre el cual se va a crear la factoría, en este caso App\User, es decir, vamos a crear usuarios de prueba y los vamos a guardar en la base de datos. Lo siguiente es el uso del componente externo Faker, una librería externa para generar datos de prueba para muchísimos conceptos (merece la pena echarle un vistazo), no es obligatorio su uso pero si ahorra mucho tiempo. Otra cosa que me llamó la atención del código es ver definido $password como variable estática:

static $password;

Tiene su explicación. En este ejemplo estamos creando un usuario y generando su contraseña encriptada con el algoritmo Bcrypt. Generar una encriptación con dicho algoritmo conlleva un uso de CPU nada despreciable. Tenemos que pensar que las model factories suelen utilizarse para crear decenas, centenas o miles de registros de pruebas. Si tenemos miles de registros y para cada uno calculamos el bcrypt de su contraseña podríamos consumir muchos recursos. Definiendo $password de manera estática global y haciendo uso del posterior operador en su asignación al campo password se consigue que realicemos el cálculo una sola vez y no miles de veces, una por cada registro. Eso si, la contraseña siempre es ‘secret’.

Método __invoke

Diría que este ha sido el descubrimiento de la semana. __invoke es un método mágico de PHP que es llamado cuando intentamos llamar a un objeto como si fuera una función. Dicho así puede que no diga mucho, de hecho, yo mismo lo había leído muchas veces pero nunca me llamó la atención…hasta esta semana.

En el curso sobre CQRS que estoy haciendo en la plataforma Codely.tv he visto como hacían uso de __invoke en todas las clases. Dejé una pregunta en el foro preguntando por el motivo. Un compañero de ‘estudios’ me dejó una respuesta bastante reveladora apoyándose un este artículo “Goodbye controllers, hello request handlers”. En dicho artículo, resumiéndolo mucho, se habla sobre como defender el Principio de Responsabilidad Única haciendo uso de __invoke para indicar que lo implementado ahí va a ser la única responsabilidad que va a tener la clase y de esta manera podríamos evitar la tentación de definir posteriormente otros métodos públicos que pudieran añadir más responsabilidades y violar dicho principio. Lo hace con los controladores como ejemplo, a los que hace llamar ‘request handlers’. Ya sabemos que si mantenemos el principio de responsabilidad única sobre una clase vamos a hacerla más fácil de mantener, refactorizar y testear.

Desde ya voy a comenzar a utilizar el método __invoke de PHP para mis desarrollos y sobre todo voy a intentar cambiar el concepto de controlador que yo tenía por los request handlers que propone el artículo. Creo que va a ser una mejora.

Evitar error de desborde de memoria al descargar archivo que necesita miles de consultas a la base de datos.

Esta semana he tenido que realizar una tarea para descargar un archivo .csv con un contenido potencial de miles de registros. Para generar el contenido tenía que hacer consultas a una tabla de miles de registros y sus correspondientes joins para traerme datos relacionados. Esto provocaba el temido error de memoria “Allowed memory size”. Una solución podría ser aumentar en tiempo de ejecución la cantidad de memoria disponible para el script pero esto no es muy escalable que digamos.

Para solucionar esto utilicé la función ‘chunk’ de Eloquent (el orm de Laravel). Ante una consulta que te puede devolver miles de registros, lo que hace chunk es ir procesándolas por grupos de un número definido liberando toda la memoria posible entre proceso y proceso de cada grupo.

Solo con lo anterior no era suficiente, así que seguí buscando y a decir verdad, la ‘magia’ de la descarga del archivo de forma correcta me llegó de la mano de este artículo de Barry vd Heuvel (un crack). En este artículo explica como utilizar el componente StreamResponse de Symfony.

Por poner un ejemplo de como queda el código de la clase que he utilizado me he creado por primera vez un Gist en Github, en el que he dejado una clase de ejemplo que resuelve esto: Ver código aquí.

Actualización: Después de probar, parece ser que para que el csv sea interpretado correctamente por Excel es necesario indicar el UTF8- BOM inmediatamente después de abrir el archivo, tal y como indica el artículo de Barry vd Heuvel, es decir, añadir lo siguiente:

fwrite($this->fileHandle, $bom = (chr(0xEF).chr(0xBB).chr(0xBF)));

Además, añadir el caracter ‘;’ para que separara cada columna de una fila por ese separador, de manera que cada escritura en una fila me quedaba así:

fputcsv($this->fileHandle, $data, ';');

Obtener registros aleatorios con Eloquent

Más de una vez he perdido unos minutos por no recordar la función que trae Eloquent de forma nativa para obtener registros de forma aleatoria en una consulta a la base de datos. Así que la dejo por escrito como castigo:

inRandomOrder()

Ea, fácil ¿no? pues a ver si para la próxima me acuerdo. La documentación está aquí.

Solución al error en migración de Laravel. Enum falla en MySql.

Este error lo tenía únicamente cuando lanzaba una migración contra MySql y no contra, por ejemplo, PostgreSQL. El error exacto era el siguiente:

[Doctrine\DBAL\DBALException]
Unknown database type enum requested, Doctrine\DBAL\Platforms\MySQL57Platform may not support it.

Como siempre, Stackoverflow tiene la solución y algún alma caritativa puso la solución en su momento. Bastó con poner justamente antes de lanzar la migración que daba el error lo siguiente:

Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping(‘enum’, ‘string’);

Esto solucionó el error.

Todo esto ha sido lo más relevante esta semana. Tenía más cosas apuntadas pero tampoco era cuestión de extenderme más. Ya las anotaré. Como siempre, cualquier comentario o sugerencia será bienvenido.

Chimpún.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response