Symfony Form handle one-to-many as one-to-one - php

In the database the relation between contact and contactAddress is a one-to-many.
I want to handle a symfony form and not use the collection type because it should be a one-to-one for the user.
$builder->add(
'contactAddresses',
new ContactAddressType()
);
Following error appears:
Neither the property "contactAddresses" nor one of the methods
"addContactAddress()"/"removeContactAddress()",
"setContactAddresses()", "contactAddresses()", "__set()" or "__call()"
exist and have public access in class
"My\Bundle\ContactBundle\Entity\Contact".
The addContactAddress method exist in my contact entity.
public function addContactAddress(ContactAddress $contactAddress)
{
$this->contactAddresses[] = $contactAddress;
return $this;
}

Try this :
$builder
->add('contactAddresses','entity', array(
'class'=>'yourBundle:ContactAddress',
'property'=>'propertyToDisplay'
));

Did fix it by use CollectionType and add a $contactAddress to it
Builder:
$builder->add(
'contactAddresses',
'collection', [
'entity' => new ContactTypeAddress()
]
);
Controller:
$contact = new Contact();
$contact->addContactAddress(new ContactAddress()); // This line did fix my problem
$this->createForm(new MyContactType(), $contact);

You should add it as follows:
$builder->add('contactAddresses',ContactAddressType()::class);

Related

Symfony Forms: Persisiting object with constructor to Databese

I am trying to persist an object of an entity to the database using symfony forms. The entity has an constructor therefore I am giving the object dummy data but I am not able to change this data with the forms. Does anyone have a solution how to create an object that requires a constructor?
public function new(Request $request)
{
$player = new Player("Dummy",0);
$form = $this->createFormBuilder($player)
->add('name', TextType::class)
->add('points', IntegerType::class)
->add('save', SubmitType::class, array('label' => 'Create Player'))
->getForm();
$form->handleRequest($request);
$data = $form->getData();
$name = $data->getName();
error_log($name);
$this->PlayerRepository->store($player);
return $this->render('default/new.html.twig', array(
'form' => $form->createView(),
));
}
$name has always the value "Dummy" no matter what I type in the form.
You save $player here:
$this->PlayerRepository->store($player);
But your actual player data from form is in $data, and this $data should be stored:
$this->PlayerRepository->store($data);
Okay, seems that I found the mistake.
I did not define the POST Route for the same controller building the view.
sorry for that :)

Doctrine Tell me "new Entity was found" when i set a relationship ManyToOne but is not

