How to handle bracket field arrays in Symfony REST API - php

I'm setting up a REST API using Symfony 3, and I can't get the array values send through a form, the latter being seen as not submitted in my controller.
The use case is to send an array of keywords through a POST form to a /media/keywords endpoint. Then, the endpoint would be able to get every single keyword and ac accordingly. There is no Doctrine entity to be involved here.
My action is as follow :
/**
* #Post(
* path = "/media/keywords",
* name = "app_media_keywords_update"
* )
* #View(StatusCode = 200)
* #RequestParam(name="keywords")
*/
public function keywordsAction(Request $request)
{
$form = $this->createFormBuilder()
->add('keywords', TextType::class)
->getForm();
$form->handleRequest($request);
echo "issub=".$form->isSubmitted()."<br>";
echo "isValid=".$form->isValid()."<br>";
die();
}
the output is as follow :
issub=
<br>isValid=
<br>
For testing purpose, I'm using Postman to simulate the POST submission as follows:
I don't understand why the form is seen as not submitted. Is something else needed ? Is my call to createFormBuilde() incorrect (in particular in respect with type, not being an array, only a TextType) ?

As you pass several items via keywords, you should use CollectionType:
$form = $this->createFormBuilder()
->add('keywords', CollectionType::class, [
'entry_type' => TextType::class
])
->getForm();
$form->handleRequest($request);
echo "issub=".$form->isSubmitted()."<br>";
echo "isValid=".$form->isValid()."<br>";
// also get form submit errors, for example:
if (!$form->isValid()) {
print_r($form->getErrors(true));
}
Form errors manual is here.

Related

Handle form data by myself

I built a form with createFormBuilder
$form = $this->createFormBuilder($post)
->add("name", TextType::class, array("label"=>"Article Title"))
->add("content", TextareaType::class, array("label"=>"Article Content"))
->add("categories",
EntityType::class,
array(
"class" => Category::class,
"choice_label" => "name",
"multiple" => true,
"label" => "Article Category",
"required" => false
)
)
->add("attachments", TextType::class, array("required"=>false))
->add("submit", SubmitType::class, array("label"=>"Add new article"))
->getForm();
the "attachments" variable is an entity variable, I want to get a json string from the form and search the database by myself, like this:
$em = $this->getDoctrine()->getManager();
$attachmentsRepository = $em->getRepository(Attachment::class);
$attachments = $post->getAttachments();
$json = json_decode($attachments);
$dataSize = sizeof($json);
for ($i = 0; $i < $dataSize; $i ++) {
$attachment = $attachmentsRepository->find($json[$i]->getId());
$post->addAttachments($attachment);
}
$em->persist($post);
$em->flush();
However, there is the error hint said that:
Could not determine access type for property "attachments" in class
"App\Entity\Post".
I don't know how to solve this problem, if I add a #ORM\Column(type="string") in my Entity the json string will also be stored, I think this is not a good solution.
How do I modify my code?
First of all to get rid of the error you need to add mapped => false to the field options of your attachments field.
If the attachments fields is configured as a mapped field the form component will look for a setAttachments() method or a public property attachments in your Post Entity to set the value - which do not exist in your Post entity. That's why you get the error message:
Could not determine access type for property "attachments" in class "App\Entity\Post".
In order to transform the submitted JSON string to Attachment entities yourself you need a custom data transformer!
A good example how create and use a data transformer can be found in the documentation chapter How to Use Data Transformers.
A clean solution would involve the attachments field being a CollectionType of EntityType, sending the whole form as a JSON POST request and using i.e FOSRestBundle's fos_rest.decoder.jsontoform body listener to decode the JSON request to a form.
The documentation for FOSRestBundle's body listener support can be found here.

Symfony2 - customizing FormType classes

I have few cases where I need to ugly customizing of my FormType classes.
First one is that I need to check if the state of user is active in this case disable possibility to edit username. But just adding disabled atribute is not protecting input to be not submitted. So I decided not to show username input field. I achieved it by passing boolean through options.
Controller:
$userForm = $this->createForm(UserType::class, $user, array(
'is_active' => ($user->getState() == 'active')
));
And then in UserType class:
if ($options['is_active']) {
$builder
->add('username', EmailType::class);
}
$builder
->add('firstName', TextType::class),
...
Second case is that I need to remove NotBlank() constraint and add 'required' => false attribute from FileType field when profile photo is uploaded. I achieved it in similar way by passing boolean through options.
Controller:
$userForm = $this->createForm(UserType::class, $user, array(
'is_uploaded' => !empty($photo)
));
UserType class:
// achieved same way as previous problem
My questions would be:
What recommendations would be dealing with these kind of cases?
Is what I did correct and acceptable?
Is there a documentation or examples dealing with any of these cases?
You can move all this form configuration's logic into the form class.
Since you pass $user entity into the form with:
$userForm = $this->createForm(UserType::class, $user, array( // <- $user is passed
'is_uploaded' => !empty($photo)
));
You can access it in builForm method with:
$user = $builder->getData();
Then you can verify all the condifions inside the form and there's no need for making mess in controller.

