Zend FR

Consultez la FAQ sur le ZF avant de poster une question

Vous n'êtes pas identifié.

#1 25-03-2014 18:21:43

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

ManyToOne, OneToMany et hydration

Salut!

J'ai constater, sans rien trouver sur les doc a ce sujet, que l'hydration ne fonctionne pas dans le sens OneToMany.

Je m'explique avec un exemple bidon :

Code:

[lang=php]
class Contact {

    /**
     * @ORM\Id
     */
    private $id;

    /**
     * @var Collection  
     * @ORM\OneToMany(targetEntity="address", mappedBy="contact") 
     */
    private $address;
    private $name;

}

class Address {

    /**
     * @var Contact
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="contact", inversedBy="address")
     */
    private $contact;
    private $city;

}

Y'a pas tout mais c'est jsute pour le consept.

si je fait:

Code:

[lang=php]
$address = new Address;
$data = array(
    'contact' => array(
        'id' => '',
        'name' => 'doctor Dumb'
    ),
    'city' => 'Dumby-Town'
);

$address = $myHydrator->hydrate($data, $address);

l'hydration fonctionne bien, mais si je fait:

Code:

[lang=php]
$contact = new Contact;
$data = array(
    'address' => array(
        array(
            'contact' => '',
            'city' => 'Dumby-town'
        ),
        array(
            'contact'=>'',
            'city'=>'Dumber-town'
        )
    ),
    'name' => 'doctor Dumb'
);

$contact = $myHydrator->hydrate($data, $contact);

La, $contact->getAddress() me retourne une collection vide sad
A moins que je place directement des objet Address dans le tableau $data. Ce qui m'oblige a faire des hydration intermédiaire...pas térible.

Moi je veut pouvoir hydraté dans ce sens. surtout que dans mon cas, la propriété $contact de Address ne me sert a rien, je passerai toujours pas contact pour avoir les address, ainsi que pour les ajouter...Mais pas moyen d'avoir un oneToMany sans un ManyToOne de l'autre coté.

j'ai lue quelque part une histoire de "owner side" et "inversed side", y a t'il un raport? si oui comment faire que la propriété $address de Contact devienne le "owner side"?

Dernière modification par Splyf (25-03-2014 18:23:21)

Hors ligne

 

#2 26-03-2014 01:49:02

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

Salut peux-tu mettre le code du constructeur de tes entités stp wink ?

Hors ligne

 

#3 26-03-2014 09:29:24

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

oh ?

bin quelque chose comme :

Code:

[lang=php]
class contact{
      public function __construct(){
            $this->recadres = new ArrayCollection();
      }
}

et je dirai rien pour le constructeur de adresse, $contact devans a priorie etre null au départ...
Après je n'est pas tester car c'est un exemple bidon, mais c'est en tout cas comme sa que Doctrine Command Tool les génères, sauf erreur de ma part.

Hors ligne

 

#4 26-03-2014 09:47:41

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

A tester si ça peut venir de là mais l'attribut du côté "Many" de la relation doit être instancié en tant que new ArrayCollection() comme c'est le cas pour l'exemple de constructeur que tu donnes. C'est possible que lorsque la valeur est null l'hydration ne fonctionne pas.
En tout cas ce qui est sûr c'est qu'elle fonctionne même dans le cas d'une ManyToOne ou OneToMany, je l'utilise régulièrement dans les formulaires et ça pose pas de soucis wink !

Hors ligne

 

#5 26-03-2014 11:36:04

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

Le coté many dans notre exemple c'est l'entité Contact, on est d'accord?

Dans mon model a moi c'est belle est bien le cas : /
Peut être le fait que le paramêtre du coté "One" est aussi l'ID ? il faut alors que la valeur de 'contact' est une valeur précisise dans le tableau $data?

Hors ligne

 

#6 26-03-2014 11:54:27

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

Oui c'est bien l'entité contact, c'est facile à voir en base c'est la table qui a la clé étrangère wink.

