Symfony 4 Validator - Auto logout if invalid - php

I wanted to validate my User Object ($user) with the Symfony Validator and return a JsonResponse ($response) if the form input data is valid / is not valid.
But I have the issue that I get logged out automatically when the data could not be validated. I have to login again and this is not the behaviour I expect when some data ist not valid. I found a workaround (see comments below) but this is not very satisfying :/
Here is the method of my Controller:
/**
* Update user profile data
*
* #Route("/api/users/updateprofile")
* #Security("is_granted('USERS_LIST')")
*/
public function apiProfileUpdate(ValidatorInterface $validator, FlashMessageBuilder $flashMessageBuilder)
{
$request = Request::createFromGlobals();
// Prepare Response
$response = new JsonResponse();
$response->setData([]);
/** #var User $user */
$user = $this->getUser();
$oldName = $user->getName();
$oldEmail = $user->getEmail();
$user->setName($request->request->get('name'));
$user->setEmail($request->request->get('email'));
$errors = $validator->validate($user);
if (count($errors) > 0) { // if this -> auto logout
$user->setName($oldName); // if I set the both attributes back to the old value
$user->setEmail($oldEmail); // then I don't get logged out automatically but this is just a workaround and not satisfying
$entityManager = $this->getDoctrine()->getManager(); // forgot to remove this
$entityManager->persist($user); // and this line, this is actually deleted in the real code
foreach ($errors as $error) {
$errorMessage = $error->getMessage();
$errorField = $error->getPropertyPath();
$flashMessageBuilder->addErrorMessage($errorMessage, $errorField);
};
return $response;
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$flashMessageBuilder->addSuccessMessage("Success!");
return $response;
}
Sorry for my bad english and thank you in advance!

You are persisting the User Object even if there are errors in validation, which can cause the problem with the logout.
Try to update ONLY in case there is no validation error:
public function apiProfileUpdate(ValidatorInterface $validator, FlashMessageBuilder $flashMessageBuilder)
{
$request = Request::createFromGlobals();
// Prepare Response
$response = new JsonResponse();
$response->setData([]);
/** #var User $user */
$user = $this->getUser();
$user->setName($request->request->get('name'));
$user->setEmail($request->request->get('email'));
$errors = $validator->validate($user);
if (count($errors) == 0) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$flashMessageBuilder->addSuccessMessage("Success!");
return $response;
}
foreach ($errors as $error) {
$errorMessage = $error->getMessage();
$errorField = $error->getPropertyPath();
$flashMessageBuilder->addErrorMessage($errorMessage, $errorField);
};
return $response;
}
But I think you should not mix API calls and the classical form approach utilizing FlashMessages, instead return a proper JSON result.
So consider to change the code accordingly:
public function apiProfileUpdate(ValidatorInterface $validator, FlashMessageBuilder $flashMessageBuilder)
{
$request = Request::createFromGlobals();
/** #var User $user */
$user = $this->getUser();
$user->setName($request->request->get('name'));
$user->setEmail($request->request->get('email'));
$errors = $validator->validate($user);
if (count($errors) == 0) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return new JsonResponse(['success' => true]);
}
$data = [];
foreach ($errors as $error) {
$data[$error->getPropertyPath()] = $error->getMessage();
};
return new JsonResponse(['success' => false, 'errors' => $data], 400);
}
Now your calling code can handle a 200 result (success) and the error case with status code 400 and display the error messages for all failed fields from the errors part in the result body.

Related

How to save data from Postman in Symfony?

I try to send data from Postman to this function
public function new(Request $request): Response
{
$tag = new Tag();
$form = $this->createForm(TagType::class, $tag);
$form->submit($request->request->all());
if ($form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($tag);
$entityManager->flush();
$message = "Tag was successfully added";
return new JsonResponse(array("message: $message"));
}
$errors = $form->getErrors();
return new JsonResponse(array("message:$errors"));
}
If i send data as 'form-data' i can save it to database.
But i can't understand how to accept 'raw' Json 'application/json'
I can only manually take value from Request with
$tagTitle = $request->query->get('title');
And i can't do it with some FOSUserBundle etc.
I can use only jms/serializer. If i will need it.
You need to fetch the json from $request->getContent() first:
public function new(Request $request): Response
{
$tag = new Tag();
$form = $this->createForm(TagType::class, $tag);
$form->submit(json_decode($request->getContent(), true));
if ($form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($tag);
$entityManager->flush();
$message = "Tag was successfully added";
return new JsonResponse(array("message: $message"));
}
$errors = $form->getErrors();
return new JsonResponse(array("message:$errors"));
}

