I have problem to complete ManyToMany relation in web app.
We have entity Contact and Tag. Relation many - to - many is written by following code:
Contact
/**
* Contact
*
* #ORM\Table(name="contacts")
* #ORM\Entity(repositoryClass="Admin\MainBundle\Repository\ContactRepository")
*/
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="contacts", cascade={"all"})
* #ORM\JoinTable(name="contact_tag_relation",
* joinColumns={#ORM\JoinColumn(name="contact_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
private $tags;
Tag
/**
* Tag
*
* #ORM\Table(name="tags")
* #ORM\Entity(repositoryClass="Admin\MainBundle\Repository\TagRepository")
*/
class Tag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Admin\MainBundle\Entity\Contact", mappedBy="tags", cascade={"all"})
* #ORM\JoinTable(name="contact_tag_relation",
* joinColumns={#ORM\JoinColumn(name="tag_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="contact_id", referencedColumnName="id")}
* )
*/
private $contacts;
Controller's request handler.
public function editAction(Request $request, Tag $tag, Contact $contact = null)
{
$deleteForm = $this->createDeleteForm($tag);
$editForm = $this->createForm('Admin\MainBundle\Form\TagType', $tag);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->flush();
return $this->redirectToRoute('tags_edit', array('id' => $tag->getId()));
}
return $this->render('AdminMainBundle:Tag:edit.html.twig', array(
'tag' => $tag,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
TagType method
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('contacts');
}
After GET request to location for editing tag, server returns input field for changing tag's title filled with current value and multiselect input field with marked contacts that are currently related to the tag.
When i make changes on multiselect field and submit, changes does not record.
What could be problem here? How to solve this and keep code clean?
The contacts attribute is represented in controller's method as persistentcollection type and its flagged as dirty.
UDATE 1:
Tag's (ge&se)tters:
/**
* Add contact
*
* #param \Admin\MainBundle\Entity\Contact $contact
*
* #return Tag
*/
public function addContact(\Admin\MainBundle\Entity\Contact $contact)
{
$this->contacts[] = $contact;
return $this;
}
/**
* Remove contact
*
* #param \Admin\MainBundle\Entity\Contact $contact
*/
public function removeContact(\Admin\MainBundle\Entity\Contact $contact)
{
$this->contacts->removeElement($contact);
}
/**
* Get contacts
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getContacts()
{
return $this->contacts;
}
Contact's (ge&se)tters:
/**
* Add tag
*
* #param \Admin\MainBundle\Entity\Tag $tag
*
* #return Contact
*/
public function addTag(\Admin\MainBundle\Entity\Tag $tag)
{
$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* #param \Admin\MainBundle\Entity\Tag $tag
*/
public function removeTag(\Admin\MainBundle\Entity\Tag $tag)
{
$this->tags->removeElement($tag);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
I have few suggestions for the mapping information.
You shouldn't have cascade={"all"} unless you create or edit or delete child entities on entity manager flush().
Secondly, why do you need to define #ORM\JoinTable information in both end of your mapping?
You should define it only from mapped-by side. In your example you should have #ORM\JoinTable only from Contact Entity as it has mapped-by information. Documents here.
Though I'm not sure this will solve your problem, But you should always have correct mapping information.
Hope this helps!
Related
I am new to Symfony and have inherited a project that was originally php custom and is now half and half with Symfony 3.
I have very little knowledge of Symfony but work a good amount in php.
I wanted to add a field to the products table named "majoritem"
I have added majoritem to the Products class, added it to the form builder, added it to the table through phpmyadmin.
I can see the new form item when the app builds the form.
If I change the field in the database manually it does not reflect in the form.
My assumption is I am not binding the field to a form field?
Any help would be much appreciated.
In the form builder I have the following now (shortened..):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$companyId = $builder->getData()->getCompanyId();
$builder
->add('productCode')
->add('name')
->add('productType', ChoiceType::class, [
'choices' => [
'Primary' => 'Primary',
'Secondary' => 'Secondary'
]
])
->add('majoritem', ChoiceType::class, [
'choices' => [
'No' => '0',
'Yes' => '1'
]
])
]);
}
The Update action for the form is as follows:
public function updateAction(Request $request, $productId)
{
$product = $this->getDoctrine()->getRepository('EcoModelBundle:Products')
->find($productId);
if (!$product)
{
throw new NotFoundHttpException('The requested product could not be found');
}
$form = $this->createForm(ProductsType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
$product = $form->getData();
try
{
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
$request->getSession()->getFlashBag()->add('success', 'Product updated successfully');
}
catch (\Exception $e)
{
$request->getSession()->getFlashBag()->add('error', 'There was an error while saving the product. '
. $e->getMessage());
}
}
else if ($form->isSubmitted() && !$form->isValid())
{
$request->getSession()->getFlashBag()->add('error', 'The submitted data is invalid');
}
return $this->render('#EcoProducts/Product/update.html.twig', [
'product' => $product,
'form' => $form->createView()
]);
}
Finally, the Products class is as follows:
class Products
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="company_id", type="integer", nullable=false)
*/
private $companyId;
/**
* #var string
*
* #ORM\Column(name="product_code", type="string", length=255, nullable=false)
*/
private $productCode;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100, nullable=false)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text", length=65535, nullable=false)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="majoritem", type="integer", nullable=false)
*/
private $majoritem;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set companyId
*
* #param integer $companyId
*
* #return Products
*/
public function setCompanyId($companyId)
{
$this->companyId = $companyId;
return $this;
}
/**
* Get companyId
*
* #return integer
*/
public function getCompanyId()
{
return $this->companyId;
}
/**
* Set productCode
*
* #param string $productCode
*
* #return Products
*/
public function setProductCode($productCode)
{
$this->productCode = $productCode;
return $this;
}
/**
* Get productCode
*
* #return string
*/
public function getProductCode()
{
return $this->productCode;
}
/**
* Set name
*
* #param string $name
*
* #return Products
*/
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 Products
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set majoritem
*
* #param integer $majoritem
*
* #return Products
*/
public function setMajoritem($majoritem)
{
$this->majoritem = $majoritem;
return $this;
}
/**
* Get majoritem
*
* #return integer
*/
public function getMajoritem()
{
return $this->majoritem;
}
/**
* Constructor
*/
public function __construct()
{
$this->details = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add detail
*
* #param \EcoModelBundle\Entity\ProjectDetails $detail
*
* #return Products
*/
public function addDetail(\EcoModelBundle\Entity\ProjectDetails $detail)
{
$this->details[] = $detail;
return $this;
}
/**
* Remove detail
*
* #param \EcoModelBundle\Entity\ProjectDetails $detail
*/
public function removeDetail(\EcoModelBundle\Entity\ProjectDetails $detail)
{
$this->details->removeElement($detail);
}
/**
* Get details
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getDetails()
{
return $this->details;
}
}
So, interestingly I made a couple of changes..
the following is within my products class
/**
* Set majoritem
*
* #param integer $majoritem
*
* #return Products
*/
public function setMajoritem($majoritem)
{
$this->majoritem = $majoritem;
return $this;
}
/**
* Get majoritem
*
* #return integer
*/
public function getMajoritem()
{
return $this->majoritem;
}
Those do not seem to be working, or at least maybe I should say the getMajoritem is not?
If I change the form field to a simple text field as opposed to a dropdown, the form shows up as blank when the underlying data of the field is a zero.
However if I enter a 1 and submit the form it updates the database field to a 1.
So I am wondering if my issue is actually that the form builder is not retrieving the field value and adding it to the form?
Check if the mapping is correct using doctrine with the following command:
php bin/console doctrine:schema:validate.
If it is correct, you can update the database schema with the schema, which we have previously validated, with the following command:
php bin/console doctrine:schema:update --force.
This will ensure that the mapping is being done through Doctrine and if there is an error in the medium, the ORM Doctrine will take care of notifying you.
Also I suggest that you change the schema by using migrations, check if your project is doing it and maybe you should update the schema through migrations:
php bin/console doctrine:migrations:diff
That will create a file in src/Migrations/Version[timestamp].php
And to update the schema of the database you must do it in the following way:
php bin/console doctrine:migrations:execute --up [timestamp]
And if you want to reverse the schema update you do it with:
php bin/console doctrine:migrations:execute --down [timestamp].
The advantage between using schema:update and migrations is that with the latter you can reverse the schema update operation
Try integer values for your form instead of string values as the mapped field in the entity is of type integer:
->add('majoritem', ChoiceType::class, [
'choices' => [
'No' => 0, // <--- instead of '0'
'Yes' => 1 // <--- instead of '1'
]
])
I am retrieving the code of an old application that have not been updated for a few years now. Our sysadmin have put the code in a php7 server (it was working correctly on a php5 previously). The code worked quite good. I wanted to make some updates and the first one I did was to upgrade symfony from 2.3 to 2.7.*. And of course, now the problems arise.
I have a form that is correctly rendered (all the fields are OK even the ones from the database). Here is my builer:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('object','text',array(
"required"=>false,
"attr"=>array(
"placeholder"=>"Object"
)
))
->add('date','date',array(
'widget'=>'single_text',
))
->add('contact', 'entity', array(
'label'=>'Contact',
'class'=>'MyApp\AppliBundle\Entity\Contact',
'choice_translation_domain' => true,
'placeholder'=>'initials',
'choice_label' => 'initials',
'multiple'=>true
))
->add('text','redactor',array(
"required"=>false,
"redactor"=>"default"
))
;
}
Here is my controller:
public function editMeetingAction($id,Request $request)
{
$em = $this->getDoctrine()->getManager();
$meeting = $em->getRepository('MyAPPAppliBundle:Meeting')-
>findOneById($id);
$form = $this->createForm(new MeetingType, $meeting);
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($meeting);
$em->flush();
$this->get('session')->getFlashBag()->add('success', 'Meeting
edited successfully');
return $this->redirect($this-
>generateUrl('myapp_appli_manage_editmeeting', array("id" => $id)));
}
return array(
"form" => $form->createView(),
"id" => $id,
);
}
Now when I try to save the form, I have the following error:
[Syntax Error] line 0, col -1: Error: Expected Literal, got end of string.
[1/2] QueryException: SELECT e FROM MyApp\AppliBundle\Entity\Contact e WHERE
It seems that the app is not able to retrieve the Contact being selected in the form.
I have no idea what is wrong here as it worked correctly in the previous version. I followed the steps in this website to help me with the migration and modified already some fields in the form (placeholder, choices_as_values etc)
https://gist.github.com/mickaelandrieu/5211d0047e7a6fbff925
It would be much appreciated if you could help me.
[EDIT1]: the form was working properly before I updated symfony from 2.3 to 2.7
[EDIT2]: Entity Contact:
<?php
namespace MyApp\AppliBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\EntityRepository;
/**
* Contact
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MyApp\AppliBundle\Entity\ContactRepository")
*/
class Contact
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="Email", type="string", length=255)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="Initials", type="string", length=255)
*/
private $initials;
/**
* #var integer
*
* #ORM\Column(name="id_binome", type="integer")
*/
private $id_binome;
/**
* #var string
*
* #ORM\Column(name="JobTitles", type="string", length=255)
*/
private $jobtitle;
/**
* Tostring method
*
*/
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Contact
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set email
*
* #param string $email
* #return Contact
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set initials
*
* #param string $initials
* #return Contact
*/
public function setInitials($initials)
{
$this->initials = $initials;
return $this;
}
/**
* Get initials
*
* #return string
*/
public function getInitials()
{
return $this->initials;
}
/**
* Get id_binome
*
* #return integer
*/
public function getIdBinome()
{
return $this->id_binome;
}
/**
* Set id_binome
*
* #param integer $id
* #return Contact
*/
public function setIdBinome($id)
{
$this->id_binome = $id;
return $this;
}
/**
* Get jobtitle
*
* #return string
*/
public function getjobtitle()
{
return $this->jobtitle;
}
/**
* Set jobtitle
*
* #param string $jobtitle
* #return Contact
*/
public function setjobtitle($jobtitle)
{
$this->jobtitle = $jobtitle;
return $this;
}
}
class ContactRepository extends EntityRepository
{
public function findEmailBinome($id_binome)
{
$querybuilder = $this->createQueryBuilder("Contact")
->select("Contact.email")
->where("Contact.id = :idbinome")
->setParameter('idbinome',$id_binome)
;
return $querybuilder
->getQuery()
->getSingleResult()
;
}
}
[EDIT getter setter]
/**
* Add contacts
*
* #param \MyApp\AppliBundle\Entity\Contact $contacts
* #return Meeting
*/
public function addContact(\MyApp\AppliBundle\Entity\Contact $contacts)
{
$this->contacts[] = $contacts;
return $this;
}
/**
* Remove contacts
*
* #param \MyApp\AppliBundle\Entity\Contact $contacts
*/
public function removeContact(\MyApp\AppliBundle\Entity\Contact $contacts)
{
$this->contacts->removeElement($contacts);
}
/**
* Get contacts
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getContacts()
{
return $this->contacts;
}
I think I found the solution. As it was a problem related to the db connection I suspected dotrine/orm to be guilty! I update the composer.json by changing :
"doctrine/orm": "=2.2.*"
to
"doctrine/orm": ">=2.2.3"
When I tried composer update doctrine/orm it did not solve the problem. However when I simply tried composer update then my app worked again.
Thanks a lot for your help
I'm building a website with symfony. I'm trying to get a page to show a form with nested objects. An Artikel can have just one category. There are many artikels and many categories.
/**
* Artikel
*
* #ORM\Table(name="artikel", indexes=
{#ORM\Index(name="IDX_A4375C338C9B60D8", columns={"idartikeltype"})})
* #ORM\Entity
*/
class Artikel
{
....
/**
* #ORM\Column(type="integer")
*
*/
private $idartikeltype;
/**
* #var ArtikelCategory
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ArtikelCategory"
,inversedBy="type")
* #ORM\JoinColumn(name="idartikeltype",
referencedColumnName="idartikeltype")
*/
private $category;
/**
* #var integer
*
* #ORM\Column(name="idArtikel", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idartikel;
......
My class ArtikelCategory
class ArtikelCategory
{
/**
* #var string
* /**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Artikel",
mappedBy="category",cascade={ALL})
*
* #ORM\Column(name="type", type="string", length=45, nullable=false)
*/
private $type;
/**
* #var integer
*
* #ORM\Column(name="idartikeltype", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idartikeltype;
/**
* #return string
*/
public function getType()
{
return $this->type;
}
/**
* #param string $type
*/
public function setType($type)
{
$this->type = $type;
}
/**
* #return int
*/
public function getIdartikeltype()
{
return $this->idartikeltype;
}
/**
* #param int $idartikeltype
*/
public function setIdartikeltype($idartikeltype)
{
$this->idartikeltype = $idartikeltype;
}
public function __toString() {
return $this->getType()
;
}
public function __construct(){
$this->type = new ArrayCollection();
}
}
ArtikelType
public function buildForm
( FormBuilderInterface $builder , array
$options
)
{
$builder
->add ('titel', TextType::class,array('label'=>'Titel'))
->add('datum',DateType::class,array('label'=>'Date'))
->add('category' ,
EntityType::class,array
('class'=>\AppBundle\Entity\ArtikelCategory::class,'label'=>'Page',
'query_builder'=>function(EntityRepository $er){ return $er-
>createQueryBuilder('t')->orderBy('t.type','ASC');}
))
->add('tekst',CKEditorType::class,array('label'=>'Text'))
->add('save',SubmitType::class,array('label'=>'Save item'))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('data_class'=>'AppBundle\Entity\Artikel',));
}
InputController
$item = $this-> getDoctrine()->getRepository(Artikel::class)->find($id);
$form = $this->createForm(ArtikelType::class, $item);
$form->handleRequest($request);
if($form->isSubmitted() && $form -> isValid())
{
$em= $this->getDoctrine()->getManager();
$em->persist($item);
$em->flush();
return $this->redirectToRoute('input');
}
return $this->render('input/input.html.twig', array('form'=>$form->createView(),));
Normally the dropdown should show the category of the artikel as in the database but ...
... In my form, the dropdownlist is not showing the category of an artikel and when i persist the artikel, the new category is not saved.
Icant figure this one out! Help.
Your annotations are not correct. Remove the #ORM\Column annotation on ArtikelCategory::type. Next add a __toString() method that returns a textual presentation of the entity. In the formType remove all the options in the first place. Just use -->add('category') Please follow the examples in the Association Mapping chapter of the Doctrine manual exactly as shown only add the #\ORM prefix on the annotations.
After your changes update them to the database with
bin/console doctrine:schema:update --force
and check if the mappings are correct with
bin/console doctrine:schema:validate
Mea Culpa, when i read Association Mapping , It got me thinking. Now i know i'm using a Many-to-One Unidirectional association.
The rest was a piece of cake....
Thx.
I have tried searching around and even the example in the Symfony2 documentation, and have been struggling with writing a query to select all blog posts by post category and to order them in descending order by Id.
However I have the following error when i run the code. Any suggestions?
ContextErrorException in SimpleArrayType.php line 51:
Warning: implode(): Invalid arguments passed
I am creating a blog, for learning purposes and am trying to retrieve posts from my posted table with the following columns.
Id |postTitle | postDescription | postContent | postCategory
My Entity looks like this, (showing most relevant sections)
/**
* posted
*
#ORM\Table()
* #ORM\Entity
*/
class posted
{
/**
* #ORM\Column(type="string", length=500)
*/
protected $postTitle;
/**
* #ORM\Column(type="string", length=500)
*/
protected $postDescription;
/**
* #ORM\Column(type="string", length=500)
*/
protected $postContent;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="datetime", name="posted_at")
*/
protected $datePosted;
/**
*#var array
* #ORM\Column(type="simple_array", length=250)
*/
protected $postCategory;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set postCategory
*
* #param string $postCategory
* #return posted
*/
public function setPostCategory($postCategory)
{
$this->postCategory = $postCategory;
return $this;
}
/**
* Get postCategory
*
* #return string
*/
public function getPostCategory()
{
return $this->postCategory;
}
My Controller looks like
/**
* this is the EPL page of posts of EPL category
*
* #Route("/EPL", name="eplposts")
* #Method("GET")
* #Template()
*/
public function eplAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('BlogBundle:posted')->findBy(array('postCategory' => 'English Premier League','id' => 'desc'));
return $this->render('BlogBundle:Default:EPLpost.html.twig',array(
'entities' => $entities,
));
}
I suggest that you create your own query method in the repository of your entity, for example, something like this :
class BoardRepository extends EntityRepository
{
public function findByCategory($category)
{
$builder = $this->createQueryBuilder('p');
$builder
->where($builder->expr()->like('p.postCategory', '%'.$category.','))
->orWhere($builder->expr()->like('p.postCategory', ','.$category.'%'))
;
return $builder->getQuery()->execute();
}
}
Im having issues with data mapping to an entity after it is submitted
The Entity:
<?php
namespace Site\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\EntityManager;
use JMS\Serializer\Annotation as Serializer;
use JMS\Serializer\Annotation\Groups;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* UserAddress
*
* #ORM\Table(name="user_address")
* #ORM\Entity
*
* #Serializer\ExclusionPolicy("all")
* ---------- SERIALIZER GROUPS -----
* all -- All entries
*/
class UserAddress
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #Serializer\Type("integer")
* #Serializer\Expose
* #serializer\SerializedName("ID")
* #serializer\Groups({"all"})
*/
public $ID;
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer", nullable=false)
*/
public $UserId;
/**
* #var integer
*
* #ORM\Column(name="level_id", type="integer", nullable=false)
*
* #Serializer\Type("integer")
* #Serializer\Expose
* #serializer\SerializedName("LevelId")
* #serializer\Groups({"all"})
*/
public $LevelId;
/**
* #var integer
*
* #ORM\Column(name="address_type_id", type="integer", nullable=false)
*
* #Serializer\Type("integer")
* #Serializer\Expose
* #serializer\SerializedName("AddressTypeId")
* #serializer\Groups({"all"})
*
* #Assert\NotBlank()
*/
public $AddressTypeId;
/**
* #var string
*
* #ORM\Column(name="address_data", type="text", nullable=false)
*
* #Serializer\Type("string")
* #Serializer\Expose
* #serializer\SerializedName("Address Data")
* #serializer\Groups({"all"})
*
* #Assert\NotBlank()
*/
public $AddressData;
/**
* #var integer
*
* #ORM\Column(name="public_yn", type="integer", nullable=false)
*
* #Serializer\Type("boolean")
* #Serializer\Expose
* #serializer\SerializedName("PublicYN")
* #serializer\Groups({"all"})
*
* #Assert\NotBlank()
*/
public $PublicYN;
/**
* #var integer
*
* #ORM\Column(name="primary_yn", type="integer", nullable=false)
*
* #Serializer\Type("boolean")
* #Serializer\Expose
* #serializer\SerializedName("PrimaryYN")
* #serializer\Groups({"all"})
*
* #Assert\NotBlank()
*/
public $PrimaryYN;
/**
* #ORM\ManyToOne(targetEntity="Site\UserBundle\Entity\UserMain", inversedBy="UserAddress")
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
*/
public $User;
/**
* #ORM\ManyToOne(targetEntity="Site\UserBundle\Entity\UserAddressType", inversedBy="UserAddress")
* #ORM\JoinColumn(name="address_type_id", referencedColumnName="address_type_id")
*
* #Serializer\Type("Site\UserBundle\Entity\UserAddressType")
* #Serializer\Expose
* #serializer\SerializedName("UserAddressType")
* #serializer\Groups({"all"})
*/
public $UserAddressType;
/**
* Get ID
*
* #return integer
*/
public function getID()
{
return $this->ID;
}
/**
* Set UserId
*
* #param integer $userId
* #return UserAddress
*/
public function setUserId($userId)
{
$this->UserId = $userId;
return $this;
}
/**
* Get UserId
*
* #return integer
*/
public function getUserId()
{
return $this->UserId;
}
/**
* Set LevelId
*
* #param integer $levelId
* #return UserAddress
*/
public function setLevelId($levelId)
{
$this->LevelId = $levelId;
return $this;
}
/**
* Get LevelId
*
* #return integer
*/
public function getLevelId()
{
return $this->LevelId;
}
/**
* Set AddressTypeId
*
* #param integer $addressTypeId
* #return UserAddress
*/
public function setAddressTypeId($addressTypeId)
{
$this->AddressTypeId = $addressTypeId;
return $this;
}
/**
* Get AddressTypeId
*
* #return integer
*/
public function getAddressTypeId()
{
return $this->AddressTypeId;
}
/**
* Set AddressData
*
* #param string $addressData
* #return UserAddress
*/
public function setAddressData($addressData)
{
$this->AddressData = $addressData;
return $this;
}
/**
* Get AddressData
*
* #return string
*/
public function getAddressData()
{
return $this->AddressData;
}
/**
* Set PublicYN
*
* #param integer $publicYN
* #return UserAddress
*/
public function setPublicYN($publicYN)
{
$this->PublicYN = $publicYN;
return $this;
}
/**
* Get PublicYN
*
* #return integer
*/
public function getPublicYN()
{
return $this->PublicYN;
}
/**
* Set PrimaryYN
*
* #param integer $primaryYN
* #return UserAddress
*/
public function setPrimaryYN($primaryYN)
{
$this->PrimaryYN = $primaryYN;
return $this;
}
/**
* Get PrimaryYN
*
* #return integer
*/
public function getPrimaryYN()
{
return $this->PrimaryYN;
}
/**
* Set User
*
* #param \Site\UserBundle\Entity\UserMain $user
* #return UserAddress
*/
public function setUser(\Site\UserBundle\Entity\UserMain $user = null)
{
$this->User = $user;
return $this;
}
/**
* Get User
*
* #return \Site\UserBundle\Entity\UserMain
*/
public function getUser()
{
return $this->User;
}
/**
* Set UserAddressType
*
* #param \Site\UserBundle\Entity\UserAddressType $userAddressType
* #return UserAddress
*/
public function setUserAddressType(\Site\UserBundle\Entity\UserAddressType $userAddressType = null)
{
$this->UserAddressType = $userAddressType;
return $this;
}
/**
* Get UserAddressType
*
* #return \Site\UserBundle\Entity\UserAddressType
*/
public function getUserAddressType()
{
return $this->UserAddressType;
}
}
The form is:
namespace Site\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\EntityRepository;
class UserAddressType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('UserId','hidden')
->add('LevelId', 'integer', array(
'label'=>'Sort Rate (Order)'
))
->add('AddressTypeId', 'entity', array(
'class'=>'SiteUserBundle:UserAddressType',
'query_builder'=> function(EntityRepository $er){
return $er->createQueryBuilder('t')
->orderBy('t.AddressDescription', 'ASC');
},
'property'=>'AddressDescription',
'label'=>'Address Type'
))
->add('AddressData', 'text')
->add('PublicYN', 'choice', array(
'choices' => array( 'false'=>'Private', 'true'=>'Public'),
'required'=>true,
'label'=>'Pubicly Visable'
))
->add('PrimaryYN', 'choice', array(
'choices' => array( 'false'=>'Secondary', 'true'=>'Primary'),
'required'=>true,
'label'=>'Primary Contact',
))
->add('save', 'submit', array(
'label'=>'Add Address',
'attr'=>array(
'class'=>'btn btn-primary',
),
))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Site\UserBundle\Entity\UserAddress',
'csrf_protection'=>false,
));
}
/**
* #return string
*/
public function getName()
{
return 'Create_User_Address';
}
}
Controller Function is: (I'm using fosrestbundle)
public function newAddressAction($userid, Request $request)
{
$statusCode = 201;
$address = new UserAddress();
$address->setUserId($userid);
$form = $this->createForm( new UserAddressType(), $address, array(
'method'=>'GET',
));
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($address);
$em->flush();
return new Response('User Added to system');
}
return $this->render('SiteUserBundle:UserAddress:newUserAddress.html.twig', array(
'form' => $form->createView(),
));
}
The Twig template is very simple. All the data is posted correctly to the server: (Query String Parameters )
Create_User_Address[LevelId]:0
Create_User_Address[AddressTypeId]:5
Create_User_Address[AddressData]:555-555-5555
Create_User_Address[PublicYN]:false
Create_User_Address[PrimaryYN]:false
Create_User_Address[save]:
Create_User_Address[UserId]:3
but i keep getting the following error:
An exception occurred while executing 'INSERT INTO user_address (user_id, level_id, address_type_id, address_data, public_yn, primary_yn) VALUES (?, ?, ?, ?, ?, ?)' with params [null, 0, null, "555-555-5555", "false", "false"]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
As you can see the UserID and AddressTypeId fields are not mapping from the form to the Entity. I have looked over the code for all 3 pieces over and ove and for the life of me i can't see where the mismatch is happening. I did at one point change the names of those two fields in the Entity but i deleted all the getters and setters and regenerated them as well as clear the dev cache.
My hunch is there is a file somewhere in Symfony2 there is a mapping class that is wrong but i can't find it..
Thanks all
EDIT:
I tried clearing the doctrine cache as stated here: Symfony2 doctrine clear cache .
app/console doctrine:cache:clear-metadata
app/console doctrine:cache:clear-query
app/console doctrine:cache:clear-result
this resulted in the same error being generated,so the cache issue may be off the table.
EDIT:
As per Isaac's suggestion i removed ->add('UserId', 'hidden') . The form still posted with the same error message. The field is being generated correctly to the page;
<input type="hidden" id="Create_User_Address_UserId" name="Create_User_Address[UserId]" class=" form-control" value="3">
and as you can see from the Query Parameters above being posted back to the server correctly.
EDIT:
I tracked the issue down to the User and UserAddressType variables. If i remove these two variables and their getters and setters the form works perfectly without other modifications of my code. I should note that these two variables are joins to other entities. Some how they seem to be wiping out the data being submitted by the forms.
Remove
->add('UserId','hidden')
It's probably overwriting the UserId with null since it's hidden and has no value
As per lsouza suggestion I had to create a data transformer.
namespace Site\UserBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Site\UserBundle\Entity\UserAddressType;
class AddressTypeToNumber implements DataTransformerInterface
{
/**
* #var ObjectManager
*/
private $om;
/**
* #param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms an object(val) to a string
*
* #param UserAddressType|null $val
* #return string
*/
public function transform($val)
{
if (null === $val) {
return "";
}
return array();
}
/**
* Transfers a string to an object (UserAddressType)
*
* #param string $val
* #return UserAddressType|null
* #throws TransformationFailedException if object is not found
*/
public function reverseTransform($val)
{
if (!$val) {
return null;
}
$addId = $this->om
->getRepository('SiteUserBundle:UserAddressType')
->findOneBy(array('AddressTypeId' => $val));
if (null === $addId) {
throw new TransformationFailedException(sprintf(
'An Address Type with the ID of "%s" does not exsist in the system',
$val
));
}
return $addId;
}
}
Please note that the Transformer function is currently returning null but this should return a value that can be used by the form.
Some other notes that may help others are that the form class needs to be changed as well.
The setDefaultOptions function needs the following added to the $resolver variable:
->setRequired(array(
'em',
))
->setAllowedTypes(array(
'em'=>'Doctrine\Common\Persistence\ObjectManager',
));
The buildForm function needs to be changed slightly to accept the em option:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$em = $options['em'];
$transformer = new AddressTypeToNumber($em);
The field needs to be change to the varrable that holds the relationship in the Entity and not the field that is use in the link:
->add(
$builder->create('UserAddressType', 'entity', array(
'class' => 'SiteUserBundle:UserAddressType',
'property' => 'AddressDescription',
'label' => 'Address Type'
))
->addModelTransformer($transformer)
)
Finally in your controller you need to pass an instance of $this->getDoctrine()->getManager() to the form class:
$form = $this->createForm( new UserAddressType(), $address, array(
'em' => $this->getDoctrine()->getManager(),
));
I hope this helps others.