Je comprend pas ta deuxième phrase ... Et honnêtement t'aider sur un problème où tu donnes des exemples bidons sans toutes les infos c'est pas simple. Je sais pas si tu as bien définis les getters et setters, si ton constructeur est correct, quel hydrator tu utilises, dans quel cas tu as besoin d'utiliser l'hydration etc ... D'autant plus qu'avec Doctrine tu n'as pas besoin de l'utiliser "manuellement" l'hydratation se fait automatiquement. Donc si tu veux de l'aide pertinente donnes toutes les infos wink

Hors ligne

 

#7 26-03-2014 12:41:20

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

bon tu a raison, je vais utiliser les vrai entité après tout:
j'ai une table media qui contient des informations a propos des fichiers ajouter par l'admin, principalements des images.

Cest images peuvent être recadré de plusieurs manière différentes, une images a donc 0, n recadrages.
Coté recadrage, l'identifiant est composé du couple "MR_media" et "MR_ratio".
MR_media est donc a la foi clé primère et clé étrangère, pointant sur l'ID de la table media.
Voila le model résumé, maintenant les identités:

Dit moi si tu veut les autres paramêtre mais j'ai lassé que ceux qui nous interesse:

Code:

[lang=php]
<?php

namespace AlManakh\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;

/**
 * Media
 *
 * @ORM\Table(name="media", indexes={@ORM\Index(name="M_typeMed", columns={"M_type"})})
 * @ORM\Entity
 */
class Media {

    /**
     * @var integer
     *
     * @ORM\Column(name="M_id", type="integer", precision=0, scale=0, nullable=false, unique=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\OneToMany(targetEntity="AlManakh\Entity\Media\Recadre", mappedBy="media")
     */
    private $recadres;


