Vistas en Doctrine ORM

Saludos! que tal? Espero que bien. Esta vez te voy a hablar acerca de las vistas sql en el orm doctrine. Quien conozca estas herramientas no hace falta que se las presente, pero para los demás ahí va una breve explicación.

En bases de datos relacionales (MySQL por ejemplo) una vista  es como un “resumen” de un conjunto de consultas. Un orm es una herramienta de programador que te ayuda a trabajar con bases de datos desde tu aplicación.

Doctrine es el ORM (junto a propel) que trae por defecto el framework php symfony (mi favorito) y como no hay mucha documentación sobre las vistas, y menos en castellano, pues aquí va este pequeño manual.

Mi manera de trabajar es trabajar lo menos posible (vagooo!!). Calma! Lo que quiero decir es que cuanto más trabaje el ordenador por mí, más rápido y mejor se desarrolla una aplicación. Por este motivo yo no me molesto en escribir ni una sóla consulta SQL salvo que sea estrictamente necesario (como veremos que será cuando hagamos las vistas).

Por supuesto no quiere decir que saber SQL sea inútil ni mucho menos. Es más, para trabajar bien (de manera eficiente) con un ORM necesitas saber SQL como el comer.  Este el motivo por el que yo “sólo” hago el diseño de la base de datos y el ordenador genera todo lo demás por mí. Esto tiene muchas ventajas como por ejemplo la independencia del sistema (da igual que uses mysql, postgres…) y reducción de errores (que si se me olvida una foreign key por ahí, que si por qué falla esta consulta si está “perfecta” y después de media hora ves que era una coma, etc).

Además, el ordenador también crea las clases para manipular los datos (geters y seters entre otras muchas cosas).

En concreto con Doctrine (y symfony en general) eso se lo indicamos mediante archivos YML

Hasta aquí todo bien pero… no hay soporte para vistas!! Entonces toca hacerlo a mano… Esto es un problema, porque empiezas a depender de tu SGBD pero por otra parte, es vital si quieres obtener un buen rendimiento para determinadas consultas.

Bien, después de toda esta insustancial parrafada vayamos al turrón

Usaré un ejemplo real, sacado de Wixet.

create or replace view search_profile
as
SELECT user_id,gender,uid,birthday,city_id,country_id,town_id FROM profile

Es simple, verdad? Así nos centraremos en lo importante, la clase Doctrine.

Diferenciamos 2 tipos de clases, las nuestras y las de doctrine. Las “nuestras” están vacías de serie mientras que en las de doctrine es donde está todo el meollo. El motivo es que si añadimos métodos propios, doctrine los machacará. Por eso están diferenciadas. Luego una hereda de otra así que el resultado final es algo totalmente homogéneo.

abstract class BasesearchProfile extends sfDoctrineRecord
{
        //Because profile is acl protected, we use this view to
        //get some data when doing an user search
    public function setTableDefinition()
    {
        $this->setTableName('search_profile');
        $this->hasColumn('user_id', 'integer', 4, array(
             'type' => 'integer',
             'primary' => true,
             'length' => 4,
             ));

        $this->hasColumn('gender', 'integer', 1, array(
             'type' => 'integer',
             'length' => 1,
             ));
        $this->hasColumn('uid', 'string', 25, array(
             'type' => 'string',
             'unique' => true,
             'length' => 25,
             ));
        $this->hasColumn('birthday', 'date', null, array(
             'type' => 'date',
             ));
        $this->hasColumn('city_id', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             ));
        $this->hasColumn('country_id', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             ));
        $this->hasColumn('town_id', 'integer', 4, array(
             'type' => 'integer',
             'length' => 4,
             ));
    }

    public function setUp()
    {
        parent::setUp();
        $this->hasOne('sfGuardUser as user', array(
             'local' => 'user_id',
             'foreign' => 'id',
             'onDelete' => 'CASCADE'));

        $this->hasMany('interest as interests', array(
             'local' => 'user_id',
             'foreign' => 'profile_id'));

        $this->hasOne('city', array(
             'local' => 'city_id',
             'foreign' => 'city_id',
             'onDelete' => 'SET NULL'));

        $this->hasOne('country', array(
             'local' => 'country_id',
             'foreign' => 'country_id',
             'onDelete' => 'SET NULL'));

       $this->hasOne('city as town', array(
             'local' => 'town_id',
             'foreign' => 'city_id',
             'onDelete' => 'SET NULL'));

       $this->hasMany('place as places', array(
             'refClass' => 'placeUser',
             'local' => 'user_id',
             'foreign' => 'user_id'));

        $this->hasMany('workplace as workplaces', array(
             'refClass' => 'workplaceUser',
             'local' => 'user_id',
             'foreign' => 'user_id'));

        $this->hasMany('school as schools', array(
             'refClass' => 'schoolUser',
             'local' => 'user_id',
             'foreign' => 'user_id'));

        $this->hasMany('city as cityplace', array(
             'refClass' => 'cityUser',
             'local' => 'user_id',
             'foreign' => 'user_id'));

        //No full relation to make searches faster
        $this->hasMany('placeUser', array(
             'local' => 'user_id',
             'foreign' => 'user_id'));
        $this->hasMany('workplaceUser', array(
             'local' => 'user_id',
             'foreign' => 'user_id'));
        $this->hasMany('cityUser', array(
             'local' => 'user_id',
             'foreign' => 'user_id'));
        $this->hasMany('schoolUser', array(
             'local' => 'user_id',
             'foreign' => 'user_id'));

    }
}

Perdón por los  “> ” pero es que el wordpress insiste en convertir los “>”

El archivo puede ser algo confuso, pero su estructura es sencilla.

//Primero definimos los datos de la tabla
//En el método "setTableDefinition"
$this->setTableName('search_profile'); //Nombre
//Y lo siguiente para cada tipo de dato
        $this->hasColumn('user_id', 'integer', 4, array(
             'type' => 'integer',
             'primary' => true,
             'length' => 4,
             ));
//En el metodo setup

        parent::setUp();//Ejecutamos el setup de la clase padre, que es la otra que hemos creado
//Las relaciones. La estructura es sencilla si conoces algo de doctrine
        $this->hasOne('sfGuardUser as user', array(
             'local' => 'user_id',
             'foreign' => 'id',
             'onDelete' => 'CASCADE'));

Ahora metemos esos archivos en sus respectivos directorios (/lib/model/doctrine en el caso de symfony) y ya lo tenemos!

Ahora ya puedes usar la clase pero ojo, es una vista, no intentes cambiar sus datos. Doctrine lanzará una excepción mysql si lo intentas. Para todo lo demás, funciona como una tabla normal.

Nota: si esto te parece muy complicado, otra opcion es crear un archivo yml una tabla equivalente a la vista (en cuanto a datos y relaciones) y posteriormente eliminar la tabla de mysql e insertar tus vista. En este caso, ojo con usar atributos tipo timestamptable o poner ondelete cascade/update en las relaciones porque recuerda que una vista es sólo de lecutra.

Nota2: Cuidado cuando hagas doctrine:clean, como no encontrará un yml correspondiente a esa tabla te dirá que si quieres borrar la clase. Tendrás que responder que no, lógicamente.

You may also like...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *