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:
- Ahora 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 ¡¡
amigo que tal … es necesario que la clase padre tenga «una tabla en la bd» ..¿? . es que lo veo irrelevante . gracias de antemano
Me gustaMe gusta
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 gustaMe gusta
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 gustaMe gusta
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 gustaMe gusta
Hermano que tal nuevamente por acá … me podrías dejar tu correo . es que tengo unas dudas a ver si me puedes ayudar …. la universidad me carga loco! . Saludos
Me gustaMe gusta
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 gustaMe gusta
ok . hermano resulta que tengo un modulo de estadistica sencillo en php y quisiera implementarlo al bundle . pero no he podido … me pudieras dar los pasos a seguir . lo del correo era para dejarte el archivo
Me gustaMe gusta
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 gustaMe gusta
Si tienes el proyecto en github, pasame la url de dicho reposiorio y le puedo pegar un vistazo si lo necesitas.
Me gustaMe gusta
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 gustaMe gusta
Hola como estas, te consulto porque defines las constantes?
Gracias
Me gustaMe gusta
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 gustaMe gusta