Return post values of form after submit

How to retrive values of posted form, for example in controller i check if is any username already if it redirect back to route which render the form, but how to retrive the last post values to not fill data of this form again.
example of controle:
/**
* #Route("/dystrybutor/pracownicy/add", name="dystrybutor_pracownicy_add")
*/
public function new(UserManagerInterface $userManager, EntityManagerInterface $entityManager, Request $request)
{
$pracownik = new Pracownik();
$form = $this->createForm(PracownikType::class, $pracownik);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$id = $this->getUser()->getDystrybutorId();
$username = $form["username"]->getData();
$password = $form["password"]->getData();
$email = $form["email"]->getData();
$userManager = $this->get('fos_user.user_manager');
$checkUser = $userManager->findUserByUsername($username);
if($checkUser) {
$this->addFlash(
'danger',
'Login jest już zajęty!'
);
return $this->redirectToRoute('dystrybutor_pracownicy_add');
}
else {
Generally you should just pass the $pracownik object to the action where you redirect to and then just pass it as argument when creating your form. This can be done with a lot of ways but I would suggest to use the forward method in your controller:
public function new(UserManagerInterface $userManager, EntityManagerInterface $entityManager, Request $request, Pracownik $pracownik = null){
$pracownik = $pracownik ?? new Pracownik();
$form = $this->createForm(PracownikType::class, $pracownik);
...
if($checkUser) {
$this->addFlash('danger','Login jest już zajęty!');
return $this->forward('App\Controller\DystrybutorController::new', array(
'pracownik' => $pracownik
));
}

Symfony2 and Doctrine: just a single record deleted from a multiple request

The action included at the end of this post is called from an ajax call via jQuery. When I try to delete more than one record, just the first is deleted but the flash messages are printed for every request.
I checked the input data and it is correct. No exception are throwed.
Any idea? Thanks!
/**
* #Route("/cancella", name="training_plan_activity_edition_final_delete")
* #Method({"POST"})
*
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Request $request)
{
if (!$request->isXmlHttpRequest()) {
throw new \Exception('this controller allows only ajax requests');
}
$ids = json_decode($request->get('ids'), true);
$em = $this->getDoctrine()->getManager();
foreach ($ids as $id) {
if($id <> 'on') {
$trainingPlanActionEditionFinal = $this->getDoctrine()
->getRepository('TrainingPlanBundle:TrainingPlanActionEditionFinal')
->find($id);
if (!$trainingPlanActionEditionFinal) {
throw $this->createNotFoundException(
'Training plan action edition final with id ' . $id . ' not found'
);
}
$trainingPlanSlug = $trainingPlanActionEditionFinal->getSlug();
try {
$em->remove($trainingPlanActionEditionFinal);
$em->flush();
$this->get('session')->getFlashbag()->add('success', 'Cancellazione dell\'edizione avvenuta con successo');
} catch (\Exception $e) {
$this->get('session')->getFlashbag()->add('error', 'Impossibile cancellare l\'edizione');
}
}
}
return new Response($this->generateUrl('training_plan_action_edition_final_list', array(
'trainingPlanSlug' => $trainingPlanSlug
)
));
}

REGISTRATION_SUCCESS event isn't triggered on FOSUserBundle

I need a little help here since my code doesn't work and I can't find where I'm failing. See this is the function I'm using to register users on my application (taked from here and changed a bit to suite my needs):
public function registerAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->container->get('fos_user.user_manager');
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->container->get('event_dispatcher');
$user = $userManager->createUser();
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$entity = new SysUsuario();
$form = $this->createForm(new UsuarioType(), $entity);
$form->handleRequest($request);
$user_data = $request->get('user_register');
$profile_data = $request->get('user_register')['perfil'];
if ($form->isValid()) {
$user->setUsername($profile_data['persJuridica'] . $profile_data['rif']);
$user->setEmail($user_data['email']);
$user->setPlainPassword($user_data['password']);
$role = $profile_data['roleType'];
if ($role === "O") {
$user->addRole("ROLE_OPERADOR");
}
elseif ($role === "CH") {
$user->addRole("ROLE_CENTRO_HIPICO");
}
$userManager->updateUser($user);
$profile = new SysPerfil();
$profile->setPersJuridica($profile_data['persJuridica']);
$profile->setRif($profile_data['rif']);
$ci = isset($profile_data['ci']) ? $profile_data['ci'] : null;
if ($ci != NULL) {
$profile->setCi($profile_data['ci']);
}
$profile->setNombre($profile_data['nombre']);
$profile->setApellido($profile_data['apellido']);
$profile->setRoleType($profile_data['roleType']);
$profile->setUser($user);
$em->persist($profile);
$em->flush();
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
if (null === $response = $event->getResponse()) {
$url = $this->container->get('router')->generate('fos_user_registration_confirmed');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
else {
$errors = $this->getFormErrors($formProfile);
}
return new JsonResponse(array('status' => true, 'errors' => $errors));
}
The problem here is that my code is never triggering the REGISTRATION_SUCCESS event so I didn't get any email and therefore I can't confirm users, what I'm doing wrong?
After dig and dig deeper in my code I found where the problem was. Thanks to #tttony user who turns on the bulb, I, accidentally, delete the confirmation configuration at config.yml and for that reason the event never was triggered, so the easy solution:
fos_user:
....
registration:
confirmation:
from_email:
address: admin#local.com
sender_name: Myself
Now before found the previous solution I come with another one, which made me study and learn and I leave here too:
/** Disable the user by default - this is done in the event */
$user->setEnabled(false);
/** #var $mailer FOS\UserBundle\Mailer\MailerInterface */
$mailer = $this->container->get('fos_user.mailer');
if (null === $user->getConfirmationToken()) {
$user->setConfirmationToken($str->generateRandomString(32));
}
$mailer->sendConfirmationEmailMessage($user);
$session = new Session();
$session->set('fos_user_send_confirmation_email/email', $user->getEmail());
The only thing I wont be able to do here was use the FOS\UserBundle\Util\TokenGeneratorInterface I don't know how to, so if any knows I'll be grateful if leave the answer to that one, both solutions works since I tested.

Entity persists even when form has error

I have an issue where I have a form type that persists the associated entity even when the form is not valid.
I have confirmed that the form indeed has errors via $form->getErrorsAsString(). I have also confirmed that the logical if statement that checks if the form is valid or not comes out false. The entity still persists despite the fact that the form is never valid.
I'm not sure what I'm doing wrong here as I have no other spot that I can find that either persists the entity or flushes the entity manager. Here's my controller:
/**
* #Route("/settings/profile", name="settings_profile")
* #Template();
*/
public function profileAction()
{
$user = $this->getUser();
$profile = $user->getUserProfile();
if (null === $profile) {
$profile = new UserProfile();
$profile->setUser($user);
$profileDataModel = $profile;
} else {
$profileDataModel = $this->getDoctrine()->getManager()->find('MyAppBundle:UserProfile',$profile->getId());
}
$form = $this->createForm(new ProfileType(),$profileDataModel);
$request = $this->getRequest();
if ($request->getMethod() === 'POST') {
$form->bind($request);
if ($form->isValid()) {
// This logic never gets executed!
$em = $this->getDoctrine()->getManager();
$profile = $form->getData();
$em->persist($profile);
$em->flush();
$this->get('session')->setFlash('profile_saved', 'Your profile was saved.');
return $this->redirect($this->generateUrl('settings_profile'));
}
}
return array(
'form' => $form->createView(),
);
}
I must have a listener or something somewhere that is persisting the user.
My work around for this temporarily is to do:
$em = $this->getDoctrine()->getManager()
if ($form->isValid()) {
// persist
} else {
$em->clear();
}
Until I can ferret out what listener or other data transformer is causing this.

Categories