    /**
     * Constructor
     */
    function __construct() {
        $this->recadres = new \Doctrine\Common\Collections\ArrayCollection ();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Add recadres
     *
     * @param Collection $recadres
     * @return Media
     */
    public function addRecadres(Collection $recadres) { 
        foreach ($recadres as $r) { 
            $this->recadres->add($r);
            $r->setMedia($this);
        }

        return $this;
    }

    /**
     * Remove recadres
     *
     * @param Collection $recadres
     */
    public function removeRecadres(Collection $recadres) {
        foreach ($recadres as $r) {
            $this->recadres->removeElement($r);
            $r->setMedia(null);
        }
    }

    /**
     * Get recadres
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getRecadres() {
        return $this->recadres;
    }

}


<?php

namespace AlManakh\Entity\Media;

use Doctrine\ORM\Mapping as ORM;

/**
 * Recadre
 *
 * @ORM\Table(name="mediaRecadre", indexes={@ORM\Index(name="MR_media", columns={"MR_media"})})
 * @ORM\Entity
 */
class Recadre {

    /**
     * @var string
     *
     * @ORM\Column(name="MR_ratio", type="string", length=5, precision=0, scale=0, nullable=false, unique=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $ratio;

    /**
     * @var integer
     * 
     * @ORM\Id
     * @ORM\Column(name="MR_media", type="integer", precision=0, scale=0, nullable=false, unique=false)
     * @ORM\GeneratedValue(strategy="NONE")
     */
    private $id;


    /**
     * @var \AlManakh\Entity\Media
     * 
     * @ORM\ManyToOne(targetEntity="AlManakh\Entity\Media", inversedBy="recadres")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="MR_media", referencedColumnName="M_id", nullable=true)
     * })
     */
    private $media;

    
    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

        /**
     * Set ratio
     *
     * @param string $ratio
     * @return Recadre
     */
    public function setRatio($ratio) {
        $this->ratio = $ratio;

        return $this;
    }

    /**
     * Get ratio
     *
     * @return string 
     */
    public function getRatio() {
        return $this->ratio;
    }

    /**
     * Set media
     *
     * @param \AlManakh\Entity\Media $media
     * @return Recadre
     */
    public function setMedia(\AlManakh\Entity\Media $media) {
        $this->media = $media;

        return $this;
    }

    /**
     * Get media
     *
     * @return \AlManakh\Entity\Media 
     */
    public function getMedia() {
        return $this->media;
    }

}

moi a écrit:

Peut être le fait que le paramêtre du coté "One" est aussi l'ID ? il faut alors que la valeur de 'contact' est une valeur précisise dans le tableau $data?

Ce que je voulai dire c'est que le problème venai peut être du fait que le parametre "media" de la table recadrage etait a la foi clé primaire et clé étrangère.

D'ailleur j'ai constater qu'en faite l'hydration d'un objet Recadre avec un tableau de donnée comportant une clé "media=>[id=>"", etc]" me donnai l'erreur: "Array to string convertion". j'ai donc décomposé en rajoutant le parametre Id comme tu peut voir, et la sa fonctionne dans ce sens.

Maintenant je fait:

Code:

[lang=php]
$data = array( 
            'name'=>'test Media',
            'recadres' => array(
                array(
                    'id' => '', 
                    'ratio' => '4:3' 
                ),
                array(
                    'id' => '',
                    'ratio' => '4:3' 
                )
            )
        ); 
$media = new \AlManakh\Entity\Media;
$media = $this->getHydrator()->hydrate($data, $media);

echo count($media->getRecadres());

//affiche 0

voila, $media->getRecadres me retourn une collection vite sad

Le problème peut il venir du faite que l'id de recadrage doit être égal a celle du media?

Dernière modification par Splyf (26-03-2014 12:57:55)

Hors ligne

 

#8 26-03-2014 12:55:32

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

ma dernière phrase ma fait tester quelque chose:
Dans mon tableau, j'ai remplis les identifiant des recadrages avec des valeur existant dans la bdd:

Code:

[lang=php]

$data = array(  
            'name'=>'test Media',
            'recadres' => array(
                array(
                    'id' => 2217, 
                    'ratio' => '4:3'
                ),
                array(
                    'id' => 2217,
                    'ratio' => '16:10'
                )
            )
        );

et la sa marche!
il faut donc ajouter obligatoirement des recadrages qui existe, mais si je veut les créer en même temps qu'un Media, pas possible?
En plus a plusieurs endroit de ma bdd est j'ai des éritages, des relations 1,1 -> 1,1...donc je vais souvent être confronté a ce pb :s

Dernière modification par Splyf (26-03-2014 12:56:46)

Hors ligne

 

#9 26-03-2014 13:44:33

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

Ce que je comprend pas c'est pourquoi tu hydrates toi même à la main l'objet ??? Pourquoi ne pas directement utiliser les getters et setters ? Tu récupères déjà les "Recadre" depuis la base de données pourquoi ne pas simplement faire un $media->addRecardre($recadre); ?

Cela dit il te manque le setter sur recadre dans media (pour remplacer toute la collection) et il te manque le addRecadre pour ajouter une entité. Ensuite pour les id normalement t'en a pas besoin tu peux très bien en rajouter met plutôt null à la place de chaîne vide et il te faut un persist cascade sur la relation wink.

Hors ligne

 

#10 26-03-2014 14:22:36

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

Orkin a écrit:

Ce que je comprend pas c'est pourquoi tu hydrates toi même à la main l'objet ??? Pourquoi ne pas directement utiliser les getters et setters ? Tu récupères déjà les "Recadre" depuis la base de données pourquoi ne pas simplement faire un $media->addRecardre($recadre); ?

non non justement mon dernière code est juste un test, je veut pouvoir créer les recadrages en même temps que le media.
je vais recevoir les données en POST depuis un formulaire mais pour certain cas en ajax, dans un tableau tel que montrer plus haut, et je veut faire mon insertion avec un simple

Code:

[lang=php]
$media = $this->getHydrator()->hydrate($data, $media);
$this->em->persist($media)

le tableau recu serra de la forme:


Code:

[lang=php]
$data = array( 
            'name'=>'test Media',
                    //...ect
            'recadres' => array(
                array(
                    'id' => '', 
                    'ratio' => '4:3'
                    //...ect
                ),
                array(
                    'id' => '',
                    'ratio' => '16:10'
                    //...ect
                )
            )
        );

je vien donc de rajouter le setter, addRecadre (sans s), ainsi que la directive cascade="persist", rien y fait! sad


Edit: en faite j'ai un addRecadres au lieu de addRecadre suite a ce qui est spécifier dans la doc, car sans sa j'ai une erreur a l'hydration: l'hydrator passe une collection, et non un object, et veut la fonction addRecadres, non addRecadre.

Dernière modification par Splyf (26-03-2014 14:30:03)

Hors ligne

 

#11 26-03-2014 14:26:03

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

C'est dommage de te galérer autant alors qu'en configurant correctement ton composant formulaire il va te retourner directement l'objet au lieu d'un tableau (avec toutes les valeurs déjà hydratés) et tu n'auras plus qu'à persister l'objet.

Du coup là je vois pas ce qui cloche, ça doit pas être bien méchant mais il a pas besoin d'avoir l'id pour hydrater, il parcourt le tableau et appel les setters en fonction des clés du tableau ...

Par contre le persiste dans le contrôleur c'est mal wink.

Hors ligne

 

#12 26-03-2014 14:51:59

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

C'est dommage de te galérer autant alors qu'en configurant correctement ton composant formulaire il va te retourner directement l'objet au lieu d'un tableau (avec toutes les valeurs déjà hydratés) et tu n'auras plus qu'à persister l'objet.

Arf j'y avait pas pensé.
En fait je refond toute une application et je me concentre pour le moment sur la couche model:
je fait des test unitaire pour voir si mes repo fonctionne comme prévu, et je test au passage l'hydration pour voir si j'ai bien redéfini correctement mes relations (car la définition faite automatiquement par doctrine command tool) ne me vas pas du tout dans bien des cas) - je pensais de plus que les formulaires utilise l'hydration et du coup il me semblai important de tester la chose

En faite j'ai l'impression que le problème c'est que je veut créer les recadres en même temps que le media. (les recadrages étant propre a chaque media).
Dans le cas des relations 1,1--> 1,1, je vais avoir le même problème.
Mais si tu me dit que les formulaire font sa tout seul...je me casse la tête pour rien.
Le truck, c'est que j'ai beaucoup d'ajax dans mon APP. dans ce cas si je pensais passé outre les formulaires, pour économisé en traitement.

Pourquoi le persiste dans le controlleur c'est mal? faut le faire ou ?

Dernière modification par Splyf (26-03-2014 14:56:29)

Hors ligne

 

#13 26-03-2014 15:15:17

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

Dans le cas d'ajax tu peux utiliser juste l'input filter pour filtrer les données c'est fait pour ça. Par contre à voir si quand tu récupères les données elles sont hydratés ou non. J'ai un trou. Dans le pire des cas rien ne t'empêche de créer un formulaire (sans csrf du coup) et ne pas l'afficher et l'utiliser uniquement lorsque tu reçois un post.

Ben faut découper les couches sinon tu peux faire toute ton application dans une seule classe avec une seule fonction wink. Faut faire une couche service qui va s'occuper de faire les traitements qui sont en rapport avec la base de données ou des traitements métiers. Par exemple lorsque tu veux mettre à jour une entité on peut imaginer que tu veux indiquer la date de modification, qui a modifié, éventuellement ajouter des informations dans un fichier ou une table de log. Tu peux effectivement le faire dans ton contrôleur sauf que si tu as besoin de le réutiliser plus tard tu vas sois devoir faire un copier/coller de ton code (bien dégueux) soit injecter le contrôleur qui contient la fonction qui le faire dans un autre contrôleur (c'est pas top non plus), soit passer par un forward si l'action ne fait que ça mais c'est rarement le cas. Donc la solution c'est de déporter ça dans une couche service qui elle a accès à l'entity manager.
Il me semble d'ailleurs avoir vu que pour le ZF3 il est prévu de retirer l'injection automatique du service locator dans les contrôleurs car beaucoup trop de monde ne l'utilise pas correctement. Et dans ton cas tu t'en sers pour récupérer l'entity manager alors que c'est pas sa place. Il est prévu pour les contrôleurs de faire des fabriques pour injecter les services, qui eux ont accès au service locator et donc à l'entity manager. D'ailleurs pour faire vraiment encore plus propre on pourrait directement injecter l'entity manager directement dans le service à la place du service locator. Faut bien garder en tête que le service locator permet avant tout de faire de l'injection de dépendances pas de récupérer ce qu'on un peut partout wink

