Reputation: 183
I want to create a form for a simple entry management. I got the entities Entry, EntryUser, and User. Tables in database: entry, entry_user and user.
I think i am close to be successful, but i got some problems here.
In my form, the author can check the users he want to add to the entry via checkboxes. Symfony/Doctrine should do the relation work for me and add rows into entry_user. One row for one selected checkbox.
Entry.php
/**
* @ORM\Entity
* @ORM\Table(name="entry")
*/
class Entry {
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="EntryUser", mappedBy="entry", cascade={"all"}, orphanRemoval=true)
*/
protected $users;
function __construct() {
$this->users = new ArrayCollection();
}
/**
* Add user
*
* @param User $user
* @return $this
*
*/
public function addUser(User $user)
{
if (!$this->users->contains($user)) {
$this->users->add($user);
}
return $this;
}
/**
* Remove user
*
* @param User $user
* @return $this
*/
public function removeUser(User $user)
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
}
return $this;
}
/**
* @return array
*/
public function getUsers()
{
return $this->users->toArray();
}
/**
* Returns the true ArrayCollection of Users.
* @return Doctrine\Common\Collections\ArrayCollection
*/
public function getUsersCollection()
{
return $this->users;
}
}
User.php
/**
* @ORM\Entity
* @ORM\Table(name="user")
*/
class User implements AdvancedUserInterface, EquatableInterface, \Serializable {
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=255)
*/
protected $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
// Many other methods for user sign in, roles and so on...
}
EntryUser.php
/**
* @ORM\Entity
* @ORM\Table(name="entry_user")
*/
class EntryUser {
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var integer
*
* @ORM\Column(name="user_id", type="integer", nullable=true)
*/
protected $user_id;
/**
* @var integer
*
* @ORM\Column(name="entry_id", type="integer", nullable=true)
*/
protected $entry_id;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="User", cascade={"persist"})
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $user;
/**
* @var Entry
*
* @ORM\ManyToOne(targetEntity="Entry", inversedBy="users")
* @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $entry;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set user_id
*
* @param integer $userId
* @return EntryUser
*/
public function setUserId($userId)
{
$this->user_id = $userId;
return $this;
}
/**
* Get user_id
*
* @return integer
*/
public function getUserId()
{
return $this->user_id;
}
/**
* Set entry_id
*
* @param integer $entryId
* @return EntryUser
*/
public function setEntryId($entryId)
{
$this->entry_id = $entryId;
return $this;
}
/**
* Get entry_id
*
* @return integer
*/
public function getEntryId()
{
return $this->entry_id;
}
/**
* Set user
*
* @param User $user
* @return EntryUser
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* Set entry
*
* @param Entry $entry
* @return EntryUser
*/
public function setEntry(Entry $entry = null)
{
$this->entry = $entry;
return $this;
}
/**
* Get entry
*
* @return Entry
*/
public function getEntry()
{
return $this->entry;
}
}
I use ManyToOne relationships here because i want to use a file named EntryUser.php to add some custom fields later. I need this because i must store some additional data there in entry_user.
Because:
And it's the right thing to do. Create a new entity with the new fields, and if you need it, create a custom repository to add the methods you need.
A <--- Many to many with field ---> B
would become
A --One to many--> C (with new fields) <-- One to many--B
and of course, C has ManyToOne relationships with both A and B.
See the comments in: ManyToMany relationship with extra fields in symfony2 orm doctrine
My EntryType.php form definition includes the following, to create the checkboxes for the template:
$builder->add('users', 'entity', array(
'class' => 'MyCompMyAppBundle:User',
'multiple' => true,
'expanded' => true,
'property' => 'name',
'label' => 'Freunde',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')->select('a')
->from('MyComp\MyAppBundle\Entity\User', 'a')
->where('EXISTS (
SELECT b
FROM MyComp\MyAppBundle\Entity\UserFriend b
WHERE b.created_by = :my_user_id
AND b.friend_user_id = a.id
)')
->andWhere('EXISTS (
SELECT c
FROM MyComp\MyAppBundle\Entity\UserFriend c
WHERE c.created_by = a.id
AND c.friend_user_id = :my_user_id
)')
->setParameter('my_user_id', $this->user->getId());
},
'required' => true,
));
As you can see, i load User objects here for the form field (type: entity). UserFriend is another entity (table: user_friend). A friend list is saved there. Here all the friends gets loaded. They will be shows as the checkboxes.
Now, if i go to my form in my browser, and check some users for the entry und if i submit the form, i get this error:
ORMException: Found entity of type MyComp\MyAppBundle\Entity\User on association MyComp\MyAppBundle\Entity\Entry#friends, but expecting MyComp\MyAppBundle\Entity\EntryUser
So it is very confusing. How can i make this work?
How can i make symfony and doctrine work to insert data automatically into entry and entry_user?
Important: I want to make it possible that the author can edit the entry later. So the checkboxes should be selected by default as it is saved in entry_user.
I just try to understand this and got a little bit confused. But i dont know how to make this work.
Upvotes: 1
Views: 7081
Reputation: 1235
For this case you should check "form collections" in Symfony: http://symfony.com/doc/current/cookbook/form/form_collections.html
With this technique you will add a form type to crate a single "EntryUser" and after that you can add a collection of that form to the parent form. Quite easy and well explained in the liked article.
Upvotes: 0