php - joined - Doctrine 2 Mapeo de herencia con asociación
orm id (2)
Creo que ha entendido mal, la sección del manual que ha citado se titula "Impacto del rendimiento", no le dicen que no puede hacer esto, solo que hay implicaciones de rendimiento si lo hace. Esto tiene sentido para la carga diferida: para las colecciones heterogéneas de entidades STI debe ir a la base de datos y cargar la entidad antes de saber qué clase será, por lo que la carga diferida no es posible / no tiene sentido. Estoy aprendiendo Doctrine 2 en este momento, así que me burlé de tu ejemplo, lo siguiente funciona bien para más:
namespace Entities;
/**
* @Entity
* @Table(name="pets")
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="pet_type", type="string")
* @DiscriminatorMap({"cat" = "Cat", "dog" = "Dog"})
*/
class Pet
{
/** @Id @Column(type="integer") @generatedValue */
private $id;
/** @Column(type="string", length=300) */
private $name;
/** @ManyToOne(targetEntity="User", inversedBy="id") */
private $owner;
}
/** @Entity */
class Dog extends Pet
{
/** @Column(type="string", length=50) */
private $kennels;
}
/** @Entity */
class Cat extends Pet
{
/** @Column(type="string", length=50) */
private $cattery;
}
/**
* @Entity
* @Table(name="users")
*/
class User
{
/** @Id @Column(type="integer") @generatedValue */
private $id;
/** @Column(length=255, nullable=false) */
private $name;
/** @OneToMany(targetEntity="Pet", mappedBy="owner") */
private $pets;
}
... y el script de prueba ...
if (false) {
$u = new Entities/User;
$u->setName("Robin");
$p = new Entities/Cat($u, ''Socks'');
$p2 = new Entities/Dog($u, ''Rover'');
$em->persist($u);
$em->persist($p);
$em->persist($p2);
$em->flush();
} else if (true) {
$u = $em->find(''Entities/User'', 1);
foreach ($u->getPets() as $p) {
printf("User %s has a pet type %s called %s/n", $u->getName(), get_class($p), $p->getName());
}
} else {
echo " [1]/n";
$p = $em->find(''Entities/Cat'', 2);
echo " [2]/n";
printf("Pet %s has an owner called %s/n", $p->getName(), $p->getOwner()->getName());
}
Todos mis gatos y perros cargan como el tipo correcto:
Si observa el SQL generado, notará que cuando OneToMany targetEntity es "pet", obtiene SQL así:
SELECT t0.id AS id1, t0.name AS name2, t0.owner_id AS owner_id3, pet_type,
t0.cattery AS cattery4, t0.kennels AS kennels5 FROM pets t0
WHERE t0.owner_id = ? AND t0.pet_type IN (''cat'', ''dog'')
Pero cuando está configurado para Cat, obtienes esto:
SELECT t0.id AS id1, t0.name AS name2, t0.cattery AS cattery3, t0.owner_id
AS owner_id4, pet_type FROM pets t0 WHERE t0.owner_id = ? AND t0.pet_type IN (''cat'')
HTH.
NOTA: si lo que deseo no es posible, se aceptará una respuesta "no posible"
En la documentación de Doctrine 2 sobre mapeo de herencia , dice que hay 2 formas:
- Herencia de tabla única (STI)
- Herencia de tabla de clase (CTI)
Para ambos, existe la advertencia:
Si usa una entidad STI / CTI como una entidad de varios a uno o de uno a uno , nunca debe usar una de las clases en los niveles superiores de la jerarquía de herencia como "targetEntity", solo aquellos que no tienen subclases. De lo contrario, Doctrine NO PUEDE crear instancias proxy de esta entidad y SIEMPRE cargará la entidad con entusiasmo.
Entonces, ¿cómo puedo proceder a usar la herencia con una asociación a la clase base (abstracta) ? (y mantener el rendimiento por supuesto)
Ejemplo
Un usuario tiene muchas Pet
(clase abstracta extendida por Dog
o Cat
).
Lo que quiero hacer :
class User {
/**
* @var array(Pet) (array of Dog or Cat)
*/
private $pets;
}
Debido a la advertencia en la documentación de Doctrine, debería hacer eso:
class User {
/**
* @var array(Dog)
*/
private $dogs;
/**
* @var array(Cat)
*/
private $cats;
}
Esto es molesto, porque pierdo los beneficios de la herencia!
Nota: No agregué las anotaciones de Doctrine para la asignación a DB, pero puede entender a qué me refiero
Estoy cansado, pero esto parece mucho ruido y pocas nueces.
Te perdiste la parte importante de esa advertencia:
Si usa una entidad STI / CTI como una entidad muchos a uno o uno a uno
¡Ese no es el caso en tu ejemplo! Si no hubiera omitido las anotaciones de doctrina, podría haberse dado cuenta.
La asociación User :: pets es OneToMany, no [One | Many] ToOne. Un usuario tiene muchas mascotas.
La asociación inversa es OneToOne, pero está dirigida al usuario, que no tiene herencia.
La respuesta de Robin debería haber sido una buena pista: ¡puede registrar las consultas sql y ver qué doctrina realmente le hace a su base de datos!
El escenario de mal rendimiento es algo así como:
abstract class Pet { ... }
class Cat extends Pet { ... }
class Dog extends Pet { ... }
class Collar {
/**
* @Column(length="16")
*/
protected $color;
/**
* ManyToOne(targetEntity="Pet")
*/
protected $owner;
}
Ahora, si quieres iterar sobre todos los collares azules, Doctrine se encuentra con algunos problemas. No sabe qué clase $ propietario va a ser, por lo que no puede usar un Proxy. En cambio, se ve forzado a cargar ansiosamente $ propietario para averiguar si es un gato o un perro.
Esto no es un problema para OneToMany o ManyToMany relaciones, porque en ese caso, la carga lenta funciona bien. En lugar de un proxy, obtienes una colección persistente. Y una PersistentCollection siempre es solo una PersistentCollection. No le importan sus propios contenidos hasta que realmente los solicite. Así que la carga lenta funciona bien.