I'm new to symfony2 and forms are quite tough to get my mind around.
This topic has been covered before, but mainly to do with the relationship aspect. I'm having issues with the form and how to manage the saving of the relationships. The scenario being A user has many friends who are users. So a self referencing many-to-many relationship.
I'm using FOSUser Bundle and have a friendship entity. Here is the YAML for creating the entities.
MH\FriendshipBundle\Entity\Friendship:
type: entity
table: mh_friendship
repositoryClass: MH\FriendshipBundle\Entity\FriendshipRepository
id:
id:
type: integer
generator:
strategy: AUTO
fields:
requested_at:
type: datetime
gedmo:
timestampable:
on: create
is_accepted:
type: boolean
nullable: true
accepted_at:
type: datetime
nullable: true
manyToOne:
user:
targetEntity: MH\UserBundle\Entity\User
inversedBy: user_friends
joinColumn:
name: user_id
referencedColumnName: id
friend:
targetEntity: MH\UserBundle\Entity\User
inversedBy: friend_users
joinColumn:
name: friend_id
referencedColumnName: id
lifecycleCallbacks:
prePersist: [ ]
postPersist: [ ]
preUpdate: [ ]
postUpdate: [ ]
MH\UserBundle\Entity\User:
type: entity
table: mh_user
repositoryClass: MH\UserBundle\Entity\UserRepository
id:
id:
type: integer
generator:
strategy: AUTO
fields:
first_name:
type: string
length: 100
nullable: true
last_name:
type: string
length: 100
nullable: true
created_at:
type: datetime
gedmo:
timestampable:
on: create
updated_at:
type: datetime
gedmo:
timestampable:
on: update
oneToMany:
user_friends:
targetEntity: MH\FriendshipBundle\Entity\Friendship
mappedBy: user
cascade: ["persist", "remove"]
friend_users:
targetEntity: MH\FriendshipBundle\Entity\Friendship
mappedBy: friend
cascade: ["persist", "remove"]
friend_groups:
targetEntity: MH\FriendshipBundle\Entity\FriendGroup
mappedBy: owner
cascade: ["persist", "remove"]
lifecycleCallbacks:
prePersist: [ ]
postPersist: [ ]
preUpdate: [ ]
postUpdate: [ ]
Now i have a form that was create via the crud generator from the Friendship resource, and this is what i'm doing.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('friend', 'entity', array(
'class' => 'UserBundle:User',
'property' => 'fullName',
'expanded' => true,
'multiple' => true,
'required' => false,
))
;
}
Which renders a list of checkboxes of users that i can select and save as "friends".
My problems are :
PHP Catchable fatal error: Argument 1 passed to MH\\FriendshipBundle\\Entity\\Friendship::setFriend() must be an instance of MH\\UserBundle\\Entity\\User, instance of Doctrine\\Common\\Collections\\ArrayCollection given, called in /Users/mohammedhamad/Sites/sociabills/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 345 and defined in /Users/mohammedhamad/Sites/sociabills/src/MH/FriendshipBundle/Entity/Friendship.php on line 155, referer: http://sociabills.local/friends/new
Not sure how to make sure that the chosen are "friends" are passed to the setFriends method which expects a collection, and not the setUser method which expects a User object.
Looking at the doc
/**
* @ManyToMany(targetEntity="User", mappedBy="myFriends")
**/
private $friendsWithMe;
/**
* @ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
* @JoinTable(name="friends",
* joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="friend_user_id", referencedColumnName="id")}
* )
**/
private $myFriends;
public function __construct() {
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
The YML equivalent of the annotations would be (keep in mind I'm not used to yml for entity mapping and this is not tested)
manyToMany:
myFriends:
targetEntity: User
inversedBy: friendsWithMe
joinTable:
name: friends
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
friends_user_id:
referencedColumnName: id
friendsWithMe:
targetEntity: User
mappedBy: myFriends
Then you can implement addMyFriend($friend)
method and removeMyFriend($friend)
in your class
public function addMyFriend($friend){
$this->myFriends[] = $friend;
}
public function removeMyFriend($friend)
{
$this->myFriends->removeElement($friend);
}
Issue 2 was answered by @Darragh
As I'm sure you noticed from the error, the first issue is occurring because setFriend()
expects a single User
entity, but is instead receiving an ArrayCollection
of one or more User
entities.
This was discussed here in a blog post by Bernhard Schussek, the creator of the form component. I assume that you are using version >= 2.1? If so, the you need to add some additional methods:
public function addFriend(User $friend)
{
$this->friend[] = $friend;
}
and:
public function removeFriend(User $friend)
{
$this->friends->removeElement($friend);
}
My understanding is that these methods are called for every element in the ArrayCollection
. I am looking at a similar example in one of my repos and this is what is implemented in one of my entities.
This is available in version >= 2.1.
To populate a form entity
element with custom choices, you can use the query_builder
option. A quick cut and paste example from my codebase here:
->add('client', 'entity', array(
'class' => 'HooluxDAPPUserBundle:Organisation',
'property' => 'name',
'query_builder' => function(EntityRepository $er) use ($builder) {
if ($builder->getData()->hasAdvertiser()) {
return $er->buildQueryClientsForAdvertiser(
$builder->getData()->getAdvertiser()
);
}
return $er->createQueryBuilder('c');
}
))
The query_builder
option accepts an anon function/closure that receives a reference to the repository class for your Entity. You can call any custom method that returns a query builder object.
Edit
In your controller:
// pass current user's own id as an option
$this->createForm(new FriendType(), $entity, array('user_id' => $this->getUser()));
In the buildForm
method:
public function buildForm($builder, $options) {
$builder
->add('friend', 'entity', array(
'class' => 'UserBundle:User',
'property' => 'fullName',
'expanded' => true,
'multiple' => true,
'required' => false,
// closure, include $options from parent scope with `use`
'query_builder' => function(EntityRepository $er) use ($options) {
// current user's own id
$userId = $options['user_id'];
// call custom method on UserRepository class
return $er->getUsersWhereIdIsNot($userId); // returns QueryBuilder object
}
));
}
Then add a method that returns all users WHERE id != :id
or whatever.
Hope this helps :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.