Foros del Web » Programando para Internet » PHP » Frameworks y PHP orientado a objetos »

Doctrine 2 accediendo a campos de una relacion mediante lazy load

Estas en el tema de Doctrine 2 accediendo a campos de una relacion mediante lazy load en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Tengo el siguiente problema. ¿ Por que no consigo obtener los datos de otras entidades relacionadas con una entidad ? @import url("http://static.forosdelweb.com/clientscript/vbulletin_css/geshi.css"); Código PHP: Ver ...
  #1 (permalink)  
Antiguo 13/04/2012, 07:18
 
Fecha de Ingreso: enero-2012
Ubicación: España
Mensajes: 150
Antigüedad: 12 años, 9 meses
Puntos: 0
Doctrine 2 accediendo a campos de una relacion mediante lazy load

Tengo el siguiente problema.

¿ Por que no consigo obtener los datos de otras entidades relacionadas con una entidad ?

Código PHP:
Ver original
  1. ...
  2. $this->_vista->categoria = $this->get('manejador_categoria')->dameUnoPorId( 19 );
  3. var_dump($this->_vista->categoria);
  4. ...
Código:
object(Entidad\Categoria)[136]
  public 'id' => int 19
  public 'categoriapadre' => 
    object(DoctrineProxies\__CG__\Entidad\Categoria)[131]
      private '_entityPersister' =>

      ...

  public 'nombre' => string 'Pipas de calabaza' (length=17)
  public 'urlrelativa' => string 'pipas-calabaza' (length=14)
  public 'descripcion' => null
  public 'visible' => boolean false
  public 'activo' => boolean true
Luego intento acceder al campo "nombre" de la relacion "categoriapadre" y me devuelve null, cuando realmente la entidad tiene categoriapadre y un campo nombre definidos.
¿ A que puede deberse esto ?
  #2 (permalink)  
Antiguo 13/04/2012, 08:00
Avatar de maycolalvarez
Colaborador
 
Fecha de Ingreso: julio-2008
Ubicación: Caracas
Mensajes: 12.120
Antigüedad: 16 años, 3 meses
Puntos: 1532
Respuesta: Doctrine 2 accediendo a campos de una relacion mediante lazy load

no se como has implementado D2, porque éste mapea las entidades a clases autenticas PHP y el uso por ejemplo de una clase Articulo 1:n con Comentarios sería más u menos así:

Código PHP:
$em $doctrineEntityManager;//obtener el Entity Manager

$articulo $em->getReference('Articulo'1);

//obtener comentarios:

$comments $articulo->getComments(); 
clases:
Articulo:
Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace Entity;
  4.  
  5. use Gedmo\Mapping\Annotation as Gedmo;
  6. use \Doctrine\Common\Collections\ArrayCollection;
  7.  
  8. /**
  9.  * Articulo
  10.  *
  11.  *
  12.  *
  13.  * @Entity(repositoryClass="Repository\ArticleRepository")
  14.  * @Table(name="prueba.article")
  15.  * @author maycolalvarez
  16.  */
  17. class Articulo
  18. {
  19.     /**
  20.      * @Id
  21.      * @Column(type="integer", nullable=false)
  22.      * @GeneratedValue(strategy="AUTO")
  23.      */
  24.     protected $id;
  25.    
  26.     /**
  27.      *
  28.      * @Column(type="string", name="_name", length=100, nullable=false)
  29.      */
  30.     protected $name;
  31.    
  32.     /**
  33.      *
  34.      * @var type
  35.      * @Gedmo\Timestampable(on="create")
  36.      * @Column(type="datetime")
  37.      */
  38.     protected $created;
  39.    
  40.     /**
  41.      *
  42.      * @var type
  43.      * @Gedmo\Timestampable(on="update")
  44.      * @Column(type="datetime")
  45.      */
  46.     protected $updated;
  47.    
  48.     /**
  49.      * @OneToMany(targetEntity="Comentario", mappedBy="article")
  50.      */
  51.     private $comments;
  52.  
  53.     public function __construct()
  54.     {
  55.         $this->comments = new \Doctrine\Common\Collections\ArrayCollection();
  56.     }
  57.    
  58.     /**
  59.      * Get id
  60.      *
  61.      * @return integer
  62.      */
  63.     public function getId()
  64.     {
  65.         return $this->id;
  66.     }
  67.  
  68.     /**
  69.      * Set name
  70.      *
  71.      * @param string $name
  72.      */
  73.     public function setName($name)
  74.     {
  75.         $this->name = $name;
  76.     }
  77.  
  78.     /**
  79.      * Get name
  80.      *
  81.      * @return string
  82.      */
  83.     public function getName()
  84.     {
  85.         return $this->name;
  86.     }
  87.  
  88.     /**
  89.      * Set created
  90.      *
  91.      * @param datetime $created
  92.      */
  93.     public function setCreated($created)
  94.     {
  95.         $this->created = $created;
  96.     }
  97.  
  98.     /**
  99.      * Get created
  100.      *
  101.      * @return datetime
  102.      */
  103.     public function getCreated()
  104.     {
  105.         return $this->created;
  106.     }
  107.  
  108.     /**
  109.      * Set updated
  110.      *
  111.      * @param datetime $updated
  112.      */
  113.     public function setUpdated($updated)
  114.     {
  115.         $this->updated = $updated;
  116.     }
  117.  
  118.     /**
  119.      * Get updated
  120.      *
  121.      * @return datetime
  122.      */
  123.     public function getUpdated()
  124.     {
  125.         return $this->updated;
  126.     }
  127.  
  128.     /**
  129.      * Add comments
  130.      *
  131.      * @param Entity\Comentario $comments
  132.      */
  133.     public function addComentario(Entity\Comentario $comments)
  134.     {
  135.         $this->comments[] = $comments;
  136.     }
  137.  
  138.     /**
  139.      * Get comments
  140.      *
  141.      * @return Doctrine\Common\Collections\Collection
  142.      */
  143.     public function getComments()
  144.     {
  145.         return $this->comments;
  146.     }
  147. }
