Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
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 :
[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:
[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:
[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
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
Salut peux-tu mettre le code du constructeur de tes entités stp ?
Hors ligne
oh ?
bin quelque chose comme :
[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
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 !
Hors ligne
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
Oui c'est bien l'entité contact, c'est facile à voir en base c'est la table qui a la clé étrangère .
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
Hors ligne
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:
[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:
[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
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
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:
[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
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 .
Hors ligne
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
[lang=php] $media = $this->getHydrator()->hydrate($data, $media); $this->em->persist($media)
le tableau recu serra de la forme:
[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!
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
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 .
Hors ligne
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
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 . 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
Hors ligne
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
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
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 :
[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
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
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
Hors ligne
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