I have a Project with Symfony
Create a form for a ner entity.
class EquipoType extends AbstractType{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$equipo = new Equipo();
$builder
->add('unidades', IntegerType::class)
->add('accionComercial', EntityType::class, array(
'class' => 'AppBundle\Entity\IndicoGpt\adelantoequipos\AccionComercial',
'em' => 'adeq',
'choices' => $equipo->getAccionComercial(),
'choice_label' => 'nombre',
'placeholder' => 'Elija Accion Comercial'
))
->add('modelo', TextType::class)
->add('save', SubmitType::class, array(
'label' => 'Guardar Equipo',
'attr' => array(
'class' => "btn btn-info",
'style' => "margin-top: 7px; margin-left:40%;"
))
);
$builder->get('modelo')
->addModelTransformer(new CallbackTransformer(
function ($modelo) {
return $modelo ? $modelo->getModDescModelo() : $modelo;
},
function ($modelo) use ($options) {
return $modelo ? $options['em']->getRepository(ModModelo::class)->findOneBy(array(
'modDescModelo' => $modelo
)) : $modelo;
}
));
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\IndicoGpt\adelantoequipos\Equipo',
'em' => "adeq"
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_indicogpt_adelantoequipos_equipo';
}
}
This Entity has a relatuionship.
(Equipo has Modelo[oneToMany], Modelo has Many Equipos[oneToMany] )
class Equipo
{
/**
* #var \AppBundle\Entity\IndicoGpt\catalogo\ModModelo
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\IndicoGpt\catalogo\ModModelo")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="modelo_id", referencedColumnName="MOD_ID_MODELO")
* })
*/
private $modelo;
The entity Modelo is full of rows (in db) with types of equip models. Ergo an Equipo only need to save an existing Id of a Modelo row.
The problem appears when i do $emAedq->flush() Here is my controller:
public function crearEquipo(Request $request){
$emAdeq = $this->getDoctrine()->getManager('adeq');
$emCatalogo = $this->getDoctrine()->getManager('catalogo');
$equipo = new Equipo();
$form = $this->createForm(EquipoType::class, $equipo, array(
'action' => "crearEquipo",
'em' => $emCatalogo,
'attr' => array(
'class' => "form"
)
));
$form->handleRequest($request);
if ($form->isSubmitted()) {
$equipo = $form->getData();
$emAdeq->persist($equipo);
$emAdeq->flush();
$this->addFlash('exito', $equipo->getId());
return $this->redirectToRoute('portadaAdelantoEquipos');
}
...
When I go to save the new Equipo it tells me that it was found a new entity in the relationship and should be saved (reffering to Modelo associated), but that entity is not new, it is an existing Modelo that is now related to the Equipo.
As shown in the image, I choose a Modelo of the existing ones through an autocomplete field.
Just before doin the $emAdeq->persist($equipo) of the entity, i can see in the debugger the object that I am going to save it appears well composed:
The Error is the next:
A new entity was found through the relationship 'AppBundle\Entity\IndicoGpt\adelantoequipos\Equipo#modelo' that was not configured to cascade persist operations for entity: AppBundle\Entity\IndicoGpt\catalogo\ModModelo#0000000011088ca600000000661c7dca. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\IndicoGpt\catalogo\ModModelo#__toString()' to get a clue.
I dont understand why thinks that is a new entity and why give me this string refering to modelo:
Why this happends?
How can i save the Equipo with his relationship?
Without make a persist of the Modelo because i can not save the Modelo that allready exist!
I have seen other example here. But this does not work to me...
I run this command php bin/console doctrine:schema:validate and everything is right.
You can configure cascade persist into your entity or you can change this:
$emAdeq->persist($equipo);
to this:
$emAdeq->merge($equipo);
The problem is that I was using two entityManager because the relationship is made in two different databases.
But I have not realized that the related entity has his database established, so I only need one EntityManager.
With one entityManager i can ask for one entity and the relations without other EntityManagers
By having two entityManagers I was asked to saved twice.
Solution:
Establish in the entity his DB: * # ORM \ Table (name = "advanced equipment.") Where I have used $emCatalgo replace it with $emAdeq and eliminate the catalog because it is unusable.
Thanks to #Alessandro Minoccheri because his answer is also valid but that make my realize what im doing wrong with 2 EntittyManagers.

Symfony 3 subform submitted as array instead of entity

I am in the middle of converting a project from Symfony 2 to Symfony 3 and have run into a problem. I have an Event entity that has a TwitterSearch entity which is defined by the following in Event.php:
/**
* #ORM\ManyToOne(targetEntity="TwitterSearch", cascade={"persist"})
* #ORM\JoinColumn(name="twitter_search_id", referencedColumnName="id")
*/
private $twitterSearch;
This is how I have set up the relationship in the EventType:
$builder->add('twitterSearch', TwitterSearchType::class, array(
'required'=>false,
));
Here is the code from the controller for when the form is submitted:
$entity = new Event();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$entity = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
}
This used to work in Symfony 2 but now when the form is submitted the following error is thrown:
Expected value of type "TwitterSearch" for association field "Event#$twitterSearch"
, got "array" instead.
I debugged the code and the twitterSearch attribute of the Event was and array. Does anyone know why it is no longer converting this to an instance of TwitterSearch?
Use EntityType instead.
Example:
$builder->add('twitterSearch', EntityType::class, array(
'class' => TwitterSearch::class,
'choice_label' => 'name',
));

How to get user ID (FOSUserBundle) in form builder in Symfony2?

I'm quite new here, be patient, please.
I'm trying to make notice board project in Symfony2 using FOSUserBundle.
I try to get logged user id to put it into form created with form builder (and then to MySQL database).
One of attempts is:
public function createNoticeAction(Request $request)
{
$notice = new Notice();
$form = $this->createFormBuilder($notice)
->add("content", "text")
->add("user_id","entity",
array("class"=>"FOS/UserBundle/FOSUserBundle:", "choice_label"=>"id"))
->add("isActive", "true")
->add("category", "entity",
array("class" => "AppBundle:Category", "choice_label" => "name"))
->add("save", "submit", array("label" => "Save"))
->getForm();
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
$em->persist($notice);
$em->flush();
return $this->redirectToRoute('app_user_showuserpage');
}
I tried many solutions again and again and I get some error.
You already have the user object Symfony > 2.1.x
In you Controller like this:
$userId = $this->getUser()->getId();
...
$notice->setUserId($userId);
$em->persist($notice);
Don't ->add field in you FormBuilder, its not safely. Set this value in you Controller and don't ->add this field in FormBuilder
for symfony 3.2.13
have excelent solution (just because is working, but is dangerous if someone discover it in pure HTML)
1) first build YourFormType class.
add normal field in Forms/YourFormType.php (if not, formbuilder tell you that you passing smth not quite right (too many fields) ; -) )
$builder
->add(
'MyModelAddedById',
HiddenType::class,
[
'label' => 'echhh', //somehow it has to be here
'attr' => ['style' => 'display:none'], //somehow it has to be here
]
);
2) in your controller
public function addSomethingAction(Request $request){
$form = $this->createForm(MyModelFormType::class);
//set field value
$request->request->set("prodModelAddedById", $this->getUser()->getId());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$product = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
$this->addFlash('success', 'record was added');
return $this->redirectToRoute('products');
}
return $this->render(
'default.add.form.html.twig',
[
'newprod' => $form->createView(),
]
);
}
explenation:
you are passing a field and variable to formbuilder (settig it already to default value!)
and important thing, becose of BUG in my opinion - you can't in your form type set method:
public function getBlockPrefix()
{
//return 'app_bundle_my_form_type';
}
because
$request->request->set
can't work properly if your POST data from form are in bag (parameterbag)
no entity managers, no services, no listeners...
hope it helps.

