Symfony Forms: Persisiting object with constructor to Databese - php

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

Related

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 Form is always empty after submitting

I'm new with Symfony2 (2.4.4).
I want to create a HTML layout which shows always a form on top (searchbar). I send the form via post and would like to redirect to another controller, which should pass the user input and generate an output. I created a new function like this:
public function searchFormAction(Request $request)
{
//$defaultData = array('sstring' => 'Suche');
$form = $this->createFormBuilder()
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($request->isMethod('POST'))
{
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData();
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
}
I extended my base layout (base.html.twig) and include the form with the render function
{% render(controller('SchmanEmployeeBundle:Employee:searchForm')) %}
This works fine and the form is always present in my layout. The given HTML looks like this:
<form name="form" method="post" action="/app_dev.php/">
<div><input type="search" id="form_sstring" name="form[sstring]" required="required"></div>
<div><button type="submit" id="form_submit" name="form[submit]">suchen</button></div>
Now I have 3 questions:
If I submit the form, I don't want to be redirected to the searchAction Controller. This is because the $request->isMethod is always GET. Why? The form actions is post?
In the Symfony Webtool the form section is also empty. I see all form fields (sstring) and the data is always null. Where's the user input?
First off, your form is set to be POST by default, so you should be good. Second, you don't pass any data to be filled by your form, and I think you should. Third, you don't check if the form is valid, which includes the test if it's submitted. You should do this:
$defaultData = array(); // No need for a class object, array is enough
$form = $this->createFormBuilder($defaultData)
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'))
->getForm();
$form->handleRequest($request);
if($form->isValid())
{
// Happens if the form is submitted
return $this->redirect('SchmanEmployeeBundle:Employee:search', array(
'sstring' => $form->get('sstring')->getData(); // TODO: This will probably produce an error, fix it
));
}
return $this->render('SchmanEmployeeBundle:Employee:searchForm.html.twig', array(
'form' => $form->createView()
));
Also, I think you shouldn't worry about the form method because you don't have different implementations for other methods. This is the usual way the forms are handled in Symfony. You should read on forms in detail before proceeding, the article is quite informative.
I guess its because you didnt specified, in your routing configuration, that the method of this function is POST.
Because the form never submitted to your function (your function want GET, but send POST)
Where is the last question?
There is a great code to make your search function, it should work (sorry if you dont use annotation).
One good point, you can now use your searchType everywhere in your project, you should make your form like that instead of formbuilder into your controller. Easier to read and to use.
Controller:
/**
* To search something
*
* #Route("/search", name="search")
* #Template()
*/
public function searchAction()
{
$form = $this->createForm(new searchType());
$request = $this->get('request');
if ($request->getMethod() == 'POST')
{
$form->bind($request);
if ($form->isValid())
{
$informations = $form->get('search')->getData();
//make things here
}
}
}
And here is the searchType class:
class searchType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fnr', 'hidden')
->add('sstring', 'search', array('label' => false))
->add('submit', 'submit', array('label' => 'suchen'));
}
/**
* #return string
*/
public function getName()
{
return 'yournamespace_searchType';
}
}

Symfony2 OneToOne annotation inserting/updating related entity

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

How to render a form without a class in other service?

I want to generate a class-less form inside my service.
The way I do it is:
class StepSummary implements StepInterface
{
public function __construct($container)
{
$this->container = $container;
}
public function getVariables()
{
$form = $this->container->get('form.factory')->createBuilder('text')
->add('accept')
->getForm();
return array('form' => $form->createView());
}
}
In the API, I've found that I need to pass a form type to the FormBuilder - I didn't find any reference to that, so I've put imaginary text string. Now it renders the form but this way:
<input type="text" id="text" name="text" required="required" />
Obviously there is no reference to the accept field.
Controller's createForm() method was quite helpful here:
public function createFormBuilder($data = null, array $options = array())
{
return $this->container->get('form.factory')->createBuilder('form', $data, $options);
}
So the solution is:
$form = $this->container->get('form.factory')->createBuilder('form')
->add('accept')
->getForm();
Look at the chapter in the Symfony2 documentation called Using a Form without a Class.
Basically, you have to use createFormBuilder and instead of a string or an object you just pass an array with the default values.
From the documentation mentioned before:
// make sure you've imported the Request namespace above the class
use Symfony\Component\HttpFoundation\Request
// ...
public function contactAction(Request $request)
{
$defaultData = array('message' => 'Type your message here');
$form = $this->createFormBuilder($defaultData)
->add('name', 'text')
->add('email', 'email')
->add('message', 'textarea')
->getForm();
if ($request->getMethod() == 'POST') {
$form->bind($request);
// data is an array with "name", "email", and "message" keys
$data = $form->getData();
}
// ... render the form
}
If you don´t want to tie your form to any particular object, you don´t need to pass any object to the builder, you can do:
$form = $this->container->get('form.factory')->createBuilder()
->add('accept')
->getForm();
If you want to set some defaults for the form, you can tie the form to an array. For example:
$data['accept'] = 'default accept';
$form = $this->container->get('form.factory')->createBuilder($data)
->add('accept')
->getForm();

Categories