Hors ligne

 

#14 26-03-2014 15:31:48

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

Je pense que je vais plaquer les test unitaire sur l'hydration pour le moment en effet.

Je sais je suis chian, mai j'aime bien chercher la petite bête quand je début dans un truck smile
Ne serai-ce pas une histoire de owning side et inverse side?
http://doctrine-orm.readthedocs.org/en/ … tions.html

OneToMany is always the inverse side of a bidirectional association.

Doctrine will only check the owning side of an association for changes.

Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine’s point of view)

Ptet sa a aucun raport je sais pas. Je veut savoir, je trouverai! x)

Concernant le persite:

Okay bon a savoir sa !
Mais c'est dingue, dans tout les tuto et doc dans les exemple il font sa dans les controlleur!

il pourai au moins précisé qu'il on simplfié sa pour l'exemple...
Moi dans chaque module je me faisais un controller contenant toute les routine du genre, et tout les autres étende ce controller x)

Hors ligne

 

#15 26-03-2014 16:29:00

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

L'hydration n'a rien à voir avec doctrine. Le truc du inverse side et owning side tu le gères déjà dans ton add*. C'est juste que quand tu ajoutes ajouter un objet à une collection il ne fait pas lui même le lien entre l'objet ajouté et l'objet auquel tu l'ajoutes :

