Herencia entre entidades y consultas DQL relacionadas

herencia entre entidades y consultas DQL
Herencia entre entidades

Una de las características que tiene Doctrine es que se puede usar la herencia de clases para representar aquellas tablas que heredan de una tabla «base».

Pongamos un ejemplo:

  • Tenemos una tabla Rol («padre») y de ella heredan 3 tipos de roles distintos(RolPlataforma, RolOrganizacion, RolEdicion), pero que comparten los mismos atributos que la tabla Rol «padre», excepto que se cada «entidad» hija se relacionan con entidades diferentes. Aqui el diagrama UML que representa el caso:
  • umlAhora la parte de las entidades:
    • Entity Padre Role
  • /**
     * @ORM\Table(name="role")
     * @ORM\InheritanceType("JOINED")
     * @ORM\DiscriminatorColumn(name="discr", type="string")
     * @ORM\DiscriminatorMap({
     *     "role" = "Role",
     *     "role_edition" = "RoleEdition",
     *     "role_org" = "RoleOrganization",
     *     "role_plat" = "RolePlatform"
     * })
     */
    class Role 
    {
     const ROLE_EDITION = 'RoleEdition';
     const ROLE_ORGANIZATION = 'RoleOrganization';
     const ROLE_PLATFORM = 'RolePlatform';
     const ROLE = 'Role';
    
     /**
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue()
     */
     protected $id;
    
     /**
     * @ORM\Column(name="rol_borrado", type="boolean", nullable=false)
     */
     private $deleted = false;
    
     /**
     * @ORM\ManyToMany(targetEntity="efor\UsuarioBundle\Entity\User", mappedBy="roleUser")
     */
     private $user;
    
    //...
    
    //OTROS CAMPOS NO RELEVANTES PARA EL EJEMPLO
    //SETTERS Y GETTERS Y OTROS METODOS.
  • Con la anotacion estamos indicando que se va a hacer herencia de tablas y que los tipos de discriminador van a ser los que pone en el «DiscriminatorMap». De esta entidad nunca se van a instanciar objetos, siempre se instanciaran objetos de las entidades hijas, por lo que podriamos definirla como Abstracta.
  • Entity descendiente RolePlatform
    • /**
       * @ORM\Table(name="rol_plataforma")
       * @ORM\Entity
       */
      class RolePlatform extends Role
      {
          protected $discr = self::ROLE_PLATFORM;
      
          /**
           * @ORM\ManyToOne(targetEntity="efor\AppBundle\Entity\Platform", inversedBy="rolePlatform")
           */
          private $platform;
      
          //otros campos
      }
  • Entity descendiente RoleOrganization
  • /**
     * @ORM\Table(name="rol_organizacion")
     * @ORM\Entity
     */
    class RoleOrganization extends Role
    {
        protected $discr = self::ROLE_ORGANIZATION;
    
        /**
         * @ORM\ManyToOne(targetEntity="efor\AppBundle\Entity\Organization", inversedBy="roleOrganization", cascade={"persist"})
         */
        private $organization;
    
        //otros campos
    }
  • Entity descendiente RoleEdition
  • /**
     * @ORM\Table(name="rol_edicion")
     * @ORM\Entity
     */
    class RoleEdition extends Role
    {
        protected $discr = self::ROLE_EDITION;
    
        /**
         * @ORM\ManyToOne(targetEntity="efor\AppBundle\Entity\Edition", inversedBy="roleEdition")
         */
        private $edition;
    
        //otros campos
    }

Si os fijas cada entidad hija tiene una relacion distinta a otra entidad, por eso necesitabamos usar la herencia entre entidades, porque podemos crear objetos que se relacionan con entidades distintas en funcion del tipo de relacion que queramos tener.

En la documentación de Doctrine explica los tipos de herencia entre entidades y algunas consideraciones a tener en cuenta cuando se usa la herencia. Os dejo aqui la documentacion:

-http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#class-table-inheritance

-http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#single-table-inheritance

-http://stackoverflow.com/questions/7504680/doctrine-2-how-to-write-a-dql-select-statement-to-search-some-but-not-all-the

Consultas DQL filtradas por el campo discriminador:

Una de las dudas que surgen al trabajar con la herencia en Doctrine es saber como puedes filtrar las consultas por el campo discriminatorio. En mi caso, necesitaba filtrar los resultados por aquellos objetos que eran de una determinada clase del discriminador(RoleOrganization) y despues de buscar, encontre que se puede hacer algo asi:

public function findUsersFilteredByRoleOrganization(User $loggedUser, Role $selectedRole)
{
    $qb = $this->getEntityManager()->createQueryBuilder();
    $usersResult = $qb
    ->select('user')
    ->from('UsuarioBundle:User', 'user')
    ->join('user.roleUser', 'roleUser')
    ->where('roleUser INSTANCE OF AppBundle:RoleOrganization')
    ->getQuery();

    return $usersResult->getResult();
}

Si os dais cuenta, hemos hecho uso del INSTANCE OF dentro del Where de la consulta DQL, la cual se traduce por esto en SQL:

SELECT t0_.*
INNER JOIN rol_usuario t2_ ON t0_.id = t2_.user_id
INNER JOIN rol t1_ ON t1_.id = t2_.role_id
LEFT JOIN rol_edition t3_ ON t1_.id = t3_.id
LEFT JOIN rol_org t4_ ON t1_.id = t4_.id
LEFT JOIN rol_plataforma t5_ ON t1_.id = t5_.id
WHERE t1_.discr IN (‘role_org’)

De esta forma solo obtenemos los objetos del tipo «RoleOrganization» que queremos.

Espero que os sirve de ayuda, ya que la documentacion de Doctrine no es muy explicita en este aspecto y no lo explica muy bien que digamos.

Recordad compartir este articulo para que llegue al maximo de amigos de Symfony.

Saludos ¡¡

 

 

12 comentarios en “Herencia entre entidades y consultas DQL relacionadas

    1. davidT

      Hola Gliffberg, si es necesaria que la clase padre tenga una tabla en la BD, ya que las «descendientes» se relacionaran a traves de una clave ajena para referenciar su «padre». Si necesitas mas informacion acerca de estos detalles, puede consultar la documentación de Doctrine que al final del documento he referenciado.

      Gracias por visitar mi blog y si no es mucho pedir, comparte los articulos que mas ayuda te aportan en tus redes sociales.

      Me gusta

      1. Gracias a tí bro . estoy intentando implementar un buscador en mi proyecto para no utilizar las consultas «LIKE» y que funcione igual …. tendrás algún bundle ya creado que me sirva de ejemplo? .

        Me gusta

  1. davidT

    Si son busquedas complejas o algo pesadas te recomiendo que investigues sobre usar ElasticSearch o Solr para optimizar esas busquedas que necesitan estar indexadas y de muy rapido acceso. Otra opcion si no quieres complicarte mucho, es indexar(añadir indices) esas columnas en tu BD para que ese LIKE sea mas ligero de procesar y mas rapido en buscar. Espero haberte podido ayudar en lo posible con tu problematica. Saludos ¡

    Me gusta

  2. davidT

    Hola Gliffberg, no tengo problema en pasarte mi email pero creo que estaria bien, que si son dudas, las pusieras aqui en los comentarios, de esa forma, otros «developers» puede que se encuentren en tu misma situacion y les ayude a encontrar la solución. Gracias de antemano ¡ Y animo con los estudios ¡¡

    Me gusta

    1. davidT

      Hola Gliffberg, supongo que tendras una/varias clases donde haces tus calculos estadisticos. Para meterla/as dentro de tu bundle puedes hacerlo creando una carpeta Util dentro de tu bundle(AppBundle/Util) y dejando alli tu/s clase/s ajustandole/s el namespace. Despues solamente tendras que usar dicha clase en tus controllers para calcular lo que necesites y pasar a la vista la informacion calculada. Si tienes mas dudas, no dudes en preguntarme. Saludos hermano¡

      Me gusta

  3. Hermano como estás tiempo sin charlar… pude solucionar el problema cargando las graficas solo usando ajax . Ahora lo que me falta es mejorar la usabilidad del sistema … Quisiera saber como puedo validar datos repetidos . es decir si ingreso un valor a un campo y guardo me indique si el dato fue registrado anteriormente

    Me gusta

    1. davidT

      Hola, es una buena practica definir constantes en las clases para aquellas cosas que has metido a «pelo» y que quizas lo usas en muchos sitios. Si te equivocas en uno de esos sitios, estas perdido. Por eso, defino las constantes y despues las uso haciendo Clase::constante. De esa forma, lo tienes centralizado en un sitio, y si ese valor cambia, solo lo cambias en un solo sitio en lugar de miles de sitios en tu codigo. Espero haberte resuelto tu duda, amigo. Saludos ¡

      Me gusta

Replica a davidT Cancelar la respuesta

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.