i have two entities Topic
and TopicContent
when i send data from Topic entity, all ok. But when i included TopicContentType to my form builder, a have an error.. Friends, help me please..
My controller
public function createAction(Request $request)
{
$entity = new Topic();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('application_club_show', array('id' => $entity->getTopicId())));
}
return $this->render('ApplicationClubBundle:Topic:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
$builder
->add('user_id', 'hidden')
->add('topic_title')
->add('topic_tags')
->add('topic_publish', 'hidden')
->add('topic_user_ip', 'hidden')
->add('topic_count_read', 'hidden')
->add('topic_count_comment', 'hidden')
;
$builder->add('topic_content', new \Application\ClubBundle\Form\TopicContentType());
TypicType
$builder
->add('user_id', 'hidden')
->add('topic_title')
->add('topic_tags')
->add('topic_publish', 'hidden')
->add('topic_user_ip', 'hidden')
->add('topic_count_read', 'hidden')
->add('topic_count_comment', 'hidden')
;
$builder->add('topic_content', new \Application\ClubBundle\Form\TopicContentType());
TopicContentType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('topic_id')
->add('topic_text')
;
}
TopicContent Entity
<?php
namespace Application\ClubBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class TopicContent
{
/**
*
* #var integer
*/
private $topic_id;
/**
* #var string
*/
private $topic_text;
/**
* #var \Application\ClubBundle\Entity\Topic
*/
private $topic;
/**
* Set topic_id
*
* #param integer $topicId
* #return TopicContent
*/
public function setTopicId($topicId)
{
$this->topic_id = $topicId;
return $this;
}
/**
* Get topic_id
*
* #return integer
*/
public function getTopicId()
{
return $this->topic_id;
}
/**
* Set topic_text
*
* #param string $topicText
* #return TopicContent
*/
public function setTopicText($topicText)
{
$this->topic_text = $topicText;
return $this;
}
/**
* Get topic_text
*
* #return string
*/
public function getTopicText()
{
return $this->topic_text;
}
/**
* Set topic
*
* #param \Application\ClubBundle\Entity\Topic $topic
* #return TopicContent
*/
public function setTopic(\Application\ClubBundle\Entity\Topic $topic = null)
{
$this->topic = $topic;
return $this;
}
/**
* Get topic
*
* #return \Application\ClubBundle\Entity\Topic
*/
public function getTopic()
{
return $this->topic;
}
}
Topic Entity
/**
* Add topic_content
*
* #param \Application\ClubBundle\Entity\TopicContent $topicContent
* #return Topic
*/
public function setTopicContent(\Application\ClubBundle\Entity\TopicContent $topicContent)
{
$this->topic_content[] = $topicContent;
return $this;
}
my entities relatoins
Topic
Application\ClubBundle\Entity\Topic:
type: entity
table: topic
id:
topic_id:
type: integer
generator: { strategy: AUTO }
fields:
topic_title:
type: string
length: 200
oneToMany:
topic_content:
targetEntity: TopicContent
mappedBy: topic
topic_tag:
targetEntity: TopicTag
mappedBy: topic
cascade: ["persist"]
TopicContent
Application\ClubBundle\Entity\TopicContent:
type: entity
table: topic_content
id:
topic_id:
type: integer
fields:
topic_text:
type: text
oneToOne:
topic:
targetEntity: Topic
inversedBy: topic_content
joinColumn:
name: topic_id
referencedColumnName: topic_id
cascade: ["persist"]
You have the answer in the error message displayed by Symfony: you need to persist the Topic entity AND the TopicContent entity. To do that you must define a cascade={"persist"} on the relation.
Edit:
Another way to persist the TopicContent would be to do it explicitly in the form processing function:
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$content = $entity->getTopicContent();
$em->persist($entity);
$em->persist($content);
$em->flush();
return $this->redirect($this->generateUrl('application_club_show', array('id' => $entity->getTopicId())));
}
Related
I'm trying to get a smooth admin interface similar as if when two entities are associated by a many-to-many relationship. I need the join table to define additional information like rank. I don't want to display the jointable entity on the backend, it should be writeable in the edit-menu of at least one entity. My example:
class Question{
/**
* #Assert\NotBlank()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Options2Questions", mappedBy="question", cascade={"persist"})
*/
private $optionQuestion;
public function __construct() {
$this->optionQuestion = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion[] = $optionQuestion;
return $this;
}
public function removeOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion->removeElement($optionQuestion);
}
public function getOptionQuestion(){
return $this->optionQuestion;
}
}
class Options{
/**
* #Assert\NotBlank()
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Options2Questions", mappedBy="options")
*/
private $optionQuestion;
public function __construct(){
$this->optionQuestion = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion){
$this->optionQuestion[] = $optionQuestion;
return $this;
}
public function removeOptionQuestion(\AppBundle\Entity\Options2Questions $optionQuestion)
{
$this->optionQuestion->removeElement($optionQuestion);
}
public function getOptionQuestion(){
return $this->optionQuestion;
}
}
class Options2Questions
{
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Options", inversedBy="optionsQuestions")
* #ORM\JoinColumn(name="options_id", referencedColumnName="id", nullable=false)
*/
private $options;
/**
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Question", inversedBy="optionsQuestions")
* #ORM\JoinColumn(name="question_id", referencedColumnName="id", nullable=false)
*/
private $question;
/**
* #var int
*
* #ORM\Column(name="rank", type="integer", nullable=true)
*/
private $rank;
/**
* Set rank
*
* #param integer $rank
*
* #return Options2Questions
*/
public function setRank($rank)
{
$this->rank = $rank;
return $this;
}
/**
* Get rank
*
* #return int
*/
public function getRank()
{
return $this->rank;
}
/**
* Set options
*
* #param \AppBundle\Entity\Options $options
*
* #return Options2Questions
*/
public function setOptions(\AppBundle\Entity\Options $options)
{
$this->options = $options;
return $this;
}
/**
* Get options
*
* #return \AppBundle\Entity\Options
*/
public function getOptions()
{
return $this->options;
}
/**
* Set question
*
* #param \AppBundle\Entity\Question $question
*
* #return Options2Questions
*/
public function setQuestion(\AppBundle\Entity\Question $question)
{
$this->question = $question;
return $this;
}
/**
* Get question
*
* #return \AppBundle\Entity\Question
*/
public function getQuestion()
{
return $this->question;
}
}
I could not find any documentation on this. Where should I look specifically?
First of all you have to create the admin class - service for the all of these three entities. Therefore you will have the next 3 classes:
a) QuestionAdmin
b) OptionAdmin
c) Options2QuestionsAdmin
If you want to remove from you admin services list the Options2QuestionsAdmin - you can add show_in_dashboard: false line to the services.yml:
app.admin.options2questions:
class: AppBundle\Options2questionsAdmin
arguments: [~, AppBundle\EntityOptions2questions, SonataAdminBundle:CRUD]
tags:
- { name: sonata.admin, manager_type: orm, group: 'your_group', label: 'your_label', show_in_dashboard: false }
In your QuestionAdmin class in formmapper method you can add this:
class QuestionAdmin extends AbstractAdmin
{
// ...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('optionQuestion', 'sonata_type_collection', [
'required' => true,
'label' => 'option_question',
'by_reference' => false,
'btn_add' => 'add_button',
'type_options' => [
'delete' => true,
],
], [
'edit' => 'inline',
'inline' => 'standard',
'sortable' => 'id',
'allow_delete' => true,
])
;
}
I have a little problem with my image upload, can u help me please:
Could not determine access type for property "file".
Controller
/**
* Creates a new Produits entity.
*
*/
public function createAction(Request $request)
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('adminProduits_show', array('id' => $entity->getId())));
}
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a form to create a Produits entity.
*
* #param Produits $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Produits $entity)
{
$form = $this->createForm(ProduitsType::class, $entity);
$form->add('submit', SubmitType::class, array('label' => 'Ajouter'));
return $form;
}
/**
* Displays a form to create a new Produits entity.
*
*/
public function newAction()
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Form
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, array('data_class' => null))
->add('name', TextType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ecommerce\EcommerceBundle\Entity\Media'
));
}
/**
* #return string
*/
public function getName()
{
return 'ecommerce_ecommercebundle_media';
}
Entity
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(type="string",length=255, nullable=true)
*/
private $path;
/**
* #Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpg", "image/bmp"},
* mimeTypesMessage = "Please upload a valid PDF"
* )
*/
public $file;
public function getUploadRootDir()
{
return __dir__.'/../../../../web/uploads';
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getAssetPath()
{
return 'uploads/'.$this->path;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->tempFile = $this->getAbsolutePath();
$this->oldFile = $this->getPath();
$this->updateAt = new \DateTime();
if (null !== $this->file)
$this->path = sha1(uniqid(mt_rand(),true)).'.'.$this->file->guessExtension();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null !== $this->file) {
$this->file->move($this->getUploadRootDir(),$this->path);
unset($this->file);
if ($this->oldFile != null) unlink($this->tempFile);
}
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFile = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if (file_exists($this->tempFile)) unlink($this->tempFile);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath()
{
return $this->path;
}
public function getName()
{
return $this->name;
}
public function getFile()
{
return $this->file;
}
/**
* Set path
*
* #param string $path
* #return String
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Set alt
*
* #param string $alt
* #return String
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Set name
*
* #param string $name
* #return String
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set updateAt
*
* #param \DateTime $updateAt
*
* #return Media
*/
public function setUpdateAt($updateAt)
{
$this->updateAt = $updateAt;
return $this;
}
/**
* Get updateAt
*
* #return \DateTime
*/
public function getUpdateAt()
{
return $this->updateAt;
}
Thanks for your help guys :)
Please add "'mapped' => false," to form builder.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class,
array(
'data_class' => null,
'mapped' => false,
))
->add('name', TextType::class)
;
}
Those who say that it is wrong to dissolve, see the test there. I'm not the one who made it wrong.
Test code:
https://github.com/symfony/property-access/blob/master/Tests/PropertyAccessorCollectionTest.php#L151
A second solution is to add function setXxx to the property that gives an error in class Entity.
public $xxx;
public function setXxx(Array $xxx)
{
$this->xxx = $xxx;
}
Or
public function __construct()
{
$this->xxx = new ArrayCollection();
}
Video Link: https://knpuniversity.com/screencast/doctrine-relations/create-genus-note
My English is bad, I can tell you.
Setting mapped => false is not the real solution, because the field IS mapped to the entity.
In my case, I have a OneToMany relation, so I need the field mapped in order to use the 'cascase' => {"all"} option.
If the field is not mapped, then you must to persist the related entity manually. That's no good.
Stumbled on this while searching solution for my problem, that was shooting the same error and I was using also ArrayCollection class, so this may be helpful to someone:
My field in ArrayCollection is called $files, so I had to add constructor like this:
use Doctrine\Common\Collections\ArrayCollection;
public function __construct()
{
$this->files = new ArrayCollection();
}
Then I added add and remove methods, like this:
public function addFile(MyFile $file) : self
{
$file->setParentMyItem($this); // Setting parent item
$this->files->add($file);
return $this;
}
public function removeFile(MyFile $file) : self
{
$this->files->removeElement($file);
return $this;
}
But catch is that even my field name was $files I had to name add and remove methods addFile() and removeFile(), without 's' at end, which is totally not logical to me, but that solved the problem.
Same error for me but on a OneToMany relation. Although setting "mapped => false" also solved the error there is another option. I only had a getter, adder and remover method on the entity. I needed to add a "setter" too
In my case it was:
/**
* #param ArrayCollection|SubStatusOptions[]
*/
public function setSubStatusOptions(ArrayCollection $subStatusOptions)
{
$this->subStatusOptions = $subStatusOptions;
}
This happened because I had an entity property but the getter/setters were missing. I used:
php bin/console make:entity --regenerate MyBundle\\Entity\\MyEntity
to regenerate them
i'm having trouble with crud and user entity ( playing a little with sf2). i've been following the symfony cookbook for saving users in database and roles and i've generated crud for user entity and role entity. on user entity i have the field for assigning the role yet in the select option it shows the id of the role instead of the name and i'm not sure how modify the field to show the role name. can you help me please ?
User Controller:
namespace UserBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use UserBundle\Entity\User;
use UserBundle\Form\UserType;
/**
* User controller.
*
* #Route("/user")
*/
class UserController extends Controller
{
/**
* Lists all User entities.
*
* #Route("/", name="user")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('UserBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
/**
* Creates a new User entity.
*
* #Route("/", name="user_create")
* #Method("POST")
* #Template("UserBundle:User:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new User();
$factory = $this->get('security.encoder_factory');
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
$encoder = $factory->getEncoder($entity);
$password = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('user_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Creates a form to create a User entity.
*
* #param User $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(User $entity)
{
$form = $this->createForm(new UserType(), $entity, array(
'action' => $this->generateUrl('user_create'),
'method' => 'POST',
));
$form->add('submit', 'submit', array('label' => 'Create'));
return $form;
}
/**
* Displays a form to create a new User entity.
*
* #Route("/new", name="user_new")
* #Method("GET")
* #Template()
*/
public function newAction()
{
$entity = new User();
$form = $this->createCreateForm($entity);
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
/**
* Finds and displays a User entity.
*
* #Route("/{id}", name="user_show")
* #Method("GET")
* #Template()
*/
public function showAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('UserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'delete_form' => $deleteForm->createView(),
);
}
/**
* Displays a form to edit an existing User entity.
*
* #Route("/{id}/edit", name="user_edit")
* #Method("GET")
* #Template()
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('UserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Creates a form to edit a User entity.
*
* #param User $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createEditForm(User $entity)
{
$form = $this->createForm(new UserType(), $entity, array(
'action' => $this->generateUrl('user_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$form->add('submit', 'submit', array('label' => 'Update'));
return $form;
}
/**
* Edits an existing User entity.
*
* #Route("/{id}", name="user_update")
* #Method("PUT")
* #Template("UserBundle:User:edit.html.twig")
*/
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('UserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
return $this->redirect($this->generateUrl('user_edit', array('id' => $id)));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
/**
* Deletes a User entity.
*
* #Route("/{id}", name="user_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, $id)
{
$form = $this->createDeleteForm($id);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('UserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$em->remove($entity);
$em->flush();
}
return $this->redirect($this->generateUrl('user'));
}
/**
* Creates a form to delete a User entity by id.
*
* #param mixed $id The entity id
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm($id)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('user_delete', array('id' => $id)))
->setMethod('DELETE')
->add('submit', 'submit', array('label' => 'Delete'))
->getForm()
;
}
}
UserType
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username')
->add('password')
->add('email')
->add('isActive')
->add('roles')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'UserBundle\Entity\User'
));
}
/**
* #return string
*/
public function getName()
{
return 'userbundle_user';
}
}
UserEntity
<?php
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* UserBundle\Entity\User
*
* #ORM\Table(name="acme_users")
* #ORM\Entity(repositoryClass="UserBundle\Entity\UserRepository")
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=25, unique=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\Column(type="string", length=60, unique=true)
*/
private $email;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
*/
private $roles;
public function __construct()
{
$this->isActive = true;
$this->roles = new ArrayCollection();
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
}
/**
* #inheritDoc
*/
public function getUsername()
{
return $this->username;
}
/**
* #inheritDoc
*/
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
/**
* #inheritDoc
*/
public function getPassword()
{
return $this->password;
}
/**
* #inheritDoc
*/
public function getRoles()
{
return $this->roles->toArray();
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* Add roles
*
* #param \UserBundle\Entity\Role $roles
* #return User
*/
public function addRole(\UserBundle\Entity\Role $roles)
{
$this->roles[] = $roles;
return $this;
}
/**
* Remove roles
*
* #param \UserBundle\Entity\Role $roles
*/
public function removeRole(\UserBundle\Entity\Role $roles)
{
$this->roles->removeElement($roles);
}
public function __toString() {
return (string) $this->getId();
}
}
Implement __toString() method in the Role entity class.
public function __toString()
{
return $this->name;
}
You could also pass 'property' => 'name' option to roles field
Read more about other form type options here http://symfony.com/doc/current/reference/forms/types/entity.html
i'm having difficulties to save a form that contains a collection from a OneToMany bidirectionnal relation.
I got the following error :
An exception occurred while executing 'INSERT INTO fieldsgroup (title, colloque_id) VALUES (?, ?)' with params ["groupe 1", null]:
Here are the queries that the profiler gives me :
START TRANSACTION;
INSERT INTO colloque (title)
VALUES
('titre du colloque');
INSERT INTO fieldsgroup (title, colloque_id)
VALUES
('titre de mon groupe de champs', null)
ROLLBACK
My controller :
public function createColloqueAction()
{
$colloque = new Colloque();
$form = $this->createForm(new ColloqueType, $colloque);
$request = $this->get('request');
if ($request->getMethod() == 'POST'){
$form->bind($request);
if ($form->isValid()) {
$this->get('session')->getFlashBag()->add('notice', 'Colloque correctement enregistré');
$em = $this->getDoctrine()->getManager();
$em->persist($colloque);
$em->flush();
return $this->redirect($this->generateUrl('ptolemee_admin_homepage'));
}
}
return $this->render('PtolemeeAdminBundle:Admin:createColloque.html.twig',array(
'form' => $form->createView()
));
}
My "Colloque" entity :
class Colloque
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Ptolemee\ColloqueBundle\Entity\FieldsGroup", mappedBy="colloque", cascade={"persist"})
*/
private $fieldsGroups;
public function __construct()
{
$this->fieldsGroups = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add fieldsGroups
*
* #param \Ptolemee\ColloqueBundle\Entity\FieldsGroup $fieldsGroups
* #return Colloque
*/
public function addFieldsGroup(\Ptolemee\ColloqueBundle\Entity\FieldsGroup $fieldsGroups)
{
$this->fieldsGroups[] = $fieldsGroups;
$fieldsGroups->setColloque($this);
return $this;
}
/**
* Remove fieldsGroups
*
* #param \Ptolemee\ColloqueBundle\Entity\FieldsGroup $fieldsGroups
*/
public function removeFieldsGroup(\Ptolemee\ColloqueBundle\Entity\FieldsGroup $fieldsGroups)
{
$this->fieldsGroups->removeElement($fieldsGroups);
}
/**
* Get fieldsGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getFieldsGroups()
{
return $this->fieldsGroups;
}
}
Its form, ColloqueType :
class ColloqueType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
->add('date', 'datetime')
->add('successMessage', 'textarea')
->add('fieldsGroups', 'collection', array( 'type' => new FieldsGroupType(),
'allow_add' => true,
'allow_delete' => true))
;
}
}
My class FieldsGroup
class FieldsGroup
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Ptolemee\ColloqueBundle\Entity\Colloque", inversedBy="fieldsGroups")
* #ORM\JoinColumn(nullable=false)
*/
private $colloque;
/**
* Set colloque
*
* #param \Ptolemee\ColloqueBundle\Entity\Colloque $colloque
* #return FieldsGroup
*/
public function setColloque(\Ptolemee\ColloqueBundle\Entity\Colloque $colloque)
{
$this->colloque = $colloque;
return $this;
}
/**
* Get colloque
*
* #return \Ptolemee\ColloqueBundle\Entity\Colloque
*/
public function getColloque()
{
return $this->colloque;
}
}
And its form, FieldsGroupType :
class FieldsGroupType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', 'text')
/*->add('fields', 'collection', array('type' => new FieldType(),
'allow_add' => true,
'allow_delete' => true))*/
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ptolemee\ColloqueBundle\Entity\FieldsGroup'
));
}
/**
* #return string
*/
public function getName()
{
return 'ptolemee_colloquebundle_fieldsgroup';
}
}
I know this should work properly without any more perist(), or anything else...
Any help would be highly appreciated, i've been working on that for hours without finding what's the right way to do....
Thanks a lot !
Just add setFieldsGroup(ArrayCollection) to your entity
public function addFieldsGroup(\Ptolemee\ColloqueBundle\Entity\FieldsGroup $fieldsGroups)
{
$this->fieldsGroups[] = $fieldsGroups;
$fieldsGroups->setColloque($this);
return $this;
}
public function setFieldsGroup(ArrayCollection $fieldsGroups)
{
foreach ($fieldsGroups as $fieldsGroup) {
$fieldsGroup->setColloque($this);
}
$this->fieldsGroups = $fieldsGroups;
}
Adding setFieldsGroup(ArrayCollection) is working but it's a trick.
Simply add , cascade={"persist"} to your ManyToOne field
/**
* #ORM\ManyToOne(targetEntity="Ptolemee\ColloqueBundle\Entity\Colloque", inversedBy="fieldsGroups", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*/
private $colloque;
PS1 :
Prefer handleRequest() instead of $request->getMethod() == 'POST' and $form->bind($request) if you have Symfony2.x x>=3.
PS2 :
You can get request like this when needed (more elegant way) :
public function createColloqueAction(Request $request)
I have two entities (Plan and PricingTier) that are set up to be mapped to each other. The PricingTier is set up to be a oneToMany and the Plan is set up to be a manyToOne. The mapped column is in the Plan entity; the plans database table has a pricing_tier_id column that links the two tables/entities together.
I have a form that creates a new plan. The form generates properly in the Twig file and when posted the $request->request->getAll(); returns an array of the posted values. In the array I can see that the pricingTierId has clearly been set to the id of the pricing tier I selected. When I peform the following:
$form->bind($request);
$newPlan = $form->getData();
$em = $this->getDoctrine()->getEntityManager();
$em->perist($newPlan);
$em->flush();
I get a thrown exception saying that pricing_tier_id can not be NULL. I have done a var_dump() to the $newPlan variable and it looks returns an object, including the object of the mapped pricing tier.
Can anyone suggest a solution to why I'd be getting this error? Relevant code and errors are below.
PlanController.php
namespace etrak\CustomerServiceBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use etrak\OnlineOrderProcessingBundle\Entity\Plan;
use etrak\OnlineOrderProcessingBundle\Entity\PricingTier;
use etrak\CustomerServiceBundle\Form\Type\PlanType;
use Symfony\Component\HttpFoundation\Request;
class PlanController extends Controller
{
public function indexAction($name)
{
return $this->render('etrakCustomerServiceBundle:Default:index.html.twig', array('name' => $name));
}
public function addPlanAction(Request $request)
{
// Set up a new Plan object
$plan = new Plan();
$form = $this->createForm(new PlanType(), $plan);
// Check to see if the form has been submitted
if ($request->isMethod('POST')) {
$form->bind($request);
var_dump($request->request->all()); die();
// Validate the form
if ($form->isValid()) {
$newPlan = $form->getData();
//var_dump($newPlan->getPricingTierId()); die();
$em = $this->getDoctrine()->getEntityManager();
$em->persist($newPlan);
$em->flush();
}
}
return $this->render('etrakCustomerServiceBundle:Plan:new.html.twig', array("form" => $form->createView()));
}
}
PlanType.php
namespace etrak\CustomerServiceBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PlanType extends AbstractType
{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'etrak\OnlineOrderProcessingBundle\Entity\Plan',
'cascade_validation' => true,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$termsConditionsArray = array("1 Year Contract" => "1 Year Contract", "2 Year Contract" => "2 Year Contract");
$billingFrequencyArray = array("1" => "Monthly", "6" => "6 Months", "12" => "Yearly");
// Create the form
$builder->add('name', 'text', array('label' => 'Plan Name: ', 'required' => false));
$builder->add('description', 'text', array('label' => 'Plan Description: '));
$builder->add('termsConditions', 'choice', array('choices' => $termsConditionsArray, 'label' => 'Terms & Conditions'));
$builder->add('amount', 'text', array('label' => 'Plan Price: '));
$builder->add('affinity', 'choice', array('choices' => array('0' => 'Yes', '1' => 'No'), 'label' => 'Affinity? ', 'expanded' => true));
$builder->add('deactivationFee', 'text', array('label' => "Deactivation Fee: "));
$builder->add('recurringInMonths', 'choice', array('choices' => $billingFrequencyArray, 'label' => 'Billing Frequency: '));
$builder->add('pricingTierId', 'entity', array(
'class' => 'etrakOnlineOrderProcessingBundle:pricingTier',
'property' => 'name',
'label' => "Select Pricing Tier: "
));
$builder->add('activeStartDate', 'datetime', array('label' => "Effective Start Date: "));
$builder->add('activeEndDate', 'datetime', array('label' => "Effective End Date: "));
}
public function getName()
{
return 'plan';
}
}
Plan.php
namespace etrak\OnlineOrderProcessingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Plan
*/
class Plan
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $description;
/**
* #var string
*/
private $termsConditions;
/**
* #var boolean
*/
private $active;
/**
* #var decimal
*/
private $amount;
/**
* #var boolean
*/
private $affinity;
/**
* #var integer
*/
private $deactivationFee;
/**
* #var integer
*/
private $gracePeriodDays;
/**
* #var integer
*/
private $recurringInMonths;
/**
* #var integer
*/
private $pricingTierId;
/**
* #var date
*/
private $activeStartDate;
/**
* #var date
*/
private $activeEndDate;
/**
* #var \etrak\OnlineOrderProcessingBundle\Entity\PricingTier
*/
private $pricingTier;
/**
* Set pricingTier
*
* #param \etrak\OnlineOrderProcessingBundle\Entity\PricingTier $pricingTier
* #return Plan
*/
public function setPricingTier(\etrak\OnlineOrderProcessingBundle\Entity\PricingTier $pricingTier = null)
{
$this->pricingTier = $pricingTier;
return $this;
}
/**
* Get pricingTier
*
* #return \etrak\OnlineOrderProcessingBundle\Entity\PricingTier
*/
public function getPricingTier()
{
return $this->pricingTier;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Plan
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
* #return Plan
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set termsConditions
*
* #param string $termsConditions
* #return Plan
*/
public function setTermsConditions($termsConditions)
{
$this->termsConditions = $termsConditions;
return $this;
}
/**
* Get termsConditions
*
* #return string
*/
public function getTermsConditions()
{
return $this->termsConditions;
}
/**
* Set active
*
* #param boolean $active
* #return Plan
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active
*
* #return boolean
*/
public function getActive()
{
return $this->active;
}
/**
* Set amount
*
* #param decimal $amount
* #return Plan
*/
public function setAmount($amount)
{
$this->amount = $amount;
return $this;
}
/**
* Get amount
*
* #return decimal
*/
public function getAmount()
{
return $this->amount;
}
/**
* Set affinity
*
* #param boolean $affinity
* #return Plan
*/
public function setAffinity($affinity)
{
$this->affinity = $affinity;
return $this;
}
/**
* Get affinity
*
* #return boolean
*/
public function getAffinity()
{
return $this->affinity;
}
/**
* Set deactivationFee
*
* #param integer $deactivationFee
* #return Plan
*/
public function setDeactivationFee($deactivationFee)
{
$this->deactivationFee = $deactivationFee;
return $this;
}
/**
* Get deactivationFee
*
* #return integer
*/
public function getDeactivationFee()
{
return $this->deactivationFee;
}
/**
* Set gracePeriodDays
*
* #param integer $gracePeriodDays
* #return Plan
*/
public function setGracePeriodDays($gracePeriodDays)
{
$this->gracePeriodDays = $gracePeriodDays;
return $this;
}
/**
* Get gracePeriodDays
*
* #return integer
*/
public function getGracePeriodDays()
{
return $this->gracePeriodDays;
}
/**
* Set recurringInMonths
*
* #param integer $recurringInMonths
* #return Plan
*/
public function setRecurringInMonths($recurringInMonths)
{
$this->recurringInMonths = $recurringInMonths;
return $this;
}
/**
* Get recurringInMonths
*
* #return integer
*/
public function getRecurringInMonths()
{
return $this->recurringInMonths;
}
/**
* Set pricingTierId
*
* #param integer $pricingTierId
* #return Plan
*/
public function setPricingTierId($pricingTierId)
{
$this->pricingTierId = $pricingTierId;
return $this;
}
/**
* Get pricingTierId
*
* #return integer
*/
public function getPricingTierId()
{
return $this->pricingTierId;
}
/**
* Set activeStartDate
*
* #param \DateTime $activeStartDate
* #return Plan
*/
public function setActiveStartDate($activeStartDate)
{
$this->activeStartDate = $activeStartDate;
return $this;
}
/**
* Get activeStartDate
*
* #return \DateTime
*/
public function getActiveStartDate()
{
return $this->activeStartDate;
}
/**
* Set activeEndDate
*
* #param \DateTime $activeEndDate
* #return Plan
*/
public function setActiveEndDate($activeEndDate)
{
$this->activeEndDate = $activeEndDate;
return $this;
}
/**
* Get activeEndDate
*
* #return \DateTime
*/
public function getActiveEndDate()
{
return $this->activeEndDate;
}
/**
*
*/
public function prePersist()
{
if (!isset($this->affinity)) {
$this->setAffinity(0);
}
if (!isset($this->active)) {
$this->setActive(1);
}
}
}
Plan.orm.yml
#etrak/OnlineOrderProcessingBundle/Resources/config/doctrine/Entity/Plan.orm.yml
etrak\OnlineOrderProcessingBundle\Entity\Plan:
type: entity
table: plans
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 255
nullable: true
description:
type: text
nullable: true
termsConditions:
column: terms_conditions
type: text
nullable: true
active:
type: boolean
nullable: true
amount:
type: decimal
nullable: true
scale: 2
precision: 5
affinity:
type: boolean
nullable: true
deactivationFee:
column: deactivation_fee
type: decimal
scale: 2
precision: 5
nullable: true
gracePeriodDays:
column: grace_period_days
type: integer
nullable: true
recurringInMonths:
column: recurring_in_months
type: integer
nullable: true
pricingTierId:
column: pricing_tier_id
type: integer
activeStartDate:
column: active_start_date
type: date
activeEndDate:
column: active_end_date
type: date
lifecycleCallbacks:
prePersist: [ prePersist ]
manyToOne:
pricingTier:
targetEntity: PricingTier
inversedBy: plans
joinColumn:
name: pricing_tier_id
referencedColumnName: id
PricingTier.php
namespace etrak\OnlineOrderProcessingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* PricingTier
*/
class PricingTier
{
/**
* #var integer
*/
private $id;
/**
* #var integer
*/
private $productId;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $description;
/**
* #var integer
*/
private $minimumDevices;
/**
* #var boolean
*/
private $isAffinity;
/**
* #var string
*/
private $keyname;
/**
* #var string
*/
private $createdBy;
/**
* #var datetime
*/
private $createdOn;
/**
* #var datetime
*/
private $updatedOn;
/**
* Set productId
*
* #param integer $productId
* #return PricingTier
*/
public function setProductId($productId)
{
$this->productId = $productId;
return $this;
}
/**
* Get productId
*
* #return integer
*/
public function getProductId()
{
return $this->productId;
}
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $plans;
/**
* Constructor
*/
public function __construct()
{
$this->plans = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add plans
*
* #param \etrak\OnlineOrderProcessingBundle\Entity\Plan $plans
* #return PricingTier
*/
public function addPlan(\etrak\OnlineOrderProcessingBundle\Entity\Plan $plans)
{
$this->plans[] = $plans;
return $this;
}
/**
* Remove plans
*
* #param \etrak\OnlineOrderProcessingBundle\Entity\Plan $plans
*/
public function removePlan(\etrak\OnlineOrderProcessingBundle\Entity\Plan $plans)
{
$this->plans->removeElement($plans);
}
/**
* Get plans
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPlans()
{
return $this->plans;
}
/**
* Set name
*
* #param string $name
* #return PricingTier
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* #param string $description
* #return PricingTier
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set minimumDevices
*
* #param integer $minimumDevices
* #return PricingTier
*/
public function setMinimumDevices($minimumDevices)
{
$this->minimumDevices = $minimumDevices;
return $this;
}
/**
* Get minimumDevices
*
* #return integer
*/
public function getMinimumDevices()
{
return $this->minimumDevices;
}
/**
* Set isAffinity
*
* #param boolean $isAffinity
* #return PricingTier
*/
public function setIsAffinity($isAffinity)
{
$this->isAffinity = $isAffinity;
return $this;
}
/**
* Get isAffinity
*
* #return boolean
*/
public function getIsAffinity()
{
return $this->isAffinity;
}
/**
* Set keyname
*
* #param string $keyname
* #return PricingTier
*/
public function setKeyname($keyname)
{
$this->keyname = $keyname;
return $this;
}
/**
* Get keyname
*
* #return string
*/
public function getKeyname()
{
return $this->keyname;
}
/**
* Set createdBy
*
* #param string $createdBy
* #return PricingTier
*/
public function setCreatedBy($createdBy)
{
$this->createdBy = $createdBy;
return $this;
}
/**
* Get createdBy
*
* #return string
*/
public function getCreatedBy()
{
return $this->createdBy;
}
/**
* Set createdOn
*
* #param \DateTime $createdOn
* #return PricingTier
*/
public function setCreatedOn($createdOn)
{
$this->createdOn = $createdOn;
return $this;
}
/**
* Get createdOn
*
* #return \DateTime
*/
public function getCreatedOn()
{
return $this->createdOn;
}
/**
* Set updatedOn
*
* #param \DateTime $updatedOn
* #return PricingTier
*/
public function setUpdatedOn($updatedOn)
{
$this->updatedOn = $updatedOn;
return $this;
}
/**
* Get updatedOn
*
* #return \DateTime
*/
public function getUpdatedOn()
{
return $this->updatedOn;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #ORM\PrePersist
*/
public function onPrePersist()
{
if (!isset($this->isAffinity)) {
$this->setIsAffinity(0);
}
$this->setCreatedOn(new \DateTime());
}
/**
* #ORM\PreUpdate
*/
public function onPreUpdate()
{
$this->setUpdatedOn(new \DateTime());
}
}
PricingTier.orm.yml
#etrak/OnlineOrderProcessingBundle/Resources/config/doctrine/Entity/PricingTier.orm.yml
etrak\OnlineOrderProcessingBundle\Entity\PricingTier:
type: entity
table: pricing_tiers
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
productId:
column: product_id
type: integer
name:
type: string
length: 50
description:
type: string
length: 100
nullable: true
minimumDevices:
column: minimum_devices
type: integer
isAffinity:
column: is_affinity
type: boolean
keyname:
type: string
length: 55
createdBy:
column: created_by
type: string
length: 20
createdOn:
column: created_on
type: datetime
updatedOn:
column: updated_on
type: datetime
nullable: true
lifecycleCallbacks:
prePersist: [ onPrePersist ]
preUpdate: [ onPreUpdate ]
oneToMany:
plans:
targetEntity: Plan
mappedBy: pricingTier
That should be all of the files that relate to this. I didn't include the Twig file because it is simply one line that renders the form that is generated by the createView() magic method in the PlanController.
Thanks in advanced!
In your Plan.orm.yml:
pricingTierId:
column: pricing_tier_id
type: integer
which is the same column name as your many-to-one join. This is probably a bad practice. This is not set to nullable: true, and is probably the source of your problem. Explicitly, you don't need this field. Also in your form, you are loading the entity class for the pricingTierId, which is not an entity, and I think symfony is quite confused by this.
$builder->add('pricingTierId', 'entity', array( // add pricingTier
'class' => 'etrakOnlineOrderProcessingBundle:pricingTier', // not pricingTierId
'property' => 'name',
'label' => "Select Pricing Tier: "
));
I expect if you delete the offending yaml portion and adjust the form type your problem will go away.