It looks like a simple task. I am doing this by using the documentation, however I am just not creating a new user. I dont get any errors, but I dont get the user too. I create my form in a seperate class like this:
class RegisterFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('firstname', 'text', array(
'label' => 'First Name * ',
'attr' => array('placeholder' => 'First Name')))
->add('email', 'email', array(
'label' => 'Email * ',
'attr' => array('placeholder' => 'Email')))
->add('password', 'password', array(
'label' => 'Password * ',
'attr' => array('placeholder' => 'Password')))
->add('save', 'submit', array('label' => 'Register'));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mp\ShopBundle\Entity\Users',
));
}
public function getName()
{
return 'register_form_users';
}
}
In my second controller I get the database, create a new user and add the information to that user? At least I think I am... What is wrong here?
public function registerAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$user = new Users();
$form = $this->createForm(new RegisterFormType(), $user);
if ($form->isValid()) {
$firstname = $form->get('firstname')->getData();
$user->setFirstname($firstname);
$email = $form->get('email')->getData();
$user->setEmail($email);
$password = $form->get('password')->getData();
$user->setPassword($password);
$em->persist($user);
$em->flush();
}
return $this->render('MpShopBundle:Frontend:registration.html.twig', array(
'products'=>$products,
'form'=>$form->createView(),
));
}
Add $form->handleRequest($request) before $form->isValid() and it should work. Because you're creating a form but not proccessing the data from request. You can look at the source if you want.
public function registerAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$user = new Users();
$form = $this->createForm(new RegisterFormType(), $user);
/** add this code */
$form->handleRequest($request);
/** add this code */
if ($form->isValid()) {
$firstname = $form->get('firstname')->getData();
$user->setFirstname($firstname);
$email = $form->get('email')->getData();
$user->setEmail($email);
$password = $form->get('password')->getData();
$user->setPassword($password);
$em->persist($user);
$em->flush();
}
return $this->render('MpShopBundle:Frontend:registration.html.twig', array(
'products'=>$products,
'form'=>$form->createView(),
));
}
Related
I'm trying to change a password of a user. The user is selected in a dropdownlist.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('password', PasswordType::class, array('label' => 'Password'))
->add('reset', SubmitType::class, array('label' => 'Reset'))
->add('username', EntityType::class, array('class' => User::class,
'query_builder' => function (EntityRepository $er) {return $er->createQueryBuilder('u')->orderBy('u.username', 'ASC');
},'choice_label'=>'username', 'label'=>'User'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('data_class' => User::class));
}
When the form is first rendered, i see the list of users. Then i fill in the new password en submit.
And then i get the error:
"Expected argument of type "string", "object" given at property path username". The error occures in the controller in line
$form->handleRequest($request);
This is the controller:
$form = $this->createForm(ResetPwdType::class, new User());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$u = $form->get('username')->getData();
$user = $this->getDoctrine()->getManager()->getRepository(User::class)->findOneBy(['username' => $u->getUsername()]);
$newPwd = $passwordEncoder->encodePassword($user, $form->get('password')->getData());
$user->setPassword($newPwd);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash('success', 'Password reset!');
$params = null;
return $this->redirectToRoute('reset_pwd');
}
return $this->render(
'security/resetpwd.html.twig',array('form' => $form->createView()));
I'm think there is a property that needs to be a string in stead of an object. And that is just has got someting to do with the username property.
I've tried to add a toString methode on the User Entity without success.
What must i change to get ride of the error and save the new password to the db.
i'm trying to pass current authenticated user to buildForm for the pass couple of hours, i searched google ... nothing worked
the error i'm getting is
The form's view data is expected to be an instance of class AppBundle\Entity\AdsList, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of AppBundle\Entity\AdsList.
on ->getForm();
i have to ideea what to do ... a view transformer ... ( https://symfony.com/doc/current/form/data_transformers.html )
but i only have an integer ...
i also want to generate unique slugs from the content ( final version wont have a title field ) if you have a good example :)
thanks in advance :)
AgencyController.php
/**
* #Route("/agency/post", name="agency_post")
*/
public function agencyNewAd(Request $request)
{
// $agency = $this->get('security.token_storage')->getToken()->getUser(); ( this didn't worked .. )
$form = $this->createForm(AgencyNewAdType::class, array(
'postedBy' => $this->getUser(),
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$ad = $form->getData();
// save the task to the database
$em = $this->getDoctrine()->getManager();
$em->persist($ad);
$em->flush();
// return new Response('Saved new Post with id ' . $ad->getId());
return $this->redirectToRoute('agency_admin');
}
return $this->render('agency/new_ad.html.twig', [
'adForm' => $form->createView()
]);
}
AgencyNewAdType.php
class AgencyNewAdType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// https://stackoverflow.com/questions/36905490/how-to-pass-parameter-to-formtype-constructor-from-controller
$builder
->add('title', TextType::class)
->add('content', TextareaType::class)
->add('category', EntityType::class, array(
// query choices from Category.Name
'class' => 'AppBundle:CategoryAd',
'choice_label' => 'name',
))
->add('postedAt', DateType::class)
->add('postedBy',HiddenType::class, array(
'data' => $options['postedBy']
))
->add('save', SubmitType::class, array('label' => 'Create Post'))
->getForm();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'postedBy' => null,
'data_class' => 'AppBundle\Entity\AdsList',
));
}
}
i needed to pass the argument to the form so it would look like
public function agencyNewAd(Request $request): Response
{
$pass = new AdsList();
$pass->setPostedBy($this->getUser());
$form = $this->createForm(AgencyNewAdType::class, $pass);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// $form->getData() holds the submitted values iN MeM
$ad = $form->getData();
// save the ad to the database
$em = $this->getDoctrine()->getManager();
$em->persist($ad);
$em->flush();
// return new Response('Saved new Post with id ' . $ad->getId());
return $this->redirectToRoute('agency_admin');
}
return $this->render('agency/new_ad.html.twig', [
'adForm' => $form->createView()
]);
}
and in the form i need to remove postedBy ...
public function buildForm(FormBuilderInterface $builder, array $options)
{
// https://stackoverflow.com/questions/36905490/how-to-pass-parameter-to-formtype-constructor-from-controller
$builder
->add('title', TextType::class)
->add('content', TextareaType::class)
->add('category', EntityType::class, array(
// query choices from Category.Name
'class' => 'AppBundle:CategoryAd',
'choice_label' => 'name',
))
->add('postedAt', DateType::class)
->add('save', SubmitType::class, array('label' => 'Create Post'))
->getForm();
}
You missed a parameter when creating you from in a controller, 2nd parameter should be your object connected to the form and 3rd is the options array, so it would look like:
public function agencyNewAd(Request $request)
{
$ad = new AdsList();
$form = $this->createForm(AgencyNewAdType::class, $ad, array(
'postedBy' => $this->getUser(),
));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ad);
$em->flush();
return $this->redirectToRoute('agency_admin');
}
return $this->render('agency/new_ad.html.twig', [
'adForm' => $form->createView()
]);
}
The alternative way is to pass your option to the construct of your form like this:
public function agencyNewAd(Request $request)
{
$ad = new AdsList();
$form = $this->createForm(new AgencyNewAdType($this->getUser()), $ad);
and then in your constructor of AgencyNewAdType you would accept your parameter, in this case currently logged in user.
I want to add hidden field with username in current session. Username is in another entity called User. How to solve it?
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, array('required' => false))
->add('description', TextareaType::class, array(
'required' => false,
'attr' => array('style' => 'resize: none')))
->add('imageFile', FileType::class);
}
public function addPhotoAction(Request $request)
{
$photo = new Photo();
$user = $this->getUser()->getId();
$form = $this->createForm(PhotoFormType::class, $photo);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($photo);
$em->flush();
$this->addFlash('success', 'You\'ve shared a photo!');
return $this->redirectToRoute('homepage');
}
return $this->render('pages/addPhoto.html.twig', [
'photoForm' => $form->createView(),
'user' => $user
]);
}
How can I add userId to $builder? id(user table) must be in addedBy(photo table). I have to do a relation?
You can push the data to the form field. If you add a hidden field to form object it looks like these rows.
$form = $this->createForm(PhotoFormType::class, $photo);
$form->get('usernameField')->setData($this->getUser()->getUsername());
In my application, only the admin user can create and, theoretically, edit users. So far, using only the Symfony security system (no FOSUserBundle management - its complexity is not required), creating users with varying roles is just fine. The challenge that totally escapes me is how to edit a user without knowing the user's password. I keep running into the expected validation error
Password cannot be empty
. How can editing be accomplished? I'm surely missing something very fundamental here.
Edit action:
public function editAction($id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$form = $this->createForm(new UserType(), $user);
return array(
'form' => $form->createView(),
'user' => $user,
'title' => 'Edit user',
);
}
Update action:
public function updateAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$originalPassword = $user->getPassword();
$form = $this->createForm(new UserType(), $user);
$form->bind($request);
if ($form->isValid()) {
$plainPassword = $form->get('password')->getData();
if (!empty($plainPassword)) {
//encode the password
$encoder = $this->container->get('security.encoder_factory')->getEncoder($entity); //get encoder for hashing pwd later
$tempPassword = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
$user->setPassword($tempPassword);
}
else {
$user->setPassword($originalPassword);
}
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('user_main', array()));
}
User form:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('enabled', 'choice', array(
'choices' => array('Yes' => 'Yes', 'No' => 'No'),
'expanded' => true,
'multiple' => false,
'label' => 'Enabled: ',
))
->add('fname')
->add('sname')
->add('email')
->add('username')
->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'Password fields do not match',
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat Password'),
))
->add('role', 'choice', array(
'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
'expanded' => true,
'multiple' => false,
'label' => 'Group: ',
))
;
}
Until I see a more elegant solution, here's what I came up with:
Create a UserEditType form class with all fields but the password field(s)
Assign UserEditType to a validation group other than Default
Configure the password length constraint to the validation group in 2.
Modify the edit and update actions to use UserEditType
And now users can be edited without having the password!
UserEditType:
class UserEditType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('enabled', 'choice', array(
'choices' => array('Yes' => 'Yes', 'No' => 'No'),
'expanded' => true,
'multiple' => false,
'label' => 'Enabled: ',
))
->add('fname')
->add('sname')
->add('email')
->add('username')
->add('role', 'choice', array(
'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
'expanded' => true,
'multiple' => false,
'label' => 'Group: ',
))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Mana\ClientBundle\Entity\User',
'validation_groups' => array('edit'),
));
}
Password in User entity:
* #ORM\Column(name="userpass", type="string", length=100, nullable=false)
* #Assert\NotBlank(message="Password may not be empty")
* #Assert\Length(
* min = "5",
* max = "12",
* minMessage = "Password must be at least 5 characters long",
* maxMessage = "Password cannot be longer than than 12 characters",
* groups = {"Default"}
* )
Update action:
public function updateAction(Request $request, $id) {
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('ManaClientBundle:User')->find($id);
$form = $this->createForm(new UserEditType(), $user);
$form->bind($request);
if ($form->isValid()) {
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('user_main', array()));
}
return array(
'form' => $form->createView(),
'user' => $user,
'title' => 'Edit user',
);
}
I've had the same problem here in my project.
I solved it by removing the password field from the form just for my edit action.
So, in my UserController, I changed the editAction:
//find the line where the form is created
$editForm = $this->createForm(new UserType($this->container), $entity)
->remove('password'); //add this to remove the password field
I do something like this (untested code)
My User entity has a password property mapped to DB
It also has a 'plainPassword' property, that is not mapped
class User {
// mapped
private string $username;
// mapped
private string $password;
// not mapped - simple php property
private string $plainPassword;
// getters/setters
...
}
The form, uses the plainPassword property, not the mapped password.
class UserType extends AbstractType {
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class)
->add('plainPassword', PasswordType::class, ['required' => false])
}
...
}
And then somewhere, controller in this example, we check if the plainPassword is not empty - thus the password is trying to be changed.
public function updateUserAction(User $user, Request $request)
{
$form = $this->formFactory->createForm(UserType::class, $user);
if($request->getMethod() === 'POST') {
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
if(0 !== strlen($user->getPlainPassword()) {
$encoder = $this->encoderFactory->getPasswordHasher($user);
$salt = rtrim(str_replace('+', '.', base64_encode(random_bytes(32))), '=');
$user->setSalt($salt);
$hashedPassword = $encoder->hash($user->getPlainPassword(), $user->getSalt());
$user->setPassword($hashedPassword);
$user->setPlainPassword(null);
}
$this->em->persist($user);
$this->em->flush();
return $this->redirectToRoute('something');
}
}
}
If you want to use the remove() function then apply also at the form setting. At least in Symfony 3.3. In this way you will avoid the password confirmation stated by #pusle above:
$form = $this->formFactory->createForm()->remove("current_password");
$form->setData($user)->remove("current_password");
Here the whole method in the ProfileController of the FOSUserBundle. It works for me:
public function editDiffAction($id, Request $request)
{
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserBy(['id' => $id]);
$event = new GetResponseUserEvent($user, $request);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $this->formFactory->createForm()->remove("current_password");
$form->setData($user)->remove("current_password");
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$userManager = $this->get('fos_user.user_manager');
$userManager->updateUser($user);
$url = $this->generateUrl('fos_user_profile_show_diff', array('id' => $user->getId() ));
$response = new RedirectResponse($url);
return $response;
}
return $this->render('#FOSUser/Profile/edit_diff.html.twig', array(
'form' => $form->createView(),
'user_id' => $user->getId(),
));
}
Just add 'disabled' => 'disabled' and this field won't be taken into account.
I have a simple user manager in my backend, and I want to be able to edit the user without setting a new password/repeating the old password every time.
Right now if I leave the password fields blank when editing a user, symfony2 complains that a password must be entered, and of course I want this functionality when I register new users, but when I edit them, I'd like for the form to just ignore the password boxes if they aren't filled out.
How is this accomplished?
For someone else's reference, I worked this one out this way.
My formType:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('username', 'text', array('label' => 'Servernamn '))
->add('plainPassword', 'repeated', array('type' => 'password', 'first_name' => 'Lösenord för server ', 'second_name' => 'Upprepa lösenord för server',));
$builder-> addValidator(new CallbackValidator(function(FormInterface $form){
$username = $form->get('username')->getData();
if (empty($username)) {
$form['username']->addError(new FormError("Du måste ange ett namn för servern"));
}
}));
}
My updateAction:
public function updateServerAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository('BizTVUserBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Container entity.');
}
$originalPassword = $entity->getPassword();
$editForm = $this->createForm(new editServerType(), $entity);
$request = $this->getRequest();
$editForm->bindRequest($request);
if ($editForm->isValid()) {
$plainPassword = $editForm->get('plainPassword')->getData();
if (!empty($plainPassword)) {
//encode the password
$encoder = $this->container->get('security.encoder_factory')->getEncoder($entity); //get encoder for hashing pwd later
$tempPassword = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
$entity->setPassword($tempPassword);
}
else {
$entity->setPassword($originalPassword);
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('Server'));
}
Thus updating my users password should it be set, otherwise keep the original password.
On your entity setter for the password property make it like this:
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
if (!is_null($password)) {
$this->password = $password;
}
return $this;
}
The trick is to check if the parameter being passed is empty and only setting if it's not.
If you need add required option to $options array. Example:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('username', 'text', array('label' => 'Servernamn '))
->add('plainPassword', 'repeated', array(
'type' => 'password',
'first_name' => 'password',
'second_name' => 'repeat_password',
'required' => false,
));
}
What I did was to leave the UserType as is and only remove the password field in the controller:
/**
* #Route("/users/edit/{username}", name="user_edit")
*/
public function editAction(Request $request, User $user)
{
$form = $this->createForm(UserType::class, $user);
// Remove the password field when editing the user
// Otherwise it must be entered each time the user is edited
// We can change the password via a special edit-user-password page
$form->remove('password');
$form->handleRequest($request);
if ($form->isValid()) {
// ...
}
}
This way you can have a single UserType, reusable in the editAction and the newAction.
Be sure to add the line $form->remove('password'); before $form->handleRequest($request);.
There is an article gives many options to achieve your request, from the Symfony2 cookbook, in particular, the section below worked for me:
Customizing your Form based on the underlying Data
Below is the implementation:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
// check if the Product object is "new"
// If no data is passed to the form, the data is "null".
// This should be considered a new "Product"
if (!$product || null === $product->getId()) {
$form->add('name', 'text');
}
});
}
For the validation use 2 different validation groups and set the correct one by the callable function.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'oldPassword',
PasswordType::class,
[
'constraints' => new UserPassword([
'groups' => 'profile_password',
]),
'mapped' => false,
'required' => false,
]
);
$builder->add(
'plainPassword',
PasswordType::class,
[
'constraints' => new NotBlank([
'groups' => 'profile_password',
]),
'mapped' => false,
'required' => false,
]
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'validation_groups' => function (FormInterface $form) {
$newPassword = $form->get('plainPassword')->getData();
$oldPassword = $form->get('oldPassword')->getData();
if ($oldPassword || $newPassword) {
return ['profile', 'profile_password'];
} else {
return ['profile'];
}
},
]
);
}
Symfony 5 method:
Inside a controller class:
/**
* #Route("/user/{id}/edit")
*/
public function updateUser(
$id, // See 'Route' annot., needed to `find()` User, adjust to your needs
User $user,
Request $request,
UserPasswordEncoderInterface $passwordEncoder,
UserRepository $userRepository
): Response {
$form = $this->createForm(UserFormType::class, $user);
// if password field is required by default, we have to `add()` it again
// with `required` set to `false`
$form->add(
'plainPassword',
PasswordType::class,
[
// do not use Constraint NotBlank()
'required' => false,
'mapped' => false
]
);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entity = $userRepository->find($id); // as above, adjust to your needs
if (!$entity) {
throw $this->createNotFoundException('User does not exist!');
}
$originalPassword = $entity->getPassword();
/** #var User $user */
$user = $form->getData();
// keep original password if new one isn't provided
if (!empty($form['plainPassword']->getData())) {
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$form['plainPassword']->getData()
)
);
} else {
// set old password
$user->setPassword($originalPassword);
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return new Response('User updated!');
}
return $this->render(
'admin/user_edit.html.twig',
[
'requestForm' => $form->createView(),
]
);
}
You can do something with
if (empty($password)) {
//edit the password
}
Try fixing that into your form's PHP.