comentario:
Código PHP:
Ver original
  1. <?php
  2.  
  3. namespace Entity;
  4.  
  5. use Gedmo\Mapping\Annotation as Gedmo;
  6. use \Doctrine\Common\Collections\ArrayCollection;
  7.  
  8. /**
  9.  * Comentario
  10.  *
  11.  *
  12.  * @Entity(repositoryClass="Repository\CommentRepository")
  13.  * @Table(name="prueba.comment")
  14.  * @author maycolalvarez
  15.  */
  16. class Comentario
  17. {
  18.     /**
  19.      * @Id
  20.      * @Column(type="integer", nullable=false)
  21.      * @GeneratedValue(strategy="AUTO")
  22.      */
  23.     protected $id;
  24.    
  25.     /**
  26.      *
  27.      * @Column(type="string", name="_name", length=100, nullable=false)
  28.      */
  29.     protected $name;
  30.    
  31.     /**
  32.      *
  33.      * @var type
  34.      * @Gedmo\Timestampable(on="create")
  35.      * @Column(type="datetime")
  36.      */
  37.     protected $created;
  38.    
  39.     /**
  40.      *
  41.      * @var type
  42.      * @Gedmo\Timestampable(on="update")
  43.      * @Column(type="datetime")
  44.      */
  45.     protected $updated;
  46.    
  47.      /**
  48.      * @ManyToOne(targetEntity="Articulo", inversedBy="comments")
  49.      * @JoinColumn(nullable=false)
  50.      *
  51.      */
  52.     private $article;
  53.  
  54.  
  55.     /**
  56.      * Get id
  57.      *
  58.      * @return integer
  59.      */
  60.     public function getId()
  61.     {
  62.         return $this->id;
  63.     }
  64.  
  65.     /**
  66.      * Set name
  67.      *
  68.      * @param string $name
  69.      */
  70.     public function setName($name)
  71.     {
  72.         $this->name = $name;
  73.     }
  74.  
  75.     /**
  76.      * Get name
  77.      *
  78.      * @return string
  79.      */
  80.     public function getName()
  81.     {
  82.         return $this->name;
  83.     }
  84.  
  85.     /**
  86.      * Set created
  87.      *
  88.      * @param datetime $created
  89.      */
  90.     public function setCreated($created)
  91.     {
  92.         $this->created = $created;
  93.     }
  94.  
  95.     /**
  96.      * Get created
  97.      *
  98.      * @return datetime
  99.      */
  100.     public function getCreated()
  101.     {
  102.         return $this->created;
  103.     }
  104.  
  105.     /**
  106.      * Set updated
  107.      *
  108.      * @param datetime $updated
  109.      */
  110.     public function setUpdated($updated)
  111.     {
  112.         $this->updated = $updated;
  113.     }
  114.  
  115.     /**
  116.      * Get updated
  117.      *
  118.      * @return datetime
  119.      */
  120.     public function getUpdated()
  121.     {
  122.         return $this->updated;
  123.     }
  124.  
  125.     /**
  126.      * Set article
  127.      *
  128.      * @param Entity\Articulo $article
  129.      */
  130.     public function setArticle(Entity\Articulo $article)
  131.     {
  132.         $this->article = $article;
  133.     }
  134.  
  135.     /**
  136.      * Get article
  137.      *
  138.      * @return Entity\Articulo
  139.      */
  140.     public function getArticle()
  141.     {
  142.         return $this->article;
  143.     }
  144. }
