I would like to understand the difference between :
$form->handleRequest($request);
if ($form->isValid()) {
//access data that was submitted by the user using:
$objectHoldingDataSubmittedIntheForm = $form->getData();
}
and
$form = $this->createForm(MyFormType::class, $objectHoldingDataSubmittedIntheForm);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//access data that was submitted by the user using:
// $objectHoldingDataSubmittedIntheForm holds by itself all the submitted data
}
My question is that, if handlerequest fills the object that was passed to the form with data submitted by the user, then why do we need $form->getData(); to get submitted data ? can't we just use the object that was passed to the form since it now holds all the info?
Related
I am running a Symfony 2.8 based web app using FOSUserBundle to manage users. Creating new users with a web form is absolutely no problem.
Now I would like to add a feature to create new users with a REST api. Of course submitting username, password, email, etc. to a controller is no big deal:
public function registerAction(Request $request) {
$requestJson = json_decode($request->getContent(), true);
$username = $requestJson[JSON_KEY_USERNAME];
$email = $requestJson[JSON_KEY_MAIL];
$password = $requestJson[JSON_KEY_PASSWORD];
...
$this->registerUser($username, $email, $password);
...
}
private function registerUser($username, $email, $password, $locale, $timezone, $currency) {
$userManager = $this->get('fos_user.user_manager');
$emailExist = $userManager->findUserByEmail($email);
$userNameExists = $userManager->findUserByUsername($username);
if ($emailExist || $userNameExists)
return false;
$user = $userManager->createUser();
$user->setUsername($username);
$user->setEmail($email);
$user->setPlainPassword($password);
...
$user->setLocked(0);
$user->setEnabled(0);
$userManager->updateUser($user);
return true;
}
However, this performs no validation at all. If for example the username is empty an NotNullConstraintViolationException is thrown when persisting the user object.
Of course I could manually re-implement the same validation process which is used by the RegistrationForm (username not empty, not taken, no invalid characters, password min length, e-mail format, etc.) and pass back the same error messages but this would mean to reinvent the wheel.
Is it somehow possible to run the exact same validation which is used by the RegistrationForm?
Symfony validator can work independently. In a controller you can use validator service like this:
$violations = $this->get('validator')->validate($user, null, ['your_groups_here']);
// Or inject Symfony\Component\Validator\Validator\ValidatorInterface into a service.
It will return a ConstraintViolationListInterface, you can loop trough this object.
You can check FOSUserBundle validation groups here: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/config/validation.xml
I want to update values in my Entity using PATCH method, but when I try to do that, doctrine create new record in table, but do not update. For example I want to update name attribute, so I send this JSON array (in url I send id of the record: api.test/item/{id}):
{
"name": "newname"
}
My Controller:
public function updateItemAction(Request $request, $id)
{
$serializer = $this->get('jms_serializer');
$content = $request->getContent();
$item = $serializer->deserialize($content,Item::class,'json');
$em = $this->getDoctrine()->getManager();
$em->getRepository(Item::class)->find($id);
$em->persist($item);
$em->flush();
return new View("updated!",Response::HTTP_OK);
}
You must withdraw the persist method from your code and it will work better.
Have you tried using the merge() function? This allows you to merge the entity into the database, updating the existing entity. Call this function instead of persist. Here is a guide
https://www.vivait.co.uk/labs/updating-entities-when-an-insert-has-a-duplicate-key-in-doctrine
I am trying to add error to form using FormError. Error must be displayed when user tries to create collection with existing name. But this code doesn't work, and I can't understand why
public function submitInObjectAction(Request $request)
{
$collection = new Collection();
$user = $this->getUser();
$form = $this->createForm(
new CollectionType(),
$collection
);
$form->handleRequest($request);
if ($form->isValid() && $form->isSubmitted()) {
$colname = $form["name"]->getData();
$existing = $this->getDoctrine()->getRepository('CollectionBundle:Collection')
->findBy(['name' => $colname, 'user' => $user]);
if ($existing != NULL) {
$error = new FormError("You already have collection with such name");
$form->get('name')->addError($error);
}
$em = $this->getDoctrine()->getManager();
$collection->setUser($user);
$em->persist($collection);
$em->flush();
return new JsonResponse([
'id' => $collection->getId(),
'name' => $collection->getName()
]);
}
}
I cannot use annotation on name field in Collection entity, because names must be unique only for particular user
I think it is too late in the chain. Form validation happens when you call $form->handleRequest() and by the time $form->isValid() is called your validation should be complete. It is better to add validation constraints further up the chain. See the Symfony guide on form validation and if necessary the validation component, for more info.
I would use annotations to set a unique constraint on the name field of the Collection entity in the CollectionBundle.
This not only validates this user input form, but any other form or component or bundle which uses CollectionBundle - and Doctrine will even prevent storage depending on the constraint leaving your database tidy!
EDIT: Another option for more advanced validation is writing a custom form event listener. Three events are dispatched when Form::handleRequest() or Form::submit() are called: FormEvents::PRE_SUBMIT, FormEvents::SUBMIT, FormEvents::POST_SUBMIT. This example also shows how to access the form itself.
$form = $formFactory->createBuilder()
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$user = $event->getData();
$form = $event->getForm();
// .. validation here
})
->getForm();
In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.
This question already has answers here:
Automatic post-registration user authentication
(9 answers)
Closed 9 years ago.
I have created an individual sign up mechanism in my Symfony2 app (using FOSUserBundle) which exists in addition to the regular registration.
Is there any way - after I have created and persisted the User to the database - to automatically login that current user. And after that the User shall be redirected to a page which requires a logged in user (and due to the automatical login, the User can access that page)?
This is basically my method to create the user:
public function signupAction(Request $request) {
$user = new User();
$form = $this->createFormBuilder($user)
->...()
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// Enable User
$user->setEnabled(true);
// Persist to DB
$em->persist($user);
$em->flush();
// Here I need the auto-login before redirecting to _root
return $this->redirect($this->generateUrl('_root'));
}
return $this->render('MyBundle:MyController:signup.html.twig', array(
'form' => $form->createView()
));
}
Attention: This doesn't seem to work in Symfony3 anymore.
Quoting from the answer to a duplicate question:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.context')->setToken($token);
$this->get('session')->set('_security_main',serialize($token));