14 de agosto de 2016

Loguear consultas de Doctrine

Tan sencillo como:


$this->em->getConnection()
                  ->getConfiguration()
                  ->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());



23 de octubre de 2013

Configurar git para usar https:// en vez de git://

Muy útil para cuando estamos detrás de un proxy o firewall:

git config --global url."https://".insteadOf git://

Visto en: http://stackoverflow.com/questions/16298986/unable-to-connect-to-github-com-for-cloning

11 de febrero de 2013

Symfony: Limitaciones de tipo de campo entity y prefetch de las entidades asociadas

Cualquiera que haya trabajado con Symfony y se haya visto en la necesidad de añadir un campo en un formulario que haga referencia a una entidad se habrá visto en la situación de que la única opción posible es usar el tipo 'entity'. Este tipo tienes dos grandes problemas:

  • Solo permite mostrar el campo mediante checkboxes, radiobuttons o selects, donde todas las opciones posibles se muestran para que el usuario escoja alguna en concreto. Esto dificulta mucho la interacción en casos como el de recuperar una entidad vía AJAX y vincularla al formulario.
  • La eficiencia de lo anterior es nula, porque obliga a recuperar todos los valores posibles de la entidad a la hora de mostrar el campo. Imagina que necesitas un campo usuario y tienes más de 5000 en tu base de datos. Sería inviable usar el tipo entity.
Este es un tema que lleva dando vueltas por los repositorios de Symfony desde hace 2 años. Una solución es la propuesta por el usuario Gregwar en este PR https://github.com/symfony/symfony/pull/1951/, que añade el tipo 'entity_id'. Del ejemplo de la misma página:
$builder->add('city', 'entity_id', array(
            'query_builder' => function($repository, $id) {
                return $repository->createQueryBuilder('c')
                    ->where('c.id = :id AND c.available = 1')
                    ->setParameter('id', $id);
            },  
            'class' => 'Gregwar\TestBundle\Entity\City',
            'required' => false,
            'hidden' => true
        )); 
Con este tipo es posible tener un campo de texto o un campo oculto en el que poder insertar automáticamente el ID de una entidad, olvidándonos de selects, radiobuttons o checkboxes, y sin hacer prefetchs de las entidades. Yo llevo usándolo desde hace 8 meses y me ha resuelto bastantes problemas.

Aunque hay bastantes solicitudes para hacer oficial este PR, parece que finalmente la solución oficial va a ser la de añadir un atributo widget al tipo entity, como se detalla aquí https://github.com/symfony/symfony/issues/6602 :

I think that we should add two new options widget and delimiter to ChoiceType.
  • widget can be one of the values selectcheckboxradio and text
  • delimiter can be any single character
Behavior:
  • widget = 'select': This is equivalent to expanded = false right now.
  • widget = 'checkbox'multiple must not be set to false. Otherwise equivalent to expanded = true.
  • widget = 'radio'multiple must not be set to true. Otherwise equivalent to expanded = true.
  • widget = 'text': A text input is shown.
    • if multiple is false, the input must equal one of the predefined choices.
    • if multiple is true, the input is split by the character defined in delimiter (a comma by default), then each value is trimmed (unless trim is false). Each resulting input must equal one of the predefined choices.

Solución bastante más genérica y limpia, y que incluye el tipo de widget text, que combinado con el atributo multiple soluciona el problema de poder referenciar entidades "manualmente". Lo que no aclara esta solución es si seguirá haciendo prefetch o no de todas las entidades posibles. A día de hoy este debate sigue abierto en Github.

9 de febrero de 2013

Recuperar datos de formularios al volver atrás en el navegador

Dado que cada navegador es un mundo a la hora de enviar un formulario y volver atrás a la página del formulario, que unos conservan los datos del formulario y otros no, que algunos tipos de campos mantienen el estado y otros no, que unos tiran de caché y otros vuelven a solicitar la página... La solución más fiable y más crossbrowser es guardar una cookie en el momento en que se envía el formulario, y hacer una petición AJAX en la página del formulario siempre que dicha cookie esté definida, cookie que recupere los datos del formulario y los vuelque en el formulario.

Ojo, para datos sensibles esta solución puede no ser la más adecuada.

Explicación por extender.

Parámetros de Symfony en variables de entorno

A veces nos interesa que ciertos parámetros de nuestra configuración estén definidos fuera del código de esta. Por ejemplo, los parámetros de conexión de la base de datos. Para estos casos podemos usar variables de entorno. En Apache lo haríamos así:

    DocumentRoot "c:/xampp/htdocs/dev/public_html/web"
    ServerName symfony.localhost
    SetEnv SYMFONY__DATABASE__USER     "usuario"
    SetEnv SYMFONY__DATABASE__PASSWORD "password"
    SetEnv SYMFONY__DATABASE__NAME "name"

