Symfony2 implement a form for OneToOne relation - php

I am trying to implement a one to one relation. Each User can be affected at a building (Etablissement for me). A building can have many people but each people can be affected at one building at most.
I have this error :
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Intranet\UserBundle\Entity\User::setUserEtab() must be an instance of Intranet\RhBundle\Entity\Etablissement, array given, called in C:\wamp\www\projet\vendor\symfony\symfony\src\Symfony\Component\PropertyAccess\PropertyAccessor.php on line 360 and defined in C:\wamp\www\projet\src\Intranet\UserBundle\Entity\User.php line 322
The line 322 of User.php is :
public function setEtablissement(\Intranet\RhBundle\Entity\Etablissement $etablissement = null)
It occurs at the line $form->handleRequest($request); of my controller. This is my controller :
public function editerAction(Request $request, User $user){
$form = $this->createForm(new EditerFormType, $user);
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
die("ici");
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$this->get('session')->getFlashBag()->add('success', "L'utilisateur ". $user->getNom() ." ". $user->getPrenom() . " a été édité avec succès !");
$em->flush();
return $this->redirect($this->generateUrl('intranet_rh_homepage'));
}else
$this->get('session')->getFlashBag()->add('danger', "Erreur de formulaire !");
}
return $this->render('IntranetRhBundle:User:editer.html.twig',array('user' => $user, 'form' => $form->createView()));
}
The die doesn't work. Before adding the form, it was working.
This is the EditForm :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', 'email', array('label' => 'form.email', 'translation_domain' => 'FOSUserBundle'))
->add('nom', 'text')
->add('prenom', 'text')
->add('naissance','date',array(
'widget' => 'single_text',
'format' => 'dd/MM/yyyy',
'attr' => array('class' => 'date', 'readonly' => 'readonly')
))
->add('sexe', 'choice', array(
'choices' => array('Homme' => 'Homme', 'Femme' => 'Femme'),
'multiple' => false
))
->add('etablissement', new UserEtabType())
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Intranet\Userbundle\Entity\User'));
}
This is my UserEtab form :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false))
;
}
And my relation on User entity :
/**
* #ORM\OneToOne(targetEntity="Intranet\RhBundle\Entity\Etablissement", cascade={"persist", "remove"})
**/
private $etablissement;
The form's view is okay, there is a select list with all the building and an empty value. But when I post, I have this error I can't understand and solve.
I have the setter and getter if User entity :
/**
* Set Etablissement
*
* #param \Intranet\RhBundle\EntityEtablissement $etablissement
* #return User
*/
public function setEtablissement(\Intranet\RhBundle\Entity\Etablissement $etablissement = null)
{
$this->etablissement = $etablissement;
return $this;
}
/**
* Get Etablissement
*
* #return \Intranet\RhBundle\Entity\Etablissement
*/
public function getEtablissement()
{
return $this->etablissement;
}
But when I var_dump $request->get('user')->get('etablissement') it doesn't work :
Error: Call to undefined method Intranet\UserBundle\Entity\User::get() in C:\wamp\www\projet\src\Intranet\RhBundle\Controller\UserController.php line 69
And the building doesn't appear in var_dump of $request->get('user').
EDIT :
I don't have a setDefaultOptions method for my UserEtabType because I don't know what I have to do. I tried to implements it but I have this kind of error :
The form's view data is expected to be an instance of class Intranet\UserBundle\Entity\User, but is an instance of class Proxies__CG__\Intranet\RhBundle\Entity\Etablissement. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms an instance of class Proxies__CG__\Intranet\RhBundle\Entity\Etablissement to an instance of Intranet\UserBundle\Entity\User.
For the new UserEtabType :
namespace Intranet\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UserEtabType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\User'));
}
public function getName()
{
return 'intranet_userbundle_useretablissementtype';
}
}

