Symfony Forms with 2 databases - php

I could not really find my specific case on the internet and therefore decided on writing my own question.
I have a form to create a Member object. This member object has a reference to an application object. Both are saved in 2 different databases.
Creating a Member object is no issue only when I edit and fill out the form, do I encounter an error ->
Entity of type "Application"
passed to the choice field must be managed. Maybe you forget to
persist it in the entity manager
Here is my Form Code
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('membberName', null, array('label' => false))
->add('memberDescription', TextareaType::class, array('label' => false))
->add('memberVisible', null, array('label' => false))
->add('memberApp', EntityType::class, [
'label' => false,
'class' => Application::class,
'choice_label' => function (Application $application) {
return sprintf('(%d) %s', $application->getAppId(), $application->getAppurlUrl());
},
'choices' => $this->applicationRepository->getAll(),
])
->add('Save', SubmitType::class, [
'attr' => ['class' => 'create-button']
]);
}
I found a lot of cases about this issue but none of them could help me.
I only encounter this issue if I load an entity that is related to another one outside their own database.
To summarize: calling the create view page and pressing on submit works.
Calling the edit view causes the above mentioned issue.
Do I have to define or configure anything so my form can load correctly?

Try setting up custom em for your entity form field memberApp in options to your second database entity manager.
like described in docs: https://symfony.com/doc/current/reference/forms/types/entity.html#em
em type: string | Doctrine\Common\Persistence\ObjectManager default:
the default entity manager
If specified, this entity manager will be used to load the choices
instead of the default entity manager.
Another way, probably would be to set ['mapped' => false] for this field and handling flush manually with correct database's em in controller or service

Related

How to solve Symfony error Variable "expanded" does not exist?

Currently, I develop an app with PHP Symfony Framework. I've got a problem with Form Builder (I think).
I have two entities. Question and Choice.
Question and Choice are OneToMany Relationship Entity. One Question has many Choice.
Another two entities, Video and Category, the relationship is just the same with Question and Choice.
I create scaffolding crud for those entity with php bin/console make:crud.
Then I add the relationship symfony like in this guide from Symfony.
The logic is, I must select the Category first to create new Video. Same with the Choice, I must select the Question first to create new Choice data.
My problem appear when I open the Choice Create Form [/choice/new]. It says
Variable "expanded" does not exist.
Then the error details show on this lines
return $this->render('choice/new.html.twig', [
'choice' => $choice,
'form' => $form->createView(), // The highlighted error appear on this line
]);
But, It just happen in the Question-Choice, My Category-Video relationship is just fine. I tried to make Question-Choice as same as Category-Video (I changed the name of the entity for sure), I triple check it, but the error on Choice Create Form still occur.
This is my App\Form\ChoiceType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('content')
->add('letter')
->add('image')
->add('question', EntityType::class, [
'class' => Question::class,
'choice_label' => 'content'
])
;
}
Notice the add('question')
and this is my App\Form\VideoType buildForm method
<?php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('url', FileType::class, [
'label' => 'Video File',
'required' => false,
])
->add('thumbnail', FileType::class, [
'required' => false,
])
->add('description')
->add('category', EntityType::class, [
'class' => Category::class,
'choice_label' => 'name'
])
;
}
Notice the add('category')
So, anyone know what is happening?
I renamed App\Form\ChoiceType to App\Form\TheChoiceType, make some adjusment for class name changing on the controller. Everything is work!
I don't believe this! The solution is to rename the form type.

optional DateType field with symfony forms

