Hello I have small problem. I've never done form validator in sf2 so I don't know where I should start. I have one field 'username' and it is unique in database so how can I try it?
My Code :
-> ENTITY
/**
* #var string $nick_allegro
*
* #ORM\Column(name="nick_allegro", type="string", length=255, unique=true, nullable=true)
*/
private $nick_allegro;
-> FORM
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('nick_allegro')
;
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'My\FrontendBundle\Entity\Licence',
);
}
-> Controller
/**
* Displays a form to create a new Licence entity.
*
* #Route("/new", name="licence_new")
* #Template()
*/
public function newAction()
{
$entity = new Licence();
$form = $this->createForm(new LicenceType(), $entity);
return array(
'entity' => $entity,
'form' => $form->createView()
);
}
/**
* Creates a new Licence entity.
*
* #Route("/create", name="licence_create")
* #Method("post")
* #Template("MyFrontendBundle:Licence:new.html.twig")
*/
public function createAction()
{
$entity = new Licence();
$request = $this->getRequest();
$form = $this->createForm(new LicenceType(), $entity);
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('licence_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView()
);
}
-> View
<form action="{{ path('licence_create') }}" method="post" {{
form_enctype(form) }}>
{{ form_widget(form) }}
<p>
<button type="submit">Create</button>
</p> </form>
You need to use Unique Entity in symfony to validate that a particular field in a model is unique.
To help you a little bit (if you have a field called nick):
1/ In your entity
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity("nick")
*/
class User
{
/**
* #var string $email
*
* #ORM\Column(name="nick", type="string", length=255, unique=true)
*/
private $nick;
Validation will directly take effect as you asserted the constraints in your entity..
Therefore, you can already check the validaiton in your controller.
2/ In your controller
if ( 'POST' === $request->getMethod()) {
$form->bind($request);
if ($form->isValid())
{
//do something if the form is valid
}
}
It's very simple. Enough add in file Entity #ORM\Column this "unique=true"
Example:
class User
{
/**
* #var string $email
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\Email()
*/
protected $email;
}
Bear in mind that form handling in sf2.1 changed a little bit, so be sure to check the right documentation:
Forms in sf 2.1
Forms in sf 2.0
How to upgrade from 2.0 to 2.1 - Form chapter
Validation is done in many ways, among the others with annotations over entity fields, and in your case you need the UniqueEntity annotation.
Be sure to check all the symfony2 docs online because it's the best way to get into the matter.
Related
I'm struggling with finding the right solution to this problem.
I have a form with a dropdown which I want to generate dynamically via a call to a DB using the EntityType field. Everything works perfectly however I have been unable to get it to default to the previous value upon edit. All other fields in the form (all text) default to the previous value and if I build the field manually using ChoiceType it defaults as expected.
I have tried using the 'data' option to manually set it but it did not work. Short of building this as a ChoiceType and sending the data via the controller are there other things I should attempt?
Here is my form class TerminologyType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class TerminologyType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Event listener added to prevent users from editing the unique
// code after it has been created.
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$terminology = $event->getData();
$form = $event->getForm();
// check if the Terminology object is "new"
// This should be considered a new "Terminology" and user must enter a code
if (!$terminology || null === $terminology->getCode()) {
$form->add('code');
}
});
$builder
->add('name')
->add('version')
->add('description')
->add('status', EntityType::class, [
'class' => 'AppBundle:CodeSet',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.terminologyCode = :code')
->setParameter('code', 'entity_status')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'code',
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Terminology',
));
}
}
Controller
<?php
namespace AppBundle\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 AppBundle\Entity\Terminology;
use AppBundle\Form\TerminologyType;
/**
* Terminology controller.
*
* #Route("/admin/terminology")
*/
class TerminologyController extends Controller
{
/**
* Lists all Terminology entities.
*
* #Route("/", name="terminology_index")
* #Method("GET")
*/
....
/**
* Displays a form to edit an existing Terminology entity.
*
* #Route("/{code}/edit", name="terminology_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Terminology $terminology)
{
$deleteForm = $this->createDeleteForm($terminology);
$editForm = $this->createForm('AppBundle\Form\TerminologyType', $terminology);
$editForm->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->persist($terminology);
$em->flush();
return $this->redirectToRoute('terminology_show', array('code' => $terminology->getCode()));
}
return $this->render('terminology/edit.html.twig', array(
'terminology' => $terminology,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Entity
<?php
// src/AppBundle/Entity/Terminology.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #Gedmo\Loggable
* #ORM\Table(name="ts_terminology")
* #Gedmo\SoftDeleteable(fieldName="dateDeleted", timeAware=false)
* #UniqueEntity("code")
*
*/
class Terminology extends TerminologyServices
{
/**
* #ORM\Column(type="string", length=75)
* #ORM\OneToMany(targetEntity="CodeSet", mappedBy="terminologyCode")
* #ORM\Id
*/
protected $code;
/**
* #ORM\Column(type="string", length=75, nullable=true)
* #ORM\OneToOne(targetEntity="CodeSet")
* #ORM\JoinColumn(name="status", referencedColumnName="code")
* #Gedmo\Versioned
*/
protected $status;
I was able to track down the problem. In the entity definition for status I had defined the column type, length, etc in the annotations.
#ORM\Column(type="string", length=75, nullable=true)
By removing this it began defaulting to the last entered value (however this subsequently broke other things). I believe this has to do with EntityType expecting an object but the entity type being defined as a string and only providing that (which explains why ChoiceType option worked).
I already have to ask a question about a similar assert problem with FosUserBundle configuation.
But now I'm trying to run a simple form, but even with a esiest form => nothing is happening.
when i click on submit :
1) isValid() stay to false
2) No Assert message appears when input name is blank/empty
UserTmp.php (entity)
<?php
namespace BISSAP\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* BISSAP\UserBundle\Entity\User
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="BISSAP\UserBundle\Entity\UserRepository")
*/
class Usertmp
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="name", type="string", length=255)
*
* #Assert\NotBlank(message="Please enter your name.", groups={"Registration", "Profile"})
* #Assert\Length(
* min=3,
* max=255,
* minMessage="The name is too short.",
* maxMessage="The name is too long.",
* groups={"Registration", "Profile"}
* )
*/
private $name;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Usertmp
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
}
UserType.php
<?php
namespace BISSAP\ForumBundle\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('name', 'text', array('required' => false))
->add('Envoyer', 'submit', array(
'attr' => array(
'class' => 'btn right-flt'
)));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BISSAP\UserBundle\Entity\Usertmp'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'Registration', ));
}
/**
* #return string
*/
public function getName()
{
return 'bissap_forumbundle_user';
}
}
TController.php
<?php
namespace BISSAP\ForumBundle\Controller;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use BISSAP\ForumBundle\Form\UserType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use BISSAP\UserBundle\Entity\Usertmp;
class TController extends Controller
{
public function indexAction()
{
$entity = new Usertmp();
$form = $this->createForm(new UserType(), $entity);
if ($form->isValid())
{
return $this->redirectToRoute('bissap_forum_index');
}
return $this->render('BISSAPForumBundle:T:index.html.twig', array('form'=> $form->createView(), 'errors_tmp' => $this->getErrorMessages($form)));
}
private function getErrorMessages(\Symfony\Component\Form\Form $form)
{
$errors = array();
foreach ($form->getErrors(true, false) as $error) {
// My personnal need was to get translatable messages
// $errors[] = $this->trans($error->current()->getMessage());
$errors[] = $error->current()->getMessage();
}
return $errors;
}
}
?>
index.html.twig
----> {{form( form )}}
{% for error in errors_tmp %}
<div>error : {{ error }}</div>
{% endfor %}
So, form didn't work cause : $form->handleRequest($request); missed in TController.php
In your UserType.php, try:
$resolver->setDefaults(array( 'data_class' => $this->class, 'intention' => 'Registration', 'validation_groups' => array('registration'),));
You can also set the validation group in your TController.php instead of hard-coding it on your UserType:
$form = $this->createForm(new UserType(), $entity, array('validation_groups' => 'registration'));
Source: Validation Groups
Also in your UserType.php you are saying that the name is not required, but at the same time you want to assert if it is not blank:
->add('name', 'text', array('required' => false))
Try removing that option too.
I'm using the Gedmo Doctrine Extensions to handle Categories as a nested set.
I'm building a REST API and I have a route to create a Category.
I want to be able to create root Categories or child Categories.
Here is the entity
<?php
namespace AppBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* #Gedmo\Tree(type="nested")
* #ORM\Table(name="bo_categories")
* use repository for handy tree functions
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
*/
class Category
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=64)
*/
private $name;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setParent(Category $parent = null)
{
$this->parent = $parent;
}
public function getParent()
{
return $this->parent;
}
}
Here is the form
<?php
namespace AppBundle\Form\Type\Classification;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Category',
'csrf_protection' => false,
));
}
public function getName()
{
return 'api_category';
}
}
And here is the controller
/**
* #Route("/create")
* #Security("has_role('ROLE_SUPER_ADMIN')")
* #Rest\View
*/
public function postCreateAction(Request $request)
{
$categoryManager = $this->get('app.manager.category');
$category = $categoryManager->createNew();
$form = $this->createForm(new CategoryFormType(), $category);
// $category->setParent(10);
$form->handleRequest($request);
if ($form->isValid()) {
$categoryManager->save($category);
return new Response('', 201);
} else {
return $this->view($form, 400);
}
}
If I want to create a child category, it works fine. But if I want to create a root category without removing the "parent" field in the form I get this error
An exception occurred while executing 'SELECT b0_.id AS id0, b0_.name AS name1, b0_.lft AS lft2, b0_.lvl AS lvl3, b0_.rgt AS rgt4, b0_.root AS root5, b0_.parent_id AS parent_id6 FROM bo_categories b0_ WHERE b0_.id IN (?)' with params [""]:\n\nSQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "",
"class": "Doctrine\DBAL\DBALException
Why do I get this error ? Can't the "parent" value be empty/null in the form ?
There is absolutely no problem parent being NULL. Your current mapping on the other hand, doesn't allow that.
First, you should modify your mapping for $parent
#ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
should allow null values:
#ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=true)
Don't forget to run app/console doctrine:schema:update --force when you change your mappings!
Next, your form looks okay, but it miss one property - empty_value. This won't force you to choose parent category.
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
))
should be like this:
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
'empty_value' => '-- Select parent --'
))
This should add extra option with no value and should be selected by default (when creating new category). Since your field is of type entity you will see all of your categories as well (when you need to select parent).
This should do the trick, give it a try.
I am running Symfony 2.3 with Doctrine and these three (relevant) entities: Publication, Author and AuthorPublication.
Both, Author and Publication have a Many-to-One relationship to AuthorPublication (so it is basically a Many-to-Many relation between Author and Publication but I need the AuthorPublication Entity to order the authors of a publication)
I want to have a form where a user can create a new publication and choose as many authors for that publication as he wants.
I studied this article: How to Embed a Collection of Forms but I do not understand how to apply that to my problem because of the AuthorPublication entity which lies in between.
Relevant Code:
Publication
<?php
namespace ind\PubBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="ind\PubBundle\Repository\PublicationRepository")
* #ORM\Table("publications")
*/
class Publication {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $pid;
/**
* #ORM\OneToMany(targetEntity="AuthorPublication", mappedBy="publication")
* #ORM\OrderBy({"order_id" = "ASC"})
*/
protected $publicationAuthors;
//some more attributes + getters/seters
?>
Author
<?php
namespace ind\PubBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table("aid_author")
*/
class Author {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $aid;
/**
* #ORM\Column(type="string", length=255)
*/
protected $author_surname;
/**
* #ORM\Column(type="string", length=255)
*/
protected $author_forename;
/**
* #ORM\OneToMany(targetEntity="AuthorPublication", mappedBy="author")
*/
protected $authorPublication;
?>
AuthorPublication
<?php
namespace ind\PubBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table("aid_pid")
*/
class AuthorPublication {
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
protected $aid;
/**
* #ORM\Id
* #ORM\Column(type="integer")
*/
protected $pid;
/**
* #ORM\Column(type="integer")
*/
protected $order_id;
/**
* #ORM\ManyToOne(targetEntity="Publication", inversedBy="publicationAuthors")
* #ORM\JoinColumn(name="pid", referencedColumnName="pid")
*/
protected $publication;
/**
* #ORM\ManyToOne(targetEntity="Author", inversedBy="authorPublication")
* #ORM\JoinColumn(name="aid", referencedColumnName="aid")
*/
protected $author;
?>
You have to make an AuthorPublicationType form. You put field author as an 'entity' and your others fields...
You make your PublicationType including AuthorPublication (Embedded Forms).
Then you can add new AuthorPublication with prototype and very simple javascript.
Note that when you have to save your entity Publication with an authorPublication attribut null first. And after update Publication with authorPublication defined by using temporary ArrayCollection in Publication and LifecycleCallbacks for example.
EDIT : Example.
My entities are Sport OneToMany SportParam ManyToOne ConfigParam. SportParam is composed of a Sport, a ConfigParam and a value.
I want to create a new Sport with multiple ConfigParam :
SportParamType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ConfigParam', 'entity', array(
'class' => 'PPHBSportScoringBundle:ConfigParam',
'property' => 'nom',
'multiple' => false,
'label'=>'Paramètre'
))
->add('valeur','number',array('precision'=>2))
;
}
SportType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nom')
->add('SportParams', 'collection', array(
'type'=> new SportParamType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference'=> false
))
;
}
My form.html.twig :
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.nom) }}
<ul class=SportParams data-prototype="{{ form_widget(form.SportParams.vars.prototype)|e }}">
{% for param in form.SportParams %}
<li>
{{ form_errors(param.ConfigParam) }}
{{ form_widget(param.ConfigParam) }}
{{ form_errors(param.valeur) }}
{{ form_widget(param.valeur) }}
</li>
{% endfor %}
</ul>
<input type="submit" class="btn btn-primary" />
{{ form_end(form) }}
My Javascript refine because it contains more code (AJAX call). It maybe contains some mistake. If it's not clear have a look to the documentation :
<script type="text/javascript">
var $container = $('ul.SportParams');
// button to add a new SportParam
var $addSportParamLink = $('Ajouter un paramètre');
var $newLinkLi = $('<li></li>').append($addSportParamLink);
$(document).ready(function() {
//delete button on each existing SportParam
$container.find('li').each(function() {
addParamFormDeleteLink($(this));
});
//add button
$container.append($newLinkLi);
// adding a new form when cliking Add button
$addSportParamLink.on('click',function(e) {
e.preventDefault(); // évite qu'un #apparaisse dans l'URL
var index = $container.children().length-1;
addParamForm($container,$newLinkLi);
var bAffiche;
return false;
});
// adding a new form SportParam
function addParamForm($container, $newLinkLi) {
var $prototype = $container.attr('data-prototype');
var newForm = $prototype.replace(/__name__/g, $container.children().length-1);
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
addParamFormDeleteLink($newFormLi);
}
function addParamFormDeleteLink($paramFormLi){
var $removeFormA = $('Supprimer');
$paramFormLi.append($removeFormA);
$removeFormA.on('click', function(e) {
e.preventDefault();
$paramFormLi.remove();
});
}
});
</script>
Sport Entity call's back :
/**
* sport
*
* #ORM\Table()
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #ORM\Entity(repositoryClass="SportRepository")
*/
class Sport
{
...Entity attribut...
/**
* #var ArrayCollection
*
* To save Sport without SportParams
*/
private $SportParamsTMP;
...getter and setter ...
/**
* #ORM\PrePersist
*/
public function saveSportParams()
{
$this->SportParamsTMP = $this->SportParams;
$this->SportParams = null;
}
/**
* #ORM\PostPersist
*/
public function restoreSportParams()
{
if ($this->SportParamsTMP == !null) {
$this->SportParams = $this->SportParamsTMP;
}
$this->SportParamsTMP = null;
}
}
Finally the controller 's function to add a new Sport :
public function addAction()
{
$sport = new Sport();
$form = $this->createForm(new SportType(), $sport);
$request = $this->getRequest();
if ($request->getMethod() == "POST") {
$form->bind($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($sport);
//saving sport without parameter
$em->flush();
//updating sport with parameter
$em->flush();
return $this->redirect($this->generateUrl('pphb_sport_liste'));
}
}
return $this->render('PPHBSportScoringBundle:Championnat/Sport:add.html.twig', array(
'form' => $form->createView(),
));
}
I hope it help.
Don't know if it's the best way to do it, but it's working for me. If something to improve let me know please.
In my Symfony2 application, there are two entities: Address and Town. The Address entity has a 4-digit postal code property ("pcNum"), which refers to the id of a Town entity. An address can only have one postal code and hence refer to one town only (however the reverse is possible: a town could have more postal codes).
For both entities I have created a form called TownType and AddressType. Users can enter and save a Town (this works fine). The form for the Address entity allows users to fill in an address, including a postal code. The postal code is linked to the id of a Town entity.
However, when I try to persist a new Address entity retrieved from the AddressType form, I receive the MySql error that the pc_num field (pcNum in the entity) is empty:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'pc_num' cannot be null
(This is the field of the Address entity that should have contained a ref key to a Town.) Somehow its value is lost before persisting the Address entity. If I fill the field manually in my controller, regardless of the user input in the form, I can persist the new Address without the error. If I drop the link to the Town entity and use an unaware form field, I can also safe without error as long as the postal code happens to exist. But in the way it should be, using two forms and entity-association, I cannot get it to work. I have tried many different things over the past weeks and I am out of ideas. Below is my code. Feel free to comment on anything, maybe my whole approach is wrong.
This is my Town entity:
namespace Xx\xBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Check;
/**
*
* Xx\xBundle\Entity\Town
*
* #ORM\Table(name="lib_towns")
* #ORM\Entity
*/
class Town
{
#id
/**
* #ORM\Id
* #ORM\Column(type="integer", length="4", nullable=false)
* #ORM\GeneratedValue(strategy="AUTO")
* #Check\NotBlank()
* #Check\MaxLength(4)
*/
protected $id;
#name
/**
* #ORM\Column(name="name", type="string", length="100", nullable=true)
* #Check\NotBlank()
* #Check\MaxLength(100)
*/
protected $name;
//some more properties
#getters and setters
/**
* Set id
*
* #param integer $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string $name
*/
public function getName()
{
return $this->name;
}
//some more getters ans setters
}
This is my Address entity:
namespace Xx\xBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Check;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Xx\xBundle\Entity\Town;
/**
*
* Xx\xBundle\Entity\Address
*
* #ORM\Entity
* #ORM\Table(name="addresses")
*/
class Address
{
#id
/**
* #ORM\Id
* #ORM\Column(type="integer", length="6", nullable=false)
* #ORM\GeneratedValue(strategy="AUTO")
* #Check\NotBlank()
* #Check\MaxLength(6)
*/
protected $id;
#town
/**
* #orm\OneToOne(targetEntity="town")
* #ORM\JoinColumn(name="pc_num", referencedColumnName="id", nullable=false)
*/
protected $town;
//some other properties...
#getters and setters
/**
* Set id
*
* #param integer $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set town entity linked to this address
*
*/
public function setTown(town $town = null)
{
$this->town = $town;
}
/**
* Get town entity linked to this address
*
*/
public function getTown()
{
return $this->town;
}
//some other functions...
}
Next, this is the form I've created for the Address:
namespace Xx\xBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Doctrine\ORM\EntityRepository;
class AddressType extends AbstractType
{
public function buildForm(Formbuilder $builder, array $options)
{
$builder->add('id', 'hidden'); //necessary for updates
$builder->add('town', 'entity', array
(
'label' => 'Postal code (4 digits):*',
'class' => 'XxxBundle:Town',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('t')
->orderBy('t.id', 'ASC'); },
//'empty_value' => '----',
'property'=> 'Id',
'property_path'=> false,
'expanded' => false,
'multiple' => false,
'required' => true
));
$builder->add('street', 'text', array
(
'label' => 'Street:*',
'required' => true
));
//some more fields...
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Xx\xBundle\Entity\Address'
);
}
public function getName()
{
return 'address';
}
}
This is the relevant action function in my Address controller:
public function editAddressAction($id = null)
{
$em = $this->getDoctrine()->getEntityManager();
$address = new Address();
$isNew = is_null($id);
//this also tests positve after a form has been sent
if($isNew)
{
#fill address object with some defaults
$address->setCreated(new \DateTime("now"));
} else
{
#fill address object with existing db data
$address = $em->getRepository('XxxBundle:Address')->find($id);
}
#create form and fill it with address object data
$form = $this->createForm(new AddressType(), $address);
#if form sent: check input
$request = $this->get('request');
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request); //calls setters
if ($form->isValid())
{
//if I leave the following lines in: no error (but also no sense)
//because the pcNum should be retrieved from the form
$pcNum = 2222;
$town = $em->getRepository('XxxBundle:Town')->find($pcNum);
$address->setTown($town);
//end
#persist and flush
$id_unknown = is_null($address->getId());
if($id_unknown)
{
#insert address
$em->persist($address);
} else
{
#update address
$address->setModified(new \DateTime("now"));
$em->merge($address);
}
#commit
$em->flush();
#get id of update or insert and redirect user
$address_id = $address->getId();
return $this->redirect($this->generateUrl('displayAddress', array('id'=>$address_id)));
}
}
return $this->render('XxxBundle:Forms:address.html.twig', array('form'=>$form->createView(), 'new' => $isNew, 'id' => $id));
}
To conclude, this is the relevant Twig snippet:
<form action="{{ path('addAddress') }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
<div class="form_row">
{{ form_errors(form.town) }}
{{ form_label(form.town, 'Postal code:*') }}
{{ form_widget(form.town, {'attr': { 'class': 'form_pcNum', 'maxlength': '4', 'size': '4' } } ) }}
</div>
<div class="form_row">
<!-- some more fields here -->
</div>
<div class="form_row">
<button name="btn_add" id="do_add" type="submit" class="" value="btn_add" title="Ok!">Ok</button>
</div>
{{ form_rest(form) }}
</form>
Any help is appreciated...
If you know of a relevant (working) example, that would also be great. Cheers!
I had similar problem with foreign key violation and I solved. Please read Symfony2 form and Doctrine2 - update foreign key in assigned entities fails [solved]