Skip to content

Commit 83570a0

Browse files
Romain Monteiljaviereguiluz
Romain Monteil
authored andcommitted
Add ability to edit user's informations and password
1 parent 559b771 commit 83570a0

File tree

10 files changed

+490
-3
lines changed

10 files changed

+490
-3
lines changed

config/packages/security.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@ security:
5151
# this is a catch-all for the admin area
5252
# additional security lives in the controllers
5353
- { path: '^/(%app_locales%)/admin', roles: ROLE_ADMIN }
54+
55+
role_hierarchy:
56+
ROLE_ADMIN: ROLE_USER

src/Controller/UserController.php

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace App\Controller;
13+
14+
use App\Form\Type\ChangePasswordType;
15+
use App\Form\UserType;
16+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
17+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
18+
use Symfony\Component\HttpFoundation\Request;
19+
use Symfony\Component\HttpFoundation\Response;
20+
use Symfony\Component\Routing\Annotation\Route;
21+
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
22+
23+
/**
24+
* Controller used to manage current user.
25+
*
26+
* @Route("/profile")
27+
* @Security("has_role('ROLE_USER')")
28+
*
29+
* @author Romain Monteil <monteil.romain@gmail.com>
30+
*/
31+
class UserController extends AbstractController
32+
{
33+
/**
34+
* @Route("/edit", methods={"GET", "POST"}, name="user_edit")
35+
*/
36+
public function edit(Request $request): Response
37+
{
38+
$user = $this->getUser();
39+
40+
$form = $this->createForm(UserType::class, $user);
41+
$form->handleRequest($request);
42+
43+
if ($form->isSubmitted() && $form->isValid()) {
44+
$this->getDoctrine()->getManager()->flush();
45+
46+
$this->addFlash('success', 'user.updated_successfully');
47+
48+
return $this->redirectToRoute('user_edit');
49+
}
50+
51+
return $this->render('user/edit.html.twig', [
52+
'user' => $user,
53+
'form' => $form->createView(),
54+
]);
55+
}
56+
57+
/**
58+
* @Route("/change-password", methods={"GET", "POST"}, name="user_change_password")
59+
*/
60+
public function changePassword(Request $request, UserPasswordEncoderInterface $encoder): Response
61+
{
62+
$user = $this->getUser();
63+
64+
$form = $this->createForm(ChangePasswordType::class);
65+
$form->handleRequest($request);
66+
67+
if ($form->isSubmitted() && $form->isValid()) {
68+
$user->setPassword($encoder->encodePassword($user, $form->get('newPassword')->getData()));
69+
70+
$this->getDoctrine()->getManager()->flush();
71+
72+
return $this->redirectToRoute('security_logout');
73+
}
74+
75+
return $this->render('user/change_password.html.twig', [
76+
'form' => $form->createView(),
77+
]);
78+
}
79+
}

src/Form/Type/ChangePasswordType.php

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace App\Form\Type;
13+
14+
use Symfony\Component\Form\AbstractType;
15+
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
16+
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
17+
use Symfony\Component\Form\FormBuilderInterface;
18+
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
19+
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
20+
use Symfony\Component\Validator\Constraints\Length;
21+
use Symfony\Component\Validator\Constraints\NotBlank;
22+
23+
/**
24+
* Defines the custom form field type used to change user's password.
25+
*
26+
* @author Romain Monteil <monteil.romain@gmail.com>
27+
*/
28+
class ChangePasswordType extends AbstractType
29+
{
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function buildForm(FormBuilderInterface $builder, array $options): void
34+
{
35+
$builder
36+
->add('currentPassword', PasswordType::class, [
37+
'constraints' => [
38+
new UserPassword(),
39+
],
40+
'label' => 'label.current_password',
41+
'attr' => [
42+
'autocomplete' => 'off',
43+
],
44+
])
45+
->add('newPassword', RepeatedType::class, [
46+
'type' => PasswordType::class,
47+
'constraints' => [
48+
new NotBlank(),
49+
new Length([
50+
'min' => 5,
51+
'max' => BCryptPasswordEncoder::MAX_PASSWORD_LENGTH,
52+
]),
53+
],
54+
'first_options' => [
55+
'label' => 'label.new_password',
56+
],
57+
'second_options' => [
58+
'label' => 'label.new_password_confirm',
59+
],
60+
])
61+
;
62+
}
63+
}