Symfony form entity with allow add

I'm building an application with Symfony 2.3.
I have a Booking entity witch is related to a Customer entity by a ManyToOne relation.
In my form, i would like to be able to select one existing customer or create a new one.
For exemple by having a "new customer" option in my customer select who will display the customer form with javascript for exemple.
In fact i'm trying to build an Entity form field with an "allow_add" option like in the collection form field.
Any idea of how i can do that ?
Thank you very much.
Thanks for contributing. I found a way to achieve it !
The solution is to have one field with the Customer form, it has to be mapped, and an entity field en the Customer entity but not mapped.
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($company) {
$form = $event->getForm();
$data = $event->getData();
$form->add('customer_list', 'entity',
[
'class' => 'SomeBunlde\Entity\Customer',
'label' => 'Client',
'property' => 'fullName',
'expanded' => false,
'multiple' => false,
'mapped' => false,
'query_builder' => function(EntityRepository $er) use ($company)
{
return $er->getByCompanyQueryBuilder($company);
},
]
)
;
if ($data->getCustomer() === null) {
$form->add('customer', new CustomerType());
}
}
After i add an extra option to the Entity form field overloading the finishView method :
public function finishView(FormView $view, FormInterface $form, array $options)
{
array_unshift($view->children['customer_list']->vars['choices'], new SfFormExt\ChoiceView('test', 'new', 'Nouveau client'));
}
Then i add two event listeners, a pre_submit to delete the mapped embeded customer form and its data :
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function(FormEvent $event) use ($em) {
$data = $event->getData();
if ($data['customer_list'] !== 'new') {
unset($data['customer']);
$event->getForm()->remove('customer');
// setting data w/out customer to avoid extra-field error
$event->setData($data);
}
}
);
and a bind event to attach the existing customer to the booking :
$builder->addEventListener(
FormEvents::BIND,
function(FormEvent $event) use ($em) {
$form = $event->getForm();
$data = $event->getData();
if (!$form->has('customer')) {
$existing_customer = $form->get('customer_list')->getData();
if ($existing_customer instanceof Customer) {
$data->setCustomer($existing_customer);
}
}
}
);
I know it may not be state of the art code but it works pretty well.
Edit : I had an issue with this technique because when the customer_list is set to new, it throws an error. I didn't find a way to avoid this error (If you have any idea of how i can achieve this !) so i decided to modify the pre_submit to set to '' the data of customer_list value if we are in the new customer case, then i detect, in the controller, if there is a form validation error on the new client form in order to correctly display it.
I think the best way to do that is managing this workflow with javascript.
If you user choose to create a new customer, you open a create new customer form in a modal and via Ajax create the new customer. The response of the create action returns the id in the response which will be used by you to create the booking with the newly created customer.
The trick is: you will always create a booking from an existing customer. Your user can create a new customer in the process, but in fact it'll be created before you create the booking record.

Categories