Currently I am working on a recipe app in Symfony 6. So far so good. Right now I want to enable the user to change his username. The username is at the same time also the UserIdentifier.
The username can also be changed successfully. I have a constraint that sets the length of the username. However, the form with the invalid value is submitted anyway and the user is logged out.
Maybe I made a mistake somewhere, but I'm really getting desperate. Actually, the problem is quite simple, but I can't find the right solution.
Thank you very much for your help!
My Controller
`
<?php
namespace App\Controller;
use App\Form\ProfileUsernameType;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class ProfileChangeUsernameController extends AbstractController
{
#[Route('/profile/changeUsername')]
public function changeUsername(UserRepository $userRepository, EntityManagerInterface $entityManager, Request $request)
{
$user = $this->getUser()->getUserIdentifier();
$username = $userRepository->findOneBy(['username' => $user ]);
$form = $this->createForm(ProfileUsernameType::class, $username);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$username->setUsername($form->get('username')->getData());
$entityManager->persist($username);
$entityManager->flush();
}
return $this->render('profile/changeUsername.html.twig', [
'form' => $form,
]);
}
}
`
My Form
`
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class ProfileUsernameType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('username', TextType::class,[
'data_class' => null,
'attr' => [
'placeholder' => 'Username',
'autocomplete' => 'off',
],
'constraints' => [
new NotBlank(),
new Length(['min' => 5])],
])
->add('submit', SubmitType::class, ['label' => 'Submit your username'])
->getForm();
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
`
My Twig file
`
{% extends 'base.html.twig' %}
{% form_theme form 'bootstrap_5_layout.html.twig' %}
{% block body %}
{{ form_start(form)}}
{{ form_errors(form) }}
{{ form_widget(form.username) }}
{{ form_row(form.submit) }}
{{ form_end(form) }}
{% endblock %}
`
I was facing the same problem some time ago and found this solution:
In your ProfileUsernameType.php, do it like this:
$builder
->add('username', TextType::class, [
'attr' => [
'class' => 'form-control',
'readonly' => 'readonly',
'value' =>$user['username']
],
'label' => 'Username: '
])
Related
I made a form with Symfony. I am using CollectionType class in order to send several information from one input that I can duplicate. (see my code below)
The field is missing. I didn't succeed to get a select and choose the language I wanted. Instead I have all code in data-prototype which is unexpected. (see the picture at the bottom)
So here is what I did :
registration.html.twig
{% extends 'base.html.twig' %}
{% block main %}
{{ form_start(userform) }}
{{ form_row(userform.wishedlanguages) }}
<button type="button" id="addwishedlanguage">+ Add language</button>
<button type="button"id="removewishedlanguage">- Delete language </button>
{{ form_row(userform.save }}
{{ form_end(userform) }}
{% endblock %}
UserType.php
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('wishedlanguages', CollectionType::class, [
'entry_type' => LanguageType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => 'Quelle langue souhaites-tu pratiquer ?',
])
->add('save', SubmitType::class, [
'label' => 'Je valide',
])
;
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => User::class,
'translation_domain' => 'forms'
]);
}
}
Here is the code I can see in the browser :
A form I have created includes an upload field to upload a file with. This file can be any format. The form itself is created inside a FormType, and a controller handles the submission of the form successfully. However, every time I submit the form to this controller and I do a print_r or var_dump(), the upload field isn't included as part of the params of the POST request. When I use $file = $request->files->get('estimateUpload'); and var_dump() that it will only ever return NULL.
Here is the formType that I am using:
<?php
namespace App\Form;
use App\Entity\IhcVehicleDamageMatrix;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\File;
class IhcVehicleDamageEntryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id')
->add('incidentId')
->add('vehicleDamageId')
->add('damageType')
->add('description')
->add('wheelTyreDamage')
->add('tyreAge')
->add('tyreDotCode')
->add('treadDepth')
->add('renumerationCost')
->add('itemAge')
->add('created')
->add('modified')
->add('estimateUpload', FileType::class, [
'required' => false
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => IhcVehicleDamageMatrix::class,
]);
}
}
In the view file this is how I am using the upload field:
{{ form_start(damageForm, {'action': path('forms_ihc_damage_details_submitform'), 'method': 'POST', 'attr': {'id': 'ihcFormDamageDetails', 'enctype': 'multipart/form-data'}}) }}
{{ form_row(damageForm._token) }}
<tr>
<th>Upload Estimate</th>
<td>
{{ form_widget(damageForm.estimateUpload) }}
<small>{{ form_help(damageForm.estimateUpload) }}</small>
<div class="form-error">
{{ form_errors(damageForm.estimateUpload) }}
</div>
</td>
</tr>
{{ form_errors(damageForm) }}
{{ form_end(damageForm, {'render_rest': false}) }}
And in the controller, this is how I am getting the form data:
public function vehicleDamageForm(Request $request)
{
// Get Form Details
$formdet = $request->get('ihc_vehicle_damage_entry');
$em = $this->getDoctrine()->getManager();
$file = $request->files->get('estimateUpload');
var_dump($file);
}
The better way is to get estimateUpload directly from IhcVehicleDamageMatrix. Try something like this inside controller:
$ihcVehicleDamageMatrix = new IhcVehicleDamageMatrix();
$form = $this->createForm(IhcVehicleDamageEntryType::class, $ihcVehicleDamageMatrix);
if ($form->isSubmitted() && $form->isValid()) {
$file = $ihcVehicleDamageMatrix->estimateUpload();
dump($file);
}
I've worked through a few of the Forms-Tutorials on the Symfony-Page (especially How to Embed a Collection of Forms, How To use a Form without a Dataclass & CollectionType Field ).
I'm trying to show a form with multiple lead partners which can be edited and submitted back to the system.
But i get a Twig_Runtime_Error saying: ''Variable "lead_partners" does not exist''.
My Twig:
{% block content %}
<div>
{{ form_start(form) }}
{% for partner in lead_partners %}
{{ form_row(partner.name) }}
{% endfor %}
{{ form_end(form) }}
</div>
{% endblock content %}
My Controller Code:
public function overview(Request $request, \App\Utility\LeadPartnerLoader $LeadPartnerLoader)
{
$leadPartnerList = $LeadPartnerLoader->loadAll();
$form = $this->createFormBuilder($leadPartnerList)
->add('lead_partners', CollectionType::class, [
'entry_type' => LeadPartnerFormType::class,
])->getForm();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid())
{
$data = $form->getData();
}
return $this->render(
'lead_partner_overview2.html.twig',
[
'form' => $form->createView()
]);
}
And the Form Type (LeadPartnerFormType):
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => LeadPartner::class,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', HiddenType::class)
->add('name', TextType::class);
}
$leadPartnerList is of type array.
What am i doing wrong/missing here?
Kind Regards
It seems your action overview soesn't return the lead_partners variable you use in your template.
You can try to do this
return $this->render(
'lead_partner_overview2.html.twig',
[
'form' => $form->createView(),
'lead_partners' => $leadPartnerList, // I gess that's the list you want to loop ?
]);
I've been trying to add a edit-user page where they can change username, email address and password.
One thing I am trying to implement is they have to type in the old password to be able to change it to a new one.
I've been reading these pages:
https://symfony.com/doc/current/validation.html
https://symfony.com/doc/current/reference/constraints/UserPassword.html
but I'm really struggling on the implementation side.
Here's my Controller for the form:
<?php
namespace App\Controller\User;
use App\Entity\User;
use App\Form\User\EditUserType;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class EditController extends Controller
{
public function edit(Request $request, UserPasswordEncoderInterface $encoder)
{
$userInfo = ['username' => null, 'plainPassword' => null, 'password' => null, 'email' => null];
$form = $this->createForm(EditUserType::class, $userInfo);
$form->handleRequest($request);
$user = new User();
$oldPassword = $user->getPassword();
if ($form->isSubmitted() && $form->isValid()) {
$userInfo = $form->getData();
$username = $userInfo['username'];
$email = $userInfo['email'];
$newPass = $userInfo['plainPassword'];
$oldPass = $userInfo['password'];
$encryptOldPass = $encoder->encodePassword($user, $oldPass);
if ($oldPassword === $encryptOldPass) {
$this->addFlash('danger', $oldPass. ' ' .$encryptOldPass. ' ' .$oldPassword);
return $this->redirectToRoute('user_edit');
} else {
$this->addFlash('success', $oldPassword. '-' .$encryptOldPass);
return $this->redirectToRoute('user_edit');
}
$pass = $encoder->encodePassword($user, $newPass);
$user->setPassword($pass);
$user->setEmail($email);
$user->setUsername($username);
echo 'trey was here';
$this->addFlash('success', 'User Details Edited');
return $this->redirectToRoute('user_edit');
}
return $this->render('user/edit.html.twig', array('form' => $form->createView()));
}
}
my EditUserType file:
<?php
namespace App\Form\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EditUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', EmailType::class)
->add('username', TextType::class)
->add('password', PasswordType::class, array())
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'New Password'),
'second_options' => array('label' => 'New Repeat Password')
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array());
}
}
my validation (file: config/validator/validation.yaml)
App\Form\User\EditUserType:
properties:
oldPassword:
- Symfony\Component\Security\Core\Validator\Constraints\UserPassword:
message: 'Invalid Password'
my template file:
{% include 'builder/header.html.twig' %}
<div class="user-container" id="user-content">
{% block body %}
{% include 'builder/notices.html.twig' %}
<div class="user-container">
<i class="fas fa-user-edit fa-5x"></i>
</div>
<hr />
{{ form_start(form) }}
{{ form_row(form.username, { 'attr': {'class': 'form-control', 'value': app.user.username} }) }}
{{ form_row(form.email, { 'attr': {'class': 'form-control', 'value': app.user.email} }) }}
{{ form_row(form.password, { 'attr': {'class': 'form-control'} }) }}
{{ form_row(form.plainPassword.first, { 'attr': {'class': 'form-control'} }) }}
{{ form_row(form.plainPassword.second, { 'attr': {'class': 'form-control'} }) }}
<div class="register-btn-container">
<button class="btn btn-danger" id="return-to-dash-btn" type="button">Cancel!</button>
<button class="btn btn-primary" type="submit">Update!</button>
</div>
{{ form_end(form) }}
{% endblock %}
</div>
{% include 'builder/footer.html.twig' %}
Typing in any old password for the old password fields seems to get by and not update the password to the newly typed value.. so how do I validate the old password against the database so the user can update it to a new password?
Thanks
Found the solution, using cerad comment on previous (now removed) answer:
updated controller:
<?php
namespace App\Controller\User;
use App\Form\User\EditUserType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class EditController extends Controller
{
public function edit(Request $request, UserPasswordEncoderInterface $encoder)
{
$userInfo = ['username' => null, 'plainPassword' => null, 'password' => null, 'email' => null];
$form = $this->createForm(EditUserType::class, $userInfo);
$form->handleRequest($request);
$user = $this->getUser();
$entityManager = $this->getDoctrine()->getManager();
if ($form->isSubmitted() && $form->isValid()) {
$userInfo = $form->getData();
$username = $userInfo['username'];
$email = $userInfo['email'];
$newPass = $userInfo['plainPassword'];
$oldPass = $userInfo['password'];
if (!$encoder->isPasswordValid($user, $oldPass)) {
$this->addFlash('danger', 'Old password is invalid. Please try again');
return $this->redirectToRoute('user_edit');
}
$pass = $encoder->encodePassword($user, $newPass);
$user->setPassword($pass);
$user->setEmail($email);
$user->setUsername($username);
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash('success', 'User Details Edited - Please Login Again');
return $this->redirectToRoute('login');
}
return $this->render('user/edit.html.twig', array('form' => $form->createView()));
}
}
the issue was, I wasn't checking the logged in user details, and I thought persist meant insert, not insert/update - so lack of knowledge on this one.
I'm having trouble rendering individual form fields in a twig template from a Symfony 2 form. In the example below, I'm simply trying to render the first_name form field.
Also, form_errors never renders anything.
Here is my Form object:
namespace ABC\WebsiteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LoanApplication extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('first_name', 'text', array('required' => true));
}
public function getName()
{
return 'LoanApplication';
}
public function getDefaultOptions(array $options)
{
return array(
//'data_class' => 'Acme\TaskBundle\Entity\Task',
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'loan_application',
);
}
}
Here is the controller:
namespace ABC\WebsiteBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use JML\WebsiteBundle\Form\LoanApplication;
use JML\WebsiteBundle\QuickBase as QuickBase;
class ApplyController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new LoanApplication());
return $this->render('ABCWebsiteBundle:Apply:index.html.twig', array(
'form' => $form->createView(),
));
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// perform some action, such as saving the task to the database
//return $this->redirect($this->generateUrl('task_success'));
}
}
}
}
And the twig:
<form action="{{ path('apply') }}" method="post" {{ form_enctype(form) }}>
<pre>
{{ dump(form_errors(form)) }} // always empty
</pre>
<div>
{{ form_label(form.first_name) }} // this renders
{{ form_widget(form.first_name) }} //empty
{{ form_row(form.first_name) }} //empty
</div>
<input type="submit" formnovalidate/>
</form>