Can you show the new UserEtabType() class ?
EDIT 1 : Try putting this line :
->add('etablissement', 'entity', array('class' => 'IntranetRhBundle:Etablissement', 'property' => 'nom', 'empty_value' => 'Aucun', 'empty_data' => -1, 'required' => false));
in the main EditForm
EDIT 2:
plus be careful :
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\User'));
must be
$resolver->setDefaults(array('data_class' => 'Intranet\UserBundle\Entity\Etablissement'));
in your UserEtabType() class

Are you getting this error when you try to insert a record ? Or before that. For your information, setUserEtab() in the User entity expects an object of Etablissement, because of the one-to-one relationship. Passing the value of an array is not sufficient. Try passing the object of Etablissement it self (the selected object after performing dql).
Hope this helps someone.
Cheers!

Related

Symfony 2 persiting 2 entities from form using ID generated from first entity

I have a form that collects data from two related entities 'Jobs' and 'Scope'. Jobs data adds successfully to the database, but i can't get the scope entity to add which also requires the unique ID that was created from the jobs entry. I'm using doctrine by the way.
Here is jobsType:
class jobsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('jobNumber', null, array('label' => 'Job Number','attr' => array('placeholder'=>'Code used for job')))
->add('description', null, array('label' => 'Job Description',))
;
$builder->add('scopes', new scopeType())
;
}
public function getName()
{
return 'jobs';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Baker\TimeSlipBundle\Entity\Jobs',
));
}
}
Here is scopeType
class scopeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', 'entity', array('label' => 'Scope', 'required' => false,
'class' => 'BakerTimeSlipBundle:Scope',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('s')
->orderBy('s.description', 'asc');
},
'property' => 'description',
'multiple' => false,
'expanded' => false,
'empty_value' => false,
'attr' => array('class' => 'form-inline'),
'label_attr' => array('class' => 'required')
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Baker\TimeSlipBundle\Entity\Scope',
));
}
public function getName()
{
return 'scope';
}
}
Here is my controller, i think the problem here is that i'm not passing the scope() instance to the form builder. Which i'm not sure how to do. The entity is mapped correctly.
class JobsController extends Controller
{
public function indexAction($limit, Request $request)
{
$jobs= new Jobs();
$scope = new Scope();
$fname=$limit;
$form = $this->createForm(new JobsType(),$jobs, array(
'method' => 'POST',
));
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($jobs);
$em->persist($scope);
$em->flush();
return $this->redirectToRoute('company',array ('limit' =>'Job Submitted Successfully'));
}
return $this->render(
'BakerTimeSlipBundle:Jobs:index.html.twig',
array('fname' => $fname,
'form' => $form->createView()
)
);
}
}
}
Any help here would be greatly appreciated. Below is listed the error message when I try to enter the scope:
An exception occurred while executing 'INSERT INTO Scope (description, jobsId, jobsid) VALUES (?, ?, ?)' with params [null, null, null]:
SQLSTATE[42000]: Syntax error or access violation: 1110 Column 'jobsId' specified twice
The problem here is the $jobs object you link to your form has no relation with the $scope object. So when you try to persist $jobs, Doctrine has no idea $scope it had to persist.
Just try this :
$jobs= new Jobs();
$scope = new Scope();
$jobs->setScope($scope);
Then, in your Jobs entity, on the annotation above $scopes, make sure you have cascade={"persist"} (but you must already have it because Doctrine tries to persist, and that's where the error is thrown), and you should be good to go.
Hope this helps

symfony 2 how to pass array collection to input select

Hi i am tying pass array collection (method getProjects() returns it) to form (select input) and fail. This code returns exception - A "__toString()" method was not found on the objects of type "Tasker\WebBundle\Entity\Project" passed to the choice field.
Can anybody help? Is needed transformer? Or what is right way?
Controller:
/**
* #Route("/pridaj", name="web.task.add")
* #Template()
*/
public function addAction(Request $request)
{
$task = new Task;
/** #var User $loggedUser */
$loggedUser = $this->get('security.token_storage')->getToken()->getUser();
$form = $this->createForm(new AddTaskType(), $task, ['user' => $loggedUser]);
if ($form->handleRequest($request) && $form->isValid()) {
// some stuff
}
return [
'form' => $form->createView()
];
}
Form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
])
// ....
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array(
'user',
));
$resolver->setDefaults(array(
'user' => null,
));
}
just add __ToString() to your Project class
Tasker\WebBundle\Entity\Project
class Project
{
....
function __toString() {
return $this->getName(); //or whatever string you have
}
}
I wanted to add another answer, because you do not have to add __toString() to your Project class. The Symfony entity field type allows you to specify which property/field to use for displaying. So instead of __toString() you could specify the property in the form configuration like so:
$builder
->add('project', 'entity', [
'label' => 'Projekt:',
'class' => 'TaskerWebBundle:Project',
'choices' => $options['user']->getProjects(),
'placeholder' => 'Označte projekt',
'property' => 'name'
])
If you check this part of the Symfony documentation you will see that __toString() is automatically called only if you do not specify the property.