src/Form/UserType.php

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace App\Form;
13+
14+
use App\Entity\User;
15+
use Symfony\Component\Form\AbstractType;
16+
use Symfony\Component\Form\Extension\Core\Type\EmailType;
17+
use Symfony\Component\Form\Extension\Core\Type\TextType;
18+
use Symfony\Component\Form\FormBuilderInterface;
19+
use Symfony\Component\OptionsResolver\OptionsResolver;
20+
21+
/**
22+
* Defines the form used to edit an user.
23+
*
24+
* @author Romain Monteil <monteil.romain@gmail.com>
25+
*/
26+
class UserType extends AbstractType
27+
{
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function buildForm(FormBuilderInterface $builder, array $options): void
32+
{
33+
// For the full reference of options defined by each form field type
34+
// see https://symfony.com/doc/current/reference/forms/types.html
35+
36+
// By default, form fields include the 'required' attribute, which enables
37+
// the client-side form validation. This means that you can't test the
38+
// server-side validation errors from the browser. To temporarily disable
39+
// this validation, set the 'required' attribute to 'false':
40+
// $builder->add('title', null, ['required' => false, ...]);
41+
42+
$builder
43+
->add('username', TextType::class, [
44+
'label' => 'label.username',
45+
'disabled' => true,
46+
])
47+
->add('fullName', TextType::class, [
48+
'label' => 'label.fullname',
49+
])
50+
->add('email', EmailType::class, [
51+
'label' => 'label.email',
52+
])
53+
;
54+
}
55+
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public function configureOptions(OptionsResolver $resolver): void
60+
{
61+
$resolver->setDefaults([
62+
'data_class' => User::class,
63+
]);
64+
}
65+
}

templates/base.html.twig

+18-3
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,25 @@
6161
</li>
6262

6363
{% if app.user %}
64-
<li>
65-
<a href="{{ path('security_logout') }}">
66-
<i class="fa fa-sign-out" aria-hidden="true"></i> {{ 'menu.logout'|trans }}
64+
<li class="dropdown">
65+
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" id="user">
66+
<i class="fa fa-user" aria-hidden="true"></i>
67+
<span class="caret"></span>
68+
<span class="sr-only">{{ app.user.fullname }}</span>
6769
</a>
70+
<ul class="dropdown-menu user" role="menu" aria-labelledby="user">
71+
<li>
72+
<a href="{{ path('user_edit') }}">
73+
<i class="fa fa-edit" aria-hidden="true"></i> {{ 'menu.user'|trans }}
74+
</a>
75+
</li>
76+
<li class="divider"></li>
77+
<li>
78+
<a href="{{ path('security_logout') }}">
79+
<i class="fa fa-sign-out" aria-hidden="true"></i> {{ 'menu.logout'|trans }}
80+
</a>
81+
</li>
82+
</ul>
6883
</li>
6984
{% endif %}
7085

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{% extends 'base.html.twig' %}
2+
3+
{% block body_id 'user_password' %}
4+
5+
{% block main %}
6+
<h1>{{ 'title.change_password'|trans }}</h1>
7+
8+
<div class="alert alert-info" role="alert">{{ 'info.change_password'|trans }}</div>
9+
10+
{{ form_start(form) }}
11+
{{ form_widget(form) }}
12+
13+
<button type="submit" class="btn btn-primary">
14+
<i class="fa fa-save" aria-hidden="true"></i> {{ 'action.save'|trans }}
15+
</button>
16+
{{ form_end(form) }}
17+
{% endblock %}
18+
19+
{% block sidebar %}
20+
<div class="section">
21+
<a href="{{ path('user_edit') }}" class="btn btn-lg btn-block btn-danger">
22+
<i class="fa fa-edit" aria-hidden="true"></i> {{ 'action.edit_user'|trans }}
23+
</a>
24+
</div>
25+
26+
{{ parent() }}
27+
28+
{{ show_source_code(_self) }}
29+
{% endblock %}

templates/user/edit.html.twig

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{% extends 'base.html.twig' %}
2+
3+
{% block body_id 'user_edit' %}
4+
5+
{% block main %}
6+
<h1>{{ 'title.edit_user'|trans }}</h1>
7+
8+
{{ form_start(form) }}
9+
{{ form_widget(form) }}
10+
11+
<button type="submit" class="btn btn-primary">
12+
<i class="fa fa-save" aria-hidden="true"></i> {{ 'action.save'|trans }}
13+
</button>
14+
{{ form_end(form) }}
15+
{% endblock %}
16+
17+
{% block sidebar %}
18+
<div class="section">
19+
<a href="{{ path('user_change_password') }}" class="btn btn-lg btn-block btn-danger">
20+
<i class="fa fa-lock" aria-hidden="true"></i> {{ 'action.change_password'|trans }}
21+
</a>
22+
</div>
23+
24+
{{ parent() }}
25+
26+
{{ show_source_code(_self) }}
27+
{% endblock %}

0 commit comments

Comments
 (0)