__________________
¡Por favor!: usa el highlight para mostrar código
El que busca, encuentra...

Última edición por maycolalvarez; 13/04/2012 a las 08:08
  #3 (permalink)  
Antiguo 13/04/2012, 08:41
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 10 meses
Puntos: 845
Respuesta: Doctrine 2 accediendo a campos de una relacion mediante lazy load

Respeta las reglas de doctrine:

Cita:
Please don’t use public properties with Doctrine 2 at all, the “Queries for Application Use-Cases” section shows you why. In combination with proxies public properties can make up for pretty nasty bugs."
setea la visibilidad correctamente y luego nos comentas.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #4 (permalink)  
Antiguo 13/04/2012, 10:55
 
Fecha de Ingreso: enero-2012
Ubicación: España
Mensajes: 150
Antigüedad: 12 años, 9 meses
Puntos: 0
Respuesta: Doctrine 2 accediendo a campos de una relacion mediante lazy load

@Masterpuppet
Ok, ahora mismo puse las atributos como protected y parece funcionar mejor que antes, eso me ha hecho cambiar algunos metodos de la clase repositorio para mejor.
( masterpuppet, ¿ me aconsejas trabajar con propiedades implementando los metodos magicos __get y __set o de alguna otra forma para poder escribir en el codigo $var->nombre en vez de $var->getNombre() para acceder a los atributos de las entidades ? )

Masterpuppet, tengo un problemilla, me ha surgido una cosa curiosa, ahora explico.
@maycolalvarez
Si, ya lo se, yo estoy trabajando por ahora con la entidad "categoria" haciendo pruebas hasta que controle un poco del tema y ponerme con el resto de entidades.

Código PHP:
<?php
namespace Entidad
;

use 
DoctrineCommonCollectionsArrayCollection;

/** 
 * @Entity(repositoryClass="Repositorio\categoriaRepositorio")
 * @Table(name="categorias")
 **/
class Categoria
{
    
/** @Id @GeneratedValue(strategy="AUTO") @Column(type="integer") **/
    
protected $id;
    
/** @OneToOne(targetEntity="Categoria", inversedBy="categoriashijo" )
     *  @JoinColumn(name="fkidcategoria", referencedColumnName="id") **/ 
    
protected $categoriapadre;
    
/** @OneToMany(targetEntity="Categoria", mappedBy="categoriapadre") **/
    
protected $categoriashijo;
    
/** @OneToMany(targetEntity="Producto", mappedBy="categoria") **/
    
protected $productos;
    
/** @Column(type="string") **/
    
protected $nombre;
    
/** @Column(type="string") **/
    
protected $urlrelativa;
    
/** @Column(type="text") **/
    
protected $descripcion;
    
/** @Column(type="boolean") **/
    
protected $visible;
    
/** @Column(type="boolean") **/
    
protected $activo;
    
    public function 
__constructCategoria $categoria null )
    {
        if ( 
$categoria instanceof Categoria )
            
$this->setCategoriaPadre$categoria );
        
$this->hijocategorias = new ArrayCollection();
        
$this->productos = new ArrayCollection();
    }

    public function 
setId$valor ){
        
$this->id = (int)$valor;
    }
    public function 
getId(){
        return 
$this->id;
    }
    public function 
setCategoriaPadreCategoria $categoria ){
        
$this->categoriapadre $categoria;
        
$categoria->addCategoriaHijo($this);
    }
    public function 
getCategoriaPadre(){
        return 
$this->categoriapadre;
    }
    public function 
addCategoriaHijoCategoria $categoria ){
        
$this->getCategoriasHijo()->add$categoria );
        
$categoria->setCategoriaPadre$this );
    }
    public function 
getCategoriasHijo(){
        return 
$this->categoriashijo;
    }
    public function 
addProductoProducto $producto ){
        
$this->getProductos()->add($producto);
        
$producto->setCategoria($this);
    }
    public function 
getProductos(){
        
$this->productos;
    }
    
/** resto de geters y seters **/ 
}
?>
Mi clase categoriarepositorio
Código PHP:
<?php
namespace Repositorio
;
 
use 
DoctrineORMEntityRepository;
use 
DoctrineORMQuery;
 