En el caso de que usemos Nginx con PHP-CGI sería algo así:

     location ~ .php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  app_dev.php;
        include        fastcgi_params;
        fastcgi_param  SYMFONY__DATABASE__USER usuario;
        fastcgi_param  SYMFONY__DATABASE__NAME name;
        fastcgi_param  SYMFONY__DATABASE__PASSWORD password;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
     }

Y luego los recuperamos en el config.yml:

doctrine:
    dbal:
        driver:   %database_driver%
        host:     %database_host%
        port:     %database_port%
        dbname:   %database.name%
        user:     %database.user%
        password: %database.password%

Ojo al uso de . y _ . En este caso database_driver, database_host y database_port siguen definidos en el fichero parameteres.ini.

Hay que tener en cuenta que si lanzamos algún script desde consola, tipo php app/console, no tendrá acceso  a las variables de entorno, generando un error:


[Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException]

You have requested a non-existent parameter "database.user".


Para que funcione correctamente, hay que definir las variables de entorno, usando SET en Windows, o EXPORT en Linux:

SET SYMFONY__DATABASE__USER=usuarioSET SYMFONY__DATABASE__PASSWORD=password
SET SYMFONY__DATABASE__NAME=name

En Windows también podemos hacerlo en la ventana de variables de entorno, Panel de control -> Sistema -> Configuración avanzada del sistema -> Variables de entorno:



Más info en: http://symfony.com/doc/current/cookbook/configuration/external_parameters.html

8 de febrero de 2013

Ojo a los orders by en MySQL

Aunque MySQL suele ser inteligente, a veces no viene mal ayudarle con los orders. Ejemplo, tabla con muchos municipios, donde queremos obtener unos cuantos haciendo búsqueda por nombre, y luego ordenarlos por capitales y códigos postales ascendentes. La consulta típica sería así:

SELECT m1.id, m1.nombre, m1.codigo_postal, m1.capital
FROM Municipio m1
WHERE m1.nombre LIKE "%Girona%"
ORDER BY m1.capital DESC , m1.codigo_postal ASC

12 total, La consulta tardó 0.5366 seg

Si le echamos una mano a MySQL:

SELECT m2.id AS id, m2.nombre AS nombre, m2.codigo_postal AS codigoPostal, m2.capital
FROM (

SELECT m1.id, m1.nombre, m1.codigo_postal, m1.capital
FROM Municipio m1
WHERE m1.nombre LIKE "%Girona%"
) m2
ORDER BY m2.capital DESC , m2.codigo_postal ASC

12 total, La consulta tardó 0.0886 seg

Parece ser que en algunos casos, como en éste, MySQL tiene a ordenar la tabla antes de realizar la búsqueda, con lo que el resultado es bastante peor.

pd: La prueba está hecha con varios municipios y tras varios reinicios de MySQL. No parece un tema de caché.

7 de febrero de 2013

¿Qué aplicación utiliza un puerto en Windows?

1. En consola:

netstat -noa



2. En el administrador de tareas buscamos el PID: (si el PID no aparece marcarlo en Ver/Seleccionar columnas...)



Actualización: Otra forma más cómoda y rápida como administrador (gracias al comentario anónimo). En consola:

netstat -nba



Mercurial y SSH bajo Windows

Tal que así:

1. Bajar putty, plink, puttygen. Generar clave SSH, guardarla y añadirla al authorized_keys del servidor.

2. Bajar e instalar Mercurial.

3. Configurar %USERPROFILE%/Mercurial.ini:

[ui]
ssh="C:\Putty\Plink.exe" -P XXXX -ssh -i "C:\Putty\key.ppk" 
username=usuario

4. En consola probar:

plink -ssh -i c:\putty\key.ppk usuario@X.X.X.X -P XXXX

5. Si ha ido bien, perfect, si no tenemos algún fallo previamente. Revisar IPs, puerto, key, etc. Aceptar añadir clave a cache.

6. Clonar repositorio:

hg -v clone ssh://usuario@X.X.X.X:XXXX/ruta/al/repo /ruta/local

¡Listo!

2 de enero de 2013

Termina el año de la valoración

Un año después, termina el año de la valoración. 2 entrevistas de trabajo, contratado durante 9 meses y ahora vuelta a empezar. Siempre será mejor que trabajar gratis.

Este año... aun en duda.