Entity form field with inactive records

I have a form definition like this (for demonstration purposes):
$builder->add('field', 'entity', [
'class' => EntityA::class,
'query_builder' => function($repo) {
return $repo->createQueryBuilder('e')
->andWhere('e.active = 1');
}
]);
This ensures that only active records can be selected in the dropdown field when using this form.
This leads to this case: When I edit an entity using the form definition from above, and this entity as an inactive EntityA assigned, it won't appear in the dropdown field. When I hit the save button, it will get the first active (if any) EntityA assigned. Also the form will suggest to the user that a different Entity is assigned than it actually is.
The correct way would be that the form displays all active records and the one inactive one that is currently assigned.
I looked into Form event listeners but this seems overly complicated. Also, extending the form just for editing could be a thing but it seems not "the right way" to me.
How can I solve this issue, preferably without using 3rd party bundles?
Get the object and load different data into dropdown depends of type of action: edit/create:
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
$form ->add('field', 'entity', [
'class' => EntityA::class,
'query_builder' => function($repo) use ($data) {
if ($data->getId()) {
// Edit mode: append the pre-selected record to dropdown
return $repo->createQueryBuilder('e')
->andWhere('e.active = 1')
->orWhere('e.id = :id')
->setParameter('id', $data->getId());
} else {
// Display only active records
return $repo->createQueryBuilder('e')->andWhere('e.active = 1');
}
}
]);
});

Validation of a form before submission

Using Symfony, version 2.3 and more recent, I want the user to click on a link to go to the edition page of an already existing entity and that the form which is displayed to be already validated, with each error associated to its corresponding field, i.e. I want
the form to be validated before the form is submitted.
I followed this entry of the cookbook :
$form = $this->container->get('form.factory')->create(new MyEntityFormType, $myEntity, array('validation_groups' => 'my_validation_group'));
$form->submit($request->request->get($form->getName()));
if ($form->isValid()) {
...
}
But the form is not populated with the entity datas : all fields are empty. I tried to replace $request->request->get($form->getName()) with $myEntity, but it triggered an exception :
$myEntity cannot be used as an array in Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php
Does anyone know a method to feed the submit method with properly formatted datas so I can achieve my goal ? Note : I don't want Javascript to be involved.
In place of:
$form->submit($request->request->get($form->getName()));
Try:
$form->submit(array(), false);
You need to bind the the request to the form in order to fill the form with the submitted values, by using: $form->bind($request);
Here is a detailed explanation of what your code should look like:
//Create the form (you can directly use the method createForm() in your controller, it's a shortcut to $this->get('form.factory')->create() )
$form = $this->createForm(new MyEntityFormType, $myEntity, array('validation_groups' => 'my_validation_group'));
// Perform validation if post has been submitted (i.e. detection of HTTP POST method)
if($request->isMethod('POST')){
// Bind the request to the form
$form->bind($request);
// Check if form is valid
if($form->isValid()){
// ... do your magic ...
}
}
// Generate your page with the form inside
return $this->render('YourBundle:yourview.html.twig', array('form' => $form->createView() ) );

How to build the generic form which is not linked to any entity in symfony2

So far , i have only built form which are related to entities using formbuilder
i normally do this
$builder->add("tasks")
Then i do this
$Form = $this->createForm(new TaskType(), $entity);
Now i just want a generic form where i have the select boxes in which i can load entities from database.
I am not persisting or binding that form i just want that when user select user and hit submit then i go to that user page
IN the above form i used to have tasks as property in user entity so i used add.
But i just want want to display tasks , whichis not linked to user can i do that
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->getForm();
Example i above code can i do that
$form = $this->createFormBuilder()
->add('task', 'text')
->add('dueDate', 'date')
->getForm();
Before reading, why would you show a <select> for doing nothing? Maybe there is a better solution to achieve what you want :)
Anyway, if your form has some field not related to an entity property, just set the option property_path to false. Assuming you want to display a <select> ot tasks i would do:
$this->createFormBuilder()
->add('task', 'entity', array(
'property_path' => false,
'class' => 'Acme\HelloBundle\Entity\Task', // Full/short FQCN
'property' => 'name', // Assuming your task as a property "name",
))
;
And if for some reason you need selected value (mean selected Task object) you can get with $form->get('task')->getData(), after binding it with the request.

Categories