Conectándose a varias Bases de Datos con Symfony2 (Doctrine)


En artículos pasados hemos abordado algo de los temas que hoy nos ocupan. Como ya sabemos, Symfony, ese maravilloso framework (mi gusto muy personal) para el desarrollo de aplicaciones web muy de moda en estos últimos años (por algo será) se vale del (a mi gusto muy personal y quizás de un par de amigos por ahí) ORM más completo que he conocido, Doctrine. Entre los dos nos abstraen de temas ya tan banales como el gestor de base de datos (MySQL, Oracle, PostgreSQL, SQL Server), la validación primaria de de valores a usar en las sentencias SQL protegiéndonos sin mucho esfuerzo de ataques SQL Injection entre otros.

Pero ya esos temas son pasados y puedes leerlos en nuestro portal. Lo que nos trae hoy es quizás el siguiente capítulo de las conexiones a múltiples bases de datos en nuestras aplicaciones. En el artículo pasado ilustramos como utilizando una biblioteca de Joomla podemos realizar conexiones múltiples sin siquiera sudar y sobrevivir en el intento. Hoy vamos a ilustrar el mismo tema en Symfony con Doctrine.

Lo primero que debemos entender es que Symfony antes que nada hay que facilitarle ciertos y determinados datos de configuración, entre ellos los parámetros de conexión a la o las bases de datos a utilizar. Cada uno de ellos ya los conocemos: host donde se encuentra el servidor de base de datos, credenciales para la correcta autenticación y posterior autorización (permisos), tipo de base de datos dependiendo del driver que utilicemos y finalmente el nombre de la base de datos. Cabe resaltar que aunque toma el valor por defecto dependiendo del driver utilizado también es configurable el puerto a conectarse en el servidor para consumir el servicio.

Una vez establecidos estos parámetros es preciso darle un nombre a cada una de las conexiones (cada conexión corresponde a un servidor/base de datos). Dicho nombre o alias como prefieran llamarlo será utilizado por el desarrollador para acceder al mapeo de la base de datos realizado por Symfony. A través del método getManager() como veremos posteriormente podemos obtener datos de cada base de datos indistintamente y siempre que sea necesario.

A continuación un ejemplo de configuración de 3 bases de datos diferentes en la misma aplicación:

Fichero app/config/parameters.yml

    database_driver: pdo_mysql
    database_host: 127.0.0.1
    database_port: null
    database_name: bd1
    database_user: root
    database_password: 12345
    
    database_driver2: pdo_mysql
    database_host2: 127.0.0.1
    database_port2: null
    database_name2: bd2
    database_user2: root
    database_password2: 12345
    
    database_driver3: pdo_mysql
    database_host3: host.example.com
    database_port3: null
    database_name3: db3
    database_user3: websitesdb
    database_password3: websitesdb12345

Como ven son tres bases de datos diferentes, las dos primeras en el mismo servidor en que la aplicación está hosteada, la tercera en un servidor externo al cual accedemos mediante las credenciales correctas. Notar también que aunque las tres son MySQL eso puede variar y es totalmente transparente para nosotros los desarrolladores pues Doctrine nos abstrae de ese “pequeño” detalle.

Ahora debemos crear el acceso a cada una de las bases para ser usado en los controladores.

Fichero app/config/config.yml

    connections:
            default:
                driver:   %database_driver%
                host:     %database_host%
                port:     %database_port%
                dbname:   %database_name%
                user:     %database_user%
                password: %database_password%
                charset:  UTF8
            cliente:
                driver:   "%database_driver2%"
                host:     "%database_host2%"
                port:     "%database_port2%"
                dbname:   "%database_name2%"
                user:     "%database_user2%"
                password: "%database_password2%"
                charset:  UTF8
             externa:
                driver:   "%database_driver3%"
                host:     "%database_host3%"
                port:     "%database_port3%"
                dbname:   "%database_name3%"
                user:     "%database_user3%"
                password: "%database_password3%"
                charset:  UTF8   

No es muy difícil notar la relación de estos dos ficheros, donde el segundo hace referencia a lo establecido en el primero. Cada uno de estos nombres de acceso o alias son completamente arbitrarios, es decir, el nombre puede ser cualquiera siempre y cuando no coincidan. Lo recomendable es usar uno que tenga sentido en todo nuestro esquema de como vamos desarrollando la aplicación. Por ejemplo mi base de datos por defecto con alias default es bd1 mientras que el acceso a la bd2 donde se encuentran los clientes de mi aplicación pues tiene un nombre de acceso del mismo nombre y así el siguiente.

Ahora mostraremos el acceso. El acceso a cada recurso se realiza a través del método getManager() de la clase Doctrine. Como parámetro acepta el alias del recurso necesario.

  /**
  *  Accedemos al recurso default, es decir las transacciones que seamos capaces de realizar a través de $em tendrán efecto 
  *  en la base de datos bd1 de la conexión default.
  */
  $em = $this->getDoctrine()->getManager('default'); 

  $em1 = $this->getDoctrine()->getManager('default');
  $query = $em1->createQuery('SELECT * FROM XBundle:Dosis WHERE fecha =:fecha')->setParameter('fecha', $fecha);
  $entities = $query->getResult();

  $em2 = $this->getDoctrine()->getManager('cliente');
  $cliente = $em2->getRepository('XBundle:Cliente')->find($id);

  $em3 = $this->getDoctrine()->getManager('externa');

  foreach ($entities as $e) {
    if ($e->getClienteNombre() == $cliente->getNombre()) {
      $em3->remove($e);
      $em3->flush();
    }
  }

El ejemplo anterior muestra la interacción entre las tres bases de datos en un mismo método. Primero obtenemos del recurso default todos los objetos de la tabla Dosis en una fecha determinada. Luego extraemos del recurso cliente uno determinado definido por la variable $id que bien puede tener su entrada de valores de algún formulario y luego procedemos a buscar dicho cliente por su nombre dentro de las dosis, chequeando si ya le fue aplicada alguna. Si lo encontramos pues lo eliminamos del recurso externa.

El ejemplo es completamente creado para demostrar la funcionalidad que hoy traemos a discusión pero es completamente adaptable a una situación real. Léanlo y analícenlo que de seguro la manera de aprovecharlo la van a encontrar. Como siempre me conformo con que lo prueben y jueguen con él. Cualquier sugerencia hágannosla saber que ustedes son la razón por la que trabajamos.

Latest posts by Leroy Ley (see all)