class 
categoriaRepositorio extends EntityRepository{
    
    public function 
dameTodas(){
        
$query $this->_em->createQuery"SELECT partial c.{id, nombre, urlrelativa, descripcion, visible} FROM Entidad\Categoria c, Entidad\Categoria p
                                            WHERE c.categoriapadre = p AND c.id > 1 AND c.activo = 1
                                            ORDER BY p.nombre DESC, c.visible DESC" 
);
        return 
$query->getResult();
    }
    public function 
damePrincipales(){
        
$query $this->_em->createQuery("SELECT partial c.{id, nombre} FROM Entidad\Categoria c, Entidad\Categoria p
                                            WHERE c.categoriapadre = p AND c.activo = true AND p.id = 1
                                            ORDER BY c.nombre ASC"
);
        return 
$query->getResult();
    }
    public function 
dameOrdenadoPor(array $whereparam null, array $orderbyparam null ){
        
$where '';
        
$orderby '';
        if ( isset(
$whereparam) && !empty($whereparam) && is_array($whereparam) ){
            foreach( 
$whereparam AS $akey => $avalue){
                if ( !( 
$avalue === '' ) && !( $avalue === null ) ){
                    if ( 
$akey == 'categoriapadre')
                        
$where .= " AND p.id = $avalue";
                    else
                        
$where .= " AND c.$akey LIKE '$avalue'";
                }
            }
        }else
            throw new 
MiExcepcion('Error en paso de parametros');
        if ( isset(
$orderbyparam) && !empty($orderbyparam) && is_array($orderbyparam) && ( count($orderbyparam) == ) ){
            if (
$orderbyparam[0] === 'categoriapadre')
                
$orderby "ORDER BY p.nombre $orderbyparam[1]";
            else
                
$orderby "ORDER BY c.$orderbyparam[0] $orderbyparam[1]";
        }else
            throw new 
MiExcepcion('Error en paso de parametros');
            
        
$query $this->_em->createQuery"SELECT partial c.{id, nombre, urlrelativa, descripcion, visible} FROM Entidad\Categoria c, Entidad\Categoria p
                                            WHERE c.categoriapadre = p AND c.id > 1 AND c.activo = true$where
                                            $orderby" 
);

        return 
$query->getResult();
    }
    public function 
dameUnoPorId$id ){
        if ( isset(
$id) && !empty($id) && is_int($id) && ($id 1)){
            
$where " AND c.id = $id";
        }
        
$query $this->_em->createQuery"SELECT c FROM Entidad\Categoria c
                                            WHERE c.activo = 1$where"
);

        return 
$query->getSingleResult();
    }
    
}

?>
Resulta que primero me traigo
$this->_vista->catePadres = $this->get('manejador_categoria')->damePrincipales();
luego
$this->_vista->categorias = $this->get('manejador_categoria')->dameOrdenadoPor( $where, $orderby );
var_dump ($this->_vista->categorias);
y resulta que
Código:
...
2 => 
    object(Entidad\Categoria)[142]
      protected 'id' => int 9
      protected 'categoriapadre' => 
        object(Entidad\Categoria)[132]
          protected 'id' => int 1
          ...
      protected 'categoriashijo' => 
        object(Entidad\Categoria)[198]
          ...
      protected 'productos' => 
        object(Doctrine\ORM\PersistentCollection)[145]
              ...
      protected 'nombre' => string 'Sección Gatos' (length=13)
      protected 'urlrelativa' => null
      protected 'descripcion' => null
      protected 'visible' => null
      protected 'activo' => null
  3 => 
    object(Entidad\Categoria)[194]
      protected 'id' => int 5
      protected 'categoriapadre' => 
        object(Entidad\Categoria)[152]
          protected 'id' => int 2
          ...
      protected 'categoriashijo' => 
        object(Doctrine\ORM\PersistentCollection)[188]
             ...
      protected 'productos' => 
        object(Doctrine\ORM\PersistentCollection)[182]
             ...
      protected 'nombre' => string 'Pienso para perros' (length=18)
      protected 'urlrelativa' => string 'pienso' (length=6)
      protected 'descripcion' => string 'Todo tipo de piensos para los perros' (length=36)
      protected 'visible' => boolean true
      protected 'activo' => null
...
resulta que solo me hidrata los valores de los campos id y nombre para la categorias que me he traido con el metodo "damePrincipales()"



Parece como si al ejecutar el metodo "dameOrdenadoPor( $where, $orderby )" me devolviera las referencias de "damePrincipales()" para las entidades que coinciden.
Todas esas categorias tienen valores para urlrelativa y para visible, sin embargo, los valores hidratados son "null".

Última edición por chemajmb; 13/04/2012 a las 11:20
  #5 (permalink)  
