Doctrine ORM, ManyToMany - duplicates on lazy fetch - php

Symfony 2.8. When using default fetch mode duplicates are returned (why?), using fetch="EAGER" - everything is ok.
I have following objects:
/**
* #ORM\Entity()
*/
class User implements AdvancedUserInterface, \Serializable
{
(...)
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
private $role;
public function addRole(\WerbeoBundle\Entity\Role $role)
{
$this->role[] = $role;
return $this;
}
public function removeRole(\WerbeoBundle\Entity\Role $role)
{
$this->role->removeElement($role);
}
public function getRole()
{
return $this->role;
}
Role:
/**
* #ORM\Entity()
*/
class Role
{
(...)
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="role")
*/
private $users;
(... and getters/setters ...)
Now I have following table user_role:
user_id | role_id
1 | ADMIN
1 | EDITOR
When I call $user->getRole() result is
ADMIN
EDITOR
EDITOR
ADMIN
EDITOR
This happens in twig/controller only when using default fetch mode (lazy). When fetch="EAGER" everything is ok.
Any ideas what am I doing wrong?
Thanks

You have to check if an entry already exists before adding
public function __construct()
{
$this-role = new \Doctrine\Common\Collections\ArrayCollection()
}
public function addRole(\WerbeoBundle\Entity\Role $role)
{
if(!$this->role->contains($role)){
$this->role->add($role)
}
return $this;
}

Related

Attempt to update entity without persisting on it DOCTRINE

I have a Product entity with a ManyToOne relationship with Category.
use Doctrine\ORM\Mapping as ORM;
class Product
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Category")
* #ORM\JoinColumn(nullable=true)
*/
private $category;
}
class Category
{
use BlameableTrait;
...
}
The Category entity implements a trait, with properties to record when a record is created, updated, etc.
use App\Entity\User\User;
use Gedmo\Mapping\Annotation as Gedmo;
trait BlameableTrait
{
/**
* #Gedmo\Blameable(on="create")
* #ORM\ManyToOne(targetEntity="App\Entity\User\User")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* #var User
*/
private $createdBy;
/**
* #var User|null
*
* #Gedmo\Blameable(on="update")
* #ORM\ManyToOne(targetEntity="App\Entity\User\User")
* #ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $updatedBy;
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): self
{
$this->createdBy = $createdBy;
return $this;
}
public function setUpdatedBy(?User $updatedBy): self
{
$this->updatedBy = $updatedBy;
return $this;
}
public function getUpdatedBy(): ?User
{
return $this->updatedBy;
}
}
When I try to update the product entity I have an error indicating that an error has occurred while trying to update the Category entity, but I have not indicated that it should be updated.
This only happens to me in the production environment and randomly, one time it works, another time it doesn't.
Locally, debugging the symfony profiler, for the same curl, only one update is done on the Product entity, which is fine.
I don't understand where Symfony or Doctrine try to update the Category entity.
Both the production and local environments run on the same Docker image.

Symfony Doctrine2 manyToMany relationship not removed - Specific to SQLite

I have several classes using a Taggable trait to set up a tag system common to several doctrine entities (Project, Note, ...).
The relationship between these entities and these tags is a ManyToMany relationship that I can not make multi-directional.
My problem: When I delete a Project entity, it is removed from the project table, but the relationships in the project_tag table between this project and the tags are not deleted. Then, if I create a new Project entity, an exception is thrown.
An exception exists while executing 'INSERT INTO project_tag (project_id, tag_id) VALUES (?,?)' With params [2, 4]:
SQLSTATE [23000]: Integrity constraint violation: 19 UNIQUE constraint failed: project_tag.project_id, project_tag.tag_id
Entities :
Tag
/**
* Tag
*
* #ORM\Table(name="tag")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
*/
class Tag
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* #ORM\Column(name="last_use_at", type="datetime", nullable=false)
* #var \DateTime
*/
private $lastUseAt;
public function __construct()
{
$this->lastUseAt = new \DateTime();
}
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Tag
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #return \DateTime
*/
public function getLastUseAt(): \DateTime
{
return $this->lastUseAt;
}
/**
* #param \DateTime $lastUseAt
*/
public function setLastUseAt(\DateTime $lastUseAt)
{
$this->lastUseAt = $lastUseAt;
}
}
Taggable
trait Taggable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", cascade={"persist"})
*/
protected $tags;
/**
* Add tag
*
* #param Tag $tag
*
* #return $this
*/
public function addTag(Tag $tag)
{
$tag->setLastUseAt(new \DateTime());
$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* #param Tag $tag
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
Project
/**
* Project
*
* #ORM\Table(name="project")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository")
*/
class Project
{
use Taggable;
}
Note
class Note
{
use Taggable;
}
Is this the only solution or is my annotation incomplete / incorrect?
I tried with JoinColumns, JoinTable and onDelete = "cascade" but nothing works.
In the meantime, I dodged the problem with this instruction placed before the suppresion.
$project->getTags()->clear();
Full code of the action in the controller :
/**
* #Route("/project/{id}/delete", name="project_delete")
*/
public function deleteAction($id) {
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('AppBundle:Project')->find($id);
if(!$project) {
return $this->redirectToRoute('index');
}
$project->getTags()->clear();
$em->remove($project);
$em->flush();
return $this->redirectToRoute('index');
}
I think I found a better solution: you can set the PRAGMA within Doctrine configuration. Like:
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_sqlite'
#server_version: '5.7'
#charset: utf8mb4
#default_table_options:
#charset: utf8mb4
#collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
options:
'PRAGMA foreign_keys': 'ON'
I just tried it on my Symfony 4 application, re-created the database and tested using DB Browser for SQLite and it works as I expected.
Hope this helps
I managed to fix the problem. Here's my solution working for SQLite conections.
Create an eventListener listening on the kernel.request event :
namespace AppBundle\EventListener;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class RequestListener
{
/**
* #var Registry
*/
private $doctrine;
public function __construct(Registry $doctrine)
{
$this->doctrine = $doctrine;
}
public function onKernelRequest(GetResponseEvent $event)
{
$this->doctrine->getConnection()->exec('PRAGMA foreign_keys = ON');
}
}
Service declaration
app.event_listener.request_listener:
class: AppBundle\EventListener\RequestListener
arguments:
- '#doctrine'
tags:
- { name: kernel.event_listener, event: kernel.request }
I think the problem is that you have your trait Taggable set as the owning side of the ManyToMany relationship but your are deleting the inverse side and expecting something to happen as a result. Doctrine will only check the owning side of the relationship in order to persist any changes. See here for docs on this.
You can solve by making the Taggable the inverse side of each of your relationships, or by manually telling doctrine to delete the owning side.
The first solution will probably not work for you since you won't (easily) specify multiple inverse sides. (Are you sure a trait is the right way to go for this??)
The second solution is easy. In your entities like Project for your deleteTag($tag) function, call a delete function on the owning side (e.g., deleteProject($project). You will have to create if one does not exist.
class Project
{
use Taggable;
public function deleteTag($tag)
{
$this->tags->removeElement($tag);
// persist on the owning side
$tag->deleteProject($this);
}
}
EDIT:
After seeing full code, it looks like you are deleting correctly. Now you need to tell doctrine to carry that through. See this post for full details, but basically you can change your trait to this:
trait Taggable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(
* targetEntity="AppBundle\Entity\Tag",
* cascade={"persist"},
* onDelete="CASCADE"
* )
*/
protected $tags;
// ...
}

Traversing Doctrine2 ODM/MongoDB Object Graph in Symfony2 fails to populate properties in 2nd level object

I am converting my otherwise working Symfony2 application to use MongoDB through Doctrine-ODM. I have the vast majority of the system working, but I can't get the user roles portion working. I can login, but then there are no roles attached to the user.
The relevant document classes are here with everything stripped out except what is relevant.
User
<?php
namespace XXXXX\UserBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Doctrine\Common\Collections\ArrayCollection;
use XXXXX\UserBundle\Interfaces\UserInterface;
/**
*
* #MongoDB\Document( collection="user")
*
*/
class User implements UserInterface {
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\ReferenceMany(targetDocument="Group")
*/
protected $groups;
/**
* Constructor
*/
public function __construct() {
$this->groups = new ArrayCollection();
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
}
public function getRoles() {
$array = array();
//parse the roles down to an array
foreach ($this->getGroups() as $group) {
/* #var $group Group */
foreach ($group->getRoles() as $role) {
/* #var $role Role */
if(!$role->getName())
throw new \Exception('Role must exist in group: '.$group->getName().' with ID: '.$group->getId().'.');
$array[$role->getName()] = $role->getName();
}
}
sort($array);
return $array;
}
/**
* Get groups
*
* #return Doctrine\Common\Collections\Collection
*/
public function getGroups() {
return $this->groups;
}
}
Group
<?php
namespace XXXXX\UserBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use XXXXX\UserBundle\Interfaces\UserInterface;
use XXXXX\UserBundle\Interfaces\RoleInterface;
use XXXXX\UserBundle\Interfaces\GroupInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #MongoDB\Document( collection="user_group" )
*/
class Group implements GroupInterface {
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
* #var string
*/
protected $name;
/**
* #MongoDB\ReferenceMany(targetDocument="User")
*/
protected $users;
/**
* #MongoDB\ReferenceMany(targetDocument="Role", inversedBy="groups")
*/
protected $roles;
/**
* Constructor
*/
public function __construct() {
$this->users = new ArrayCollection();
$this->roles = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Get roles
*
* #return Doctrine\Common\Collections\Collection
*/
public function getRoles()
{
return $this->roles;
}
}
Role
<?php
namespace XXXXX\UserBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use XXXXX\UserBundle\Interfaces\UserInterface;
use XXXXX\UserBundle\Interfaces\GroupInterface;
use XXXXX\UserBundle\Interfaces\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #MongoDB\Document( collection="user_role")
*/
class Role implements RoleInterface {
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
* #var string
*/
protected $name;
/**
* #MongoDB\String
* #var string
*/
protected $description;
/**
* #MongoDB\ReferenceMany(targetDocument="Group", mappedBy="roles")
*/
protected $groups;
/**
* Set name
*
* #param string $name
* #return RoleInterface
*/
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName() {
return $this->name;
}
public function getId() {
return $this->id;
}
public function getDescription() {
return $this->description;
}
public function setDescription($description) {
$this->description = $description;
}
}
I use fixtures to load the data into the database, and the data in MongoDB is as follows. ( I stripped the additional data elements.)
User.
{ "_id" : ObjectId("5091a7241311fae01f00000d"), "groups" : [ DBRef("user_group", ObjectId("5091a7241311fae01f00000b")), DBRef("user_group", ObjectId("5091a7241311fae01f00000c")) ] }
Groups that are referenced by the User. (This is from the query that is run by Symfony2)
db.user_group.find({ "_id": { "$in": { "5091a7241311fae01f00000b":ObjectId("5091a7241311fae01f00000b"), "5091a7241311fae01f00000c": ObjectId("5091a7241311fae01f00000c") } } }).sort([ ]);
{ "_id" : ObjectId("5091a7241311fae01f00000b"), "name" : "Base.Users", "roles" : [ DBRef("user_role", ObjectId("5091a7241311fae01f000009")) ] }
{ "_id" : ObjectId("5091a7241311fae01f00000c"), "name" : "AdminPortal.Base", "roles" : [ DBRef("user_role", ObjectId("5091a7241311fae01f000009")), DBRef("user_role", ObjectId("5091a7241311fae01f00000a")) ] }
And finally, the roles referenced by the groups. (Also taken from the exact query being run by Symfony2)
db.user_role.find({ "_id": { "$in": { "5091a7241311fae01f000009": ObjectId("5091a7241311fae01f000009") } } }).sort([ ]);
{ "_id" : ObjectId("5091a7241311fae01f000009"), "name" : "ROLE_USER", "description" : "Role required for all system users." }
Further, the exception in the getRoles() function for the user is called and the following text is returned.
Role must exist in group: Base.Users with ID:
5091a7241311fae01f00000b.
The problem is that the roles are being queried from the database, but are not then being populated into the role object. I can verify that they are being loaded, as when I comment the exception, it will run and attempt to add the correct number of roles per group. The problem is that the name property of the role is set to NULL. The role object itself is a persisted and loaded object as when I do a print_r($role);exit; directly before the if statement, I will get the hugely recursive output that doctrine objects exhibit. The only thing that doesn't happen is that the "name" (and other) properties are not loaded from the database.
Any insight into how I can solve this would be greatly appreciated. Thanks.
I was able to determine a work-around. Basically, using the convientent functions like find, findBy, findOneBy, etc do not seem to be setting the objects up for traversing. I was able to get the correct result by modifying the loading function to use a querybuilder instead of the convenient function "findOneBy".
My modified query is below. Hopefully this helps somebody in the future.
/**
*
* #param string $username
* #return User|Null
*/
public function findUserByUserName($username) {
$qb = $this->createQueryBuilder();
$qb->find($this->getClassName());
$qb->field('username');
$qb->equals($username);
$query = $qb->getQuery();
return $query->getSingleResult();
}
I suppose it could be more concise, but I had to break it apart to debug it, and am moving on with my life. :)

Slow Doctrine Find

I am trying to figure out why one of my doctrine finds is running so slow. I don't really know where to start, so please bear with me.
I do a pretty basic find to fetch a user object. This find is taking ~160ms. When I run the query via phpmyadmin, it takes .7ms.
$this->em->find('Entities\User', $userId)
I have already tried adding skip-name-resolve to mysql's my.cnf. The id field in the user table is indexed. I really don't know what else to try. Let me know if there is additional information I can provide.
Below is the entity file:
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
/** #Entity(repositoryClass = "Entities\UserRepository")
* #Table(name="user")
*/
class User extends \Company_Resource_AbstractEntity
{
/** #Id #Column(type="integer") #GeneratedValue */
protected $id;
/** #Column(type="string") */
protected $name;
/** #Column(type="string") */
protected $password;
/** #Column(type="string") */
protected $email;
/** #Column(type="string") */
protected $first_name;
/** #Column(type="string") */
protected $last_name;
/** #Column(type="integer") */
protected $password_reset;
/** #Column(type="string") */
protected $salt;
/** #Column(type="integer") */
protected $active;
/** #Column(type="string") */
protected $cookie_hash;
/**
* #ManyToOne(targetEntity="Company" , inversedBy="user")
*/
protected $company;
/**
* #ManyToOne(targetEntity="Privilege" , inversedBy="user")
*/
protected $privilege;
/**
* #OneToMany(targetEntity="CompanySubscription" , mappedBy="user")
*/
protected $subscription;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_in_user")
*/
protected $check_in;
/**
* #OneToMany(targetEntity="EquipmentEvent" , mappedBy="check_out_user")
*/
protected $check_out;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_in_user")
*/
protected $check_in_group;
/**
* #OneToMany(targetEntity="GroupEvent" , mappedBy="check_out_user")
*/
protected $check_out_group;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="submit_user")
*/
protected $maintenance_submit;
/**
* #OneToMany(targetEntity="Maintenance" , mappedBy="completed_user")
*/
protected $maintenance_complete;
/**
* #OneToMany(targetEntity="UserLogin" , mappedBy="user")
*/
protected $login;
}
Abstract entity:
use \Doctrine\Common\Collections\ArrayCollection;
abstract class Company_Resource_AbstractEntity implements ArrayAccess
{
public function offsetExists($offset)
{
return property_exists($this, $offset);
}
// The get/set functions should check to see if an appropriately named function exists before just returning the
// property. This way classes can control how data is returned from the object more completely.
public function offsetGet($offset)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'get'. $property->filter($offset);
return $this->{$method}();
}
public function offsetSet($offset, $value)
{
$property = new Zend_Filter_Word_UnderscoreToCamelCase();
$method = 'set'. $property->filter($offset);
return $this->{$method}($value);
}
public function offsetUnset($offset)
{
// can't do this
}
/*==-====-====-====-====-====-====-====-====-====-====-==*/
/*
* Provides magic method access for getFieldName() and setFieldName()
* where field_name is a simple field and not a relation
* A special getData implementation returns all of the current object vars
*/
public function __call($method, $arguments)
{
preg_match('#^([a-z]+)(.*)#', $method, $matches);
$action = $matches[1];
$property = $matches[2];
$underscore = new Zend_Filter_Word_CamelCaseToUnderscore();
$offset = strtolower($underscore->filter($property));
if ($action == 'get')
{
if ($property == 'Data')
return get_object_vars($this);
if ($this->offsetExists($offset))
return $this->{$offset};
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else if ($action == 'set')
{
if ($this->offsetExists($offset))
return $this->{$offset} = $arguments[0];
else
throw new Zend_Exception(sprintf("'%s' does not have property '%s'", get_class($this), $offset));
}
else
throw new Zend_Exception(sprintf("'%s' does not have method '%s'", get_class($this), $method));
}
}
The SQL that the find produces:
SELECT t0.id AS id1,
t0.name AS name2,
t0.password AS password3,
t0.email AS email4,
t0.first_name AS first_name5,
t0.last_name AS last_name6,
t0.password_reset AS password_reset7,
t0.salt AS salt8,
t0.active AS active9,
t0.cookie_hash AS cookie_hash10,
t0.company_id AS company_id11,
t0.privilege_id AS privilege_id12
FROM user t0 WHERE t0.id = ?
Anyone see anything wrong or know where to go further with this?
Using Doctrine 2.2.2.
The explain I get when I run that query with phpmyadmin: http://i.imgur.com/wWeGO.png
The table schema: http://i.imgur.com/BQsRX.jpg
I believe the problem with my setup was the actual number of lines in the file. Doctrine was reading through those every time. I enabled APC for the meta-cache and load time decreased dramatically after the first load. Without query or result cache, that query ACTUALLY only takes about 6 MS which is what I was aiming for all along. Wish I would have tried that sooner.