Silex Framework Collection form type

I have a problem with collection form type. My entities:
User
use Doctrine\Common\Collections\ArrayCollection;
/**
* #OneToMany(targetEntity="Comment", mappedBy="user")
*/
protected $comments;
public function __construct() {
$this->comments= new ArrayCollection();
}
Comment
/**
* #ManyToOne(targetEntity="User", inversedBy="comments")
* #JoinColumn(name="user_id", referencedColumnName="id")
**/
protected $user;
Formbuilder:
$form = $silex['form.factory']->createBuilder('form', $user)
->add('comments', 'collection', array(
'type' => 'text',
'options' => array(
'required' => false,
'data_class' => 'Site\Entity\Comment'
),
))
->getForm();
and is returned error:
Catchable fatal error: Object of class Site\Entity\Comment could not be converted to string in C:\XXX\vendor\twig\twig\lib\Twig\Environment.php(331) : eval()'d code on line 307 Call Stack
I think that you might struggle to use a text type collection field here, since you want a field which is a collection of complex entities and not just one which is an array of strings.
I would suggest adding a new Form type for the Comment Entity:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CommentType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('text', 'text', array());
}
public function getName() {
return 'comment';
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'Site\Entity\Comment'
);
}
}
Then in the original Formbuilder, reference this type:
$form = $silex['form.factory']->createBuilder('form', $user)
->add('comments', 'collection', array(
'type' => new CommentType(),
'options' => array(
'required' => false,
'data_class' => 'Site\Entity\Comment'
'allow_add' => true,
'allow_delete' => true
),
))
->getForm();

Multiple (oneToMany) Entities form generation with symfony2 and file upload