Antiguo 13/04/2012, 13:16
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 10 meses
Puntos: 845
Respuesta: Doctrine 2 accediendo a campos de una relacion mediante lazy load

Personalmente prefiero menos magia, por lo tanto utilizo setters & getters, pero es a gusto de cada uno.

Con respecto al tema de las queries, porque utilizas partials ?, mismo la gente de doctrine te dice que solo lo utilices para optimizar y que tengas en cuenta que hace a tu código mas frágil y me extraña hasta que te este funcionando, el DQL para dameTodas debería ser algo así:

Código DQL:
Ver original
  1. SELECT c FROM Category c,
  2. JOIN c.parent p
  3. WHERE c.id > 1 AND c.activo = 1
  4. ORDER BY p.nombre DESC, c.visible DESC

luego no estas utilizando los parametros en el DQL, esto:

Código PHP:
Ver original
  1. $where .= " AND p.id = $avalue";

debería ser:

Código PHP:
Ver original
  1. $where .= " AND p.id = :id";
  2. ...
  3. $query->setParameter('id', $id);

te sugiero que revises todo el DQL prestando atención a la documentación http://docs.doctrine-project.org/pro...-language.html

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #6 (permalink)  
Antiguo 13/04/2012, 14:58
 
Fecha de Ingreso: enero-2012
Ubicación: España
Mensajes: 150
Antigüedad: 12 años, 9 meses
Puntos: 0
Respuesta: Doctrine 2 accediendo a campos de una relacion mediante lazy load

Hola, genial!! gracias!.

Código DQL:
Ver original
  1. SELECT c FROM Category c,
  2. JOIN c.parent p
  3. WHERE c.id > 1 AND c.activo = 1
  4. ORDER BY p.nombre DESC, c.visible DESC
Empece haciendolo asi pero hice distintas variaciones hasta llegar mas o menos a lo que viste, aunque ese metodo nisiquiera lo uso. Pero ok entendido, ahora que todo funciona bien y lo veo mas claro, retomo esta manera con JOIN. Gracias por la aclaracion.


Código PHP:
Ver original
  1. $where .= " AND p.id = :id";
  2. ...
  3. $query->setParameter('id', $id);

Ok, leere lo que me has dicho, pero ¿ podrias decirme algun pro principal o importante de usar el metodo setParameter o setParameters ? A simple vista no veo mucha diferencia.


A ver masterpuppet, lo de pasar los parametros del where en un parametro que es un array se puede hacer de mil maneras, se me pasan por la cabeza distintas maneras que se hacer y otras que no se hacer.

Me gustaria me dijeras tu como lo haces o me dieras las nociones basicas pues veo que controlas bastante y no quiero matarme la cabeza a hacerlo de una manera que puede ser bastante fea.

Aun asi te explico como tengo pensado hacerlo yo.
Como puedes observar yo paso un array con keys los atributos de la entidad y valores el valor para el filtrado/busqueda.
Esto podria funcionar si las consultas solo fueran filtradas por campos de la propia entidad, pero no es ese el caso.
Y es que se complica cuando quieres hacer un filtrado por un campo de una relacion. Pues la key ya no coincide con el nombre del campo de la relacion.
Es por eso que yo lo hago de la siguiente manera
Código PHP:
public function dameOrdenadoPor(array $whereparam = array() ){
    
$where '';
    if ( !empty(
$whereparam) && (!in_array(''$whereparam)) && (!in_array(null$whereparam)) ){
        foreach( 
$whereparam AS $akey => $avalue){
            if ( 
$akey == 'categoriapadre')
                
$where .= " AND p.id = :categoriapadre";
            else
                
$where .= " AND c.$akey LIKE :$akey";
        }
    }else
        throw new 
MiExcepcion('Error en paso de parametros');
    
$query $this->_em->createQuery"SELECT c FROM Entidad\Categoria c
                                        JOIN c.categoriapadre p
                                        WHERE c.id > 1 AND c.activo = true$where "
);
    
$query->setParameters$whereparam );
    return 
$query->getResult();

cuando la key es 'categoriapadre' que contiene la id de la categoria padre por la cual quiero filtrar, entonces tengo que escribir p.id = :categoriapadre
y para el resto ya la key coincide con el nombre del atributo con lo que
c.$akey LIKE :$akey

Pues eso, lo principal que quiero saber es una buena manera de pasar parametros y filtrarlos y hacer este tipo de metodos.

Última edición por chemajmb; 13/04/2012 a las 16:11

Etiquetas: campos, doctrine, lazy, load, mediante, relacion
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 18:33.