Symfony2 : Many-To-Many with a custom link table

I'm working on a form with 3 entities :
order (idorder)
support reference table (idsupport)
link table (idorder, idsupport)
And when i try to select one or more support i got this error:
Catchable Fatal Error: Argument 1 passed to Myapp\MyBundle\Entity\PcastCmdsupports::setIdsupports() must be an instance of Myapp\MyBundle\Entity\PcastSupports, instance of Doctrine\Common\Collections\ArrayCollection given,
called in C:\wamp\www\php\Symfony\vendor\symfony\src\Symfony\Component\Form\Util\PropertyPath.php on line 347 and defined in C:\wamp\www\php\Symfony\src\Myapp\MyBundle\Entity\PcastCmdsupports.php line 62
Since i already created my link table i saw on the web that i can simply create 2 Many-To-One relation in my link table :
/**
* #var PcastSupports
*
* #ORM\ManyToOne(targetEntity="PcastSupports")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="IDSUPPORTS", referencedColumnName="IDSUPPORTS")
* })
*/
private $idsupports;
/**
* #var PcastOrder
*
* #ORM\ManyToOne(targetEntity="PcastOrder")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="IDORDER", referencedColumnName="IDORDER")
* })
*/
private $idorder;
and my setters and getters :
/**
* Set idsupports
*
*/
public function setIdsupports(\Myapp\MyBundle\Entity\PcastSupports $idsupports)
{
$this->idsupports = $idsupports;
}
/**
* Get idsupports
*
*/
public function getIdsupports()
{
return $this->idsupports;
}
/**
* Set idorder
*
*/
public function setIdcommande(\Myapp\MyBundle\Entity\PcastOrder $idorder)
{
$this->idorder = $idorder;
}
/**
* Get idorder
*
*/
public function getIdorder()
{
return $this->idorder;
}
In my order form i can choose one or many supports so i created my form like this:
$form_clips = $this->createFormBuilder($cmdclips)
->add('idorder', new CmdsupportsType)
->getForm();
And finally my supportsType form:
$builder
->add('idsupports', 'entity', array(
'class' => 'MyappMyBundle:PcastSupports',
'property' => 'name',
'expanded' => true,
'multiple' => true,
'query_builder' => function(EntityRepository $er)
{
return $er->createQueryBuilder('pts')
->orderBy('pts.idsupports','ASC');
},
));
I'm not using any arraycollection so i don't understand the issue. And the issue happened during this action:
$form_clips->bindRequest($request);
Thank a lot for your help !
I tried to make it work with the many-to-many relation in a simple case (user, company and a user_company entities) but i got a problem when i try to add a company to a user:
Warning: oci_bind_by_name() [<a href='function.oci-bind-by-name'>function.oci-bind-by-name</a>]: Invalid variable used for bind in C:\wamp\www\php\Promocast\Symfony\vendor\doctrine-dbal\lib\Doctrine\DBAL\Driver\OCI8\OCI8Statement.php line 113
I googling a lot but i didn't find anything on this error... According to stack trace the error is when doctrine try to add the company object :
array('column' => ':param10', 'variable' => object(PcastCompany), 'type' => '1')
My user entity (societe = company):
/**
* #ORM\ManyToMany(targetEntity="PcastSociete", inversedBy="users")
* #ORM\JoinTable(name="PcastLienusersociete",
* joinColumns={#ORM\JoinColumn(name="ImUser_iduser", referencedColumnName="iduser")},
* inverseJoinColumns={#ORM\JoinColumn(name="PcastLienusersociete_idsociete", referencedColumnName="idsociete")}
* )
*/
private $societes;
public function getSocietes()
{
return $this->societes;
}
public function addSociete(\Myapp\MyBundle\Entity\PcastSociete $societe)
{
$this->societes[] = $societe;
}
My company entity:
/**
* #ORM\ManyToMany(targetEntity="ImUser", mappedBy="societes")
*/
private $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
If anybody have any idea...
Thanks
You should not have an entity representing the link table. If you annotate both your entities correctly, Doctrine will handle the creation of the link table by itself.
Moreover, you do not need any link table to do a Many-to-One relationship in the first place, what you want to do is use the Many-to-Many annotations in both entities.
http://readthedocs.org/docs/doctrine-orm/en/latest/reference/association-mapping.html?highlight=many%20to%20one#many-to-many-bidirectional
Start with the basics. I was curious about something else concerning ManyToMany so I grabbed your entities as a test case. Before diving into forms and such, make sure you can execute a simple test case from the command line such as:
use Zayso\ArbiterBundle\Entity\PcastSociete as Company;
use Zayso\ArbiterBundle\Entity\ImUser as User;
protected function test1()
{
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
$company = new Company();
$em->persist($company);
$user = new User();
$user->addSociete($company);
$em->persist($user);
$em->flush();
}
For entities I used:
namespace Zayso\ArbiterBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class ImUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer",name="iduser")
* #ORM\GeneratedValue
*/
protected $id;
public function getId() { return $this->id; }
/**
* #ORM\ManyToMany(targetEntity="PcastSociete", inversedBy="users")
* #ORM\JoinTable(name="PcastLienusersociete",
* joinColumns={#ORM\JoinColumn(name="ImUser_iduser", referencedColumnName="iduser")},
* inverseJoinColumns={#ORM\JoinColumn(name="PcastLienusersociete_idsociete", referencedColumnName="idsociete")}
* )
*/
private $societes;
public function getSocietes()
{
return $this->societes;
}
public function addSociete(PcastSociete $societe)
{
$this->societes[] = $societe;
}
public function __construct()
{
$this->societes = new ArrayCollection();
}
}
namespace Zayso\ArbiterBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
*/
class PcastSociete
{
/**
* #ORM\Id
* #ORM\Column(type="integer", name="idsociete")
* #ORM\GeneratedValue
*/
protected $id;
public function getId() { return $this->id; }
/**
* #ORM\ManyToMany(targetEntity="ImUser", mappedBy="societes")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
}
Get the above working then we can move on to the forms problem.

Categories