Symfony2 OneToOne annotation inserting/updating related entity - php

I have Product entity related to Settings entity with one-to-one relation. All products are imported and Product don't have related row in Setting table at start.
When updating the product using Symfony proper relation is created so I have OneToOne between Product and Setting. This is done using simple INSERT statement:
INSERT INTO settings (id_product, setting_1, setting_3, setting_3) VALUES (11153, '0', '1', '1');
This insert is made automaticaly by Symfony thanks to annotations and method:
$em->merge($product);
$em->flush();
Unfortunately when I try to update product next time another insert to setting table is done and obviously I have and error becouse id_product is PK and have to be unique. What should I do to force Symfony to do an UPDATE instead of INSERT if there is a relation between Product and Setting? I create my music using the newest version of Symfony.

Update is not done in the same way that you would create an entity. Here is a very quick example (you'd have to handle a few exceptions etc.. but code is 'short' to show you the differences:
1 When your create a Task, you create a new entity Task and save it to the database
2 When you update a Task, you first fetch that task from the database and then save it to the database.
Create:
public function newAction(Request $request)
{
// create a task and give it some dummy data for this example
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
return $this->redirect($this->generateUrl('task_success'));
}
}
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
}
Update :
public function updateAction(Request $request, $taskId)
{
// get the task
$task = $this->getDoctrine()
->getRepository('AcmeTaskBundle:Task');
->find($taskId);
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->add('save', 'submit')
->getForm();
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
// update the task
$em->flush();
return $this->redirect($this->generateUrl('task_success'));
}
}
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView(),
));
}
I'd recommend that you check out:
Databases and Doctrine
Forms

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 :)

Symfony 3.4 - how are forms whose actions point to a different controller handled?

I want to process a form in a separate controller. While the Symfony docs show how to change a form's action and method, they don't show how the controller selected in $form->setAction() is actually supposed to handle the form. Is it present in Request? Do we make another Form object so we can check $form->isSubmitted() and $form->isValid()?
It's a pretty glaring omission.
Here is a quick example:
Controller for displaying the form
/**
* #route("/form", name="form_route")
*/
public function formAction()
{
$form = $this->createFormBuilder()
->setAction($this->generateUrl('task_route'))
->setMethod('POST')
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
return $this->render('form.html.twig', [
'form' => $form->createView(),
]);
}
Controller to handle submission
Is it present in Request?
Yes, the form data is in the request.
Do we make another Form object so we can check $form->isSubmitted() and $form->isValid()?
The form has to be recreated so that you can handle and validate the request.
/**
* #route("/task", name="task_route")
*/
public function postAction(Request $request)
{
$form = $this->createFormBuilder()
->add('task', TextType::class)
->add('dueDate', DateType::class)
->add('save', SubmitType::class)
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()){
$task = $form->getData();
/* ... */
}
//render task to view submission
return $this->render('task.html.twig', [
'task' => $task,
]);
}
This does have some duplicate code even when using entities and Symfony's Form Classes, which is why Symfony Docs recommends using the same controller for processing forms.

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.

Symfony2 Remove collection duplicates from the form in the controller

I have a form containing a collection (allowing add and remove entities) and a validation constraint to check if there are duplicates user's selections.
In case of there is of duplicates, the user is redirected in the new / edit form with an error message.
In this case, I am looking for a way to remove the duplicates fields from the form before rendering the view?
I am able to remove the duplicates from the list in my entity, but not from the form fields.
public function createAction(Request $request)
{
...
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
...
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('ispc_show', array('id' => $entity->getId())));
}
// Remove the duplicates from the list in the entity
$entity->removeDuplicate();
// And apply to the form
$form->remove....
return $this->render('CompanyDocumentBundle:ISPC:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Update
I don't want to remove the duplicates before validation because the error message is important to report to the user. Also, the duplicates are part of the "$request" object, and the form fields are filled with this object, not with the entity.

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