Code:

[lang=php]
public function addB(B $b) {
$this->add($b);
$b->setA($this); // ça c'est à toi de le faire en fait si tu ne le fais pas quand tu feras $b->getA() tu n'auras pas $b par contre si tu fais $a->getB() tu auras $b dans la liste
}

Parce que les tutos n'ont pas pour vocation de faire de l'architecture. Ils se basent sur le MVC classique après c'est à toi de savoir comment architecturer ton application pour qu'elle soit propre. Je suis même pas sûr que dans la doc de Symfony2 ça soit détaillé non plus. Pourtant c'est des architecture classique.

Je faisais ça aussi dans un contrôleur et j'étendais avec les autres, le problème c'est que niveau perf tu charges des trucs dont t'as pas besoin ...

Hors ligne

 

#16 26-03-2014 16:51:28

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

C'est le gros danger de vouloir apprendre tout seul , au saute parfois des étapes. x)
Mais c'est aussi pour sa que je suis chian ^^ .

voila pourquoi je ne trouve rien dans la doc a propos de l'hydration, d'ailleur maintenant que tu le dit je me souvient que c'est dans la doc de DoctrineModule...

Bon, j'irrai voir dedans demain.
Merci encore une foi pour ton aide et tes éclaircissement en tout cas!

Hors ligne

 

#17 26-03-2014 17:16:33

Orkin
Administrateur
Lieu: Paris
Date d'inscription: 09-12-2011
Messages: 1261

Re: ManyToOne, OneToMany et hydration

Splyf a écrit:

C'est le gros danger de vouloir apprendre tout seul , au saute parfois des étapes. x)

C'est largement faisable je me suis formé tout seul sur le ZF2, avec l'aide de ce forum et de quelques contacts et au final je suis certifié ZF2 wink

Hors ligne

 

#18 26-03-2014 17:21:00

Splyf
Membre
Date d'inscription: 24-10-2013
Messages: 115

Re: ManyToOne, OneToMany et hydration

wo la classe ^^
Cest pour sa que même si je n'en est pas réelmenet besoin, il faut que je trouve pourquoi je ne peut pas faire l'hydration que je veut...:p

Hors ligne

 

Pied de page des forums

Propulsé par PunBB
© Copyright 2002–2005 Rickard Andersson
Traduction par punbb.fr

Graphisme réalisé par l'agence Rodolphe Eveilleau
Développement par Kitpages