I am trying to make an user profile edition form in my Symfony 3 project.
In this attempt, I've written the following code :
The controller :
/**
* #Route("/profile/edit", name="user_edit_profile", methods={"GET"})
* #Security("has_role('ROLE_USER')")
* #return \Symfony\Component\HttpFoundation\Response
*/
public function editMyProfileAction($form = null) {
return $this->render('user/edit_profile.html.twig', [
'form' => ($form !== null) ? $form->createView() : $this->createForm(UserProfileType::class, $this->getUser())->createView()
]);
}
/**
* #Route("/profile/edit", name="user_update_profile", methods={"POST"})
* #Security("has_role('ROLE_USER')")
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function updateMyProfileAction(Request $request) {
$user = $this->getUser();
$form = $this->createForm(UserProfileType::class, $user);
$form->handleRequest($request);
if(!$form->isValid()) {
return $this->forward('AppBundle:User:editMyProfile', [
'form' => $form
]);
}
$this->get('medievistes.user_manager')->updateUser($user);
return $this->redirectToRoute('user_profile');
}
The UserType, used for registration :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\{AbstractType, FormBuilderInterface};
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\{EmailType, TextType, RepeatedType, PasswordType};
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class)
->add('email', EmailType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'invalid_message' => 'users.registration.password_mismatch',
'first_options' => ['label' => 'Password'],
'second_options' => ['label' => 'Repeat Password'],
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
}
Then the UserProfileType, used for user edition :
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\{AbstractType, FormBuilderInterface};
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\{
EmailType,
TextType,
RepeatedType,
PasswordType,
DateType
};
class UserProfileType extends UserType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('username', TextType::class, [
'disabled' => true
])
->add('email', TextType::class, [
'disabled' => true
])
->add('firstname', TextType::class)
->add('lastname', TextType::class)
->add('birthdate', DateType::class, [
'placeholder' => '',
'widget' => 'single_text',
'required' => false,
'inherit_data' => true,
'empty_data' => null
])
->add('avatar', AvatarType::class)
->remove('plainPassword')
;
}
}
My Doctrine field $birthdate is nullable, and I haven't set any validation constraints yet. As you can see I tried a lot of options for the "birthdate" field :
If I just let the "widget" option, my form is displaying well, with a default value for birthdate. I want it to be optional, and set to null if the user does not fill it. But when I submit the form with the default value, I have the error "Expected argument of type "DateTime", "NULL" given".
So I tried adding some options."required" set to false didn't help, same error.
I set then "empty_data" to null. First improvement, I hadn't a Symfony exception when submitting, but a form error, saying "This value is invalid". I went in the Symfony profiler, telling me the DateTime Data Transformer couldn't handle null values, expecting string. I tried a string in "empty_values", but the filth tried to parse it. I do not want a datetime value when the user does not give it. Avoiding mental suicide I continued my quest for a working code.
Reading the docs, I met with the unknown "inherit_data" options, originally disabled. I read a very interesting thing, telling me that this option, when enabled, avoid the use of Data Transformers. The filth would be ignored, right the thing I wanted. AND YES !! It worked ! After a little victory dance, I admired my blank birthdate data in the profile displaying. To ensure my victory, I tried to click on the "edit my form" button, leading to my form. And then, the neuronal slaughter :
`An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class AppBundle\Entity\User could not be converted to string") in form_div_layout.html.twig at line 13.``
Bowing to so much hatred, I tried to give to that mysterious evil Form Component what it wanted. To my shame, I implemented a __toString method to my user class, returning the username. The form appeared again, I thought my pain was at an end. The validation worked with an empty value. With tears of joy, I committed and pushed my hotfix (if it wasn't in production, there wouldn't be any challenge). Happy again, I tried to fill the birthdate field. Even with a correct value, it remains empty.
I removed the __toString method, regaining my developer honor, observing before that that the returned username was used as a value attribute to the birthdate input (the real WTF). I'm really going to believe this is a framework issue. But I hope I'm wrong. I do not often use form component.
Please save my soul.

Symfony User Roles not selected in user form

I have created a simple user/role form. The form shows the user's detail correctly and displays all the possible roles, but for some reason it does not pre-select the users' current role. For the relationship between the user and role I had the following in the user entity class:
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users", cascade={"persist","remove"})
* #ORM\JoinTable(name="user_role")
*/
protected $roles;
The formtype class was built using:
$builder->add('firstname')
->add('lastname')
->add('email')
->add('roles');
The database looks like this:
Any hints/assistance would be appreciated.
You need to define your roles fields as entity
http://symfony.com/doc/current/reference/forms/types/entity.html
change this line ->add('roles'); to:
->add('roles', 'entity', array(
'multiple' => true,
'expanded' => true,
'property' => 'name',
'class' => 'Your_Path\Entity\Roles',
));
it should work.
Second option:
you can try to create role type form as mentioned here and then do something like this
$builder->add('roles', 'collection', array('type' => new RoleType()));
its recomended to read this this about mapped option and other as by_reference
Had the same problem in symfony4, adding this did the trick for me:
'choice_value' => function ($choice) {
return $choice;
},