I'm on a problem related to a Symfony2 application which i'm building. The problem concerns an article (News) linked to one or many pictures (Illustration). It seems pretty simple. But i'm stacking on the controller which should persist the News, the Illustration and uploading the picture file.
My News form type:
namespace Fcbg\NewsBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class NewsType extends AbstractType
{
public function buildForm( FormBuilderInterface $builder, array $options )
{
$builder
->add('date', 'date')
->add('titre', 'text')
->add('contenu', 'textarea')
->add('publication', 'checkbox', array('required' => false))
->add('type', 'entity', array( 'class' => 'FcbgNewsBundle:Illustration', 'property' => 'value', 'multiple' => true ));
}
public function getName()
{
return 'News';
}
}
My picture(s) form type:
namespace Fcbg\NewsBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class IllustrationType extends AbstractType
{
public function buildForm( FormBuilderInterface $builder, array $options )
{
$builder
->add('file', 'file');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Fcbg\NewsBundle\Entity\Illustration',
'cascade_validation' => true,
));
}
public function getName()
{
return 'News';
}
}
My controller action:
public function addAction()
{
//link works properly I think
$news = new News();
$illustration = new Illustration();
$illustration->setNews($news);
$news->addIllustration($illustration);
$form = $this->createForm(new NewsType(), $news);
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$doctrine = $this->getDoctrine();
$newsManager = $doctrine->getManager();
$newsManager->persist($news);
$newsManager->persist($illustration);
$newsManager->flush();
return $this->redirect(...);
}
}
return $this->render('base.html.twig',
array(
'content' => 'FcbgNewsBundle:Default:formulaireNews.html.twig',
'form' => $form->createView(),
'name' => "add a news"
)
);
}
The error i get on the execution:
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
The problem here is that my entity gets a method called "getIllustrations()" which returns logically an arrey of Illustration. So i'm not able to understand this error/question. I assume that my "illustration should be a file field and not a choice field...
Any idea about how i can go further? thx a lot!
I think the problem is that you are using the 'entity' form field here:
->add('type', 'entity', array( 'class' => 'FcbgNewsBundle:Illustration', 'property' => 'value', 'multiple' => true ));
and this type of form field acts as a choice and it's used to work with elements created in the database. You can see this in http://symfony.com/doc/current/reference/forms/types/entity.html
A possible solution could be use the "prototype" like here http://symfony.com/doc/current/cookbook/form/form_collections.html#cookbook-form-collections-new-prototype
where you could have:
public function buildForm( FormBuilderInterface $builder, array $options )
{
$builder
->add('date', 'date')
->add('titre', 'text')
->add('contenu', 'textarea')
->add('publication', 'checkbox', array('required' => false))
->add('type', 'collection', array(
'type' => new IllustrationType(),
'allow_add' => true,
));
}
I hope that this be useful for you.
Kind regards.
So, the code which I was talking about:
public function buildForm( FormBuilderInterface $builder, array $options )
{
$builder
->add('date', 'date')
->add('titre', 'text')
->add('contenu', 'textarea')
->add('publication', 'checkbox', array('required' => false))
->add('illustrations', 'collection', array(
'type' => new IllustrationType(),
'allow_add' => true
));
}

Symfony form collection

I'm trying to add a formType in a other form type.
CustomerType:
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstname', 'text', array(
'required' => 'required'
))
->add('middlename')
->add('lastname')
->add('email', 'email')
->add('groups', 'entity', array(
'class' => 'MV\CMSBundle\Entity\Group',
'property' => 'name',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('g')
->orderBy('g.name', 'ASC');
}
))
->add('profile', 'collection', array(
'type' => new ProfileType()
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MV\CMSBundle\Entity\User',
));
}
public function getName()
{
return 'customer';
}
}
ProfileType:
class ProfileType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('isActive', 'checkbox', array(
'required' => false
))
->add('phone')
->add('address')
->add('city')
->add('zipcode')
->add('country')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MV\NameBundle\Entity\Profile',
));
}
public function getName()
{
return 'profile';
}
}
Then I get this message:
Expected argument of type "array or (\Traversable and \ArrayAccess)", "MV\NameBundle\Entity\Profile" given.
If I comment that line i get: (just to check what will goes wrong ;) )
The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class MV\NameBundle\Entity\Profile. You can avoid this error by setting the "data_class" option to "MV\NameBundle\Entity\Profile" or by adding a view transformer that transforms an instance of class MV\NameBundle\Entity\Profile to scalar, array or an instance of \ArrayAccess.
What do I do wrong?
Symfony2: 2.1.8-DEV
It's not clear what your mapping is between your Customer entity and your Profile entity, but I guess there are 2 options:
Option 1: You have a OneToOne relationship (One customer can only have One profile). Therefore, you don't need the collection at all but simply need to embed a single object.
->add('profile', new ProfileType());
Option 2: You want a ManyToOne relationship (One customer can have many profiles). In that case, you need to use to embed a collection of forms. If this is what you want, rename $profile to $profiles in your Customer entity, and do the following:
//MV\CMSBundle\Entity\Customer
use Doctrine\Common\Collections\ArrayCollection;
/**
* Your mapping here.
*
* #ORM\ManyToOne(targetEntity="MV\NameBundle\Entity\Profile")
*/
protected $profiles;
public function __construct()
{
//This is why you had an error, this is missing from your entity
//An object was sent instead of a collection.
$this->profiles = new ArrayCollection();
}
Finally, update your database.

Categories