From 3069516eea543cc81b4c28a6b126854b79f151fb Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Fri, 13 Oct 2023 16:06:39 +0600 Subject: [PATCH 01/11] Migrating Annotation route to Attribute --- composer.json | 2 +- src/Controller/HomeController.php | 7 ++-- src/Controller/PasswordChangeController.php | 26 ++++++-------- src/Controller/ProfileController.php | 38 ++++++-------------- src/Controller/RegisterController.php | 30 +++++++--------- src/Controller/ResetPasswordController.php | 39 +++++++++------------ src/Controller/SecurityController.php | 12 +------ src/Controller/UserController.php | 10 +----- 8 files changed, 55 insertions(+), 109 deletions(-) diff --git a/composer.json b/composer.json index 32c3524..495e411 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "type": "project", "license": "proprietary", "require": { - "php": "^7.4|^8.0", + "php": "^8.2", "ext-ctype": "*", "ext-iconv": "*", "composer/package-versions-deprecated": "^1.10", diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index f3d6546..3b86480 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -8,10 +8,9 @@ class HomeController extends AbstractController { - /** - * @Route("/home", name="home") - * @IsGranted("IS_AUTHENTICATED_FULLY") - */ + + #[Route("/home", name: "home")] + #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index() { return $this->render('home/index.html.twig', [ diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index 4e25ad1..90d2cc3 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -14,27 +14,21 @@ class PasswordChangeController extends AbstractController { - /** - * @Route("/password/change", name="password_change",methods="GET|HEAD") - * @IsGranted("IS_AUTHENTICATED_FULLY") - */ + + #[Route("/password/change", name: "password_change", methods: ["GET", "HEAD"])] + #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index() { return $this->render('password_change/index.html.twig'); } - /** - * @Route("/password_save/save", name="password_save",methods="POST|PUT") - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface $passwordEncoder - * @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator - * - * @param \Doctrine\ORM\EntityManagerInterface $entityManager - * - * @return \Symfony\Component\HttpFoundation\Response|\Symfony\Component\HttpFoundation\RedirectResponse - */ - public function change(Request $request, UserPasswordHasherInterface $passwordEncoder, ValidatorInterface $validator, EntityManagerInterface $entityManager) - { + #[Route("/password_save/save", name: "password_save", methods: ["POST", "PUT"])] + public function change( + Request $request, + UserPasswordHasherInterface $passwordEncoder, + ValidatorInterface $validator, + EntityManagerInterface $entityManager + ) { $input = $request->request->all(); $constraint = new Assert\Collection([ 'old_password' => new UserPassword(["message" => "Wrong value for your current password"]), diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index b2774b8..4704ffb 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -18,37 +18,23 @@ class ProfileController extends AbstractController { - - /** - * @Route("/profile", name="profile") - * @param \Symfony\Component\Security\Core\User\UserInterface $user - * @IsGranted("IS_AUTHENTICATED_FULLY") - * - * @return \Symfony\Component\HttpFoundation\Response - * - */ + #[Route("/profile", name: "profile")] + #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index(UserInterface $user) { return $this->render('profile/index.html.twig', [ 'user' => $user, ]); } - - /** - * @Route("/profile/update", name="profile_update") - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Doctrine\ORM\EntityManagerInterface $entityManager - * @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator - * @param \Symfony\Component\Security\Csrf\CsrfTokenManagerInterface $csrfTokenManager - * - * @param \Symfony\Component\String\Slugger\SluggerInterface $slugger - * - * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response - * @IsGranted("IS_AUTHENTICATED_FULLY") - * - */ - public function store(Request $request, EntityManagerInterface $entityManager, ValidatorInterface $validator, CsrfTokenManagerInterface $csrfTokenManager, SluggerInterface $slugger) - { + #[Route("/profile/update", name: "profile_update")] + #[IsGranted("IS_AUTHENTICATED_FULLY")] + public function store( + Request $request, + EntityManagerInterface $entityManager, + ValidatorInterface $validator, + CsrfTokenManagerInterface $csrfTokenManager, + SluggerInterface $slugger + ) { $fileErrors = []; $token = new CsrfToken('authenticate', $request->get('_csrf_token')); if (!$csrfTokenManager->isTokenValid($token)) { @@ -96,7 +82,6 @@ public function store(Request $request, EntityManagerInterface $entityManager, V $errors = $validator->validate($user); if (count($errors) > 0) { - return $this->render('profile/index.html.twig', [ 'user' => $user, 'errors' => $errors, @@ -107,6 +92,5 @@ public function store(Request $request, EntityManagerInterface $entityManager, V $entityManager->flush(); return $this->redirectToRoute('home'); - } } diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php index 24f6ce6..5871825 100644 --- a/src/Controller/RegisterController.php +++ b/src/Controller/RegisterController.php @@ -14,9 +14,7 @@ class RegisterController extends AbstractController { - /** - * @Route("/register", name="register_form",methods="GET|HEAD") - */ + #[Route("/register", name: "register_form", methods: ["GET", "HEAD"])] public function index() { return $this->render('register/index.html.twig', [ @@ -25,20 +23,15 @@ public function index() ]); } - /** - * @Route("/register/store", name="register",methods="POST") - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator - * @param \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface $passwordEncoder - * - * @param \Doctrine\ORM\EntityManagerInterface $entityManager - * - * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher - * - * @return string - */ - public function store(Request $request, ValidatorInterface $validator, UserPasswordHasherInterface $passwordEncoder, EntityManagerInterface $entityManager, EventDispatcherInterface $dispatcher) - { + + #[Route("/register/store", name: "register", methods: "POST")] + public function store( + Request $request, + ValidatorInterface $validator, + UserPasswordHasherInterface $passwordEncoder, + EntityManagerInterface $entityManager, + EventDispatcherInterface $dispatcher + ) { $user = new User(); $user->setName($request->get('name')); $user->setEmail($request->get('email')); @@ -46,7 +39,8 @@ public function store(Request $request, ValidatorInterface $validator, UserPassw $errors = $validator->validate($user); if (count($errors) > 0) { - return $this->render('register/index.html.twig', + return $this->render( + 'register/index.html.twig', [ 'errors' => $errors, 'user' => $user, diff --git a/src/Controller/ResetPasswordController.php b/src/Controller/ResetPasswordController.php index 6b838fa..7798212 100644 --- a/src/Controller/ResetPasswordController.php +++ b/src/Controller/ResetPasswordController.php @@ -32,11 +32,7 @@ public function __construct(ResetPasswordHelperInterface $resetPasswordHelper) $this->resetPasswordHelper = $resetPasswordHelper; } - /** - * Display & process form to request a password reset. - * - * @Route("", name="app_forgot_password_request") - */ + #[Route("password/forget", name: "app_forgot_password_request")] public function request(Request $request, MailerInterface $mailer): Response { $form = $this->createForm(ResetPasswordRequestFormType::class); @@ -54,11 +50,7 @@ public function request(Request $request, MailerInterface $mailer): Response ]); } - /** - * Confirmation page after a user has requested a password reset. - * - * @Route("/check-email", name="app_check_email") - */ + #[Route("/check-email", name: "app_check_email")] public function checkEmail(): Response { // We prevent users from directly accessing this page @@ -71,13 +63,12 @@ public function checkEmail(): Response ]); } - /** - * Validates and process the reset URL that the user clicked in their email. - * - * @Route("/reset/{token}", name="app_reset_password") - */ - public function reset(Request $request, UserPasswordHasherInterface $passwordEncoder, string $token = null): Response - { + #[Route("/reset/{token}", name:"app_reset_password")] + public function reset( + Request $request, + UserPasswordHasherInterface $passwordEncoder, + string $token = null + ): Response { if ($token) { // We store the token in session and remove it from the URL, to avoid the URL being // loaded in a browser and potentially leaking the token to 3rd party JavaScript. @@ -94,10 +85,13 @@ public function reset(Request $request, UserPasswordHasherInterface $passwordEnc try { $user = $this->resetPasswordHelper->validateTokenAndFetchUser($token); } catch (ResetPasswordExceptionInterface $e) { - $this->addFlash('reset_password_error', sprintf( - 'There was a problem validating your reset request - %s', - $e->getReason() - )); + $this->addFlash( + 'reset_password_error', + sprintf( + 'There was a problem validating your reset request - %s', + $e->getReason() + ) + ); return $this->redirectToRoute('app_forgot_password_request'); } @@ -167,8 +161,7 @@ private function processSendingPasswordResetEmail(string $emailFormData, MailerI ->context([ 'resetToken' => $resetToken, 'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime(), - ]) - ; + ]); $mailer->send($email); diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 5d67316..a87d99d 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -9,9 +9,7 @@ class SecurityController extends AbstractController { - /** - * @Route("/login", name="app_login") - */ + #[Route("/login", name:"app_login")] public function login(AuthenticationUtils $authenticationUtils): Response { if ($this->getUser()) { @@ -25,12 +23,4 @@ public function login(AuthenticationUtils $authenticationUtils): Response return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); } - - /** - * @Route("/logout", name="app_logout") - */ - public function logout() - { - throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); - } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index d00b730..adadb9a 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -10,15 +10,7 @@ class UserController extends AbstractController { - /** - * @Route("/user", name="user") - * @IsGranted("ROLE_ADMIN") - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @param \App\Repository\UserRepository $userRepository - * - * @return \Symfony\Component\HttpFoundation\Response - */ + #[Route("/user", name:"user")] public function index(Request $request, UserRepository $userRepository) { return $this->render('user/index.html.twig', [ From 7ce9d35d40bd71bebaa2046c46830bd682497995 Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Fri, 13 Oct 2023 16:14:12 +0600 Subject: [PATCH 02/11] Refactoring Routes --- config/packages/security.yaml | 4 ++-- src/Controller/HomeController.php | 3 +-- .../{SecurityController.php => LoginController.php} | 4 ++-- src/Controller/PasswordChangeController.php | 6 ++++-- src/Controller/ProfileController.php | 4 ++-- src/Controller/RegisterController.php | 4 ++-- src/Controller/ResetPasswordController.php | 6 +++--- 7 files changed, 16 insertions(+), 15 deletions(-) rename src/Controller/{SecurityController.php => LoginController.php} (89%) diff --git a/config/packages/security.yaml b/config/packages/security.yaml index f58181d..8580314 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -42,5 +42,5 @@ security: # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } - # - { path: ^/profile, roles: ROLE_USER } + - { path: ^/admin, roles: ROLE_ADMIN } + - { path: ^/app, roles: ROLE_USER } diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 3b86480..cbfd51b 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -9,8 +9,7 @@ class HomeController extends AbstractController { - #[Route("/home", name: "home")] - #[IsGranted("IS_AUTHENTICATED_FULLY")] + #[Route("/app/home", name: "home")] public function index() { return $this->render('home/index.html.twig', [ diff --git a/src/Controller/SecurityController.php b/src/Controller/LoginController.php similarity index 89% rename from src/Controller/SecurityController.php rename to src/Controller/LoginController.php index a87d99d..8ee359c 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/LoginController.php @@ -7,9 +7,9 @@ use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; -class SecurityController extends AbstractController +class LoginController extends AbstractController { - #[Route("/login", name:"app_login")] + #[Route("/auth/login", name:"app_login")] public function login(AuthenticationUtils $authenticationUtils): Response { if ($this->getUser()) { diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index 90d2cc3..cfb5e31 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -15,14 +15,16 @@ class PasswordChangeController extends AbstractController { - #[Route("/password/change", name: "password_change", methods: ["GET", "HEAD"])] + #[Route("/app/password/change", name: "password_change", methods: ["GET", "HEAD"])] #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index() { return $this->render('password_change/index.html.twig'); } - #[Route("/password_save/save", name: "password_save", methods: ["POST", "PUT"])] + #[Route("/app/password/save", name: "password_save", methods: ["POST", "PUT"])] + #[IsGranted("IS_AUTHENTICATED_FULLY")] + public function change( Request $request, UserPasswordHasherInterface $passwordEncoder, diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 4704ffb..64fbe82 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -18,7 +18,7 @@ class ProfileController extends AbstractController { - #[Route("/profile", name: "profile")] + #[Route("/app/profile", name: "profile")] #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index(UserInterface $user) { @@ -26,7 +26,7 @@ public function index(UserInterface $user) 'user' => $user, ]); } - #[Route("/profile/update", name: "profile_update")] + #[Route("/app/profile/update", name: "profile_update")] #[IsGranted("IS_AUTHENTICATED_FULLY")] public function store( Request $request, diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php index 5871825..3e2478f 100644 --- a/src/Controller/RegisterController.php +++ b/src/Controller/RegisterController.php @@ -14,7 +14,7 @@ class RegisterController extends AbstractController { - #[Route("/register", name: "register_form", methods: ["GET", "HEAD"])] + #[Route("/auth/register", name: "register_form", methods: ["GET", "HEAD"])] public function index() { return $this->render('register/index.html.twig', [ @@ -24,7 +24,7 @@ public function index() } - #[Route("/register/store", name: "register", methods: "POST")] + #[Route("/auth/register/store", name: "register", methods: "POST")] public function store( Request $request, ValidatorInterface $validator, diff --git a/src/Controller/ResetPasswordController.php b/src/Controller/ResetPasswordController.php index 7798212..3830f4a 100644 --- a/src/Controller/ResetPasswordController.php +++ b/src/Controller/ResetPasswordController.php @@ -32,7 +32,7 @@ public function __construct(ResetPasswordHelperInterface $resetPasswordHelper) $this->resetPasswordHelper = $resetPasswordHelper; } - #[Route("password/forget", name: "app_forgot_password_request")] + #[Route("/auth/password/forget", name: "app_forgot_password_request")] public function request(Request $request, MailerInterface $mailer): Response { $form = $this->createForm(ResetPasswordRequestFormType::class); @@ -50,7 +50,7 @@ public function request(Request $request, MailerInterface $mailer): Response ]); } - #[Route("/check-email", name: "app_check_email")] + #[Route("/auth/check-email", name: "app_check_email")] public function checkEmail(): Response { // We prevent users from directly accessing this page @@ -63,7 +63,7 @@ public function checkEmail(): Response ]); } - #[Route("/reset/{token}", name:"app_reset_password")] + #[Route("/auth/reset/{token}", name:"app_reset_password")] public function reset( Request $request, UserPasswordHasherInterface $passwordEncoder, From 6ce84ee23f1cbb131b8173fdf5e2bbb5d4b18e5b Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Sat, 14 Oct 2023 11:11:29 +0600 Subject: [PATCH 03/11] Move DB related to Persistence Folder and Remove Annotation to Attribute in Entity --- config/packages/doctrine.yaml | 2 +- config/packages/reset_password.yaml | 2 +- src/Controller/LoginController.php | 2 + src/Controller/PasswordChangeController.php | 3 - src/Controller/ProfileController.php | 2 - src/Controller/UserController.php | 3 +- src/Entity/ResetPasswordRequest.php | 25 +- src/Entity/User.php | 50 +-- .../DataFixtures/AdminFixture.php | 2 +- .../DataFixtures/AppFixtures.php | 2 +- .../ResetPasswordRequestRepository.php | 2 +- .../Repository/UserRepository.php | 3 +- src/Repository/.gitignore | 0 symfony.lock | 299 ++++++++++++++++++ 14 files changed, 334 insertions(+), 63 deletions(-) rename src/{ => Persistence}/DataFixtures/AdminFixture.php (97%) rename src/{ => Persistence}/DataFixtures/AppFixtures.php (88%) rename src/{ => Persistence}/Repository/ResetPasswordRequestRepository.php (97%) rename src/{ => Persistence}/Repository/UserRepository.php (95%) delete mode 100644 src/Repository/.gitignore create mode 100644 symfony.lock diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 5e80e77..893350e 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -12,7 +12,7 @@ doctrine: mappings: App: is_bundle: false - type: annotation + type: attribute dir: '%kernel.project_dir%/src/Entity' prefix: 'App\Entity' alias: App diff --git a/config/packages/reset_password.yaml b/config/packages/reset_password.yaml index 796ff0c..037412d 100644 --- a/config/packages/reset_password.yaml +++ b/config/packages/reset_password.yaml @@ -1,2 +1,2 @@ symfonycasts_reset_password: - request_password_repository: App\Repository\ResetPasswordRequestRepository + request_password_repository: App\Persistence\Repository\ResetPasswordRequestRepository diff --git a/src/Controller/LoginController.php b/src/Controller/LoginController.php index 8ee359c..23b5361 100644 --- a/src/Controller/LoginController.php +++ b/src/Controller/LoginController.php @@ -23,4 +23,6 @@ public function login(AuthenticationUtils $authenticationUtils): Response return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); } + #[Route("/logout", name:"app_logout")] + public function logout(){} } diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index cfb5e31..acc9d29 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -16,15 +16,12 @@ class PasswordChangeController extends AbstractController { #[Route("/app/password/change", name: "password_change", methods: ["GET", "HEAD"])] - #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index() { return $this->render('password_change/index.html.twig'); } #[Route("/app/password/save", name: "password_save", methods: ["POST", "PUT"])] - #[IsGranted("IS_AUTHENTICATED_FULLY")] - public function change( Request $request, UserPasswordHasherInterface $passwordEncoder, diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 64fbe82..0f300d0 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -19,7 +19,6 @@ class ProfileController extends AbstractController { #[Route("/app/profile", name: "profile")] - #[IsGranted("IS_AUTHENTICATED_FULLY")] public function index(UserInterface $user) { return $this->render('profile/index.html.twig', [ @@ -27,7 +26,6 @@ public function index(UserInterface $user) ]); } #[Route("/app/profile/update", name: "profile_update")] - #[IsGranted("IS_AUTHENTICATED_FULLY")] public function store( Request $request, EntityManagerInterface $entityManager, diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index adadb9a..dc3b60d 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -2,8 +2,7 @@ namespace App\Controller; -use App\Repository\UserRepository; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use App\Persistence\Repository\UserRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; diff --git a/src/Entity/ResetPasswordRequest.php b/src/Entity/ResetPasswordRequest.php index fdafca5..8c25c17 100644 --- a/src/Entity/ResetPasswordRequest.php +++ b/src/Entity/ResetPasswordRequest.php @@ -2,32 +2,27 @@ namespace App\Entity; -use App\Repository\ResetPasswordRequestRepository; +use App\Persistence\Repository\ResetPasswordRequestRepository; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Security\Core\User\UserInterface; use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestInterface; use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordRequestTrait; -/** - * @ORM\Entity(repositoryClass=ResetPasswordRequestRepository::class) - */ +#[ORM\Entity(repositoryClass: ResetPasswordRequestRepository::class)] class ResetPasswordRequest implements ResetPasswordRequestInterface { use ResetPasswordRequestTrait; - /** - * @ORM\Id() - * @ORM\GeneratedValue() - * @ORM\Column(type="integer") - */ + #[ORM\Id()] + #[ORM\GeneratedValue()] + #[ORM\Column(type:"integer")] private $id; - /** - * @ORM\ManyToOne(targetEntity=User::class) - * @ORM\JoinColumn(nullable=false) - */ + #[ORM\ManyToOne(targetEntity:User::class)] + #[ORM\JoinColumn(nullable:false)] private $user; - public function __construct(object $user, \DateTimeInterface $expiresAt, string $selector, string $hashedToken) + public function __construct(User $user, \DateTimeInterface $expiresAt, string $selector, string $hashedToken) { $this->user = $user; $this->initialize($expiresAt, $selector, $hashedToken); @@ -38,7 +33,7 @@ public function getId(): ?int return $this->id; } - public function getUser(): object + public function getUser(): User { return $this->user; } diff --git a/src/Entity/User.php b/src/Entity/User.php index 72e5dba..fa05578 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -2,60 +2,42 @@ namespace App\Entity; -use App\Repository\UserRepository; +use App\Persistence\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; -/** - * @ORM\Entity(repositoryClass=UserRepository::class) - * @UniqueEntity("username") - * @UniqueEntity("email") - */ +#[ORM\Entity(repositoryClass: UserRepository::class)] +#[UniqueEntity("username")] +#[UniqueEntity("email")] class User implements UserInterface, PasswordAuthenticatedUserInterface { - /** - * @ORM\Id() - * @ORM\GeneratedValue() - * @ORM\Column(type="integer") - */ + #[ORM\Id()] + #[ORM\GeneratedValue()] + #[ORM\Column(type: "integer")] private $id; - /** - * @ORM\Column(type="string", length=191) - * @Assert\NotBlank - */ + #[ORM\Column(type:"string", length:191)] private $name; - /** - * @ORM\Column(type="string", length=180, unique=true) - * @Assert\NotBlank - */ + #[ORM\Column(type:"string", length:180, unique:true)] + #[Assert\NotBlank] private $username; - /** - * @ORM\Column(type="string", length=191,unique=true) - * @Assert\Email - * @Assert\NotBlank - */ + #[ORM\Column(type:"string", length:191,unique:true)] + #[Assert\Email] + #[Assert\NotBlank] private $email; - /** - * @ORM\Column(type="string", length=255, nullable=true) - */ + #[ORM\Column(type:"string", length:255, nullable:true)] private $avatar; - /** - * @ORM\Column(type="json") - */ + #[ORM\Column(type:"json")] private $roles = []; - /** - * @var string The hashed password - * @ORM\Column(type="string") - */ + #[ORM\Column(type:"string")] private $password; public function getId(): ?int diff --git a/src/DataFixtures/AdminFixture.php b/src/Persistence/DataFixtures/AdminFixture.php similarity index 97% rename from src/DataFixtures/AdminFixture.php rename to src/Persistence/DataFixtures/AdminFixture.php index 3bd4020..2264aae 100644 --- a/src/DataFixtures/AdminFixture.php +++ b/src/Persistence/DataFixtures/AdminFixture.php @@ -1,6 +1,6 @@ Date: Mon, 16 Oct 2023 08:47:57 +0600 Subject: [PATCH 04/11] Create a New Attribute for Request DTO and fill up --- src/Attribute/FillDto.php | 13 +++++ src/Attribute/FillDtoResolver.php | 43 +++++++++++++++ src/Controller/HomeController.php | 2 +- src/Controller/PasswordChangeController.php | 54 ++++++++----------- src/EventSubscriber/KernelEventSubscriber.php | 23 ++++++++ .../Password/PasswordChangeRequest.php | 20 +++++++ .../Password/PasswordChangeService.php | 24 +++++++++ templates/home/index.html.twig | 7 +-- templates/password_change/index.html.twig | 2 +- 9 files changed, 151 insertions(+), 37 deletions(-) create mode 100644 src/Attribute/FillDto.php create mode 100644 src/Attribute/FillDtoResolver.php create mode 100644 src/EventSubscriber/KernelEventSubscriber.php create mode 100644 src/Service/Password/PasswordChangeRequest.php create mode 100644 src/Service/Password/PasswordChangeService.php diff --git a/src/Attribute/FillDto.php b/src/Attribute/FillDto.php new file mode 100644 index 0000000..2d99c4a --- /dev/null +++ b/src/Attribute/FillDto.php @@ -0,0 +1,13 @@ +getAttributesOfType( + FillDto::class, + ArgumentMetadata::IS_INSTANCEOF + )[0] ?? null; + + if (!$attribute) { + return []; + } + + if ($argument->isVariadic()) { + throw new \LogicException( + sprintf('Mapping variadic argument "$%s" is not supported.', $argument->getName()) + ); + } + $reflectionClass = new \ReflectionClass($argument->getType()); + $dtoClass = $reflectionClass->newInstanceWithoutConstructor(); + $paramBag = $request->getMethod() === 'POST' ? $request->request : $request->query; + foreach ($paramBag->all() as $property => $value) { + $attribute = u($property)->camel(); + if (property_exists($dtoClass, $attribute)) { + $reflectionProperty = $reflectionClass->getProperty($attribute); + $reflectionProperty->setValue($dtoClass, $value); + } + } + + return [$dtoClass]; + } +} \ No newline at end of file diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index cbfd51b..6cff456 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -9,7 +9,7 @@ class HomeController extends AbstractController { - #[Route("/app/home", name: "home")] + #[Route("/", name: "home")] public function index() { return $this->render('home/index.html.twig', [ diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index acc9d29..f22c30a 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -2,55 +2,45 @@ namespace App\Controller; +use App\Service\Password\PasswordChangeRequest; +use App\Service\Password\PasswordChangeService; use Doctrine\ORM\EntityManagerInterface; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Core\Validator\Constraints\UserPassword; use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Exception\ValidationFailedException; use Symfony\Component\Validator\Validator\ValidatorInterface; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; +use App\Attribute\FillDto; class PasswordChangeController extends AbstractController { - - #[Route("/app/password/change", name: "password_change", methods: ["GET", "HEAD"])] - public function index() + public function __construct(private PasswordChangeService $passwordChangeService) { - return $this->render('password_change/index.html.twig'); } - #[Route("/app/password/save", name: "password_save", methods: ["POST", "PUT"])] + #[Route("/app/password/change", name: "password_change", methods: ['GET', 'POST'])] + #[Template('password_change/index.html.twig')] public function change( + #[FillDto] PasswordChangeRequest $passwordChangeRequest, Request $request, - UserPasswordHasherInterface $passwordEncoder, - ValidatorInterface $validator, - EntityManagerInterface $entityManager + ValidatorInterface $validator ) { - $input = $request->request->all(); - $constraint = new Assert\Collection([ - 'old_password' => new UserPassword(["message" => "Wrong value for your current password"]), - 'new_password' => new Assert\Length(['min' => 6]), - 'confirm_new_password' => new Assert\EqualTo([ - 'value' => $request->get('new_password'), - 'message' => 'Confirm password does not match.', - ]), - ]); - $errors = $validator->validate($input, $constraint); - - if (count($errors) > 0) { - return $this->render('password_change/index.html.twig', [ - 'errors' => $errors, - ]); + $errors = []; + if ($request->getMethod() === 'POST') { + $errors = $validator->validate($passwordChangeRequest); + if (count($errors) == 0) { + $this->passwordChangeService->execute($passwordChangeRequest); + $this->addFlash('message', 'Password changed successfully'); + return $this->redirectToRoute('home'); + } } - $user = $this->getUser(); - - $user->setPassword($passwordEncoder->hashPassword($user, $request->get('new_password'))); - $entityManager->persist($user); - $entityManager->flush(); - $this->addFlash('message', 'Password changed successfully'); - return $this->redirectToRoute('home'); + return ['errors' => $errors]; } } diff --git a/src/EventSubscriber/KernelEventSubscriber.php b/src/EventSubscriber/KernelEventSubscriber.php new file mode 100644 index 0000000..9aaa752 --- /dev/null +++ b/src/EventSubscriber/KernelEventSubscriber.php @@ -0,0 +1,23 @@ +getThrowable(); + var_dump($exception->getMessage()); + exit(); + + } + + public static function getSubscribedEvents(): array + { + return [KernelEvents::EXCEPTION => 'onKernalException']; + } +} \ No newline at end of file diff --git a/src/Service/Password/PasswordChangeRequest.php b/src/Service/Password/PasswordChangeRequest.php new file mode 100644 index 0000000..f9c9ba0 --- /dev/null +++ b/src/Service/Password/PasswordChangeRequest.php @@ -0,0 +1,20 @@ + 6])] + #[Assert\NotCompromisedPassword] + #[Assert\PasswordStrength(minScore: Assert\PasswordStrength::STRENGTH_VERY_STRONG)] + public string $newPassword, + public string $confirmNewPassword + ) { + } +} \ No newline at end of file diff --git a/src/Service/Password/PasswordChangeService.php b/src/Service/Password/PasswordChangeService.php new file mode 100644 index 0000000..9cd6787 --- /dev/null +++ b/src/Service/Password/PasswordChangeService.php @@ -0,0 +1,24 @@ +security->getUser(); + $hash = $this->passwordEncoder->hashPassword($user, $request->newPassword); + $this->userRepository->upgradePassword($user, $hash); + } +} \ No newline at end of file diff --git a/templates/home/index.html.twig b/templates/home/index.html.twig index f80445f..7fceb29 100644 --- a/templates/home/index.html.twig +++ b/templates/home/index.html.twig @@ -1,10 +1,11 @@ -{% extends 'app.html.twig' %} +{% extends 'base.html.twig' %} {% block title %}Dashboard{% endblock %} {% block body %} -

Your Dashboard

- +

Your Front Page

+ Login + Register {% endblock %} diff --git a/templates/password_change/index.html.twig b/templates/password_change/index.html.twig index 4eae4bd..a23d1b8 100644 --- a/templates/password_change/index.html.twig +++ b/templates/password_change/index.html.twig @@ -5,7 +5,7 @@ {% endblock %} {% block body %} -
+
Date: Wed, 18 Oct 2023 07:29:34 +0600 Subject: [PATCH 05/11] Unique Value Validator added and Register Refactored --- src/Controller/ProfileController.php | 4 +- src/Controller/RegisterController.php | 52 +++++++++---------- src/Controller/ResetPasswordController.php | 17 +++--- src/Enum/UserRole.php | 9 ++++ src/EventSubscriber/KernelEventSubscriber.php | 23 -------- src/Persistence/Repository/UserRepository.php | 7 +++ .../Password/PasswordChangeRequest.php | 2 +- src/Service/User/CreateUserRequest.php | 31 +++++++++++ src/Service/User/CreateUserService.php | 35 +++++++++++++ src/Validator/UniqueValue.php | 21 ++++++++ src/Validator/UniqueValueValidator.php | 39 ++++++++++++++ templates/app.html.twig | 4 +- 12 files changed, 180 insertions(+), 64 deletions(-) create mode 100644 src/Enum/UserRole.php delete mode 100644 src/EventSubscriber/KernelEventSubscriber.php create mode 100644 src/Service/User/CreateUserRequest.php create mode 100644 src/Service/User/CreateUserService.php create mode 100644 src/Validator/UniqueValue.php create mode 100644 src/Validator/UniqueValueValidator.php diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index 0f300d0..f1ab2e5 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -4,6 +4,7 @@ use Doctrine\ORM\EntityManagerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\Request; @@ -18,7 +19,7 @@ class ProfileController extends AbstractController { - #[Route("/app/profile", name: "profile")] + #[Route("/app/profile", name: "profile_show")] public function index(UserInterface $user) { return $this->render('profile/index.html.twig', [ @@ -26,6 +27,7 @@ public function index(UserInterface $user) ]); } #[Route("/app/profile/update", name: "profile_update")] + #[Template('profile/index.html.twig')] public function store( Request $request, EntityManagerInterface $entityManager, diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php index 3e2478f..e797792 100644 --- a/src/Controller/RegisterController.php +++ b/src/Controller/RegisterController.php @@ -2,18 +2,24 @@ namespace App\Controller; +use App\Attribute\FillDto; use App\Entity\User; use App\Events\UserRegisteredEvent; -use Doctrine\ORM\EntityManagerInterface; +use App\Service\User\CreateUserRequest; +use App\Service\User\CreateUserService; +use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Validator\Validator\ValidatorInterface; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; class RegisterController extends AbstractController { + + public function __construct(private CreateUserService $userService) + { + } + #[Route("/auth/register", name: "register_form", methods: ["GET", "HEAD"])] public function index() { @@ -25,35 +31,25 @@ public function index() #[Route("/auth/register/store", name: "register", methods: "POST")] + #[Template('register/index.html.twig')] public function store( + #[FillDto] CreateUserRequest $createUserRequest, Request $request, ValidatorInterface $validator, - UserPasswordHasherInterface $passwordEncoder, - EntityManagerInterface $entityManager, - EventDispatcherInterface $dispatcher ) { - $user = new User(); - $user->setName($request->get('name')); - $user->setEmail($request->get('email')); - $user->setUsername($request->get('username')); - - $errors = $validator->validate($user); - if (count($errors) > 0) { - return $this->render( - 'register/index.html.twig', - [ - 'errors' => $errors, - 'user' => $user, - ] - ); + if ($request->getMethod() === 'POST') { + $user = new User(); + + $errors = $validator->validate($createUserRequest); + if (count($errors) === 0) { + $user = $this->userService->execute($createUserRequest); + $this->addFlash('message', 'Registration completed successfully'); + return $this->redirectToRoute('app_login'); + } } - - $user->setPassword($passwordEncoder->hashPassword($user, $request->get('password'))); - $entityManager->persist($user); - $entityManager->flush(); - - $dispatcher->dispatch(new UserRegisteredEvent($user), UserRegisteredEvent::NAME); - - return $this->redirectToRoute('app_login'); + return [ + 'errors' => $errors, + 'user' => new User() + ]; } } diff --git a/src/Controller/ResetPasswordController.php b/src/Controller/ResetPasswordController.php index 3830f4a..bbb5769 100644 --- a/src/Controller/ResetPasswordController.php +++ b/src/Controller/ResetPasswordController.php @@ -5,6 +5,7 @@ use App\Entity\User; use App\Form\ChangePasswordFormType; use App\Form\ResetPasswordRequestFormType; +use App\Persistence\Repository\UserRepository; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -25,11 +26,11 @@ class ResetPasswordController extends AbstractController { use ResetPasswordControllerTrait; - private $resetPasswordHelper; - public function __construct(ResetPasswordHelperInterface $resetPasswordHelper) - { - $this->resetPasswordHelper = $resetPasswordHelper; + public function __construct( + private ResetPasswordHelperInterface $resetPasswordHelper, + private UserRepository $userRepository + ) { } #[Route("/auth/password/forget", name: "app_forgot_password_request")] @@ -63,7 +64,7 @@ public function checkEmail(): Response ]); } - #[Route("/auth/reset/{token}", name:"app_reset_password")] + #[Route("/auth/reset/{token}", name: "app_reset_password")] public function reset( Request $request, UserPasswordHasherInterface $passwordEncoder, @@ -111,7 +112,7 @@ public function reset( ); $user->setPassword($encodedPassword); - $this->getDoctrine()->getManager()->flush(); + $this->userRepository->upgradePassword($user, $encodedPassword); // The session is cleaned up after the password has been changed. $this->cleanSessionAfterReset(); @@ -126,9 +127,7 @@ public function reset( private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer): RedirectResponse { - $user = $this->getDoctrine()->getRepository(User::class)->findOneBy([ - 'email' => $emailFormData, - ]); + $user = $this->userRepository->findOneBy(['email' => $emailFormData,]); // Marks that you are allowed to see the app_check_email page. $this->setCanCheckEmailInSession(); diff --git a/src/Enum/UserRole.php b/src/Enum/UserRole.php new file mode 100644 index 0000000..0d72a6b --- /dev/null +++ b/src/Enum/UserRole.php @@ -0,0 +1,9 @@ +getThrowable(); - var_dump($exception->getMessage()); - exit(); - - } - - public static function getSubscribedEvents(): array - { - return [KernelEvents::EXCEPTION => 'onKernalException']; - } -} \ No newline at end of file diff --git a/src/Persistence/Repository/UserRepository.php b/src/Persistence/Repository/UserRepository.php index 9d46158..aad9181 100644 --- a/src/Persistence/Repository/UserRepository.php +++ b/src/Persistence/Repository/UserRepository.php @@ -36,6 +36,13 @@ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $this->_em->flush(); } + public function save(User $user): User + { + $this->_em->persist($user); + $this->_em->flush(); + + return $user; + } /* diff --git a/src/Service/Password/PasswordChangeRequest.php b/src/Service/Password/PasswordChangeRequest.php index f9c9ba0..73849ed 100644 --- a/src/Service/Password/PasswordChangeRequest.php +++ b/src/Service/Password/PasswordChangeRequest.php @@ -12,7 +12,7 @@ public function __construct( public string $oldPassword, #[Assert\Length(['min' => 6])] #[Assert\NotCompromisedPassword] - #[Assert\PasswordStrength(minScore: Assert\PasswordStrength::STRENGTH_VERY_STRONG)] + #[Assert\PasswordStrength(minScore: Assert\PasswordStrength::STRENGTH_MEDIUM)] public string $newPassword, public string $confirmNewPassword ) { diff --git a/src/Service/User/CreateUserRequest.php b/src/Service/User/CreateUserRequest.php new file mode 100644 index 0000000..284b452 --- /dev/null +++ b/src/Service/User/CreateUserRequest.php @@ -0,0 +1,31 @@ + 6])] + #[Assert\NotCompromisedPassword] + #[Assert\PasswordStrength(minScore: Assert\PasswordStrength::STRENGTH_MEDIUM)] + public string $password, + ) { + } +} \ No newline at end of file diff --git a/src/Service/User/CreateUserService.php b/src/Service/User/CreateUserService.php new file mode 100644 index 0000000..c0e48c0 --- /dev/null +++ b/src/Service/User/CreateUserService.php @@ -0,0 +1,35 @@ +setName($request->name); + $user->setUsername($request->username); + $user->setEmail($request->email); + $user->setPassword($this->passwordEncoder->hashPassword($user, $request->password)); + $user->setRoles([UserRole::USER]); + $this->userRepository->save($user); + + $this->dispatcher->dispatch(new UserRegisteredEvent($user), UserRegisteredEvent::NAME); + + return $user; + } +} \ No newline at end of file diff --git a/src/Validator/UniqueValue.php b/src/Validator/UniqueValue.php new file mode 100644 index 0000000..1f5f06c --- /dev/null +++ b/src/Validator/UniqueValue.php @@ -0,0 +1,21 @@ +em->getRepository($constraint->entity); + $result = $repository->count([$constraint->field => $value]); + if ($result === 0) { + return; + } + // the argument must be a string or an object implementing __toString() + $this->context->buildViolation($constraint->message) + ->setParameter('{{ string }}', $value) + ->addViolation(); + // access your configuration options like this: + } +} \ No newline at end of file diff --git a/templates/app.html.twig b/templates/app.html.twig index 91f2f71..d553c18 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -25,9 +25,9 @@
From fb2de1619a98eec1104d6471b4419b278ea24460 Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Wed, 18 Oct 2023 15:50:35 +0600 Subject: [PATCH 06/11] UniqueValidator Updated and Profile Controller Refactored --- config/packages/security.yaml | 3 + src/Attribute/FillDto.php | 4 +- src/Attribute/FillDtoResolver.php | 16 +++- src/Controller/HomeController.php | 6 +- src/Controller/ProfileController.php | 85 ++++++------------- src/Enum/UserRole.php | 4 +- src/Persistence/Repository/UserRepository.php | 16 ++++ src/Service/ImageUploadService.php | 29 +++++++ src/Service/User/UpdateProfileRequest.php | 29 +++++++ src/Service/User/UpdateProfileService.php | 31 +++++++ src/Validator/UniqueValue.php | 2 + src/Validator/UniqueValueValidator.php | 21 ++++- templates/app.html.twig | 2 +- templates/profile/index.html.twig | 2 +- 14 files changed, 176 insertions(+), 74 deletions(-) create mode 100644 src/Service/ImageUploadService.php create mode 100644 src/Service/User/UpdateProfileRequest.php create mode 100644 src/Service/User/UpdateProfileService.php diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 8580314..f9c8db6 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -20,6 +20,9 @@ security: form_login: login_path: app_login check_path: app_login + default_target_path: profile_show + post_only: true + use_referer: true logout: path: app_logout # where to redirect after logout diff --git a/src/Attribute/FillDto.php b/src/Attribute/FillDto.php index 2d99c4a..1d69335 100644 --- a/src/Attribute/FillDto.php +++ b/src/Attribute/FillDto.php @@ -5,9 +5,9 @@ use Attribute; #[Attribute(Attribute::TARGET_PARAMETER)] -class FillDto +readonly class FillDto { - public function __construct(public readonly string $method = 'POST') + public function __construct() { } } \ No newline at end of file diff --git a/src/Attribute/FillDtoResolver.php b/src/Attribute/FillDtoResolver.php index 4f580bc..af1f472 100644 --- a/src/Attribute/FillDtoResolver.php +++ b/src/Attribute/FillDtoResolver.php @@ -22,6 +22,8 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable return []; } + $fillDto = $argument->getAttributesOfType(FillDto::class, ArgumentMetadata::IS_INSTANCEOF)[0]; + if ($argument->isVariadic()) { throw new \LogicException( sprintf('Mapping variadic argument "$%s" is not supported.', $argument->getName()) @@ -29,14 +31,24 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable } $reflectionClass = new \ReflectionClass($argument->getType()); $dtoClass = $reflectionClass->newInstanceWithoutConstructor(); - $paramBag = $request->getMethod() === 'POST' ? $request->request : $request->query; - foreach ($paramBag->all() as $property => $value) { + + $queryParams = $request->query->all(); + $requestParams = $request->request->all(); + foreach (array_merge($queryParams, $requestParams) as $property => $value) { $attribute = u($property)->camel(); if (property_exists($dtoClass, $attribute)) { $reflectionProperty = $reflectionClass->getProperty($attribute); $reflectionProperty->setValue($dtoClass, $value); } } + $files = $request->files->all(); + foreach ($files as $fileKey => $file) { + $attribute = u($fileKey)->camel(); + if (property_exists($dtoClass, $attribute)) { + $reflectionProperty = $reflectionClass->getProperty($attribute); + $reflectionProperty->setValue($dtoClass, $file); + } + } return [$dtoClass]; } diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index 6cff456..c4c42fa 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -5,13 +5,15 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; - +use Symfony\Component\Security\Http\Attribute\CurrentUser; +use Symfony\Component\Security\Core\User\UserInterface; class HomeController extends AbstractController { #[Route("/", name: "home")] - public function index() + public function index(#[CurrentUser] UserInterface $user) { + var_dump($user); return $this->render('home/index.html.twig', [ 'controller_name' => 'HomeController', ]); diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index f1ab2e5..ad35d3a 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -2,6 +2,9 @@ namespace App\Controller; +use App\Attribute\FillDto; +use App\Service\User\UpdateProfileRequest; +use App\Service\User\UpdateProfileService; use Doctrine\ORM\EntityManagerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Bridge\Twig\Attribute\Template; @@ -13,84 +16,44 @@ use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Csrf\CsrfToken; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\String\Slugger\SluggerInterface; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Validator\ValidatorInterface; class ProfileController extends AbstractController { - #[Route("/app/profile", name: "profile_show")] - public function index(UserInterface $user) + public function __construct(private UpdateProfileService $profileService) { - return $this->render('profile/index.html.twig', [ - 'user' => $user, - ]); } - #[Route("/app/profile/update", name: "profile_update")] + + #[Route("/app/profile", name: "profile_show", methods: ['GET', 'POST'])] #[Template('profile/index.html.twig')] - public function store( + public function update( Request $request, - EntityManagerInterface $entityManager, ValidatorInterface $validator, CsrfTokenManagerInterface $csrfTokenManager, - SluggerInterface $slugger + #[CurrentUser] UserInterface $user, + #[FillDto] UpdateProfileRequest $updateProfileRequest, ) { - $fileErrors = []; - $token = new CsrfToken('authenticate', $request->get('_csrf_token')); - if (!$csrfTokenManager->isTokenValid($token)) { - throw new InvalidCsrfTokenException(); - } - - $user = $this->getUser(); - $user->setName($request->get('name')); - $user->setUsername($request->get('username')); - $user->setEmail($request->get('email')); - - - if ($request->files->has('avatar')) { - $avatarFile = $request->files->get('avatar'); - - $constraint = new Assert\Collection([ - 'avatar' => new Assert\Image([ - 'maxSize' => 2048, - ]), - ]); - $fileErrors = $validator->validate(['avatar' => $request->files->has('avatar')]); - if (count($fileErrors) > 0) { - return $this->render('profile/index.html.twig', [ - 'user' => $user, - 'errors' => $fileErrors, - ]); + $errors = []; + if ($request->getMethod() === 'POST') { + $fileErrors = []; + $token = new CsrfToken('authenticate', $request->get('_csrf_token')); + if (!$csrfTokenManager->isTokenValid($token)) { + throw new InvalidCsrfTokenException(); } - $originalFilename = pathinfo($avatarFile->getClientOriginalName(), PATHINFO_FILENAME); - // this is needed to safely include the file name as part of the URL - $safeFilename = $slugger->slug($originalFilename); - $newFilename = $safeFilename . '-' . uniqid() . '.' . $avatarFile->guessExtension(); - // Move the file to the directory where avatar are stored - try { - $avatarFile->move( - 'images', - $newFilename - ); - $user->setAvatar('/images/' . $newFilename); - } catch (FileException $e) { - $this->addFlash('message', $e->getMessage()); - return $this->redirectToRoute('profile'); + $errors = $validator->validate($updateProfileRequest); + if (count($errors) === 0) { + $this->profileService->execute($updateProfileRequest); + $this->addFlash('message', 'Profile successfully updated'); } } - $errors = $validator->validate($user); - if (count($errors) > 0) { - return $this->render('profile/index.html.twig', [ - 'user' => $user, - 'errors' => $errors, - ]); - } - - $entityManager->persist($user); - $entityManager->flush(); - - return $this->redirectToRoute('home'); + return [ + 'errors' => $errors, + 'user' => $user + ]; } } diff --git a/src/Enum/UserRole.php b/src/Enum/UserRole.php index 0d72a6b..d9b8083 100644 --- a/src/Enum/UserRole.php +++ b/src/Enum/UserRole.php @@ -4,6 +4,6 @@ enum UserRole: string { - case USER = 'user'; - case ADMIN = 'admin'; + case USER = 'ROLE_USER'; + case ADMIN = 'ROLE_ADMIN'; } \ No newline at end of file diff --git a/src/Persistence/Repository/UserRepository.php b/src/Persistence/Repository/UserRepository.php index aad9181..b02c4ee 100644 --- a/src/Persistence/Repository/UserRepository.php +++ b/src/Persistence/Repository/UserRepository.php @@ -5,6 +5,7 @@ use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; @@ -44,6 +45,21 @@ public function save(User $user): User return $user; } + public function validate(string $field, mixed $value, Request $request, ?User $user): int + { + $builder = $this->createQueryBuilder('u') + ->select('count(u.id) as total') + ->andWhere("u.$field = :val") + ->setParameter('val', $value); + + if ($user) { + $builder->andWhere('u.id !=:id') + ->setParameter('id', $user->getId()); + } + + return $builder->getQuery()->getSingleScalarResult(); + } + /* public function findOneBySomeField($value): ?User diff --git a/src/Service/ImageUploadService.php b/src/Service/ImageUploadService.php new file mode 100644 index 0000000..361cb6d --- /dev/null +++ b/src/Service/ImageUploadService.php @@ -0,0 +1,29 @@ +getClientOriginalName(), PATHINFO_FILENAME); + // this is needed to safely include the file name as part of the URL + $safeFilename = $this->slugger->slug($originalFilename); + $newFilename = $safeFilename . '-' . uniqid('user_', true) . '.' . $file->guessExtension(); + + // Move the file to the directory where avatar are stored + $file->move( + 'images', + $newFilename + ); + + return '/images/' . $newFilename; + } +} \ No newline at end of file diff --git a/src/Service/User/UpdateProfileRequest.php b/src/Service/User/UpdateProfileRequest.php new file mode 100644 index 0000000..e26d32e --- /dev/null +++ b/src/Service/User/UpdateProfileRequest.php @@ -0,0 +1,29 @@ +security->getUser(); + $user->setName($request->name); + $user->setUsername($request->username); + $user->setEmail($request->email); + if($request->avatar){ + $user->setAvatar($this->imageUploadService->upload($request->avatar)); + } + + return $this->userRepository->save($user); + } +} \ No newline at end of file diff --git a/src/Validator/UniqueValue.php b/src/Validator/UniqueValue.php index 1f5f06c..606a20b 100644 --- a/src/Validator/UniqueValue.php +++ b/src/Validator/UniqueValue.php @@ -12,6 +12,8 @@ class UniqueValue extends Constraint public function __construct( public string $entity, public string $field, + public string $repositoryMethod = '', + public bool $currentUser = false, public ?string $message = '"{{ string }}" This value is already used.', array $groups = null, $payload = null diff --git a/src/Validator/UniqueValueValidator.php b/src/Validator/UniqueValueValidator.php index 0f1114f..1a0e79c 100644 --- a/src/Validator/UniqueValueValidator.php +++ b/src/Validator/UniqueValueValidator.php @@ -3,6 +3,8 @@ namespace App\Validator; use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -10,8 +12,11 @@ class UniqueValueValidator extends ConstraintValidator { - public function __construct(private EntityManagerInterface $em) - { + public function __construct( + private EntityManagerInterface $em, + private RequestStack $requestStack, + private Security $security + ) { } public function validate(mixed $value, Constraint $constraint) @@ -26,7 +31,17 @@ public function validate(mixed $value, Constraint $constraint) throw new UnexpectedValueException($value, 'scalar'); } $repository = $this->em->getRepository($constraint->entity); - $result = $repository->count([$constraint->field => $value]); + if ($method = $constraint->repositoryMethod) { + $user = $constraint->currentUser===true ? $this->security->getUser() : null; + $result = $repository->{$method}( + $constraint->field, + $value, + $this->requestStack->getCurrentRequest(), + $user + ); + } else { + $result = $repository->count([$constraint->field => $value]); + } if ($result === 0) { return; } diff --git a/templates/app.html.twig b/templates/app.html.twig index d553c18..60dcffb 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -48,7 +48,7 @@
{% if app.request.hasPreviousSession %} {% for message in app.flashes('message') %} -

{{ message }}

+

{{ message }}

{% endfor %} {% endif %} diff --git a/templates/profile/index.html.twig b/templates/profile/index.html.twig index 1a68eeb..1c53756 100644 --- a/templates/profile/index.html.twig +++ b/templates/profile/index.html.twig @@ -3,7 +3,7 @@ {% block title %} Your Profile{% endblock %} {% block body %} - +
From 85b17b783002f9dd04d31874811da3f903b46beb Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Thu, 19 Oct 2023 11:26:39 +0600 Subject: [PATCH 07/11] Reset Password Controller Refactoring --- src/Controller/HomeController.php | 1 - src/Controller/ResetPasswordController.php | 61 ++++--------------- .../Password/ResetUserPasswordService.php | 14 +++++ .../SendResetPasswordEmailService.php | 51 ++++++++++++++++ 4 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 src/Service/Password/ResetUserPasswordService.php create mode 100644 src/Service/Password/SendResetPasswordEmailService.php diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index c4c42fa..e050737 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -13,7 +13,6 @@ class HomeController extends AbstractController #[Route("/", name: "home")] public function index(#[CurrentUser] UserInterface $user) { - var_dump($user); return $this->render('home/index.html.twig', [ 'controller_name' => 'HomeController', ]); diff --git a/src/Controller/ResetPasswordController.php b/src/Controller/ResetPasswordController.php index bbb5769..32b3b0c 100644 --- a/src/Controller/ResetPasswordController.php +++ b/src/Controller/ResetPasswordController.php @@ -6,6 +6,7 @@ use App\Form\ChangePasswordFormType; use App\Form\ResetPasswordRequestFormType; use App\Persistence\Repository\UserRepository; +use App\Service\Password\SendResetPasswordEmailService; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -28,22 +29,24 @@ class ResetPasswordController extends AbstractController public function __construct( + private SendResetPasswordEmailService $sendResetPasswordEmailService, private ResetPasswordHelperInterface $resetPasswordHelper, - private UserRepository $userRepository + private UserRepository $userRepository, ) { } #[Route("/auth/password/forget", name: "app_forgot_password_request")] - public function request(Request $request, MailerInterface $mailer): Response + public function request(Request $request): Response { $form = $this->createForm(ResetPasswordRequestFormType::class); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - return $this->processSendingPasswordResetEmail( - $form->get('email')->getData(), - $mailer - ); + $token = $this->sendResetPasswordEmailService->execute($form->get('email')->getData()); + if ($token) { + $this->setTokenObjectInSession($token); + } + return $this->redirectToRoute('app_check_email'); } return $this->render('reset_password/request.html.twig', [ @@ -55,7 +58,7 @@ public function request(Request $request, MailerInterface $mailer): Response public function checkEmail(): Response { // We prevent users from directly accessing this page - if (!$this->canCheckEmail()) { + if (!$this->getTokenObjectFromSession()) { return $this->redirectToRoute('app_forgot_password_request'); } @@ -117,53 +120,11 @@ public function reset( // The session is cleaned up after the password has been changed. $this->cleanSessionAfterReset(); - return $this->redirectToRoute('home'); + return $this->redirectToRoute('app_login'); } return $this->render('reset_password/reset.html.twig', [ 'resetForm' => $form->createView(), ]); } - - private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer): RedirectResponse - { - $user = $this->userRepository->findOneBy(['email' => $emailFormData,]); - - // Marks that you are allowed to see the app_check_email page. - $this->setCanCheckEmailInSession(); - - // Do not reveal whether a user account was found or not. - if (!$user) { - return $this->redirectToRoute('app_check_email'); - } - - try { - $resetToken = $this->resetPasswordHelper->generateResetToken($user); - } catch (ResetPasswordExceptionInterface $e) { - // If you want to tell the user why a reset email was not sent, uncomment - // the lines below and change the redirect to 'app_forgot_password_request'. - // Caution: This may reveal if a user is registered or not. - // - // $this->addFlash('reset_password_error', sprintf( - // 'There was a problem handling your password reset request - %s', - // $e->getReason() - // )); - - return $this->redirectToRoute('app_check_email'); - } - - $email = (new TemplatedEmail()) - ->from(new Address('no-reply@tuhinbepari.com', 'Symfony Auth App')) - ->to($user->getEmail()) - ->subject('Your password reset request') - ->htmlTemplate('reset_password/email.html.twig') - ->context([ - 'resetToken' => $resetToken, - 'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime(), - ]); - - $mailer->send($email); - - return $this->redirectToRoute('app_check_email'); - } } diff --git a/src/Service/Password/ResetUserPasswordService.php b/src/Service/Password/ResetUserPasswordService.php new file mode 100644 index 0000000..3c3f59b --- /dev/null +++ b/src/Service/Password/ResetUserPasswordService.php @@ -0,0 +1,14 @@ +userRepository->findOneBy(['email' => $email,]); + + // Marks that you are allowed to see the app_check_email page. + + // Do not reveal whether a user account was found or not. + if (!$user) { + return false; + + } + + $resetToken = $this->resetPasswordHelper->generateResetToken($user); + $email = (new TemplatedEmail()) + ->from(new Address('no-reply@tuhinbepari.com', 'Symfony Auth App')) + ->to($user->getEmail()) + ->subject('Your password reset request') + ->htmlTemplate('reset_password/email.html.twig') + ->context([ + 'resetToken' => $resetToken, + 'tokenLifetime' => $this->resetPasswordHelper->getTokenLifetime(), + ]); + + $this->mailer->send($email); + + return $resetToken; + + + + } +} \ No newline at end of file From 96b8900807f5efdc382d856596f7a5c5dcf8bdd7 Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Thu, 19 Oct 2023 11:39:52 +0600 Subject: [PATCH 08/11] Refactoring Controllers --- src/Controller/.gitignore | 0 src/Controller/LoginController.php | 2 -- src/Controller/RegisterController.php | 20 +++++--------------- templates/register/index.html.twig | 2 +- 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 src/Controller/.gitignore diff --git a/src/Controller/.gitignore b/src/Controller/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/src/Controller/LoginController.php b/src/Controller/LoginController.php index 23b5361..8ee359c 100644 --- a/src/Controller/LoginController.php +++ b/src/Controller/LoginController.php @@ -23,6 +23,4 @@ public function login(AuthenticationUtils $authenticationUtils): Response return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); } - #[Route("/logout", name:"app_logout")] - public function logout(){} } diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php index e797792..4d823b8 100644 --- a/src/Controller/RegisterController.php +++ b/src/Controller/RegisterController.php @@ -20,26 +20,16 @@ public function __construct(private CreateUserService $userService) { } - #[Route("/auth/register", name: "register_form", methods: ["GET", "HEAD"])] - public function index() - { - return $this->render('register/index.html.twig', [ - 'errors' => [], - 'user' => new User(), - ]); - } - - - #[Route("/auth/register/store", name: "register", methods: "POST")] + #[Route("/auth/register", name: "register", methods: ["POST", "GET"])] #[Template('register/index.html.twig')] public function store( #[FillDto] CreateUserRequest $createUserRequest, Request $request, ValidatorInterface $validator, ) { - if ($request->getMethod() === 'POST') { - $user = new User(); + $user = new User(); + if ($request->getMethod() === 'POST') { $errors = $validator->validate($createUserRequest); if (count($errors) === 0) { $user = $this->userService->execute($createUserRequest); @@ -48,8 +38,8 @@ public function store( } } return [ - 'errors' => $errors, - 'user' => new User() + 'errors' => $errors ?? [], + 'user' => $user ]; } } diff --git a/templates/register/index.html.twig b/templates/register/index.html.twig index def422d..d81fb4a 100644 --- a/templates/register/index.html.twig +++ b/templates/register/index.html.twig @@ -6,7 +6,7 @@

Register

- + From 60d38cb805fbef4d6fab3d118e740288f6f2805b Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Thu, 19 Oct 2023 19:45:04 +0600 Subject: [PATCH 09/11] Working with Translation --- src/Controller/PasswordChangeController.php | 4 +++- templates/home/index.html.twig | 2 +- templates/reset_password/check_email.html.twig | 5 +++-- templates/reset_password/request.html.twig | 9 ++++----- templates/security/login.html.twig | 4 ++-- .../{messages.bn.yaml => messages+intl-icu.bn.yaml} | 0 translations/messages+intl-icu.en.yaml | 13 +++++++++++++ translations/messages.en.yaml | 8 -------- 8 files changed, 26 insertions(+), 19 deletions(-) rename translations/{messages.bn.yaml => messages+intl-icu.bn.yaml} (100%) create mode 100644 translations/messages+intl-icu.en.yaml delete mode 100644 translations/messages.en.yaml diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index f22c30a..f31fc1e 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -18,6 +18,8 @@ use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use App\Attribute\FillDto; +use function Symfony\Component\Translation\t; + class PasswordChangeController extends AbstractController { public function __construct(private PasswordChangeService $passwordChangeService) @@ -36,7 +38,7 @@ public function change( $errors = $validator->validate($passwordChangeRequest); if (count($errors) == 0) { $this->passwordChangeService->execute($passwordChangeRequest); - $this->addFlash('message', 'Password changed successfully'); + $this->addFlash('message',t('password.changed_successfully')); return $this->redirectToRoute('home'); } } diff --git a/templates/home/index.html.twig b/templates/home/index.html.twig index 7fceb29..65fc435 100644 --- a/templates/home/index.html.twig +++ b/templates/home/index.html.twig @@ -6,6 +6,6 @@

Your Front Page

Login - Register + Register {% endblock %} diff --git a/templates/reset_password/check_email.html.twig b/templates/reset_password/check_email.html.twig index e0d0b9d..455391b 100644 --- a/templates/reset_password/check_email.html.twig +++ b/templates/reset_password/check_email.html.twig @@ -4,8 +4,9 @@ {% block body %}
-

An email has been sent that contains a link that you can click to reset your password. - This link will expire in {{ tokenLifetime|date('g') }} hour(s).

+

+ {{ 'password.email_sent'|trans({hour: tokenLifetime|date('g')}) }} +

If you don't receive an email please check your spam folder or try again.

diff --git a/templates/reset_password/request.html.twig b/templates/reset_password/request.html.twig index 0f1e9ca..217759a 100644 --- a/templates/reset_password/request.html.twig +++ b/templates/reset_password/request.html.twig @@ -1,23 +1,22 @@ {% extends 'base.html.twig' %} -{% block title %}Reset your password{% endblock %} +{% block title %}{{ 'Reset your password'|trans }}{% endblock %} {% block body %}
{% for flashError in app.flashes('reset_password_error') %} {% endfor %} -

Reset your password

+

{{'Reset your password'|trans}}

{{ form_start(requestForm) }} {{ form_row(requestForm.email) }}

- Enter your email address and we we will send you a - link to reset your password. + {{ 'password.enter_email'|trans }}

- + {{ form_end(requestForm) }}
diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index 315340d..14dd7dc 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -11,7 +11,7 @@ {% if app.user %}
- You are logged in as {{ app.user.username }}, Logout + You are logged in as {{ app.user.username }}, Logout
{% endif %} @@ -47,7 +47,7 @@

Does not have an account? Register + href="{{ path('register') }}">Register Now

Forget your password? Click diff --git a/translations/messages.bn.yaml b/translations/messages+intl-icu.bn.yaml similarity index 100% rename from translations/messages.bn.yaml rename to translations/messages+intl-icu.bn.yaml diff --git a/translations/messages+intl-icu.en.yaml b/translations/messages+intl-icu.en.yaml new file mode 100644 index 0000000..14585c0 --- /dev/null +++ b/translations/messages+intl-icu.en.yaml @@ -0,0 +1,13 @@ +password: + change: Change Your Password + old: Old Password + current: Current Password + new: New Password + type_new: Type your new password + confirm: Confirm your new password + length: Password must be 6 digit long + changed_successfully: Password changed successfully + enter_email: Enter your email address and we we will send you a link to reset your password. + email_sent: An email has been sent that contains a link that you can click to reset your password. This link will expire in {hour} hour(s) + send_email_btn: Send password reset email + diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml deleted file mode 100644 index f2c4364..0000000 --- a/translations/messages.en.yaml +++ /dev/null @@ -1,8 +0,0 @@ -password: - change: Change Your Password - old: Old Password - currrent: Current Password - new: New Password - type_new: Type your new password - confirm: Confirm your new password - length: Password must be 6 digit long From c6b9013f735794c81f44dac202e2ff78d2b4fac7 Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Fri, 20 Oct 2023 06:27:23 +0600 Subject: [PATCH 10/11] Translation completed --- src/Controller/PasswordChangeController.php | 10 +++++++--- src/Controller/ProfileController.php | 5 +++-- src/Controller/RegisterController.php | 5 +++-- src/Controller/ResetPasswordController.php | 11 ++++++----- translations/messages+intl-icu.en.yaml | 6 ++++++ 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/Controller/PasswordChangeController.php b/src/Controller/PasswordChangeController.php index f31fc1e..5e6a403 100644 --- a/src/Controller/PasswordChangeController.php +++ b/src/Controller/PasswordChangeController.php @@ -18,12 +18,16 @@ use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use App\Attribute\FillDto; +use Symfony\Contracts\Translation\TranslatorInterface; + use function Symfony\Component\Translation\t; class PasswordChangeController extends AbstractController { - public function __construct(private PasswordChangeService $passwordChangeService) - { + public function __construct( + private PasswordChangeService $passwordChangeService, + private TranslatorInterface $translator + ) { } #[Route("/app/password/change", name: "password_change", methods: ['GET', 'POST'])] @@ -38,7 +42,7 @@ public function change( $errors = $validator->validate($passwordChangeRequest); if (count($errors) == 0) { $this->passwordChangeService->execute($passwordChangeRequest); - $this->addFlash('message',t('password.changed_successfully')); + $this->addFlash('message', $this->translator->trans('password.changed_successfully')); return $this->redirectToRoute('home'); } } diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index ad35d3a..e3292cd 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -20,10 +20,11 @@ use Symfony\Component\String\Slugger\SluggerInterface; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class ProfileController extends AbstractController { - public function __construct(private UpdateProfileService $profileService) + public function __construct(private UpdateProfileService $profileService, private TranslatorInterface $translator) { } @@ -47,7 +48,7 @@ public function update( $errors = $validator->validate($updateProfileRequest); if (count($errors) === 0) { $this->profileService->execute($updateProfileRequest); - $this->addFlash('message', 'Profile successfully updated'); + $this->addFlash('message', $this->translator->trans('user.profile_updated')); } } diff --git a/src/Controller/RegisterController.php b/src/Controller/RegisterController.php index 4d823b8..bcc8160 100644 --- a/src/Controller/RegisterController.php +++ b/src/Controller/RegisterController.php @@ -12,11 +12,12 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class RegisterController extends AbstractController { - public function __construct(private CreateUserService $userService) + public function __construct(private CreateUserService $userService, private TranslatorInterface $translator) { } @@ -33,7 +34,7 @@ public function store( $errors = $validator->validate($createUserRequest); if (count($errors) === 0) { $user = $this->userService->execute($createUserRequest); - $this->addFlash('message', 'Registration completed successfully'); + $this->addFlash('message', $this->translator->trans('user.registration_successful')); return $this->redirectToRoute('app_login'); } } diff --git a/src/Controller/ResetPasswordController.php b/src/Controller/ResetPasswordController.php index 32b3b0c..a99ef26 100644 --- a/src/Controller/ResetPasswordController.php +++ b/src/Controller/ResetPasswordController.php @@ -16,10 +16,13 @@ use Symfony\Component\Mime\Address; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait; use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface; use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface; +use function Symfony\Component\Translation\t; + /** * @Route("/reset-password") */ @@ -32,6 +35,7 @@ public function __construct( private SendResetPasswordEmailService $sendResetPasswordEmailService, private ResetPasswordHelperInterface $resetPasswordHelper, private UserRepository $userRepository, + private TranslatorInterface $translator ) { } @@ -83,7 +87,7 @@ public function reset( $token = $this->getTokenFromSession(); if (null === $token) { - throw $this->createNotFoundException('No reset password token found in the URL or in the session.'); + throw $this->createNotFoundException($this->translator->trans('password.no_token_found')); } try { @@ -91,10 +95,7 @@ public function reset( } catch (ResetPasswordExceptionInterface $e) { $this->addFlash( 'reset_password_error', - sprintf( - 'There was a problem validating your reset request - %s', - $e->getReason() - ) + $this->translator->trans('password.reset_password_error', ['reason' => $e->getReason()]) ); return $this->redirectToRoute('app_forgot_password_request'); diff --git a/translations/messages+intl-icu.en.yaml b/translations/messages+intl-icu.en.yaml index 14585c0..d72cc5f 100644 --- a/translations/messages+intl-icu.en.yaml +++ b/translations/messages+intl-icu.en.yaml @@ -10,4 +10,10 @@ password: enter_email: Enter your email address and we we will send you a link to reset your password. email_sent: An email has been sent that contains a link that you can click to reset your password. This link will expire in {hour} hour(s) send_email_btn: Send password reset email + reset_password_error: There was a problem validating your reset request - {reason} + no_token_found: No reset password token found in the URL or in the session. + +user: + profile_updated: Profile successfully updated + registration_successful: Registration completed successfully From 7c9fb06aa88877fc96d4e21f19190cf73e0d988c Mon Sep 17 00:00:00 2001 From: Tuhin Bepari Date: Fri, 20 Oct 2023 06:51:59 +0600 Subject: [PATCH 11/11] working with Translation to Bangla --- translations/messages+intl-icu.bn.yaml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/translations/messages+intl-icu.bn.yaml b/translations/messages+intl-icu.bn.yaml index 88c2c65..4338668 100644 --- a/translations/messages+intl-icu.bn.yaml +++ b/translations/messages+intl-icu.bn.yaml @@ -1,2 +1,21 @@ password: - change: আপনার পাসওয়ার্ড পরিবর্তন করুন \ No newline at end of file + change: আপনার পাসওয়ার্ড পরিবর্তন করুন + old: পুরানো পাসওয়ার্ড + current: বর্তমান পাসওয়ার্ড + new: নতুন পাসওয়ার্ড + type_new: আপনার নতুন পাসওয়ার্ড টাইপ করুন + confirm: আপনার নতুন পাসওয়ার্ড নিশ্চিত করুন + length: পাসওয়ার্ড অবশ্যই ৬ সংখ্যার লম্বা হতে হবে + changed_successfully: পাসওয়ার্ড সফলভাবে পরিবর্তিত হয়েছে + enter_email: আপনার ইমেল ঠিকানা লিখুন এবং আমরা আপনাকে আপনার পাসওয়ার্ড রিসেট করার জন্য একটি লিঙ্ক পাঠাব। + email_sent: একটি ইমেল পাঠানো হয়েছে যাতে একটি লিঙ্ক রয়েছে যা আপনি আপনার পাসওয়ার্ড রিসেট করতে ক্লিক করতে পারেন৷ এই লিঙ্কটির মেয়াদ {hour} ঘণ্টার মধ্যে শেষ হবে + send_email_btn: পাসওয়ার্ড রিসেট ইমেল পাঠান + reset_password_error: আপনার রিসেট অনুরোধ যাচাই করতে একটি সমস্যা হয়েছে - {reason} + no_token_found: ইউআরএলে বা সেশনে কোনো রিসেট পাসওয়ার্ড টোকেন পাওয়া যায়নি। + +user: + profile_updated: প্রোফাইল সফলভাবে আপডেট হয়েছে + registration_successful: রেজিস্ট্রেশন সফলভাবে সম্পন্ন হয়েছে + +'Reset your password': আপনার পাসওয়ার্ড পুনরায় সেট করুন +'Change Password': পাসওয়ার্ড পরিবর্তন করুন