Set Default value of choice field Symfony FormType

I want from the user to select a type of questionnaire, so I set a select that contains questionnaires types.
Types are loaded from a an entity QuestionType .
$builder
->add('questionType', 'entity', array(
'class' => 'QuizmooQuestionnaireBundle:QuestionType',
'property' => 'questionTypeName',
'multiple' => false,
'label' => 'Question Type'))
->add('type', 'hidden')
;
What am not able to achieve is to set a default value to the resulted select.
I have googled a lot but I got only preferred_choice solution which works only with arrays
I made it by setting a type in the newAction of my Controller I will get the seted type as default value.
public function newAction($id)
{
$entity = new RankingQuestion();
//getting values form database
$em = $this->getDoctrine()->getManager();
$type = $em->getRepository('QuizmooQuestionnaireBundle:QuestionType')->findBy(array('name'=>'Ranking Question'));
$entity->setQuestionType($type); // <- default value is set here
// Now in this form the default value for the select input will be 'Ranking Question'
$form = $this->createForm(new RankingQuestionType(), $entity);
return $this->render('QuizmooQuestionnaireBundle:RankingQuestion:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
'id_questionnaire' =>$id
));
}
You can use data attribute if you have a constant default value (http://symfony.com/doc/current/reference/forms/types/form.html)
but it wont be helpful if you are using the form to edit the entity ( not to create a new one )
If you are using the entity results to create a select menu then you can use preferred_choices.
The preferred choice(s) will be rendered at the top of the list as it says on the docs and so the first will technically be the default providing you don't add an empty value.
class MyFormType extends AbstractType{
public function __construct($foo){
$this->foo = $foo;
}
$builder
->add('questionType', 'entity', array(
'class' => 'QuizmooQuestionnaireBundle:QuestionType',
'property' => 'questionTypeName',
'multiple' => false,
'label' => 'Question Type'
'data' => $this->foo))
->add('type', 'hidden')
;
}
In controller
$this->createForm(new MyFormType($foo));
The accepted answer of setting in the model beforehand is a good one. However, I had a situation where I needed a default value for a certain field of each object in a collection type. The collection has the allow_add and allow_remove options enabled, so I can't pre-instantiate the values in the collection because I don't know how many objects the client will request. So I used the empty_data option with the primary key of the desired default object, like so:
class MyChildType
extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('optionalField', 'entity', array(
'class' => 'MyBundle:MyEntity',
// Symfony appears to convert this ID into the entity correctly!
'empty_data' => MyEntity::DEFAULT_ID,
'required' => false,
));
}
}
class MyParentType
extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('children', 'collection', array(
'type' => new MyChildType(),
'allow_add' => true
'allow_delete' => true,
'prototype' => true, // client can add as many as it wants
));
}
}
Set a default value on the member variable inside your entity (QuestionType), e.g.
/**
* default the numOfCourses to 10
*
* #var integer
*/
private $numCourses = 10;

Concatenate properties in Symfony2 buildForm()

I have a set of entities in Doctrine which depends on each other to build certain data, and I need to create a form which uses data from two of those entities.
I have a Magazine entity, an Issue entity and a Chapter entity. The Magazine (Mag1, Mag2) has it's name, the Issue, that belongs to only one Magazine, has it's 'number' (Mag1->Issue 1, Mag1->Issue 2, Mag3-> Issue 1, Mag2 -> Issue 'Summer'). The Chapter have to belong to just one Issue, but when creating the form, to build the Issue selector I need to concatenate properties from two entities:
class ChapterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('number')
->add('issue', 'entity', array(
'class' => 'Bundle:Issue',
'property' => 'magazine.name'
))
;
}
...
What I need to do is concatenate in the 'property' something like 'magazine.name'+'number' (where 'number' is the Issue which will be added number. Trying to concatenate with the . like in php strings doesn't work since they aren't strings so I don't know what I have to do or if It's possible to do It this way.
In the Issue create a new getter that does the concat. Given that you have properly setup the ManyToOne relationship, the getter should be something like:
public function getMagazzineAndIssue() {
return $this->magazine->getName() . $this->number;
}
in the form, use this new method as the property:
$builder
->add('name')
->add('number')
->add('issue', 'entity', array(
'class' => 'Bundle:Issue',
'property' => 'magazineAndIssue'
))

Categories